diff --git a/relengapi/lib/auth/ldap_group_authz.py b/relengapi/lib/auth/ldap_group_authz.py index 5102b30f..eec2473b 100644 --- a/relengapi/lib/auth/ldap_group_authz.py +++ b/relengapi/lib/auth/ldap_group_authz.py @@ -57,7 +57,9 @@ def get_user_groups(self, mail): try: l = ldap.initialize(self.uri) l.simple_bind_s(self.login_dn, self.login_password) - # convert mail to DN + groups = [] + + # convert mail to DN and search for groupOfNames with member=$dn people = l.search_s(self.user_base, ldap.SCOPE_SUBTREE, '(&(objectClass=inetOrgPerson)(mail=%s))' % (mail,), []) if not people or len(people) != 1: @@ -65,9 +67,18 @@ def get_user_groups(self, mail): user_dn = people[0][0] result = l.search_s(self.group_base, ldap.SCOPE_SUBTREE, '(&(objectClass=groupOfNames)(member=%s))' % user_dn, ['cn']) - groups = [] for glist in [g[1]['cn'] for g in result]: groups.extend(glist) + + # Mozilla uses some POSIX groups, which have a different class. + # The scm_level_N groups also have the unusual trait of using the mail + # in the memberUid attribute; other POSIX groups use the actual uid, + # and won't be matched by this clause + result = l.search_s(self.group_base, ldap.SCOPE_SUBTREE, + '(&(objectClass=posixGroup)(memberUid=%s))' % mail, ['cn']) + for glist in [g[1]['cn'] for g in result]: + groups.extend(glist) + return list(set(groups)) except ldap.LDAPError: self.logger.exception("While connecting to the LDAP server") diff --git a/relengapi/tests/test_lib_auth_ldap_group_authz.py b/relengapi/tests/test_lib_auth_ldap_group_authz.py index b4166f5a..693eaf64 100644 --- a/relengapi/tests/test_lib_auth_ldap_group_authz.py +++ b/relengapi/tests/test_lib_auth_ldap_group_authz.py @@ -63,6 +63,11 @@ class TestGetUserGroups(unittest.TestCase): 'cn': ['mary'], 'mail': ['mary@org.org'], }, + 'cn=tom,ou=people,o=users': { + 'objectClass': ['inetOrgPerson'], + 'cn': ['tom'], + 'mail': ['tom@tom.com'], + }, 'o=groups': {'o': 'groups'}, 'cn=authors,o=groups': { 'objectClass': ['groupOfNames'], @@ -79,6 +84,11 @@ class TestGetUserGroups(unittest.TestCase): 'cn': ['c-suite'], 'member': [], }, + 'cn=scm_level_17,o=groups': { + 'objectClass': ['posixGroup'], + 'cn': ['scm_level_17'], + 'memberUid': ['tom@tom.com', 'mary@org.org'], + }, } @classmethod @@ -110,11 +120,16 @@ def test_get_user_groups_single(self): self.call(mail='jimmy@org.org', exp_groups=['authors']) def test_get_user_groups_multiple(self): - self.call(mail='mary@org.org', exp_groups=['authors', 'editors']) + # note that this includes both POSIX and normal groups + self.call(mail='mary@org.org', + exp_groups=['authors', 'editors', 'scm_level_17']) def test_get_user_groups_nosuch(self): self.call(mail='steve@org.org', exp_groups=None) + def test_get_user_groups_posix(self): + self.call(mail='tom@tom.com', exp_groups=['scm_level_17']) + @test_context.specialize(config=BAD_CONFIG) def test_login_fail(self, app): hdlr = logging.handlers.BufferingHandler(100)