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 |
["{}://{} |
|
233 | ["{}://{}".format( | |
196 |
self.ldap_server_type, host |
|
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