Browse Source

fix ldappool bad password retry logic

This patch fixes a bug in ldappool which causes a bind attempt
utilizing a bad password to be retried until the retry limit has been
reached. Instead ldappool will now break out of the retry loop if the
ldap connection try block catches a ldap.INVALID_PASSWORD exception.

Previously ldappool would attempt to catch ldap.LDAPError which is
the base exception class for all ldap errors in the python-ldap
library. This is an issue because Keystone by default enables
ldappool and configures the default retry value to be 3. An LDAP
server with a password lockout threshold of 3 bad passwords will
lock out a user after a single bad password attempt through Keystone.

Change-Id: I2a9b850ce977260d4df1e9edf86417b8042a6fb8
Closes-Bug: #1785898
Nick Wilburn 8 months ago
parent
commit
459000d7aa
2 changed files with 32 additions and 0 deletions
  1. 5
    0
      ldappool/__init__.py
  2. 27
    0
      ldappool/tests/test_ldapconnection.py

+ 5
- 0
ldappool/__init__.py View File

@@ -252,6 +252,11 @@ class ConnectionManager(object):
252 252
                 conn.timeout = self.timeout
253 253
                 self._bind(conn, bind, passwd)
254 254
                 connected = True
255
+            except ldap.INVALID_CREDENTIALS as error:
256
+                exc = error
257
+                log.error('Invalid credentials. Cancelling retry',
258
+                          exc_info=True)
259
+                break
255 260
             except ldap.LDAPError as error:
256 261
                 exc = error
257 262
                 time.sleep(self.retry_delay)

+ 27
- 0
ldappool/tests/test_ldapconnection.py View File

@@ -55,6 +55,10 @@ def _bind_fails2(self, who='', cred='', **kw):
55 55
     raise ldap.SERVER_DOWN('LDAP connection invalid')
56 56
 
57 57
 
58
+def _bind_fails_invalid_credentials(self, who='', cred='', **kw):
59
+    raise ldap.INVALID_CREDENTIALS('LDAP connection invalid')
60
+
61
+
58 62
 def _start_tls_s(self):
59 63
     if self.start_tls_already_called_flag:
60 64
         raise ldap.LOCAL_ERROR
@@ -157,3 +161,26 @@ class TestLDAPConnection(unittest.TestCase):
157 161
             pass
158 162
         else:
159 163
             raise AssertionError()
164
+
165
+    def test_simple_bind_fails_invalid_credentials(self):
166
+        unbinds = []
167
+
168
+        def _unbind(self):
169
+            unbinds.append(1)
170
+
171
+        # the binding fails with an LDAPError
172
+        ldappool.StateConnector.simple_bind_s = _bind_fails_invalid_credentials
173
+        ldappool.StateConnector.unbind_s = _unbind
174
+        uri = ''
175
+        dn = 'uid=adminuser,ou=logins,dc=mozilla'
176
+        passwd = 'adminuser'
177
+        cm = ldappool.ConnectionManager(uri, dn, passwd, use_pool=True, size=2)
178
+        self.assertEqual(len(cm), 0)
179
+
180
+        try:
181
+            with cm.connection('dn', 'pass'):
182
+                pass
183
+        except ldap.INVALID_CREDENTIALS:
184
+            pass
185
+        else:
186
+            raise AssertionError()

Loading…
Cancel
Save