diff --git a/README.md b/README.md index 3849329..aaeb748 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,35 @@ c.LDAPAuthenticator.allowed_groups = [ ] ``` +#### `LDAPAuthenticator.group_filter` #### + +The LDAP group search filter. + +The default value is a LDAP OR search that looks like the following: + +``` +(|(member={userdn})(uniqueMember={userdn})(memberUid={uid})) +``` + +So it basically compares the `userdn` attribute against the `member` attribute, +then against the `uniqueMember`, and finally checks the `memberUid` against +the `uid`. + +If you modify this value, you probably want to change `group_attributes` too. +Here is an example that should work with OpenLDAP servers. + +``` +(member={userdn}) +``` + +#### `LDAPAuthenticator.group_attributes` #### + +A list of attributes used when searching for LDAP groups. + +By default uses `member`, `uniqueMember`, and `memberUid`. Certain +servers may reject invalid values causing exceptions during +authentication. + #### `LDAPAuthenticator.valid_username_regex` #### All usernames will be checked against this before being sent diff --git a/ldapauthenticator/ldapauthenticator.py b/ldapauthenticator/ldapauthenticator.py index 0cc80d1..e745bff 100644 --- a/ldapauthenticator/ldapauthenticator.py +++ b/ldapauthenticator/ldapauthenticator.py @@ -86,6 +86,23 @@ def _server_port_default(self): """, ) + group_filter = Unicode( + config=True, + default_value="(|(member={userdn})(uniqueMember={userdn})(memberUid={uid}))", + help=""" + The search filter used to locate groups. + + Certain server types may use different values, and may also + reject invalid values by raising exceptions. + """, + ) + + group_attributes = List( + config=True, + default_value=["member", "uniqueMember", "memberUid"], + help="List of attributes to be searched", + ) + # FIXME: Use something other than this? THIS IS LAME, akin to websites restricting things you # can use in usernames / passwords to protect from SQL injection! valid_username_regex = Unicode( @@ -428,18 +445,17 @@ def authenticate(self, handler, data): return None if self.allowed_groups: + if not self.group_filter or not self.group_attributes: + self.log.warning( + "Missing group_filter or group_attributes. Both are required." + ) + return None self.log.debug("username:%s Using dn %s", username, userdn) found = False for group in self.allowed_groups: - group_filter = ( - "(|" - "(member={userdn})" - "(uniqueMember={userdn})" - "(memberUid={uid})" - ")" - ) + group_filter = self.group_filter group_filter = group_filter.format(userdn=userdn, uid=username) - group_attributes = ["member", "uniqueMember", "memberUid"] + group_attributes = self.group_attributes found = conn.search( group, search_scope=ldap3.BASE, diff --git a/ldapauthenticator/tests/conftest.py b/ldapauthenticator/tests/conftest.py index 545744f..21649da 100644 --- a/ldapauthenticator/tests/conftest.py +++ b/ldapauthenticator/tests/conftest.py @@ -28,6 +28,8 @@ def authenticator(): authenticator.escape_userdn = True authenticator.attributes = ["uid", "cn", "mail", "ou"] authenticator.use_lookup_dn_username = False + authenticator.group_filter = '(|(member={userdn})(uniqueMember={userdn})(memberUid={uid}))' + authenticator.group_attributes = ["member", "uniqueMember", "memberUid"] authenticator.allowed_groups = [ "cn=admin_staff,ou=people,dc=planetexpress,dc=com", diff --git a/ldapauthenticator/tests/test_ldapauthenticator.py b/ldapauthenticator/tests/test_ldapauthenticator.py index 6471213..8e52163 100644 --- a/ldapauthenticator/tests/test_ldapauthenticator.py +++ b/ldapauthenticator/tests/test_ldapauthenticator.py @@ -100,3 +100,15 @@ async def test_ldap_auth_state_attributes(authenticator): ) assert authorized["name"] == "fry" assert authorized["auth_state"] == {"employeeType": ["Delivery boy"]} + + +async def test_ldap_auth_state_attributes2(authenticator): + authenticator.group_filter = "(cn=ship_crew)" + authenticator.group_attributes = ["cn"] + authenticator.auth_state_attributes = ["description"] + # proper username and password in allowed group + authorized = await authenticator.get_authenticated_user( + None, {"username": "leela", "password": "leela"} + ) + assert authorized["name"] == "leela" + assert authorized["auth_state"] == {"description": ["Mutant"]}