diff --git a/README.md b/README.md index bb040ba..4ec50fe 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,35 @@ c.LDAPAuthenticator.allowed_groups = [ ] ``` +#### `LDAPAuthenticator.group_search_filter` + +The LDAP group search filter. + +The default value is an 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, it 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 2fc97b0..68ef51a 100644 --- a/ldapauthenticator/ldapauthenticator.py +++ b/ldapauthenticator/ldapauthenticator.py @@ -94,6 +94,23 @@ def _validate_bind_dn_template(self, proposal): """, ) + group_search_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( @@ -412,25 +429,24 @@ async def authenticate(self, handler, data): return None if self.allowed_groups: + if not self.group_search_filter or not self.group_attributes: + self.log.warning( + "Missing group_search_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 = group_filter.format( + group_search_filter = self.group_search_filter + group_search_filter = group_search_filter.format( userdn=escape_filter_chars(userdn), uid=escape_filter_chars(username), ) - group_attributes = ["member", "uniqueMember", "memberUid"] + group_attributes = self.group_attributes found = conn.search( group, search_scope=ldap3.BASE, - search_filter=group_filter, + search_filter=group_search_filter, attributes=group_attributes, ) if found: diff --git a/ldapauthenticator/tests/test_ldapauthenticator.py b/ldapauthenticator/tests/test_ldapauthenticator.py index dc15b75..f8fc057 100644 --- a/ldapauthenticator/tests/test_ldapauthenticator.py +++ b/ldapauthenticator/tests/test_ldapauthenticator.py @@ -104,3 +104,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_search_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"]}