##// END OF EJS Templates
ldap: use connection ping only in case of single server specifed.
marcink -
r2736:f1074c0b stable
parent child Browse files
Show More
@@ -21,7 +21,8 b''
21 21 """
22 22 Authentication modules
23 23 """
24
24 import socket
25 import string
25 26 import colander
26 27 import copy
27 28 import logging
@@ -31,14 +32,13 b' import warnings'
31 32 import functools
32 33
33 34 from pyramid.threadlocal import get_current_registry
34 from zope.cachedescriptors.property import Lazy as LazyProperty
35 35
36 36 from rhodecode.authentication.interface import IAuthnPluginRegistry
37 37 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
38 38 from rhodecode.lib import caches
39 39 from rhodecode.lib.auth import PasswordGenerator, _RhodeCodeCryptoBCrypt
40 from rhodecode.lib.utils2 import safe_int
41 from rhodecode.lib.utils2 import safe_str
40 from rhodecode.lib.utils2 import safe_int, safe_str
41 from rhodecode.lib.exceptions import LdapConnectionError
42 42 from rhodecode.model.db import User
43 43 from rhodecode.model.meta import Session
44 44 from rhodecode.model.settings import SettingsModel
@@ -556,6 +556,63 b' class RhodeCodeExternalAuthPlugin(RhodeC'
556 556 return auth
557 557
558 558
559 class AuthLdapBase(object):
560
561 @classmethod
562 def _build_servers(cls, ldap_server_type, ldap_server, port):
563 def host_resolver(host, port, full_resolve=True):
564 """
565 Main work for this function is to prevent ldap connection issues,
566 and detect them early using a "greenified" sockets
567 """
568 host = host.strip()
569 if not full_resolve:
570 return '{}:{}'.format(host, port)
571
572 log.debug('LDAP: Resolving IP for LDAP host %s', host)
573 try:
574 ip = socket.gethostbyname(host)
575 log.debug('Got LDAP server %s ip %s', host, ip)
576 except Exception:
577 raise LdapConnectionError(
578 'Failed to resolve host: `{}`'.format(host))
579
580 log.debug('LDAP: Checking if IP %s is accessible', ip)
581 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
582 try:
583 s.connect((ip, int(port)))
584 s.shutdown(socket.SHUT_RD)
585 except Exception:
586 raise LdapConnectionError(
587 'Failed to connect to host: `{}:{}`'.format(host, port))
588
589 return '{}:{}'.format(host, port)
590
591 if len(ldap_server) == 1:
592 # in case of single server use resolver to detect potential
593 # connection issues
594 full_resolve = True
595 else:
596 full_resolve = False
597
598 return ', '.join(
599 ["{}://{}".format(
600 ldap_server_type,
601 host_resolver(host, port, full_resolve=full_resolve))
602 for host in ldap_server])
603
604 @classmethod
605 def _get_server_list(cls, servers):
606 return map(string.strip, servers.split(','))
607
608 @classmethod
609 def get_uid(cls, username, server_addresses):
610 uid = username
611 for server_addr in server_addresses:
612 uid = chop_at(username, "@%s" % server_addr)
613 return uid
614
615
559 616 def loadplugin(plugin_id):
560 617 """
561 618 Loads and returns an instantiated authentication plugin.
@@ -613,7 +670,7 b' def authenticate(username, password, env'
613 670 authn_registry = get_authn_registry(registry)
614 671 plugins_to_check = authn_registry.get_plugins_for_authentication()
615 672 log.debug('Starting ordered authentication chain using %s plugins',
616 plugins_to_check)
673 [x.name for x in plugins_to_check])
617 674 for plugin in plugins_to_check:
618 675 plugin.set_auth_type(auth_type)
619 676 plugin.set_calling_scope_repo(acl_repo_name)
@@ -0,0 +1,19 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2012-2018 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
@@ -22,16 +22,13 b''
22 22 RhodeCode authentication plugin for LDAP
23 23 """
24 24
25 import socket
26 import re
27 import colander
28 25 import logging
29 26 import traceback
30 import string
31 27
28 import colander
32 29 from rhodecode.translation import _
33 30 from rhodecode.authentication.base import (
34 RhodeCodeExternalAuthPlugin, chop_at, hybrid_property)
31 RhodeCodeExternalAuthPlugin, AuthLdapBase, hybrid_property)
35 32 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
36 33 from rhodecode.authentication.routes import AuthnPluginResourceBase
37 34 from rhodecode.lib.colander_utils import strip_whitespace
@@ -199,40 +196,7 b' class LdapSettingsSchema(AuthnPluginSett'
199 196 widget='string')
200 197
201 198
202 class AuthLdap(object):
203
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
232 return ', '.join(
233 ["{}://{}".format(
234 self.ldap_server_type, host_resolver(host, port))
235 for host in self.SERVER_ADDRESSES])
199 class AuthLdap(AuthLdapBase):
236 200
237 201 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
238 202 tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3,
@@ -255,8 +219,9 b' class AuthLdap(object):'
255 219 OPT_X_TLS_DEMAND = 2
256 220 self.TLS_REQCERT = getattr(ldap, 'OPT_X_TLS_%s' % tls_reqcert,
257 221 OPT_X_TLS_DEMAND)
222 self.LDAP_SERVER = server
258 223 # split server into list
259 self.SERVER_ADDRESSES = server.split(',')
224 self.SERVER_ADDRESSES = self._get_server_list(server)
260 225 self.LDAP_SERVER_PORT = port
261 226
262 227 # USE FOR READ ONLY BIND TO LDAP SERVER
@@ -264,13 +229,12 b' class AuthLdap(object):'
264 229
265 230 self.LDAP_BIND_DN = safe_str(bind_dn)
266 231 self.LDAP_BIND_PASS = safe_str(bind_pass)
267 self.LDAP_SERVER = self._build_servers()
232
268 233 self.SEARCH_SCOPE = getattr(ldap, 'SCOPE_%s' % search_scope)
269 234 self.BASE_DN = safe_str(base_dn)
270 235 self.LDAP_FILTER = safe_str(ldap_filter)
271 236
272 237 def _get_ldap_conn(self):
273 log.debug('initializing LDAP connection to:%s', self.LDAP_SERVER)
274 238
275 239 if self.debug:
276 240 ldap.set_option(ldap.OPT_DEBUG_LEVEL, 255)
@@ -284,7 +248,10 b' class AuthLdap(object):'
284 248 ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
285 249
286 250 # init connection now
287 ldap_conn = ldap.initialize(self.LDAP_SERVER)
251 ldap_servers = self._build_servers(
252 self.ldap_server_type, self.SERVER_ADDRESSES, self.LDAP_SERVER_PORT)
253 log.debug('initializing LDAP connection to:%s', ldap_servers)
254 ldap_conn = ldap.initialize(ldap_servers)
288 255 ldap_conn.set_option(ldap.OPT_NETWORK_TIMEOUT, self.timeout)
289 256 ldap_conn.set_option(ldap.OPT_TIMEOUT, self.timeout)
290 257 ldap_conn.timeout = self.timeout
@@ -304,12 +271,6 b' class AuthLdap(object):'
304 271
305 272 return ldap_conn
306 273
307 def get_uid(self, username):
308 uid = username
309 for server_addr in self.SERVER_ADDRESSES:
310 uid = chop_at(username, "@%s" % server_addr)
311 return uid
312
313 274 def fetch_attrs_from_simple_bind(self, server, dn, username, password):
314 275 try:
315 276 log.debug('Trying simple bind with %s', dn)
@@ -335,7 +296,9 b' class AuthLdap(object):'
335 296 :param password: password
336 297 """
337 298
338 uid = self.get_uid(username)
299 uid = self.get_uid(username, self.SERVER_ADDRESSES)
300 user_attrs = {}
301 dn = ''
339 302
340 303 if not password:
341 304 msg = "Authenticating user %s with blank password not allowed"
@@ -349,8 +312,8 b' class AuthLdap(object):'
349 312 ldap_conn = self._get_ldap_conn()
350 313 filter_ = '(&%s(%s=%s))' % (
351 314 self.LDAP_FILTER, self.attr_login, username)
352 log.debug("Authenticating %r filter %s at %s", self.BASE_DN,
353 filter_, self.LDAP_SERVER)
315 log.debug(
316 "Authenticating %r filter %s", self.BASE_DN, filter_)
354 317 lobjects = ldap_conn.search_ext_s(
355 318 self.BASE_DN, self.SEARCH_SCOPE, filter_)
356 319
General Comments 0
You need to be logged in to leave comments. Login now