diff --git a/web/config.py b/web/config.py index 607447d80..adc5f60d9 100644 --- a/web/config.py +++ b/web/config.py @@ -527,6 +527,16 @@ LDAP_BASE_DN = '' # whilst in AD, 'sAMAccountName' might be appropriate. (REQUIRED) LDAP_USERNAME_ATTRIBUTE = '' +# LDAP Bind User DN Example: cn=username,dc=example,dc=com (OPTIONAL) +# Set this parameter to allow the connection to bind using a dedicated user. +# After the connection is made, the pgadmin login user will be further +# authenticated by the username and password provided +# at the login screen. (OPTIONAL) +LDAP_BIND_USER = None + +# LDAP Bind User Password (OPTIONAL) +LDAP_BIND_PASSWORD = None + # Search ldap for further authentication LDAP_SEARCH_BASE_DN = '' diff --git a/web/pgadmin/authenticate/ldap.py b/web/pgadmin/authenticate/ldap.py index 18ebcdcd9..a86ad026e 100644 --- a/web/pgadmin/authenticate/ldap.py +++ b/web/pgadmin/authenticate/ldap.py @@ -36,16 +36,51 @@ class LDAPAuthentication(BaseAuthentication): def authenticate(self, form): self.username = form.data['email'] self.password = form.data['password'] + user_email = None + dedicated_user = True + # Check the dedicated ldap user + self.bind_user = getattr(config, 'LDAP_BIND_USER', None) + self.bind_pass = getattr(config, 'LDAP_BIND_PASSWORD', None) + + if self.bind_user and not self.bind_pass: + return False, "LDAP configuration error: Set the bind password." + + # if no dedicated ldap user is configured then use the login + # username and password + if not self.bind_user or not self.bind_pass: + user_dn = "{0}={1},{2}".format(config.LDAP_USERNAME_ATTRIBUTE, + self.username, + config.LDAP_BASE_DN + ) + + self.bind_user = user_dn + self.bind_pass = self.password + dedicated_user = False + + # Connect ldap server status, msg = self.connect() if not status: return status, msg - status, user_email = self.search_ldap_user() + status, ldap_user = self.search_ldap_user() if not status: - return status, user_email + return status, ldap_user + + # If dedicated user is configured + if dedicated_user: + # Get the user DN from the user ldap entry + self.bind_user = ldap_user.entry_dn + self.bind_pass = self.password + status, msg = self.connect() + + if not status: + return status, msg + + if 'mail' in ldap_user: + user_email = ldap_user['mail'].value return self.__auto_create_user(user_email) @@ -101,13 +136,10 @@ class LDAPAuthentication(BaseAuthentication): # Create the connection try: - user_dn = "{0}={1},{2}".format(config.LDAP_USERNAME_ATTRIBUTE, - self.username, - config.LDAP_BASE_DN - ) + self.conn = Connection(server, - user=user_dn, - password=self.password, + user=self.bind_user, + password=self.bind_pass, auto_bind=True ) @@ -187,8 +219,7 @@ class LDAPAuthentication(BaseAuthentication): user_email = None if config.LDAP_USERNAME_ATTRIBUTE in entry and self.username == \ entry[config.LDAP_USERNAME_ATTRIBUTE].value: - if 'mail' in entry: - user_email = entry['mail'].value - return True, user_email + user_dn = entry.entry_dn + return True, entry return False, ERROR_SEARCHING_LDAP_DIRECTORY.format( "Could not find the specified user.") diff --git a/web/pgadmin/browser/tests/test_ldap_login.py b/web/pgadmin/browser/tests/test_ldap_login.py index 4a76569d1..500db74a0 100644 --- a/web/pgadmin/browser/tests/test_ldap_login.py +++ b/web/pgadmin/browser/tests/test_ldap_login.py @@ -29,6 +29,9 @@ class LDAPLoginTestCase(BaseTestGenerator): ('LDAP With TLS Authentication', dict( config_key_param='ldap_with_tls', is_gravtar_image_check=False)), + ('LDAP With Dedicated User Authentication', dict( + config_key_param='ldap_with_dedicated_user', + is_gravtar_image_check=False)), ] @classmethod @@ -59,6 +62,10 @@ class LDAPLoginTestCase(BaseTestGenerator): app_config.LDAP_CA_CERT_FILE = ldap_config['ca_cert_file'] app_config.LDAP_CERT_FILE = ldap_config['cert_file'] app_config.LDAP_KEY_FILE = ldap_config['key_file'] + if ldap_config['bind_user'] != "" and\ + ldap_config['bind_password'] != "": + app_config.LDAP_BIND_USER = ldap_config['bind_user'] + app_config.LDAP_BIND_PASSWORD = ldap_config['bind_password'] else: self.skipTest( "LDAP config not set." diff --git a/web/regression/test_config.json.in b/web/regression/test_config.json.in index dfa3a5031..7036bed5f 100644 --- a/web/regression/test_config.json.in +++ b/web/regression/test_config.json.in @@ -20,6 +20,8 @@ "ldap": { "name": "Ldap scenario name", "uri": "ldap://IP-ADDRESS/HOSTNAME:389", + "bind_user": "", + "bind_password": "", "base_dn": "BASE-DN", "search_base_dn": "SEARCH-BASE-DN", "username_atr": "UID", @@ -32,6 +34,8 @@ "ldap_with_ssl": { "name": "Ldap scenario name", "uri": "ldaps://IP-ADDRESS/HOSTNAME:636", + "bind_user": "", + "bind_password": "", "base_dn": "BASE-DN", "search_base_dn": "SEARCH-BASE-DN", "username_atr": "UID", @@ -44,6 +48,22 @@ "ldap_with_tls": { "name": "Ldap scenario name", "uri": "ldap://IP-ADDRESS/HOSTNAME:389", + "bind_user": "", + "bind_password": "", + "base_dn": "BASE-DN", + "search_base_dn": "SEARCH-BASE-DN", + "username_atr": "UID", + "search_filter": "(objectclass=*)", + "use_starttls": true, + "ca_cert_file": "", + "cert_file": "", + "key_file": "" + }, + "ldap_with_dedicated_user": { + "name": "Ldap scenario name", + "uri": "ldap://IP-ADDRESS/HOSTNAME:389", + "bind_user": "", + "bind_password": "", "base_dn": "BASE-DN", "search_base_dn": "SEARCH-BASE-DN", "username_atr": "UID",