##// END OF EJS Templates
auth-ldap: added connection pinning and timeout option to ldap plugin
marcink -
r2654:c156e0f4 default
parent child Browse files
Show More
@@ -86,6 +86,7 b' class TestAuthSettingsView(object):'
86
86
87 'host': 'dc.example.com',
87 'host': 'dc.example.com',
88 'port': '999',
88 'port': '999',
89 'timeout': 3600,
89 'tls_kind': 'PLAIN',
90 'tls_kind': 'PLAIN',
90 'tls_reqcert': 'NEVER',
91 'tls_reqcert': 'NEVER',
91
92
@@ -22,6 +22,7 b''
22 RhodeCode authentication plugin for LDAP
22 RhodeCode authentication plugin for LDAP
23 """
23 """
24
24
25 import socket
25 import re
26 import re
26 import colander
27 import colander
27 import logging
28 import logging
@@ -90,6 +91,16 b' class LdapSettingsSchema(AuthnPluginSett'
90 title=_('Port'),
91 title=_('Port'),
91 validator=colander.Range(min=0, max=65536),
92 validator=colander.Range(min=0, max=65536),
92 widget='int')
93 widget='int')
94
95 timeout = colander.SchemaNode(
96 colander.Int(),
97 default=60 * 5,
98 description=_('Timeout for LDAP connection'),
99 preparer=strip_whitespace,
100 title=_('Connection timeout'),
101 validator=colander.Range(min=1),
102 widget='int')
103
93 dn_user = colander.SchemaNode(
104 dn_user = colander.SchemaNode(
94 colander.String(),
105 colander.String(),
95 default='',
106 default='',
@@ -191,19 +202,47 b' class LdapSettingsSchema(AuthnPluginSett'
191 class AuthLdap(object):
202 class AuthLdap(object):
192
203
193 def _build_servers(self):
204 def _build_servers(self):
205 def host_resolver(host, port):
206 """
207 Main work for this function is to prevent ldap connection issues,
208 and detect them early using a "greenified" sockets
209 """
210 host = host.strip()
211
212 log.info('Resolving LDAP host %s', host)
213 try:
214 ip = socket.gethostbyname(host)
215 log.info('Got LDAP server %s ip %s', host, ip)
216 except Exception:
217 raise LdapConnectionError(
218 'Failed to resolve host: `{}`'.format(host))
219
220 log.info('Checking LDAP IP access %s', ip)
221 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
222 try:
223 s.connect((ip, int(port)))
224 s.shutdown(socket.SHUT_RD)
225 except Exception:
226 raise LdapConnectionError(
227 'Failed to connect to host: `{}:{}`'.format(host, port))
228
229 return '{}:{}'.format(host, port)
230
231 port = self.LDAP_SERVER_PORT
194 return ', '.join(
232 return ', '.join(
195 ["{}://{}:{}".format(
233 ["{}://{}".format(
196 self.ldap_server_type, host.strip(), self.LDAP_SERVER_PORT)
234 self.ldap_server_type, host_resolver(host, port))
197 for host in self.SERVER_ADDRESSES])
235 for host in self.SERVER_ADDRESSES])
198
236
199 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
237 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
200 tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3,
238 tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3,
201 search_scope='SUBTREE', attr_login='uid',
239 search_scope='SUBTREE', attr_login='uid',
202 ldap_filter=''):
240 ldap_filter='', timeout=None):
203 if ldap == Missing:
241 if ldap == Missing:
204 raise LdapImportError("Missing or incompatible ldap library")
242 raise LdapImportError("Missing or incompatible ldap library")
205
243
206 self.debug = False
244 self.debug = False
245 self.timeout = timeout or 60 * 5
207 self.ldap_version = ldap_version
246 self.ldap_version = ldap_version
208 self.ldap_server_type = 'ldap'
247 self.ldap_server_type = 'ldap'
209
248
@@ -231,22 +270,25 b' class AuthLdap(object):'
231 self.LDAP_FILTER = safe_str(ldap_filter)
270 self.LDAP_FILTER = safe_str(ldap_filter)
232
271
233 def _get_ldap_conn(self):
272 def _get_ldap_conn(self):
273 log.debug('initializing LDAP connection to:%s', self.LDAP_SERVER)
274
234 if self.debug:
275 if self.debug:
235 ldap.set_option(ldap.OPT_DEBUG_LEVEL, 255)
276 ldap.set_option(ldap.OPT_DEBUG_LEVEL, 255)
236
277
237 if hasattr(ldap, 'OPT_X_TLS_CACERTDIR'):
278 if hasattr(ldap, 'OPT_X_TLS_CACERTDIR'):
238 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR,
279 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, '/etc/openldap/cacerts')
239 '/etc/openldap/cacerts')
240 ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
241 ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
242 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 60 * 10)
243 ldap.set_option(ldap.OPT_TIMEOUT, 60 * 10)
244
245 if self.TLS_KIND != 'PLAIN':
280 if self.TLS_KIND != 'PLAIN':
246 ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT)
281 ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT)
247
282
248 log.debug('initializing LDAP connection to:%s', self.LDAP_SERVER)
283 ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
284 ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
285
286 # init connection now
249 ldap_conn = ldap.initialize(self.LDAP_SERVER)
287 ldap_conn = ldap.initialize(self.LDAP_SERVER)
288 ldap_conn.set_option(ldap.OPT_NETWORK_TIMEOUT, self.timeout)
289 ldap_conn.set_option(ldap.OPT_TIMEOUT, self.timeout)
290 ldap_conn.timeout = self.timeout
291
250 if self.ldap_version == 2:
292 if self.ldap_version == 2:
251 ldap_conn.protocol = ldap.VERSION2
293 ldap_conn.protocol = ldap.VERSION2
252 else:
294 else:
@@ -446,6 +488,7 b' class RhodeCodeAuthPlugin(RhodeCodeExter'
446 'attr_login': settings.get('attr_login'),
488 'attr_login': settings.get('attr_login'),
447 'ldap_version': 3,
489 'ldap_version': 3,
448 'ldap_filter': settings.get('filter'),
490 'ldap_filter': settings.get('filter'),
491 'timeout': settings.get('timeout')
449 }
492 }
450
493
451 ldap_attrs = self.try_dynamic_binding(username, password, ldap_args)
494 ldap_attrs = self.try_dynamic_binding(username, password, ldap_args)
@@ -496,3 +539,4 b' class RhodeCodeAuthPlugin(RhodeCodeExter'
496 except (Exception,):
539 except (Exception,):
497 log.exception("Other exception")
540 log.exception("Other exception")
498 return None
541 return None
542
General Comments 0
You need to be logged in to leave comments. Login now