##// END OF EJS Templates
AD fix when search could return empty dn
marcink -
r1444:d17aa797 beta
parent child Browse files
Show More
@@ -1,144 +1,147 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.changelog
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 RhodeCode authentication library for LDAP
7 7
8 8 :created_on: Created on Nov 17, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27
28 28 from rhodecode.lib.exceptions import LdapConnectionError, LdapUsernameError, \
29 29 LdapPasswordError
30 30
31 31 log = logging.getLogger(__name__)
32 32
33 33
34 34 try:
35 35 import ldap
36 36 except ImportError:
37 37 # means that python-ldap is not installed
38 38 pass
39 39
40 40
41 41 class AuthLdap(object):
42 42
43 43 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
44 44 tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3,
45 45 ldap_filter='(&(objectClass=user)(!(objectClass=computer)))',
46 46 search_scope='SUBTREE',
47 47 attr_login='uid'):
48 48 self.ldap_version = ldap_version
49 49 ldap_server_type = 'ldap'
50 50
51 51 self.TLS_KIND = tls_kind
52 52
53 53 if self.TLS_KIND == 'LDAPS':
54 54 port = port or 689
55 55 ldap_server_type = ldap_server_type + 's'
56 56
57 57 self.TLS_REQCERT = ldap.__dict__['OPT_X_TLS_' + tls_reqcert]
58 58 self.LDAP_SERVER_ADDRESS = server
59 59 self.LDAP_SERVER_PORT = port
60 60
61 61 #USE FOR READ ONLY BIND TO LDAP SERVER
62 62 self.LDAP_BIND_DN = bind_dn
63 63 self.LDAP_BIND_PASS = bind_pass
64 64
65 65 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
66 66 self.LDAP_SERVER_ADDRESS,
67 67 self.LDAP_SERVER_PORT)
68 68
69 69 self.BASE_DN = base_dn
70 70 self.LDAP_FILTER = ldap_filter
71 71 self.SEARCH_SCOPE = ldap.__dict__['SCOPE_' + search_scope]
72 72 self.attr_login = attr_login
73 73
74 74 def authenticate_ldap(self, username, password):
75 75 """Authenticate a user via LDAP and return his/her LDAP properties.
76 76
77 77 Raises AuthenticationError if the credentials are rejected, or
78 78 EnvironmentError if the LDAP server can't be reached.
79 79
80 80 :param username: username
81 81 :param password: password
82 82 """
83 83
84 84 from rhodecode.lib.helpers import chop_at
85 85
86 86 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
87 87
88 88 if "," in username:
89 89 raise LdapUsernameError("invalid character in username: ,")
90 90 try:
91 91 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, '/etc/openldap/cacerts')
92 92 ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
93 93 ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
94 94 ldap.set_option(ldap.OPT_TIMEOUT, 20)
95 95 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
96 96 ldap.set_option(ldap.OPT_TIMELIMIT, 15)
97 97 if self.TLS_KIND != 'PLAIN':
98 98 ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT)
99 99 server = ldap.initialize(self.LDAP_SERVER)
100 100 if self.ldap_version == 2:
101 101 server.protocol = ldap.VERSION2
102 102 else:
103 103 server.protocol = ldap.VERSION3
104 104
105 105 if self.TLS_KIND == 'START_TLS':
106 106 server.start_tls_s()
107 107
108 108 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
109 109 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
110 110
111 111 filt = '(&%s(%s=%s))' % (self.LDAP_FILTER, self.attr_login,
112 112 username)
113 113 log.debug("Authenticating %r filt %s at %s", self.BASE_DN,
114 114 filt, self.LDAP_SERVER)
115 115 lobjects = server.search_ext_s(self.BASE_DN, self.SEARCH_SCOPE,
116 116 filt)
117 117
118 118 if not lobjects:
119 119 raise ldap.NO_SUCH_OBJECT()
120 120
121 121 for (dn, _attrs) in lobjects:
122 if dn is None:
123 continue
124
122 125 try:
123 126 server.simple_bind_s(dn, password)
124 127 attrs = server.search_ext_s(dn, ldap.SCOPE_BASE,
125 128 '(objectClass=*)')[0][1]
126 129 break
127 130
128 131 except ldap.INVALID_CREDENTIALS, e:
129 132 log.debug("LDAP rejected password for user '%s' (%s): %s",
130 133 uid, username, dn)
131 134
132 135 else:
133 136 log.debug("No matching LDAP objects for authentication "
134 137 "of '%s' (%s)", uid, username)
135 138 raise LdapPasswordError()
136 139
137 140 except ldap.NO_SUCH_OBJECT, e:
138 141 log.debug("LDAP says no such user '%s' (%s)", uid, username)
139 142 raise LdapUsernameError()
140 143 except ldap.SERVER_DOWN, e:
141 144 raise LdapConnectionError("LDAP can't access "
142 145 "authentication server")
143 146
144 147 return (dn, attrs)
General Comments 0
You need to be logged in to leave comments. Login now