forked from Github-Mirrors/canaille
Add groups field on user profile (WIP)
This commit is contained in:
parent
22b702c065
commit
75df94216a
8 changed files with 69 additions and 8 deletions
|
@ -23,7 +23,7 @@ from flask_babel import Babel
|
||||||
from .flaskutils import current_user
|
from .flaskutils import current_user
|
||||||
from .ldaputils import LDAPObject
|
from .ldaputils import LDAPObject
|
||||||
from .oauth2utils import config_oauth
|
from .oauth2utils import config_oauth
|
||||||
from .models import User, Token, AuthorizationCode, Client, Consent
|
from .models import User, Token, AuthorizationCode, Client, Consent, Group
|
||||||
|
|
||||||
try: # pragma: no cover
|
try: # pragma: no cover
|
||||||
import sentry_sdk
|
import sentry_sdk
|
||||||
|
@ -123,10 +123,12 @@ def setup_app(app):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
LDAPObject.root_dn = app.config["LDAP"]["ROOT_DN"]
|
LDAPObject.root_dn = app.config["LDAP"]["ROOT_DN"]
|
||||||
base = app.config["LDAP"]["USER_BASE"]
|
user_base = app.config["LDAP"]["USER_BASE"]
|
||||||
if base.endswith(app.config["LDAP"]["ROOT_DN"]):
|
if user_base.endswith(app.config["LDAP"]["ROOT_DN"]):
|
||||||
base = base[: -len(app.config["LDAP"]["ROOT_DN"]) - 1]
|
user_base = user_base[: -len(app.config["LDAP"]["ROOT_DN"]) - 1]
|
||||||
User.base = base
|
User.base = user_base
|
||||||
|
group_base = app.config["LDAP"]["GROUP_BASE"]
|
||||||
|
Group.base = group_base
|
||||||
|
|
||||||
app.url_map.strict_slashes = False
|
app.url_map.strict_slashes = False
|
||||||
|
|
||||||
|
|
|
@ -272,6 +272,8 @@ def profile_edit(user, username):
|
||||||
user[attribute.name] = data
|
user[attribute.name] = data
|
||||||
else:
|
else:
|
||||||
user[attribute.name] = [data]
|
user[attribute.name] = [data]
|
||||||
|
elif attribute.name == "groups":
|
||||||
|
user.groups = attribute.data
|
||||||
|
|
||||||
if (
|
if (
|
||||||
not form["password1"].data or user.set_password(form["password1"].data)
|
not form["password1"].data or user.set_password(form["password1"].data)
|
||||||
|
|
|
@ -127,6 +127,7 @@ PROFILE_FORM_FIELDS = dict(
|
||||||
"placeholder": _("1234"),
|
"placeholder": _("1234"),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
groups=wtforms.SelectMultipleField(_("Groups"), choices=["foo", "bar"])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ class User(LDAPObject):
|
||||||
id = "cn"
|
id = "cn"
|
||||||
admin = False
|
admin = False
|
||||||
moderator = False
|
moderator = False
|
||||||
|
_groups = []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get(cls, login=None, dn=None, filter=None, conn=None):
|
def get(cls, login=None, dn=None, filter=None, conn=None):
|
||||||
|
@ -114,6 +115,27 @@ class User(LDAPObject):
|
||||||
return self.cn[0]
|
return self.cn[0]
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def groups(self):
|
||||||
|
return self._groups
|
||||||
|
|
||||||
|
@groups.setter
|
||||||
|
def groups(self, value):
|
||||||
|
self._groups = value
|
||||||
|
|
||||||
|
|
||||||
|
class Group(LDAPObject):
|
||||||
|
id = "cn"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def available_groups(cls, conn=None):
|
||||||
|
conn = conn or cls.ldap()
|
||||||
|
groups = cls.filter(objectClass=current_app.config["LDAP"].get("GROUP_CLASS"), conn=conn)
|
||||||
|
Group.attr_type_by_name(conn=conn)
|
||||||
|
attribute = current_app.config["LDAP"].get("GROUP_NAME_ATTRIBUTE")
|
||||||
|
return [(group[attribute][0], group.dn) for group in groups]
|
||||||
|
|
||||||
|
|
||||||
class Client(LDAPObject, ClientMixin):
|
class Client(LDAPObject, ClientMixin):
|
||||||
object_class = ["oauthClient"]
|
object_class = ["oauthClient"]
|
||||||
base = "ou=clients,ou=oauth"
|
base = "ou=clients,ou=oauth"
|
||||||
|
|
|
@ -82,6 +82,9 @@
|
||||||
{% if "employeeNumber" in form %}
|
{% if "employeeNumber" in form %}
|
||||||
{{ sui.render_field(form.employeeNumber) }}
|
{{ sui.render_field(form.employeeNumber) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if "groups" in form %}
|
||||||
|
{{ sui.render_field(form.groups) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<h4 class="ui dividing header">{% trans %}Account information{% endtrans %}</h4>
|
<h4 class="ui dividing header">{% trans %}Account information{% endtrans %}</h4>
|
||||||
{% if "uid" in form %}
|
{% if "uid" in form %}
|
||||||
|
|
|
@ -9,7 +9,7 @@ from cryptography.hazmat.backends import default_backend as crypto_default_backe
|
||||||
from flask_webtest import TestApp
|
from flask_webtest import TestApp
|
||||||
from werkzeug.security import gen_salt
|
from werkzeug.security import gen_salt
|
||||||
from canaille import create_app
|
from canaille import create_app
|
||||||
from canaille.models import User, Client, Token, AuthorizationCode, Consent
|
from canaille.models import User, Client, Token, AuthorizationCode, Consent, Group
|
||||||
from canaille.ldaputils import LDAPObject
|
from canaille.ldaputils import LDAPObject
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,6 +93,10 @@ def slapd_server():
|
||||||
"dn: ou=users," + slapd.suffix,
|
"dn: ou=users," + slapd.suffix,
|
||||||
"objectClass: organizationalUnit",
|
"objectClass: organizationalUnit",
|
||||||
"ou: users",
|
"ou: users",
|
||||||
|
"",
|
||||||
|
"dn: ou=groups," + slapd.suffix,
|
||||||
|
"objectClass: organizationalUnit",
|
||||||
|
"ou: groups",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
+ "\n"
|
+ "\n"
|
||||||
|
@ -100,6 +104,7 @@ def slapd_server():
|
||||||
|
|
||||||
LDAPObject.root_dn = slapd.suffix
|
LDAPObject.root_dn = slapd.suffix
|
||||||
User.base = "ou=users"
|
User.base = "ou=users"
|
||||||
|
Group.base = "ou=groups"
|
||||||
|
|
||||||
yield slapd
|
yield slapd
|
||||||
finally:
|
finally:
|
||||||
|
@ -143,7 +148,11 @@ def app(slapd_server, keypair_path):
|
||||||
"userPassword",
|
"userPassword",
|
||||||
"telephoneNumber",
|
"telephoneNumber",
|
||||||
"employeeNumber",
|
"employeeNumber",
|
||||||
|
"groups",
|
||||||
],
|
],
|
||||||
|
"GROUP_BASE": "ou=groups",
|
||||||
|
"GROUP_CLASS": "groupOfNames",
|
||||||
|
"GROUP_NAME_ATTRIBUTE": "cn",
|
||||||
},
|
},
|
||||||
"JWT": {
|
"JWT": {
|
||||||
"PUBLIC_KEY": public_key_path,
|
"PUBLIC_KEY": public_key_path,
|
||||||
|
@ -338,3 +347,14 @@ def cleanups(slapd_connection):
|
||||||
yield
|
yield
|
||||||
for consent in Consent.filter(conn=slapd_connection):
|
for consent in Consent.filter(conn=slapd_connection):
|
||||||
consent.delete(conn=slapd_connection)
|
consent.delete(conn=slapd_connection)
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def foo_group(user, slapd_connection):
|
||||||
|
Group.ocs_by_name(slapd_connection)
|
||||||
|
g = Group(
|
||||||
|
objectClass = ["groupOfNames"],
|
||||||
|
member=[user.dn],
|
||||||
|
cn="foo",
|
||||||
|
)
|
||||||
|
g.save(slapd_connection)
|
||||||
|
return g
|
9
tests/test_groups.py
Normal file
9
tests/test_groups.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from canaille.models import Group
|
||||||
|
|
||||||
|
def test_no_group(app, slapd_connection):
|
||||||
|
with app.app_context():
|
||||||
|
assert Group.available_groups(conn=slapd_connection) == []
|
||||||
|
|
||||||
|
def test_foo_group(app, slapd_connection, foo_group):
|
||||||
|
with app.app_context():
|
||||||
|
assert Group.available_groups(conn=slapd_connection) == [("foo", foo_group.dn)]
|
|
@ -2,8 +2,9 @@ import mock
|
||||||
from canaille.models import User
|
from canaille.models import User
|
||||||
|
|
||||||
|
|
||||||
def test_profile(testclient, slapd_connection, logged_user):
|
def test_profile(testclient, slapd_connection, logged_user, foo_group):
|
||||||
res = testclient.get("/profile/user", status=200)
|
res = testclient.get("/profile/user", status=200)
|
||||||
|
assert res.form["groups"].options == [('foo', False, 'foo')]
|
||||||
|
|
||||||
res.form["uid"] = "user"
|
res.form["uid"] = "user"
|
||||||
res.form["givenName"] = "given_name"
|
res.form["givenName"] = "given_name"
|
||||||
|
@ -11,7 +12,7 @@ def test_profile(testclient, slapd_connection, logged_user):
|
||||||
res.form["mail"] = "email@mydomain.tld"
|
res.form["mail"] = "email@mydomain.tld"
|
||||||
res.form["telephoneNumber"] = "555-666-777"
|
res.form["telephoneNumber"] = "555-666-777"
|
||||||
res.form["employeeNumber"] = 666
|
res.form["employeeNumber"] = 666
|
||||||
|
res.form["groups"] = ["foo", "bar"]
|
||||||
res = res.form.submit(name="action", value="edit", status=200)
|
res = res.form.submit(name="action", value="edit", status=200)
|
||||||
assert "Profile updated successfuly." in res, str(res)
|
assert "Profile updated successfuly." in res, str(res)
|
||||||
|
|
||||||
|
@ -23,6 +24,7 @@ def test_profile(testclient, slapd_connection, logged_user):
|
||||||
assert ["email@mydomain.tld"] == logged_user.mail
|
assert ["email@mydomain.tld"] == logged_user.mail
|
||||||
assert ["555-666-777"] == logged_user.telephoneNumber
|
assert ["555-666-777"] == logged_user.telephoneNumber
|
||||||
assert "666" == logged_user.employeeNumber
|
assert "666" == logged_user.employeeNumber
|
||||||
|
assert ["foo", "bar"] == logged_user.groups
|
||||||
|
|
||||||
with testclient.app.app_context():
|
with testclient.app.app_context():
|
||||||
assert logged_user.check_password("correct horse battery staple")
|
assert logged_user.check_password("correct horse battery staple")
|
||||||
|
|
Loading…
Reference in a new issue