ldap: use connection ping only in case of single server specifed.
This commit is contained in:
parent
e46713343e
commit
12d382294e
3 changed files with 96 additions and 57 deletions
|
|
@ -21,7 +21,8 @@
|
|||
"""
|
||||
Authentication modules
|
||||
"""
|
||||
|
||||
import socket
|
||||
import string
|
||||
import colander
|
||||
import copy
|
||||
import logging
|
||||
|
|
@ -31,14 +32,13 @@ import warnings
|
|||
import functools
|
||||
|
||||
from pyramid.threadlocal import get_current_registry
|
||||
from zope.cachedescriptors.property import Lazy as LazyProperty
|
||||
|
||||
from rhodecode.authentication.interface import IAuthnPluginRegistry
|
||||
from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
|
||||
from rhodecode.lib import caches
|
||||
from rhodecode.lib.auth import PasswordGenerator, _RhodeCodeCryptoBCrypt
|
||||
from rhodecode.lib.utils2 import safe_int
|
||||
from rhodecode.lib.utils2 import safe_str
|
||||
from rhodecode.lib.utils2 import safe_int, safe_str
|
||||
from rhodecode.lib.exceptions import LdapConnectionError
|
||||
from rhodecode.model.db import User
|
||||
from rhodecode.model.meta import Session
|
||||
from rhodecode.model.settings import SettingsModel
|
||||
|
|
@ -556,6 +556,63 @@ class RhodeCodeExternalAuthPlugin(RhodeCodeAuthPluginBase):
|
|||
return auth
|
||||
|
||||
|
||||
class AuthLdapBase(object):
|
||||
|
||||
@classmethod
|
||||
def _build_servers(cls, ldap_server_type, ldap_server, port):
|
||||
def host_resolver(host, port, full_resolve=True):
|
||||
"""
|
||||
Main work for this function is to prevent ldap connection issues,
|
||||
and detect them early using a "greenified" sockets
|
||||
"""
|
||||
host = host.strip()
|
||||
if not full_resolve:
|
||||
return '{}:{}'.format(host, port)
|
||||
|
||||
log.debug('LDAP: Resolving IP for LDAP host %s', host)
|
||||
try:
|
||||
ip = socket.gethostbyname(host)
|
||||
log.debug('Got LDAP server %s ip %s', host, ip)
|
||||
except Exception:
|
||||
raise LdapConnectionError(
|
||||
'Failed to resolve host: `{}`'.format(host))
|
||||
|
||||
log.debug('LDAP: Checking if IP %s is accessible', ip)
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
s.connect((ip, int(port)))
|
||||
s.shutdown(socket.SHUT_RD)
|
||||
except Exception:
|
||||
raise LdapConnectionError(
|
||||
'Failed to connect to host: `{}:{}`'.format(host, port))
|
||||
|
||||
return '{}:{}'.format(host, port)
|
||||
|
||||
if len(ldap_server) == 1:
|
||||
# in case of single server use resolver to detect potential
|
||||
# connection issues
|
||||
full_resolve = True
|
||||
else:
|
||||
full_resolve = False
|
||||
|
||||
return ', '.join(
|
||||
["{}://{}".format(
|
||||
ldap_server_type,
|
||||
host_resolver(host, port, full_resolve=full_resolve))
|
||||
for host in ldap_server])
|
||||
|
||||
@classmethod
|
||||
def _get_server_list(cls, servers):
|
||||
return map(string.strip, servers.split(','))
|
||||
|
||||
@classmethod
|
||||
def get_uid(cls, username, server_addresses):
|
||||
uid = username
|
||||
for server_addr in server_addresses:
|
||||
uid = chop_at(username, "@%s" % server_addr)
|
||||
return uid
|
||||
|
||||
|
||||
def loadplugin(plugin_id):
|
||||
"""
|
||||
Loads and returns an instantiated authentication plugin.
|
||||
|
|
@ -613,7 +670,7 @@ def authenticate(username, password, environ=None, auth_type=None,
|
|||
authn_registry = get_authn_registry(registry)
|
||||
plugins_to_check = authn_registry.get_plugins_for_authentication()
|
||||
log.debug('Starting ordered authentication chain using %s plugins',
|
||||
plugins_to_check)
|
||||
[x.name for x in plugins_to_check])
|
||||
for plugin in plugins_to_check:
|
||||
plugin.set_auth_type(auth_type)
|
||||
plugin.set_calling_scope_repo(acl_repo_name)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2012-2018 RhodeCode GmbH
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License, version 3
|
||||
# (only), as published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# This program is dual-licensed. If you wish to learn more about the
|
||||
# RhodeCode Enterprise Edition, including its added features, Support services,
|
||||
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
||||
|
|
@ -22,16 +22,13 @@
|
|||
RhodeCode authentication plugin for LDAP
|
||||
"""
|
||||
|
||||
import socket
|
||||
import re
|
||||
import colander
|
||||
import logging
|
||||
import traceback
|
||||
import string
|
||||
|
||||
import colander
|
||||
from rhodecode.translation import _
|
||||
from rhodecode.authentication.base import (
|
||||
RhodeCodeExternalAuthPlugin, chop_at, hybrid_property)
|
||||
RhodeCodeExternalAuthPlugin, AuthLdapBase, hybrid_property)
|
||||
from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
|
||||
from rhodecode.authentication.routes import AuthnPluginResourceBase
|
||||
from rhodecode.lib.colander_utils import strip_whitespace
|
||||
|
|
@ -199,40 +196,7 @@ class LdapSettingsSchema(AuthnPluginSettingsSchemaBase):
|
|||
widget='string')
|
||||
|
||||
|
||||
class AuthLdap(object):
|
||||
|
||||
def _build_servers(self):
|
||||
def host_resolver(host, port):
|
||||
"""
|
||||
Main work for this function is to prevent ldap connection issues,
|
||||
and detect them early using a "greenified" sockets
|
||||
"""
|
||||
host = host.strip()
|
||||
|
||||
log.info('Resolving LDAP host %s', host)
|
||||
try:
|
||||
ip = socket.gethostbyname(host)
|
||||
log.info('Got LDAP server %s ip %s', host, ip)
|
||||
except Exception:
|
||||
raise LdapConnectionError(
|
||||
'Failed to resolve host: `{}`'.format(host))
|
||||
|
||||
log.info('Checking LDAP IP access %s', ip)
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
s.connect((ip, int(port)))
|
||||
s.shutdown(socket.SHUT_RD)
|
||||
except Exception:
|
||||
raise LdapConnectionError(
|
||||
'Failed to connect to host: `{}:{}`'.format(host, port))
|
||||
|
||||
return '{}:{}'.format(host, port)
|
||||
|
||||
port = self.LDAP_SERVER_PORT
|
||||
return ', '.join(
|
||||
["{}://{}".format(
|
||||
self.ldap_server_type, host_resolver(host, port))
|
||||
for host in self.SERVER_ADDRESSES])
|
||||
class AuthLdap(AuthLdapBase):
|
||||
|
||||
def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
|
||||
tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3,
|
||||
|
|
@ -255,8 +219,9 @@ class AuthLdap(object):
|
|||
OPT_X_TLS_DEMAND = 2
|
||||
self.TLS_REQCERT = getattr(ldap, 'OPT_X_TLS_%s' % tls_reqcert,
|
||||
OPT_X_TLS_DEMAND)
|
||||
self.LDAP_SERVER = server
|
||||
# split server into list
|
||||
self.SERVER_ADDRESSES = server.split(',')
|
||||
self.SERVER_ADDRESSES = self._get_server_list(server)
|
||||
self.LDAP_SERVER_PORT = port
|
||||
|
||||
# USE FOR READ ONLY BIND TO LDAP SERVER
|
||||
|
|
@ -264,13 +229,12 @@ class AuthLdap(object):
|
|||
|
||||
self.LDAP_BIND_DN = safe_str(bind_dn)
|
||||
self.LDAP_BIND_PASS = safe_str(bind_pass)
|
||||
self.LDAP_SERVER = self._build_servers()
|
||||
|
||||
self.SEARCH_SCOPE = getattr(ldap, 'SCOPE_%s' % search_scope)
|
||||
self.BASE_DN = safe_str(base_dn)
|
||||
self.LDAP_FILTER = safe_str(ldap_filter)
|
||||
|
||||
def _get_ldap_conn(self):
|
||||
log.debug('initializing LDAP connection to:%s', self.LDAP_SERVER)
|
||||
|
||||
if self.debug:
|
||||
ldap.set_option(ldap.OPT_DEBUG_LEVEL, 255)
|
||||
|
|
@ -284,7 +248,10 @@ class AuthLdap(object):
|
|||
ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
|
||||
|
||||
# init connection now
|
||||
ldap_conn = ldap.initialize(self.LDAP_SERVER)
|
||||
ldap_servers = self._build_servers(
|
||||
self.ldap_server_type, self.SERVER_ADDRESSES, self.LDAP_SERVER_PORT)
|
||||
log.debug('initializing LDAP connection to:%s', ldap_servers)
|
||||
ldap_conn = ldap.initialize(ldap_servers)
|
||||
ldap_conn.set_option(ldap.OPT_NETWORK_TIMEOUT, self.timeout)
|
||||
ldap_conn.set_option(ldap.OPT_TIMEOUT, self.timeout)
|
||||
ldap_conn.timeout = self.timeout
|
||||
|
|
@ -304,12 +271,6 @@ class AuthLdap(object):
|
|||
|
||||
return ldap_conn
|
||||
|
||||
def get_uid(self, username):
|
||||
uid = username
|
||||
for server_addr in self.SERVER_ADDRESSES:
|
||||
uid = chop_at(username, "@%s" % server_addr)
|
||||
return uid
|
||||
|
||||
def fetch_attrs_from_simple_bind(self, server, dn, username, password):
|
||||
try:
|
||||
log.debug('Trying simple bind with %s', dn)
|
||||
|
|
@ -335,7 +296,9 @@ class AuthLdap(object):
|
|||
:param password: password
|
||||
"""
|
||||
|
||||
uid = self.get_uid(username)
|
||||
uid = self.get_uid(username, self.SERVER_ADDRESSES)
|
||||
user_attrs = {}
|
||||
dn = ''
|
||||
|
||||
if not password:
|
||||
msg = "Authenticating user %s with blank password not allowed"
|
||||
|
|
@ -349,8 +312,8 @@ class AuthLdap(object):
|
|||
ldap_conn = self._get_ldap_conn()
|
||||
filter_ = '(&%s(%s=%s))' % (
|
||||
self.LDAP_FILTER, self.attr_login, username)
|
||||
log.debug("Authenticating %r filter %s at %s", self.BASE_DN,
|
||||
filter_, self.LDAP_SERVER)
|
||||
log.debug(
|
||||
"Authenticating %r filter %s", self.BASE_DN, filter_)
|
||||
lobjects = ldap_conn.search_ext_s(
|
||||
self.BASE_DN, self.SEARCH_SCOPE, filter_)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue