From 61839f4acadd3dfac12b21bb0572d1ddc795a236 Mon Sep 17 00:00:00 2001 From: Ronald Moesbergen Date: Mon, 19 Nov 2018 13:42:42 +0100 Subject: [PATCH 1/3] Add support for nested group membership in ldap authenticator --- lemur/auth/ldap.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lemur/auth/ldap.py b/lemur/auth/ldap.py index 398a5830..dc8f8941 100644 --- a/lemur/auth/ldap.py +++ b/lemur/auth/ldap.py @@ -41,7 +41,6 @@ class LdapPrincipal(): self.ldap_default_role = current_app.config.get("LEMUR_DEFAULT_ROLE", None) self.ldap_required_group = current_app.config.get("LDAP_REQUIRED_GROUP", None) self.ldap_groups_to_roles = current_app.config.get("LDAP_GROUPS_TO_ROLES", None) - self.ldap_attrs = ['memberOf'] self.ldap_client = None self.ldap_groups = None @@ -168,11 +167,21 @@ class LdapPrincipal(): except ldap.LDAPError as e: raise Exception("ldap error: {0}".format(e)) - lgroups = self.ldap_client.search_s(self.ldap_base_dn, - ldap.SCOPE_SUBTREE, ldap_filter, self.ldap_attrs)[0][1]['memberOf'] - # lgroups is a list of utf-8 encoded strings - # convert to a single string of groups to allow matching - self.ldap_groups = b''.join(lgroups).decode('ascii') + # Lookup user DN, needed to search for group membership + userdn = self.ldap_client.search_s(self.ldap_base_dn, + ldap.SCOPE_SUBTREE, ldap_filter, + ['distinguishedName'])[0][1]['distinguishedName'][0] + userdn = userdn.decode('utf-8') + # Search all groups that have the userDN as a member + groupfilter = '(&(objectclass=group)(member:1.2.840.113556.1.4.1941:={0}))'.format(userdn) + lgroups = self.ldap_client.search_s(self.ldap_base_dn, ldap.SCOPE_SUBTREE, groupfilter, ['cn']) + + # Create a list of group CN's from the result + self.ldap_groups = [] + for group in lgroups: + (dn, values) = group + self.ldap_groups.append(values['cn'][0].decode('ascii')) + self.ldap_client.unbind() def _ldap_validate_conf(self): From da10913045082669a66b5b2e7f2508da477f9714 Mon Sep 17 00:00:00 2001 From: Ronald Moesbergen Date: Tue, 20 Nov 2018 10:37:36 +0100 Subject: [PATCH 2/3] Only search nested group memberships when LDAP_IS_ACTIVE_DIRECTORY is True --- lemur/auth/ldap.py | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/lemur/auth/ldap.py b/lemur/auth/ldap.py index dc8f8941..7eded060 100644 --- a/lemur/auth/ldap.py +++ b/lemur/auth/ldap.py @@ -41,6 +41,8 @@ class LdapPrincipal(): self.ldap_default_role = current_app.config.get("LEMUR_DEFAULT_ROLE", None) self.ldap_required_group = current_app.config.get("LDAP_REQUIRED_GROUP", None) self.ldap_groups_to_roles = current_app.config.get("LDAP_GROUPS_TO_ROLES", None) + self.ldap_is_active_directory = current_app.config.get("LDAP_IS_ACTIVE_DIRECTORY", False) + self.ldap_attrs = ['memberOf'] self.ldap_client = None self.ldap_groups = None @@ -167,20 +169,27 @@ class LdapPrincipal(): except ldap.LDAPError as e: raise Exception("ldap error: {0}".format(e)) - # Lookup user DN, needed to search for group membership - userdn = self.ldap_client.search_s(self.ldap_base_dn, - ldap.SCOPE_SUBTREE, ldap_filter, - ['distinguishedName'])[0][1]['distinguishedName'][0] - userdn = userdn.decode('utf-8') - # Search all groups that have the userDN as a member - groupfilter = '(&(objectclass=group)(member:1.2.840.113556.1.4.1941:={0}))'.format(userdn) - lgroups = self.ldap_client.search_s(self.ldap_base_dn, ldap.SCOPE_SUBTREE, groupfilter, ['cn']) + if self.ldap_is_active_directory: + # Lookup user DN, needed to search for group membership + userdn = self.ldap_client.search_s(self.ldap_base_dn, + ldap.SCOPE_SUBTREE, ldap_filter, + ['distinguishedName'])[0][1]['distinguishedName'][0] + userdn = userdn.decode('utf-8') + # Search all groups that have the userDN as a member + groupfilter = '(&(objectclass=group)(member:1.2.840.113556.1.4.1941:={0}))'.format(userdn) + lgroups = self.ldap_client.search_s(self.ldap_base_dn, ldap.SCOPE_SUBTREE, groupfilter, ['cn']) - # Create a list of group CN's from the result - self.ldap_groups = [] - for group in lgroups: - (dn, values) = group - self.ldap_groups.append(values['cn'][0].decode('ascii')) + # Create a list of group CN's from the result + self.ldap_groups = [] + for group in lgroups: + (dn, values) = group + self.ldap_groups.append(values['cn'][0].decode('ascii')) + else: + lgroups = self.ldap_client.search_s(self.ldap_base_dn, + ldap.SCOPE_SUBTREE, ldap_filter, self.ldap_attrs)[0][1]['memberOf'] + # lgroups is a list of utf-8 encoded strings + # convert to a single string of groups to allow matching + self.ldap_groups = b''.join(lgroups).decode('ascii') self.ldap_client.unbind() From 5fc5a058b65f574784372cafff7aa9e465ebd468 Mon Sep 17 00:00:00 2001 From: Ronald Moesbergen Date: Tue, 20 Nov 2018 10:51:14 +0100 Subject: [PATCH 3/3] Add documentation for the LDAP_IS_ACTIVE_DIRECTORY setting --- docs/administration.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/administration.rst b/docs/administration.rst index eec01cc5..9d6c8d12 100644 --- a/docs/administration.rst +++ b/docs/administration.rst @@ -324,6 +324,7 @@ Here is an example LDAP configuration stanza you can add to your config. Adjust LDAP_CACERT_FILE = '/opt/lemur/trusted.pem' LDAP_REQUIRED_GROUP = 'certificate-management-access' LDAP_GROUPS_TO_ROLES = {'certificate-management-admin': 'admin', 'certificate-management-read-only': 'read-only'} + LDAP_IS_ACTIVE_DIRECTORY = True The lemur ldap module uses the `user principal name` (upn) of the authenticating user to bind. This is done once for each user at login time. The UPN is effectively the email address in AD/LDAP of the user. If the user doesn't provide the email address, it constructs one based on the username supplied (which should normally match the samAccountName) and the value provided by the config LDAP_EMAIL_DOMAIN. @@ -406,6 +407,17 @@ The following LDAP options are not required, however TLS is always recommended. LDAP_GROUPS_TO_ROLES = {'lemur_admins': 'admin', 'Lemur Team DL Group': 'team@example.com'} +.. data:: LDAP_IS_ACTIVE_DIRECTORY + :noindex: + + When set to True, nested group memberships are supported, by searching for groups with the member:1.2.840.113556.1.4.1941 attribute set to the user DN. + When set to False, the list of groups will be determined by the 'memberof' attribute of the LDAP user logging in. + + :: + + LDAP_IS_ACTIVE_DIRECTORY = False + + Authentication Providers ~~~~~~~~~~~~~~~~~~~~~~~~