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