diff --git a/canaille/backends/ldap/ldapobject.py b/canaille/backends/ldap/ldapobject.py index f5c8e08a..9dbdada8 100644 --- a/canaille/backends/ldap/ldapobject.py +++ b/canaille/backends/ldap/ldapobject.py @@ -292,9 +292,10 @@ class LDAPObject(metaclass=LDAPObjectMetaclass): return None @classmethod - def query(cls, base=None, filter=None, conn=None, **kwargs): + def query(cls, id=None, filter=None, conn=None, **kwargs): conn = conn or cls.ldap_connection() + base = id or kwargs.get("id") if base is None: base = f"{cls.base},{cls.root_dn}" elif "=" not in base: @@ -331,7 +332,12 @@ class LDAPObject(metaclass=LDAPObjectMetaclass): ldapfilter = f"(&{class_filter}{arg_filter}{filter})" base = base or f"{cls.base},{cls.root_dn}" - result = conn.search_s(base, ldap.SCOPE_SUBTREE, ldapfilter or None, ["+", "*"]) + try: + result = conn.search_s( + base, ldap.SCOPE_SUBTREE, ldapfilter or None, ["+", "*"] + ) + except ldap.NO_SUCH_OBJECT: + result = [] return LDAPObjectQuery(cls, result) @classmethod diff --git a/tests/backends/test_models.py b/tests/backends/test_models.py new file mode 100644 index 00000000..32149fe7 --- /dev/null +++ b/tests/backends/test_models.py @@ -0,0 +1,195 @@ +from canaille.core.models import Group +from canaille.core.models import User + + +def test_model_comparison(testclient, slapd_connection): + foo1 = User( + user_name="foo", + family_name="foo", + formatted_name="foo", + ) + foo2 = User( + user_name="foo", + family_name="foo", + formatted_name="foo", + ) + bar = User( + user_name="bar", + family_name="bar", + formatted_name="foo", + ) + + assert foo1 == foo2 + assert foo1 != bar + + +def test_model_lifecycle(testclient, slapd_connection, slapd_server): + user = User( + user_name="user_name", + family_name="family_name", + formatted_name="formatted_name", + ) + + assert not User.query() + assert not User.query(id=user.id) + assert not User.query(id="invalid") + assert not User.get(id=user.id) + + user.save() + + assert User.query() == [user] + assert User.query(id=user.id) == [user] + assert not User.query(id="invalid") + assert User.get(id=user.id) == user + + user.family_name = "new_family_name" + + assert user.family_name == ["new_family_name"] + + user.reload() + + assert user.family_name == ["family_name"] + + user.delete() + + assert not User.query(id=user.id) + assert not User.get(id=user.id) + + +def test_model_attribute_edition(testclient, slapd_connection): + user = User( + user_name="user_name", + family_name="family_name", + formatted_name="formatted_name", + display_name="display_name", + email=["email1@user.com", "email2@user.com"], + ) + user.save() + + assert user.user_name == ["user_name"] + assert user.family_name == ["family_name"] + assert user.email == ["email1@user.com", "email2@user.com"] + + user = User.get(id=user.id) + assert user.user_name == ["user_name"] + assert user.family_name == ["family_name"] + assert user.email == ["email1@user.com", "email2@user.com"] + + user.family_name = ["new_family_name"] + user.email = ["email1@user.com"] + user.save() + + assert user.family_name == ["new_family_name"] + assert user.email == ["email1@user.com"] + + user = User.get(id=user.id) + assert user.family_name == ["new_family_name"] + assert user.email == ["email1@user.com"] + + user.display_name = [""] + user.save() + + assert not user.display_name + + user.delete() + + +def test_model_indexation(testclient, slapd_connection): + user = User( + user_name="user_name", + family_name="family_name", + formatted_name="formatted_name", + email=["email1@user.com", "email2@user.com"], + ) + user.save() + + assert User.get(family_name="family_name") == user + assert not User.get(family_name="new_family_name") + assert User.get(email="email1@user.com") == user + assert User.get(email="email2@user.com") == user + assert not User.get(email="email3@user.com") + + user.family_name = "new_family_name" + user.email = ["email2@user.com"] + + assert User.get(family_name="family_name") != user + assert not User.get(family_name="new_family_name") + assert User.get(email="email1@user.com") != user + assert User.get(email="email2@user.com") != user + assert not User.get(email="email3@user.com") + + user.save() + + assert not User.get(family_name="family_name") + assert User.get(family_name="new_family_name") == user + assert not User.get(email="email1@user.com") + assert User.get(email="email2@user.com") == user + assert not User.get(email="email3@user.com") + + user.delete() + + assert not User.get(family_name="family_name") + assert not User.get(family_name="new_family_name") + assert not User.get(email="email1@user.com") + assert not User.get(email="email2@user.com") + assert not User.get(email="email3@user.com") + + +def test_fuzzy(user, moderator, admin, slapd_connection): + assert set(User.query()) == {user, moderator, admin} + assert set(User.fuzzy("Jack")) == {moderator} + assert set(User.fuzzy("Jack", ["formatted_name"])) == {moderator} + assert set(User.fuzzy("Jack", ["user_name"])) == set() + assert set(User.fuzzy("Jack", ["user_name", "formatted_name"])) == {moderator} + assert set(User.fuzzy("moderator")) == {moderator} + assert set(User.fuzzy("oderat")) == {moderator} + assert set(User.fuzzy("oDeRat")) == {moderator} + assert set(User.fuzzy("ack")) == {moderator} + + +# def test_model_references(user, admin, foo_group, bar_group): +def test_model_references( + testclient, user, foo_group, admin, bar_group, slapd_connection +): + assert foo_group.members == [user] + assert user.groups == [foo_group] + assert foo_group in Group.query(members=user) + assert user in User.query(groups=foo_group) + + assert user not in bar_group.members + assert bar_group not in user.groups + user.groups = user.groups + [bar_group] + user.save() + bar_group.reload() + + assert user in bar_group.members + assert bar_group in user.groups + + bar_group.members = [admin] + bar_group.save() + user.reload() + + assert user not in bar_group.members + assert bar_group not in user.groups + + +def test_model_references_set_unsaved_object( + testclient, logged_moderator, user, slapd_connection +): + group = Group(members=[user], display_name="foo") + group.save() + user.reload() # an LDAP group can be inconsistent by containing members which doesn't exist + + non_existent_user = User(formatted_name="foo", family_name="bar") + group.members = group.members + [non_existent_user] + assert group.members == [user, non_existent_user] + + group.save() + assert group.members == [user, non_existent_user] + + group.reload() + assert group.members == [user] + + testclient.get("/groups/foo", status=200) + + group.delete() diff --git a/tests/core/test_groups.py b/tests/core/test_groups.py index 8171de50..00c06544 100644 --- a/tests/core/test_groups.py +++ b/tests/core/test_groups.py @@ -204,23 +204,6 @@ def test_simple_user_cannot_view_or_edit_groups(testclient, logged_user, foo_gro testclient.get("/groups/foo", status=403) -def test_get_members_filters_non_existent_user( - testclient, logged_moderator, foo_group, user, slapd_server -): - # an LDAP group can be inconsistent by containing members which doesn't exist - non_existent_user = User(formatted_name="foo", family_name="bar") - foo_group.members = foo_group.members + [non_existent_user] - assert foo_group.members == [user, non_existent_user] - - foo_group.save() - assert foo_group.members == [user, non_existent_user] - - foo_group.reload() - assert foo_group.members == [user] - - testclient.get("/groups/foo", status=200) - - def test_invalid_form_request(testclient, logged_moderator, foo_group): res = testclient.get("/groups/foo") form = res.forms["editgroupform"]