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