001
014
015 package com.liferay.portal.security.auth;
016
017 import com.liferay.portal.NoSuchUserException;
018 import com.liferay.portal.PasswordExpiredException;
019 import com.liferay.portal.UserLockoutException;
020 import com.liferay.portal.kernel.ldap.LDAPFilterException;
021 import com.liferay.portal.kernel.log.Log;
022 import com.liferay.portal.kernel.log.LogFactoryUtil;
023 import com.liferay.portal.kernel.util.AutoResetThreadLocal;
024 import com.liferay.portal.kernel.util.GetterUtil;
025 import com.liferay.portal.kernel.util.MapUtil;
026 import com.liferay.portal.kernel.util.PropsKeys;
027 import com.liferay.portal.kernel.util.StringBundler;
028 import com.liferay.portal.kernel.util.StringPool;
029 import com.liferay.portal.kernel.util.StringUtil;
030 import com.liferay.portal.kernel.util.Validator;
031 import com.liferay.portal.model.User;
032 import com.liferay.portal.security.ldap.LDAPSettingsUtil;
033 import com.liferay.portal.security.ldap.LDAPUserImporterUtil;
034 import com.liferay.portal.security.ldap.PortalLDAPUtil;
035 import com.liferay.portal.security.pwd.PasswordEncryptorUtil;
036 import com.liferay.portal.service.UserLocalServiceUtil;
037 import com.liferay.portal.util.PrefsPropsUtil;
038 import com.liferay.portal.util.PropsValues;
039 import com.liferay.portlet.admin.util.OmniadminUtil;
040
041 import java.util.HashMap;
042 import java.util.Hashtable;
043 import java.util.Map;
044 import java.util.Properties;
045
046 import javax.naming.Context;
047 import javax.naming.NamingEnumeration;
048 import javax.naming.directory.Attribute;
049 import javax.naming.directory.Attributes;
050 import javax.naming.directory.SearchControls;
051 import javax.naming.directory.SearchResult;
052 import javax.naming.ldap.Control;
053 import javax.naming.ldap.InitialLdapContext;
054 import javax.naming.ldap.LdapContext;
055
056
061 public class LDAPAuth implements Authenticator {
062
063 public static final String AUTH_METHOD_BIND = "bind";
064
065 public static final String AUTH_METHOD_PASSWORD_COMPARE =
066 "password-compare";
067
068 public static final String RESULT_PASSWORD_EXP_WARNING =
069 "2.16.840.1.113730.3.4.5";
070
071 public static final String RESULT_PASSWORD_RESET =
072 "2.16.840.1.113730.3.4.4";
073
074 @Override
075 public int authenticateByEmailAddress(
076 long companyId, String emailAddress, String password,
077 Map<String, String[]> headerMap, Map<String, String[]> parameterMap)
078 throws AuthException {
079
080 try {
081 return authenticate(
082 companyId, emailAddress, StringPool.BLANK, 0, password);
083 }
084 catch (Exception e) {
085 _log.error(e, e);
086
087 throw new AuthException(e);
088 }
089 }
090
091 @Override
092 public int authenticateByScreenName(
093 long companyId, String screenName, String password,
094 Map<String, String[]> headerMap, Map<String, String[]> parameterMap)
095 throws AuthException {
096
097 try {
098 return authenticate(
099 companyId, StringPool.BLANK, screenName, 0, password);
100 }
101 catch (Exception e) {
102 _log.error(e, e);
103
104 throw new AuthException(e);
105 }
106 }
107
108 @Override
109 public int authenticateByUserId(
110 long companyId, long userId, String password,
111 Map<String, String[]> headerMap, Map<String, String[]> parameterMap)
112 throws AuthException {
113
114 try {
115 return authenticate(
116 companyId, StringPool.BLANK, StringPool.BLANK, userId,
117 password);
118 }
119 catch (Exception e) {
120 _log.error(e, e);
121
122 throw new AuthException(e);
123 }
124 }
125
126 protected LDAPAuthResult authenticate(
127 LdapContext ctx, long companyId, Attributes attributes,
128 String userDN, String password)
129 throws Exception {
130
131 LDAPAuthResult ldapAuthResult = null;
132
133
134
135
136
137 String authMethod = PrefsPropsUtil.getString(
138 companyId, PropsKeys.LDAP_AUTH_METHOD);
139
140 if (authMethod.equals(AUTH_METHOD_BIND)) {
141 Hashtable<String, Object> env =
142 (Hashtable<String, Object>)ctx.getEnvironment();
143
144 env.put(Context.SECURITY_PRINCIPAL, userDN);
145 env.put(Context.SECURITY_CREDENTIALS, password);
146 env.put(
147 Context.REFERRAL,
148 PrefsPropsUtil.getString(companyId, PropsKeys.LDAP_REFERRAL));
149
150
151
152 env.put("com.sun.jndi.ldap.connect.pool", "false");
153
154 ldapAuthResult = getFailedLDAPAuthResult(env);
155
156 if (ldapAuthResult != null) {
157 return ldapAuthResult;
158 }
159
160 ldapAuthResult = new LDAPAuthResult();
161
162 InitialLdapContext initialLdapContext = null;
163
164 try {
165 initialLdapContext = new InitialLdapContext(env, null);
166
167
168
169 Control[] responseControls =
170 initialLdapContext.getResponseControls();
171
172 ldapAuthResult.setAuthenticated(true);
173 ldapAuthResult.setResponseControl(responseControls);
174 }
175 catch (Exception e) {
176 if (_log.isDebugEnabled()) {
177 _log.debug(
178 "Failed to bind to the LDAP server with userDN " +
179 userDN + " and password " + password,
180 e);
181 }
182
183 ldapAuthResult.setAuthenticated(false);
184 ldapAuthResult.setErrorMessage(e.getMessage());
185
186 setFailedLDAPAuthResult(env, ldapAuthResult);
187 }
188 finally {
189 if (initialLdapContext != null) {
190 initialLdapContext.close();
191 }
192 }
193 }
194 else if (authMethod.equals(AUTH_METHOD_PASSWORD_COMPARE)) {
195 ldapAuthResult = new LDAPAuthResult();
196
197 Attribute userPassword = attributes.get("userPassword");
198
199 if (userPassword != null) {
200 String ldapPassword = new String((byte[])userPassword.get());
201
202 String encryptedPassword = password;
203
204 String algorithm = PrefsPropsUtil.getString(
205 companyId,
206 PropsKeys.LDAP_AUTH_PASSWORD_ENCRYPTION_ALGORITHM);
207
208 if (Validator.isNotNull(algorithm)) {
209 encryptedPassword = PasswordEncryptorUtil.encrypt(
210 algorithm, password, ldapPassword);
211 }
212
213 if (ldapPassword.equals(encryptedPassword)) {
214 ldapAuthResult.setAuthenticated(true);
215 }
216 else {
217 ldapAuthResult.setAuthenticated(false);
218
219 if (_log.isDebugEnabled()) {
220 _log.debug(
221 "Passwords do not match for userDN " + userDN);
222 }
223 }
224 }
225 }
226
227 return ldapAuthResult;
228 }
229
230 protected int authenticate(
231 long ldapServerId, long companyId, String emailAddress,
232 String screenName, long userId, String password)
233 throws Exception {
234
235 String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);
236
237 LdapContext ldapContext = PortalLDAPUtil.getContext(
238 ldapServerId, companyId);
239
240 if (ldapContext == null) {
241 return FAILURE;
242 }
243
244 NamingEnumeration<SearchResult> enu = null;
245
246 try {
247 String baseDN = PrefsPropsUtil.getString(
248 companyId, PropsKeys.LDAP_BASE_DN + postfix);
249
250
251
252 String filter = LDAPSettingsUtil.getAuthSearchFilter(
253 ldapServerId, companyId, emailAddress, screenName,
254 String.valueOf(userId));
255
256 Properties userMappings = LDAPSettingsUtil.getUserMappings(
257 ldapServerId, companyId);
258
259 String userMappingsScreenName = GetterUtil.getString(
260 userMappings.getProperty("screenName"));
261
262 userMappingsScreenName = StringUtil.toLowerCase(
263 userMappingsScreenName);
264
265 SearchControls searchControls = new SearchControls(
266 SearchControls.SUBTREE_SCOPE, 1, 0,
267 new String[] {userMappingsScreenName}, false, false);
268
269 enu = ldapContext.search(baseDN, filter, searchControls);
270
271 if (enu.hasMoreElements()) {
272 if (_log.isDebugEnabled()) {
273 _log.debug("Search filter returned at least one result");
274 }
275
276 SearchResult result = enu.nextElement();
277
278 String fullUserDN = PortalLDAPUtil.getNameInNamespace(
279 ldapServerId, companyId, result);
280
281 Attributes attributes = PortalLDAPUtil.getUserAttributes(
282 ldapServerId, companyId, ldapContext, fullUserDN);
283
284 LDAPAuthResult ldapAuthResult = authenticate(
285 ldapContext, companyId, attributes, fullUserDN, password);
286
287
288
289 String errorMessage = ldapAuthResult.getErrorMessage();
290
291 if (errorMessage != null) {
292 int pos = errorMessage.indexOf(
293 PrefsPropsUtil.getString(
294 companyId, PropsKeys.LDAP_ERROR_USER_LOCKOUT));
295
296 if (pos != -1) {
297 throw new UserLockoutException.LDAPLockout(
298 fullUserDN, errorMessage);
299 }
300
301 pos = errorMessage.indexOf(
302 PrefsPropsUtil.getString(
303 companyId, PropsKeys.LDAP_ERROR_PASSWORD_EXPIRED));
304
305 if (pos != -1) {
306 throw new PasswordExpiredException();
307 }
308 }
309
310 if (!ldapAuthResult.isAuthenticated()) {
311 return FAILURE;
312 }
313
314
315
316 User user = LDAPUserImporterUtil.importUser(
317 ldapServerId, companyId, ldapContext, attributes, password);
318
319
320
321 String resultCode = ldapAuthResult.getResponseControl();
322
323 if (resultCode.equals(LDAPAuth.RESULT_PASSWORD_RESET)) {
324 UserLocalServiceUtil.updatePasswordReset(
325 user.getUserId(), true);
326 }
327 }
328 else {
329 if (_log.isDebugEnabled()) {
330 _log.debug("Search filter did not return any results");
331 }
332
333 return DNE;
334 }
335 }
336 catch (Exception e) {
337 if (e instanceof LDAPFilterException ||
338 e instanceof PasswordExpiredException ||
339 e instanceof UserLockoutException) {
340
341 throw e;
342 }
343
344 _log.error("Problem accessing LDAP server", e);
345
346 return FAILURE;
347 }
348 finally {
349 if (enu != null) {
350 enu.close();
351 }
352
353 if (ldapContext != null) {
354 ldapContext.close();
355 }
356 }
357
358 return SUCCESS;
359 }
360
361 protected int authenticate(
362 long companyId, String emailAddress, String screenName, long userId,
363 String password)
364 throws Exception {
365
366 if (!PrefsPropsUtil.getBoolean(
367 companyId, PropsKeys.LDAP_AUTH_ENABLED,
368 PropsValues.LDAP_AUTH_ENABLED)) {
369
370 if (_log.isDebugEnabled()) {
371 _log.debug("Authenticator is not enabled");
372 }
373
374 return SUCCESS;
375 }
376
377 if (_log.isDebugEnabled()) {
378 _log.debug("Authenticator is enabled");
379 }
380
381 int preferredLDAPServerResult = authenticateAgainstPreferredLDAPServer(
382 companyId, emailAddress, screenName, userId, password);
383
384 if (preferredLDAPServerResult == SUCCESS) {
385 if (PrefsPropsUtil.getBoolean(
386 companyId, PropsKeys.LDAP_IMPORT_USER_PASSWORD_ENABLED)) {
387
388 return preferredLDAPServerResult;
389 }
390
391 return Authenticator.SKIP_LIFERAY_CHECK;
392 }
393
394 long[] ldapServerIds = StringUtil.split(
395 PrefsPropsUtil.getString(companyId, "ldap.server.ids"), 0L);
396
397 for (long ldapServerId : ldapServerIds) {
398 int result = authenticate(
399 ldapServerId, companyId, emailAddress, screenName, userId,
400 password);
401
402 if (result == SUCCESS) {
403 if (PrefsPropsUtil.getBoolean(
404 companyId,
405 PropsKeys.LDAP_IMPORT_USER_PASSWORD_ENABLED)) {
406
407 return result;
408 }
409
410 return Authenticator.SKIP_LIFERAY_CHECK;
411 }
412 }
413
414 for (int ldapServerId = 0;; ldapServerId++) {
415 String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);
416
417 String providerUrl = PrefsPropsUtil.getString(
418 companyId, PropsKeys.LDAP_BASE_PROVIDER_URL + postfix);
419
420 if (Validator.isNull(providerUrl)) {
421 break;
422 }
423
424 int result = authenticate(
425 ldapServerId, companyId, emailAddress, screenName, userId,
426 password);
427
428 if (result == SUCCESS) {
429 if (PrefsPropsUtil.getBoolean(
430 companyId,
431 PropsKeys.LDAP_IMPORT_USER_PASSWORD_ENABLED)) {
432
433 return result;
434 }
435
436 return Authenticator.SKIP_LIFERAY_CHECK;
437 }
438 }
439
440 return authenticateRequired(
441 companyId, userId, emailAddress, screenName, true, FAILURE);
442 }
443
444 protected int authenticateAgainstPreferredLDAPServer(
445 long companyId, String emailAddress, String screenName, long userId,
446 String password)
447 throws Exception {
448
449 int result = DNE;
450
451 User user = null;
452
453 try {
454 if (userId > 0) {
455 user = UserLocalServiceUtil.getUserById(companyId, userId);
456 }
457 else if (Validator.isNotNull(emailAddress)) {
458 user = UserLocalServiceUtil.getUserByEmailAddress(
459 companyId, emailAddress);
460 }
461 else if (Validator.isNotNull(screenName)) {
462 user = UserLocalServiceUtil.getUserByScreenName(
463 companyId, screenName);
464 }
465 else {
466 if (_log.isDebugEnabled()) {
467 _log.debug("Unable to get preferred LDAP server");
468 }
469
470 return result;
471 }
472 }
473 catch (NoSuchUserException nsue) {
474 if (_log.isDebugEnabled()) {
475 _log.debug("Unable to get preferred LDAP server", nsue);
476 }
477
478 return result;
479 }
480
481 long ldapServerId = user.getLdapServerId();
482
483 if (ldapServerId < 0) {
484 return result;
485 }
486
487 String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);
488
489 String providerUrl = PrefsPropsUtil.getString(
490 user.getCompanyId(), PropsKeys.LDAP_BASE_PROVIDER_URL + postfix);
491
492 if (Validator.isNull(providerUrl)) {
493 return result;
494 }
495
496 if (_log.isDebugEnabled()) {
497 _log.debug(
498 "Using LDAP server ID " + ldapServerId +
499 " to authenticate user " + user.getUserId());
500 }
501
502 result = authenticate(
503 ldapServerId, companyId, emailAddress, screenName, userId,
504 password);
505
506 return result;
507 }
508
509 protected int authenticateOmniadmin(
510 long companyId, String emailAddress, String screenName, long userId)
511 throws Exception {
512
513
514
515 if (!PropsValues.AUTH_PIPELINE_ENABLE_LIFERAY_CHECK) {
516 return FAILURE;
517 }
518
519 if (userId > 0) {
520 if (OmniadminUtil.isOmniadmin(userId)) {
521 return SUCCESS;
522 }
523 }
524 else if (Validator.isNotNull(emailAddress)) {
525 User user = UserLocalServiceUtil.fetchUserByEmailAddress(
526 companyId, emailAddress);
527
528 if (user != null) {
529 if (OmniadminUtil.isOmniadmin(user)) {
530 return SUCCESS;
531 }
532 }
533 }
534 else if (Validator.isNotNull(screenName)) {
535 User user = UserLocalServiceUtil.fetchUserByScreenName(
536 companyId, screenName);
537
538 if (user != null) {
539 if (OmniadminUtil.isOmniadmin(user)) {
540 return SUCCESS;
541 }
542 }
543 }
544
545 return FAILURE;
546 }
547
548 protected int authenticateRequired(
549 long companyId, long userId, String emailAddress, String screenName,
550 boolean allowOmniadmin, int failureCode)
551 throws Exception {
552
553
554
555
556 if (allowOmniadmin &&
557 (authenticateOmniadmin(
558 companyId, emailAddress, screenName, userId) == SUCCESS)) {
559
560 return SUCCESS;
561 }
562
563 if (PrefsPropsUtil.getBoolean(
564 companyId, PropsKeys.LDAP_AUTH_REQUIRED)) {
565
566 return failureCode;
567 }
568 else {
569 return SUCCESS;
570 }
571 }
572
573 protected LDAPAuthResult getFailedLDAPAuthResult(Map<String, Object> env) {
574 Map<String, LDAPAuthResult> failedLDAPAuthResults =
575 _failedLDAPAuthResults.get();
576
577 String cacheKey = getKey(env);
578
579 return failedLDAPAuthResults.get(cacheKey);
580 }
581
582 protected String getKey(Map<String, Object> env) {
583 StringBundler sb = new StringBundler(5);
584
585 sb.append(MapUtil.getString(env, Context.PROVIDER_URL));
586 sb.append(StringPool.POUND);
587 sb.append(MapUtil.getString(env, Context.SECURITY_PRINCIPAL));
588 sb.append(StringPool.POUND);
589 sb.append(MapUtil.getString(env, Context.SECURITY_CREDENTIALS));
590
591 return sb.toString();
592 }
593
594 protected void setFailedLDAPAuthResult(
595 Map<String, Object> env, LDAPAuthResult ldapAuthResult) {
596
597 Map<String, LDAPAuthResult> failedLDAPAuthResults =
598 _failedLDAPAuthResults.get();
599
600 String cacheKey = getKey(env);
601
602 if (failedLDAPAuthResults.containsKey(cacheKey)) {
603 return;
604 }
605
606 failedLDAPAuthResults.put(cacheKey, ldapAuthResult);
607 }
608
609 private static Log _log = LogFactoryUtil.getLog(LDAPAuth.class);
610
611 private ThreadLocal<Map<String, LDAPAuthResult>>
612 _failedLDAPAuthResults =
613 new AutoResetThreadLocal<Map<String, LDAPAuthResult>>(
614 LDAPAuth.class + "._failedLDAPAuthResultCache",
615 new HashMap<String, LDAPAuthResult>());
616
617 }