Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow users to configure group search filter and attributes (group_search_filter and group_attributes config) #168

Merged
merged 2 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 26 additions & 10 deletions ldapauthenticator/ldapauthenticator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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:
Expand Down
12 changes: 12 additions & 0 deletions ldapauthenticator/tests/test_ldapauthenticator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]}
Loading