From e2efb6c4582b0df6e3bf7762b9cb0f9f3728bc9f Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 19 Dec 2016 11:53:06 -0500 Subject: [PATCH] Add default and configurable LDAP timeouts Fixes https://www.pivotaltracker.com/story/show/135885019 --- data/users/__init__.py | 5 ++++- data/users/externalldap.py | 26 +++++++++++++++++++++----- test/test_ldap.py | 14 ++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/data/users/__init__.py b/data/users/__init__.py index 11fc92458..1f083e8ff 100644 --- a/data/users/__init__.py +++ b/data/users/__init__.py @@ -45,11 +45,14 @@ def get_users_handler(config, _, override_config_dir): uid_attr = config.get('LDAP_UID_ATTR', 'uid') email_attr = config.get('LDAP_EMAIL_ATTR', 'mail') secondary_user_rdns = config.get('LDAP_SECONDARY_USER_RDNS', []) + timeout = config.get('LDAP_TIMEOUT') + network_timeout = config.get('LDAP_NETWORK_TIMEOUT') allow_tls_fallback = config.get('LDAP_ALLOW_INSECURE_FALLBACK', False) return LDAPUsers(ldap_uri, base_dn, admin_dn, admin_passwd, user_rdn, uid_attr, email_attr, allow_tls_fallback, secondary_user_rdns=secondary_user_rdns, - requires_email=features.MAILING) + requires_email=features.MAILING, timeout=timeout, + network_timeout=network_timeout) if authentication_type == 'JWT': verify_url = config.get('JWT_VERIFY_ENDPOINT') diff --git a/data/users/externalldap.py b/data/users/externalldap.py index eae3dcea2..cd6e460fb 100644 --- a/data/users/externalldap.py +++ b/data/users/externalldap.py @@ -8,24 +8,34 @@ from data.users.federated import FederatedUsers, UserInformation logger = logging.getLogger(__name__) +_DEFAULT_NETWORK_TIMEOUT = 10.0 # seconds +_DEFAULT_TIMEOUT = 10.0 # seconds + class LDAPConnectionBuilder(object): - def __init__(self, ldap_uri, user_dn, user_pw, allow_tls_fallback=False): + def __init__(self, ldap_uri, user_dn, user_pw, allow_tls_fallback=False, + timeout=None, network_timeout=None): self._ldap_uri = ldap_uri self._user_dn = user_dn self._user_pw = user_pw self._allow_tls_fallback = allow_tls_fallback + self._timeout = timeout + self._network_timeout = network_timeout def get_connection(self): - return LDAPConnection(self._ldap_uri, self._user_dn, self._user_pw, self._allow_tls_fallback) + return LDAPConnection(self._ldap_uri, self._user_dn, self._user_pw, self._allow_tls_fallback, + self._timeout, self._network_timeout) class LDAPConnection(object): - def __init__(self, ldap_uri, user_dn, user_pw, allow_tls_fallback=False): + def __init__(self, ldap_uri, user_dn, user_pw, allow_tls_fallback=False, + timeout=None, network_timeout=None): self._ldap_uri = ldap_uri self._user_dn = user_dn self._user_pw = user_pw self._allow_tls_fallback = allow_tls_fallback + self._timeout = timeout + self._network_timeout = network_timeout self._conn = None def __enter__(self): @@ -33,6 +43,9 @@ class LDAPConnection(object): self._conn = ldap.initialize(self._ldap_uri, trace_level=trace_level) self._conn.set_option(ldap.OPT_REFERRALS, 1) + self._conn.set_option(ldap.OPT_NETWORK_TIMEOUT, + self._network_timeout or _DEFAULT_NETWORK_TIMEOUT) + self._conn.set_option(ldap.OPT_TIMEOUT, self._timeout or _DEFAULT_TIMEOUT) if self._allow_tls_fallback: logger.debug('TLS Fallback enabled in LDAP') @@ -53,9 +66,12 @@ class LDAPUsers(FederatedUsers): _LDAPResult = namedtuple('LDAPResult', ['dn', 'attrs']) def __init__(self, ldap_uri, base_dn, admin_dn, admin_passwd, user_rdn, uid_attr, email_attr, - allow_tls_fallback=False, secondary_user_rdns=None, requires_email=True): + allow_tls_fallback=False, secondary_user_rdns=None, requires_email=True, + timeout=None, network_timeout=None): super(LDAPUsers, self).__init__('ldap', requires_email) - self._ldap = LDAPConnectionBuilder(ldap_uri, admin_dn, admin_passwd, allow_tls_fallback) + + self._ldap = LDAPConnectionBuilder(ldap_uri, admin_dn, admin_passwd, allow_tls_fallback, + timeout, network_timeout) self._ldap_uri = ldap_uri self._uid_attr = uid_attr self._email_attr = email_attr diff --git a/test/test_ldap.py b/test/test_ldap.py index 68b26c083..d04e1b97f 100644 --- a/test/test_ldap.py +++ b/test/test_ldap.py @@ -275,6 +275,20 @@ class TestLDAP(unittest.TestCase): self.assertEquals(0, len(response)) self.assertEquals('ldap', federated_id) + def test_timeout(self): + base_dn = ['dc=quay', 'dc=io'] + admin_dn = 'uid=testy,ou=employees,dc=quay,dc=io' + admin_passwd = 'password' + user_rdn = ['ou=employees'] + uid_attr = 'uid' + email_attr = 'mail' + secondary_user_rdns = ['ou=otheremployees'] + + with self.assertRaisesRegexp(Exception, "Can't contact LDAP server"): + ldap = LDAPUsers('ldap://localhost', base_dn, admin_dn, admin_passwd, user_rdn, + uid_attr, email_attr, secondary_user_rdns=secondary_user_rdns, + requires_email=False, timeout=5) + ldap.query_users('cool') if __name__ == '__main__': unittest.main()