Show More
@@ -89,7 +89,7 b' class TestAuthSettingsView(object):' | |||
|
89 | 89 | 'timeout': 3600, |
|
90 | 90 | 'tls_kind': 'PLAIN', |
|
91 | 91 | 'tls_reqcert': 'NEVER', |
|
92 | ||
|
92 | 'tls_cert_dir':'/etc/openldap/cacerts', | |
|
93 | 93 | 'dn_user': 'test_user', |
|
94 | 94 | 'dn_pass': 'test_pass', |
|
95 | 95 | 'base_dn': 'test_base_dn', |
@@ -38,7 +38,8 b' from rhodecode.authentication.schema imp' | |||
|
38 | 38 | from rhodecode.lib import rc_cache |
|
39 | 39 | from rhodecode.lib.auth import PasswordGenerator, _RhodeCodeCryptoBCrypt |
|
40 | 40 | from rhodecode.lib.utils2 import safe_int, safe_str |
|
41 | from rhodecode.lib.exceptions import LdapConnectionError | |
|
41 | from rhodecode.lib.exceptions import LdapConnectionError, LdapUsernameError, \ | |
|
42 | LdapPasswordError | |
|
42 | 43 | from rhodecode.model.db import User |
|
43 | 44 | from rhodecode.model.meta import Session |
|
44 | 45 | from rhodecode.model.settings import SettingsModel |
@@ -577,7 +578,8 b' class RhodeCodeExternalAuthPlugin(RhodeC' | |||
|
577 | 578 | class AuthLdapBase(object): |
|
578 | 579 | |
|
579 | 580 | @classmethod |
|
580 | def _build_servers(cls, ldap_server_type, ldap_server, port): | |
|
581 | def _build_servers(cls, ldap_server_type, ldap_server, port, use_resolver=True): | |
|
582 | ||
|
581 | 583 | def host_resolver(host, port, full_resolve=True): |
|
582 | 584 | """ |
|
583 | 585 | Main work for this function is to prevent ldap connection issues, |
@@ -616,7 +618,7 b' class AuthLdapBase(object):' | |||
|
616 | 618 | return ', '.join( |
|
617 | 619 | ["{}://{}".format( |
|
618 | 620 | ldap_server_type, |
|
619 | host_resolver(host, port, full_resolve=full_resolve)) | |
|
621 | host_resolver(host, port, full_resolve=use_resolver and full_resolve)) | |
|
620 | 622 | for host in ldap_server]) |
|
621 | 623 | |
|
622 | 624 | @classmethod |
@@ -630,6 +632,19 b' class AuthLdapBase(object):' | |||
|
630 | 632 | uid = chop_at(username, "@%s" % server_addr) |
|
631 | 633 | return uid |
|
632 | 634 | |
|
635 | @classmethod | |
|
636 | def validate_username(cls, username): | |
|
637 | if "," in username: | |
|
638 | raise LdapUsernameError( | |
|
639 | "invalid character `,` in username: `{}`".format(username)) | |
|
640 | ||
|
641 | @classmethod | |
|
642 | def validate_password(cls, username, password): | |
|
643 | if not password: | |
|
644 | msg = "Authenticating user %s with blank password not allowed" | |
|
645 | log.warning(msg, username) | |
|
646 | raise LdapPasswordError(msg) | |
|
647 | ||
|
633 | 648 | |
|
634 | 649 | def loadplugin(plugin_id): |
|
635 | 650 | """ |
@@ -22,7 +22,6 b'' | |||
|
22 | 22 | RhodeCode authentication plugin for LDAP |
|
23 | 23 | """ |
|
24 | 24 | |
|
25 | import os | |
|
26 | 25 | import logging |
|
27 | 26 | import traceback |
|
28 | 27 | |
@@ -67,6 +66,171 b' class LdapAuthnResource(AuthnPluginResou' | |||
|
67 | 66 | pass |
|
68 | 67 | |
|
69 | 68 | |
|
69 | class AuthLdap(AuthLdapBase): | |
|
70 | default_tls_cert_dir = '/etc/openldap/cacerts' | |
|
71 | ||
|
72 | def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='', | |
|
73 | tls_kind='PLAIN', tls_reqcert='DEMAND', tls_cert_file=None, | |
|
74 | tls_cert_dir=None, ldap_version=3, | |
|
75 | search_scope='SUBTREE', attr_login='uid', | |
|
76 | ldap_filter='', timeout=None): | |
|
77 | if ldap == Missing: | |
|
78 | raise LdapImportError("Missing or incompatible ldap library") | |
|
79 | ||
|
80 | self.debug = False | |
|
81 | self.timeout = timeout or 60 * 5 | |
|
82 | self.ldap_version = ldap_version | |
|
83 | self.ldap_server_type = 'ldap' | |
|
84 | ||
|
85 | self.TLS_KIND = tls_kind | |
|
86 | ||
|
87 | if self.TLS_KIND == 'LDAPS': | |
|
88 | port = port or 689 | |
|
89 | self.ldap_server_type += 's' | |
|
90 | ||
|
91 | OPT_X_TLS_DEMAND = 2 | |
|
92 | self.TLS_REQCERT = getattr(ldap, 'OPT_X_TLS_%s' % tls_reqcert, OPT_X_TLS_DEMAND) | |
|
93 | self.TLS_CERT_FILE = tls_cert_file or '' | |
|
94 | self.TLS_CERT_DIR = tls_cert_dir or self.default_tls_cert_dir | |
|
95 | ||
|
96 | # split server into list | |
|
97 | self.SERVER_ADDRESSES = self._get_server_list(server) | |
|
98 | self.LDAP_SERVER_PORT = port | |
|
99 | ||
|
100 | # USE FOR READ ONLY BIND TO LDAP SERVER | |
|
101 | self.attr_login = attr_login | |
|
102 | ||
|
103 | self.LDAP_BIND_DN = safe_str(bind_dn) | |
|
104 | self.LDAP_BIND_PASS = safe_str(bind_pass) | |
|
105 | ||
|
106 | self.SEARCH_SCOPE = getattr(ldap, 'SCOPE_%s' % search_scope) | |
|
107 | self.BASE_DN = safe_str(base_dn) | |
|
108 | self.LDAP_FILTER = safe_str(ldap_filter) | |
|
109 | ||
|
110 | def _get_ldap_conn(self): | |
|
111 | ||
|
112 | if self.debug: | |
|
113 | ldap.set_option(ldap.OPT_DEBUG_LEVEL, 255) | |
|
114 | ||
|
115 | if self.TLS_CERT_FILE and hasattr(ldap, 'OPT_X_TLS_CACERTFILE'): | |
|
116 | ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, self.TLS_CERT_FILE) | |
|
117 | ||
|
118 | elif hasattr(ldap, 'OPT_X_TLS_CACERTDIR'): | |
|
119 | ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, self.TLS_CERT_DIR) | |
|
120 | ||
|
121 | if self.TLS_KIND != 'PLAIN': | |
|
122 | ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT) | |
|
123 | ||
|
124 | ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF) | |
|
125 | ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON) | |
|
126 | ||
|
127 | # init connection now | |
|
128 | ldap_servers = self._build_servers( | |
|
129 | self.ldap_server_type, self.SERVER_ADDRESSES, self.LDAP_SERVER_PORT) | |
|
130 | log.debug('initializing LDAP connection to:%s', ldap_servers) | |
|
131 | ldap_conn = ldap.initialize(ldap_servers) | |
|
132 | ldap_conn.set_option(ldap.OPT_NETWORK_TIMEOUT, self.timeout) | |
|
133 | ldap_conn.set_option(ldap.OPT_TIMEOUT, self.timeout) | |
|
134 | ldap_conn.timeout = self.timeout | |
|
135 | ||
|
136 | if self.ldap_version == 2: | |
|
137 | ldap_conn.protocol = ldap.VERSION2 | |
|
138 | else: | |
|
139 | ldap_conn.protocol = ldap.VERSION3 | |
|
140 | ||
|
141 | if self.TLS_KIND == 'START_TLS': | |
|
142 | ldap_conn.start_tls_s() | |
|
143 | ||
|
144 | if self.LDAP_BIND_DN and self.LDAP_BIND_PASS: | |
|
145 | log.debug('Trying simple_bind with password and given login DN: %s', | |
|
146 | self.LDAP_BIND_DN) | |
|
147 | ldap_conn.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS) | |
|
148 | ||
|
149 | return ldap_conn | |
|
150 | ||
|
151 | def fetch_attrs_from_simple_bind(self, server, dn, username, password): | |
|
152 | try: | |
|
153 | log.debug('Trying simple bind with %s', dn) | |
|
154 | server.simple_bind_s(dn, safe_str(password)) | |
|
155 | user = server.search_ext_s( | |
|
156 | dn, ldap.SCOPE_BASE, '(objectClass=*)', )[0] | |
|
157 | _, attrs = user | |
|
158 | return attrs | |
|
159 | ||
|
160 | except ldap.INVALID_CREDENTIALS: | |
|
161 | log.debug( | |
|
162 | "LDAP rejected password for user '%s': %s, org_exc:", | |
|
163 | username, dn, exc_info=True) | |
|
164 | ||
|
165 | def authenticate_ldap(self, username, password): | |
|
166 | """ | |
|
167 | Authenticate a user via LDAP and return his/her LDAP properties. | |
|
168 | ||
|
169 | Raises AuthenticationError if the credentials are rejected, or | |
|
170 | EnvironmentError if the LDAP server can't be reached. | |
|
171 | ||
|
172 | :param username: username | |
|
173 | :param password: password | |
|
174 | """ | |
|
175 | ||
|
176 | uid = self.get_uid(username, self.SERVER_ADDRESSES) | |
|
177 | user_attrs = {} | |
|
178 | dn = '' | |
|
179 | ||
|
180 | self.validate_password(username, password) | |
|
181 | self.validate_username(username) | |
|
182 | ||
|
183 | ldap_conn = None | |
|
184 | try: | |
|
185 | ldap_conn = self._get_ldap_conn() | |
|
186 | filter_ = '(&%s(%s=%s))' % ( | |
|
187 | self.LDAP_FILTER, self.attr_login, username) | |
|
188 | log.debug("Authenticating %r filter %s", self.BASE_DN, filter_) | |
|
189 | ||
|
190 | lobjects = ldap_conn.search_ext_s( | |
|
191 | self.BASE_DN, self.SEARCH_SCOPE, filter_) | |
|
192 | ||
|
193 | if not lobjects: | |
|
194 | log.debug("No matching LDAP objects for authentication " | |
|
195 | "of UID:'%s' username:(%s)", uid, username) | |
|
196 | raise ldap.NO_SUCH_OBJECT() | |
|
197 | ||
|
198 | log.debug('Found matching ldap object, trying to authenticate') | |
|
199 | for (dn, _attrs) in lobjects: | |
|
200 | if dn is None: | |
|
201 | continue | |
|
202 | ||
|
203 | user_attrs = self.fetch_attrs_from_simple_bind( | |
|
204 | ldap_conn, dn, username, password) | |
|
205 | if user_attrs: | |
|
206 | break | |
|
207 | else: | |
|
208 | raise LdapPasswordError( | |
|
209 | 'Failed to authenticate user `{}`' | |
|
210 | 'with given password'.format(username)) | |
|
211 | ||
|
212 | except ldap.NO_SUCH_OBJECT: | |
|
213 | log.debug("LDAP says no such user '%s' (%s), org_exc:", | |
|
214 | uid, username, exc_info=True) | |
|
215 | raise LdapUsernameError('Unable to find user') | |
|
216 | except ldap.SERVER_DOWN: | |
|
217 | org_exc = traceback.format_exc() | |
|
218 | raise LdapConnectionError( | |
|
219 | "LDAP can't access authentication " | |
|
220 | "server, org_exc:%s" % org_exc) | |
|
221 | finally: | |
|
222 | if ldap_conn: | |
|
223 | log.debug('ldap: connection release') | |
|
224 | try: | |
|
225 | ldap_conn.unbind_s() | |
|
226 | except Exception: | |
|
227 | # for any reason this can raise exception we must catch it | |
|
228 | # to not crush the server | |
|
229 | pass | |
|
230 | ||
|
231 | return dn, user_attrs | |
|
232 | ||
|
233 | ||
|
70 | 234 | class LdapSettingsSchema(AuthnPluginSettingsSchemaBase): |
|
71 | 235 | tls_kind_choices = ['PLAIN', 'LDAPS', 'START_TLS'] |
|
72 | 236 | tls_reqcert_choices = ['NEVER', 'ALLOW', 'TRY', 'DEMAND', 'HARD'] |
@@ -134,6 +298,22 b' class LdapSettingsSchema(AuthnPluginSett' | |||
|
134 | 298 | title=_('Certificate Checks'), |
|
135 | 299 | validator=colander.OneOf(tls_reqcert_choices), |
|
136 | 300 | widget='select') |
|
301 | tls_cert_file = colander.SchemaNode( | |
|
302 | colander.String(), | |
|
303 | default='', | |
|
304 | description=_('This specifies the PEM-format file path containing ' | |
|
305 | 'certificates for use in TLS connection.\n' | |
|
306 | 'If not specified `TLS Cert dir` will be used'), | |
|
307 | title=_('TLS Cert file'), | |
|
308 | missing='', | |
|
309 | widget='string') | |
|
310 | tls_cert_dir = colander.SchemaNode( | |
|
311 | colander.String(), | |
|
312 | default=AuthLdap.default_tls_cert_dir, | |
|
313 | description=_('This specifies the path of a directory that contains individual ' | |
|
314 | 'CA certificates in separate files.'), | |
|
315 | title=_('TLS Cert dir'), | |
|
316 | widget='string') | |
|
137 | 317 | base_dn = colander.SchemaNode( |
|
138 | 318 | colander.String(), |
|
139 | 319 | default='', |
@@ -198,175 +378,6 b' class LdapSettingsSchema(AuthnPluginSett' | |||
|
198 | 378 | widget='string') |
|
199 | 379 | |
|
200 | 380 | |
|
201 | class AuthLdap(AuthLdapBase): | |
|
202 | ||
|
203 | def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='', | |
|
204 | tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3, | |
|
205 | search_scope='SUBTREE', attr_login='uid', | |
|
206 | ldap_filter='', timeout=None): | |
|
207 | if ldap == Missing: | |
|
208 | raise LdapImportError("Missing or incompatible ldap library") | |
|
209 | ||
|
210 | self.debug = False | |
|
211 | self.timeout = timeout or 60 * 5 | |
|
212 | self.ldap_version = ldap_version | |
|
213 | self.ldap_server_type = 'ldap' | |
|
214 | ||
|
215 | self.TLS_KIND = tls_kind | |
|
216 | ||
|
217 | if self.TLS_KIND == 'LDAPS': | |
|
218 | port = port or 689 | |
|
219 | self.ldap_server_type += 's' | |
|
220 | ||
|
221 | OPT_X_TLS_DEMAND = 2 | |
|
222 | self.TLS_REQCERT = getattr(ldap, 'OPT_X_TLS_%s' % tls_reqcert, | |
|
223 | OPT_X_TLS_DEMAND) | |
|
224 | self.LDAP_SERVER = server | |
|
225 | # split server into list | |
|
226 | self.SERVER_ADDRESSES = self._get_server_list(server) | |
|
227 | self.LDAP_SERVER_PORT = port | |
|
228 | ||
|
229 | # USE FOR READ ONLY BIND TO LDAP SERVER | |
|
230 | self.attr_login = attr_login | |
|
231 | ||
|
232 | self.LDAP_BIND_DN = safe_str(bind_dn) | |
|
233 | self.LDAP_BIND_PASS = safe_str(bind_pass) | |
|
234 | ||
|
235 | self.SEARCH_SCOPE = getattr(ldap, 'SCOPE_%s' % search_scope) | |
|
236 | self.BASE_DN = safe_str(base_dn) | |
|
237 | self.LDAP_FILTER = safe_str(ldap_filter) | |
|
238 | ||
|
239 | def _get_ldap_conn(self): | |
|
240 | ||
|
241 | if self.debug: | |
|
242 | ldap.set_option(ldap.OPT_DEBUG_LEVEL, 255) | |
|
243 | ||
|
244 | default_cert_path = os.environ.get('SSL_CERT_FILE') | |
|
245 | default_cert_dir = os.environ.get('SSL_CERT_DIR', '/etc/openldap/cacerts') | |
|
246 | if default_cert_path and hasattr(ldap, 'OPT_X_TLS_CACERTFILE'): | |
|
247 | ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, default_cert_path) | |
|
248 | ||
|
249 | elif hasattr(ldap, 'OPT_X_TLS_CACERTDIR'): | |
|
250 | ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, default_cert_dir) | |
|
251 | ||
|
252 | if self.TLS_KIND != 'PLAIN': | |
|
253 | ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT) | |
|
254 | ||
|
255 | ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF) | |
|
256 | ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON) | |
|
257 | ||
|
258 | # init connection now | |
|
259 | ldap_servers = self._build_servers( | |
|
260 | self.ldap_server_type, self.SERVER_ADDRESSES, self.LDAP_SERVER_PORT) | |
|
261 | log.debug('initializing LDAP connection to:%s', ldap_servers) | |
|
262 | ldap_conn = ldap.initialize(ldap_servers) | |
|
263 | ldap_conn.set_option(ldap.OPT_NETWORK_TIMEOUT, self.timeout) | |
|
264 | ldap_conn.set_option(ldap.OPT_TIMEOUT, self.timeout) | |
|
265 | ldap_conn.timeout = self.timeout | |
|
266 | ||
|
267 | if self.ldap_version == 2: | |
|
268 | ldap_conn.protocol = ldap.VERSION2 | |
|
269 | else: | |
|
270 | ldap_conn.protocol = ldap.VERSION3 | |
|
271 | ||
|
272 | if self.TLS_KIND == 'START_TLS': | |
|
273 | ldap_conn.start_tls_s() | |
|
274 | ||
|
275 | if self.LDAP_BIND_DN and self.LDAP_BIND_PASS: | |
|
276 | log.debug('Trying simple_bind with password and given login DN: %s', | |
|
277 | self.LDAP_BIND_DN) | |
|
278 | ldap_conn.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS) | |
|
279 | ||
|
280 | return ldap_conn | |
|
281 | ||
|
282 | def fetch_attrs_from_simple_bind(self, server, dn, username, password): | |
|
283 | try: | |
|
284 | log.debug('Trying simple bind with %s', dn) | |
|
285 | server.simple_bind_s(dn, safe_str(password)) | |
|
286 | user = server.search_ext_s( | |
|
287 | dn, ldap.SCOPE_BASE, '(objectClass=*)', )[0] | |
|
288 | _, attrs = user | |
|
289 | return attrs | |
|
290 | ||
|
291 | except ldap.INVALID_CREDENTIALS: | |
|
292 | log.debug( | |
|
293 | "LDAP rejected password for user '%s': %s, org_exc:", | |
|
294 | username, dn, exc_info=True) | |
|
295 | ||
|
296 | def authenticate_ldap(self, username, password): | |
|
297 | """ | |
|
298 | Authenticate a user via LDAP and return his/her LDAP properties. | |
|
299 | ||
|
300 | Raises AuthenticationError if the credentials are rejected, or | |
|
301 | EnvironmentError if the LDAP server can't be reached. | |
|
302 | ||
|
303 | :param username: username | |
|
304 | :param password: password | |
|
305 | """ | |
|
306 | ||
|
307 | uid = self.get_uid(username, self.SERVER_ADDRESSES) | |
|
308 | user_attrs = {} | |
|
309 | dn = '' | |
|
310 | ||
|
311 | if not password: | |
|
312 | msg = "Authenticating user %s with blank password not allowed" | |
|
313 | log.warning(msg, username) | |
|
314 | raise LdapPasswordError(msg) | |
|
315 | if "," in username: | |
|
316 | raise LdapUsernameError( | |
|
317 | "invalid character `,` in username: `{}`".format(username)) | |
|
318 | ldap_conn = None | |
|
319 | try: | |
|
320 | ldap_conn = self._get_ldap_conn() | |
|
321 | filter_ = '(&%s(%s=%s))' % ( | |
|
322 | self.LDAP_FILTER, self.attr_login, username) | |
|
323 | log.debug( | |
|
324 | "Authenticating %r filter %s", self.BASE_DN, filter_) | |
|
325 | lobjects = ldap_conn.search_ext_s( | |
|
326 | self.BASE_DN, self.SEARCH_SCOPE, filter_) | |
|
327 | ||
|
328 | if not lobjects: | |
|
329 | log.debug("No matching LDAP objects for authentication " | |
|
330 | "of UID:'%s' username:(%s)", uid, username) | |
|
331 | raise ldap.NO_SUCH_OBJECT() | |
|
332 | ||
|
333 | log.debug('Found matching ldap object, trying to authenticate') | |
|
334 | for (dn, _attrs) in lobjects: | |
|
335 | if dn is None: | |
|
336 | continue | |
|
337 | ||
|
338 | user_attrs = self.fetch_attrs_from_simple_bind( | |
|
339 | ldap_conn, dn, username, password) | |
|
340 | if user_attrs: | |
|
341 | break | |
|
342 | ||
|
343 | else: | |
|
344 | raise LdapPasswordError( | |
|
345 | 'Failed to authenticate user `{}`' | |
|
346 | 'with given password'.format(username)) | |
|
347 | ||
|
348 | except ldap.NO_SUCH_OBJECT: | |
|
349 | log.debug("LDAP says no such user '%s' (%s), org_exc:", | |
|
350 | uid, username, exc_info=True) | |
|
351 | raise LdapUsernameError('Unable to find user') | |
|
352 | except ldap.SERVER_DOWN: | |
|
353 | org_exc = traceback.format_exc() | |
|
354 | raise LdapConnectionError( | |
|
355 | "LDAP can't access authentication " | |
|
356 | "server, org_exc:%s" % org_exc) | |
|
357 | finally: | |
|
358 | if ldap_conn: | |
|
359 | log.debug('ldap: connection release') | |
|
360 | try: | |
|
361 | ldap_conn.unbind_s() | |
|
362 | except Exception: | |
|
363 | # for any reason this can raise exception we must catch it | |
|
364 | # to not crush the server | |
|
365 | pass | |
|
366 | ||
|
367 | return dn, user_attrs | |
|
368 | ||
|
369 | ||
|
370 | 381 | class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin): |
|
371 | 382 | # used to define dynamic binding in the |
|
372 | 383 | DYNAMIC_BIND_VAR = '$login' |
@@ -459,6 +470,8 b' class RhodeCodeAuthPlugin(RhodeCodeExter' | |||
|
459 | 470 | 'bind_pass': settings.get('dn_pass'), |
|
460 | 471 | 'tls_kind': settings.get('tls_kind'), |
|
461 | 472 | 'tls_reqcert': settings.get('tls_reqcert'), |
|
473 | 'tls_cert_file': settings.get('tls_cert_file'), | |
|
474 | 'tls_cert_dir': settings.get('tls_cert_dir'), | |
|
462 | 475 | 'search_scope': settings.get('search_scope'), |
|
463 | 476 | 'attr_login': settings.get('attr_login'), |
|
464 | 477 | 'ldap_version': 3, |
@@ -490,10 +503,8 b' class RhodeCodeAuthPlugin(RhodeCodeExter' | |||
|
490 | 503 | groups = [] |
|
491 | 504 | user_attrs = { |
|
492 | 505 | 'username': username, |
|
493 | 'firstname': safe_unicode( | |
|
494 |
|
|
|
495 | 'lastname': safe_unicode( | |
|
496 | get_ldap_attr('attr_lastname') or lastname), | |
|
506 | 'firstname': safe_unicode(get_ldap_attr('attr_firstname') or firstname), | |
|
507 | 'lastname': safe_unicode(get_ldap_attr('attr_lastname') or lastname), | |
|
497 | 508 | 'groups': groups, |
|
498 | 509 | 'user_group_sync': False, |
|
499 | 510 | 'email': get_ldap_attr('attr_email') or email, |
@@ -503,6 +514,7 b' class RhodeCodeAuthPlugin(RhodeCodeExter' | |||
|
503 | 514 | 'extern_name': user_dn, |
|
504 | 515 | 'extern_type': extern_type, |
|
505 | 516 | } |
|
517 | ||
|
506 | 518 | log.debug('ldap user: %s', user_attrs) |
|
507 | 519 | log.info('user `%s` authenticated correctly', user_attrs['username']) |
|
508 | 520 | |
@@ -514,4 +526,3 b' class RhodeCodeAuthPlugin(RhodeCodeExter' | |||
|
514 | 526 | except (Exception,): |
|
515 | 527 | log.exception("Other exception") |
|
516 | 528 | return None |
|
517 |
General Comments 0
You need to be logged in to leave comments.
Login now