From 6c1445d8db794897241bf23b5e9a81b04d4cf53a Mon Sep 17 00:00:00 2001 From: Peter Marschall Date: Wed, 1 Jan 2025 20:41:55 +0100 Subject: [PATCH] LDAP auth: introduce config option 'ldap_groups_attribute' This attribute is supposed to hold the group membership information if the config option 'ldap_load_groups' is True. If not given, it defaults to 'memberOf' for Active Directory. Introducing this options allows one to use radicale's LDAP auth with groups even on LDAP servers that keep their group memberships in a different attribute than 'memberOf', e.g. Novell eDirectory which uses 'groupMembership'. --- DOCUMENTATION.md | 6 ++++++ config | 3 +++ radicale/auth/ldap.py | 10 +++++++--- radicale/config.py | 4 ++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 7c02cc58..cbca8899 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -941,6 +941,12 @@ Load the ldap groups of the authenticated user. These groups can be used later o Default: False +##### ldap_groups_attribute + +The LDAP attribute to read the group memberships from in the user's LDAP entry if `ldap_load_groups` is True. + +Default: `memberOf` + ##### ldap_use_ssl Use ssl on the ldap connection diff --git a/config b/config index ef7263a0..64fd0f9f 100644 --- a/config +++ b/config @@ -89,6 +89,9 @@ # If the ldap groups of the user need to be loaded #ldap_load_groups = True +# the attribute to read the group memberships from in the user's LDAP entry if ldap_load_groups is True. +#ldap_groups_attribute = memberOf + # The filter to find the DN of the user. This filter must contain a python-style placeholder for the login #ldap_filter = (&(objectClass=person)(uid={0})) diff --git a/radicale/auth/ldap.py b/radicale/auth/ldap.py index 50b2768a..4d576ef2 100644 --- a/radicale/auth/ldap.py +++ b/radicale/auth/ldap.py @@ -24,6 +24,7 @@ Following parameters are needed in the configuration: ldap_secret_file The path of the file containing the password of the ldap_reader_dn ldap_filter The search filter to find the user to authenticate by the username ldap_user_attribute The attribute to be used as username after authentication + ldap_groups_attribute The attribute containing group memberships in the LDAP user entry ldap_load_groups If the groups of the authenticated users need to be loaded Following parameters controls SSL connections: ldap_use_ssl If the connection @@ -46,6 +47,7 @@ class Auth(auth.BaseAuth): _ldap_attributes: list[str] = [] _ldap_user_attr: str _ldap_load_groups: bool + _ldap_groups_attr: str = "memberOf" _ldap_module_version: int = 3 _ldap_use_ssl: bool = False _ldap_ssl_verify_mode: int = ssl.CERT_REQUIRED @@ -70,6 +72,7 @@ class Auth(auth.BaseAuth): self._ldap_secret = configuration.get("auth", "ldap_secret") self._ldap_filter = configuration.get("auth", "ldap_filter") self._ldap_user_attr = configuration.get("auth", "ldap_user_attribute") + self._ldap_groups_attr = configuration.get("auth", "ldap_groups_attribute") ldap_secret_file_path = configuration.get("auth", "ldap_secret_file") if ldap_secret_file_path: with open(ldap_secret_file_path, 'r') as file: @@ -92,6 +95,7 @@ class Auth(auth.BaseAuth): logger.info("auth.ldap_user_attribute : %r" % self._ldap_user_attr) else: logger.info("auth.ldap_user_attribute : (not provided)") + logger.info("auth.ldap_groups_attribute: %r" % self._ldap_groups_attr) if ldap_secret_file_path: logger.info("auth.ldap_secret_file_path: %r" % ldap_secret_file_path) if self._ldap_secret: @@ -112,7 +116,7 @@ class Auth(auth.BaseAuth): logger.info("auth.ldap_ssl_ca_file : (not provided)") """Extend attributes to to be returned in the user query""" if self._ldap_load_groups: - self._ldap_attributes.append('memberOf') + self._ldap_attributes.append(self._ldap_groups_attr) if self._ldap_user_attr: self._ldap_attributes.append(self._ldap_user_attr) logger.info("ldap_attributes : %r" % self._ldap_attributes) @@ -155,7 +159,7 @@ class Auth(auth.BaseAuth): tmp: list[str] = [] if self._ldap_load_groups: tmp = [] - for g in user_entry[1]['memberOf']: + for g in user_entry[1][self._ldap_groups_attr]: """Get group g's RDN's attribute value""" g = g.decode('utf-8').split(',')[0] tmp.append(g.partition('=')[2]) @@ -225,7 +229,7 @@ class Auth(auth.BaseAuth): tmp: list[str] = [] if self._ldap_load_groups: tmp = [] - for g in user_entry['attributes']['memberOf']: + for g in user_entry['attributes'][self._ldap_groups_attr]: """Get group g's RDN's attribute value""" g = g.split(',')[0] tmp.append(g.partition('=')[2]) diff --git a/radicale/config.py b/radicale/config.py index 3af6c807..6b3205d1 100644 --- a/radicale/config.py +++ b/radicale/config.py @@ -251,6 +251,10 @@ DEFAULT_CONFIG_SCHEMA: types.CONFIG_SCHEMA = OrderedDict([ "value": "False", "help": "load the ldap groups of the authenticated user", "type": bool}), + ("ldap_groups_attribute", { + "value": "memberOf", + "help": "attribute to read the group memberships from", + "type": str}), ("ldap_use_ssl", { "value": "False", "help": "Use ssl on the ldap connection",