2 * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
\r
4 * This library is free software; you can redistribute it and/or modify it under
\r
5 * the terms of the GNU Lesser General Public License as published by the Free
\r
6 * Software Foundation; either version 2.1 of the License, or (at your option)
\r
9 * This library is distributed in the hope that it will be useful, but WITHOUT
\r
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
\r
11 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
\r
15 package com.liferay.portal.security.auth;
\r
17 import java.util.Hashtable;
\r
18 import java.util.Map;
\r
19 import java.util.Properties;
\r
21 import javax.naming.Context;
\r
22 import javax.naming.NamingEnumeration;
\r
23 import javax.naming.directory.Attribute;
\r
24 import javax.naming.directory.Attributes;
\r
25 import javax.naming.directory.SearchControls;
\r
26 import javax.naming.directory.SearchResult;
\r
27 import javax.naming.ldap.Control;
\r
28 import javax.naming.ldap.InitialLdapContext;
\r
29 import javax.naming.ldap.LdapContext;
\r
31 import com.liferay.portal.NoSuchUserException;
\r
32 import com.liferay.portal.PasswordExpiredException;
\r
33 import com.liferay.portal.UserLockoutException;
\r
34 import com.liferay.portal.kernel.log.Log;
\r
35 import com.liferay.portal.kernel.log.LogFactoryUtil;
\r
36 import com.liferay.portal.kernel.util.GetterUtil;
\r
37 import com.liferay.portal.kernel.util.PropsKeys;
\r
38 import com.liferay.portal.kernel.util.StringBundler;
\r
39 import com.liferay.portal.kernel.util.StringPool;
\r
40 import com.liferay.portal.kernel.util.StringUtil;
\r
41 import com.liferay.portal.kernel.util.Validator;
\r
42 import com.liferay.portal.model.User;
\r
43 import com.liferay.portal.security.ldap.LDAPSettingsUtil;
\r
44 import com.liferay.portal.security.ldap.PortalLDAPUtil;
\r
45 import com.liferay.portal.security.pwd.PwdEncryptor;
\r
46 import com.liferay.portal.service.UserLocalServiceUtil;
\r
47 import com.liferay.portal.util.PrefsPropsUtil;
\r
48 import com.liferay.portal.util.PropsValues;
\r
49 import com.liferay.portlet.admin.util.OmniadminUtil;
\r
50 import com.pentila.entSavoie.synchroLdap.impl.SynchronizationManager;
\r
53 * @author Brian Wing Shun Chan
\r
56 public class LDAPAuth implements Authenticator {
\r
58 public static final String AUTH_METHOD_BIND = "bind";
\r
60 public static final String AUTH_METHOD_PASSWORD_COMPARE =
\r
63 public static final String RESULT_PASSWORD_EXP_WARNING =
\r
64 "2.16.840.1.113730.3.4.5";
\r
66 public static final String RESULT_PASSWORD_RESET =
\r
67 "2.16.840.1.113730.3.4.4";
\r
69 public int authenticateByEmailAddress(
\r
70 long companyId, String emailAddress, String password,
\r
71 Map<String, String[]> headerMap, Map<String, String[]> parameterMap)
\r
72 throws AuthException {
\r
75 return authenticate(
\r
76 companyId, emailAddress, StringPool.BLANK, 0, password);
\r
78 catch (Exception e) {
\r
81 throw new AuthException(e);
\r
85 public int authenticateByScreenName(
\r
86 long companyId, String screenName, String password,
\r
87 Map<String, String[]> headerMap, Map<String, String[]> parameterMap)
\r
88 throws AuthException {
\r
91 return authenticate(
\r
92 companyId, StringPool.BLANK, screenName, 0, password);
\r
94 catch (Exception e) {
\r
97 throw new AuthException(e);
\r
101 public int authenticateByUserId(
\r
102 long companyId, long userId, String password,
\r
103 Map<String, String[]> headerMap, Map<String, String[]> parameterMap)
\r
104 throws AuthException {
\r
107 return authenticate(
\r
108 companyId, StringPool.BLANK, StringPool.BLANK, userId,
\r
111 catch (Exception e) {
\r
114 throw new AuthException(e);
\r
118 protected LDAPAuthResult authenticate(
\r
119 LdapContext ctx, long companyId, Attributes attributes,
\r
120 String userDN, String password)
\r
123 LDAPAuthResult ldapAuthResult = new LDAPAuthResult();
\r
125 // Check passwords by either doing a comparison between the passwords or
\r
126 // by binding to the LDAP server. If using LDAP password policies, bind
\r
127 // auth method must be used in order to get the result control codes.
\r
129 String authMethod = PrefsPropsUtil.getString(
\r
130 companyId, PropsKeys.LDAP_AUTH_METHOD);
\r
131 InitialLdapContext innerCtx = null;
\r
133 if (authMethod.equals(AUTH_METHOD_BIND)) {
\r
136 //Hashtable<String, Object> env =
\r
137 // (Hashtable<String, Object>)ctx.getEnvironment();
\r
139 // Begin fix for webdav
\r
141 Properties env = new Properties();
\r
143 env.put(Context.INITIAL_CONTEXT_FACTORY, PrefsPropsUtil.getString(companyId, PropsKeys.LDAP_FACTORY_INITIAL));
\r
144 env.put(Context.PROVIDER_URL, PrefsPropsUtil.getString(companyId, PropsKeys.LDAP_BASE_PROVIDER_URL));
\r
146 env.put("com.sun.jndi.ldap.connect.pool", "true");
\r
147 env.put("com.sun.jndi.ldap.connect.pool.maxsize", "50");
\r
148 env.put("com.sun.jndi.ldap.connect.pool.timeout", "100000");
\r
150 // End fix for webdav
\r
153 env.put(Context.SECURITY_PRINCIPAL, userDN);
\r
154 env.put(Context.SECURITY_CREDENTIALS, password);
\r
157 PrefsPropsUtil.getString(
\r
158 companyId, PropsKeys.LDAP_REFERRAL));
\r
160 // Do not use pooling because principal changes
\r
162 env.put("com.sun.jndi.ldap.connect.pool", "false");
\r
164 innerCtx = new InitialLdapContext(env, null);
\r
166 // Get LDAP bind results
\r
168 Control[] responseControls = innerCtx.getResponseControls();
\r
170 ldapAuthResult.setAuthenticated(true);
\r
171 ldapAuthResult.setResponseControl(responseControls);
\r
173 catch (Exception e) {
\r
174 if (_log.isDebugEnabled()) {
\r
176 "Failed to bind to the LDAP server with userDN "
\r
177 + userDN + " and password " + password);
\r
180 _log.error("Failed to bind to the LDAP server", e);
\r
182 ldapAuthResult.setAuthenticated(false);
\r
183 ldapAuthResult.setErrorMessage(e.getMessage());
\r
186 if (innerCtx != null) {
\r
191 else if (authMethod.equals(AUTH_METHOD_PASSWORD_COMPARE)) {
\r
192 Attribute userPassword = attributes.get("userPassword");
\r
194 if (userPassword != null) {
\r
195 String ldapPassword = new String((byte[])userPassword.get());
\r
197 String encryptedPassword = password;
\r
199 String algorithm = PrefsPropsUtil.getString(
\r
201 PropsKeys.LDAP_AUTH_PASSWORD_ENCRYPTION_ALGORITHM);
\r
203 if (Validator.isNotNull(algorithm)) {
\r
204 StringBundler sb = new StringBundler(4);
\r
206 sb.append(StringPool.OPEN_CURLY_BRACE);
\r
207 sb.append(algorithm);
\r
208 sb.append(StringPool.CLOSE_CURLY_BRACE);
\r
210 PwdEncryptor.encrypt(
\r
211 algorithm, password, ldapPassword));
\r
213 encryptedPassword = sb.toString();
\r
216 if (ldapPassword.equals(encryptedPassword)) {
\r
217 ldapAuthResult.setAuthenticated(true);
\r
220 ldapAuthResult.setAuthenticated(false);
\r
222 if (_log.isWarnEnabled()) {
\r
224 "Passwords do not match for userDN " + userDN);
\r
230 return ldapAuthResult;
\r
233 protected int authenticate(
\r
234 long companyId, long ldapServerId, String emailAddress,
\r
235 String screenName, long userId, String password)
\r
238 String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);
\r
240 LdapContext ldapContext = PortalLDAPUtil.getContext(companyId);
\r
243 if (ldapContext == null) {
\r
247 NamingEnumeration<SearchResult> enu = null;
\r
250 String baseDN = PrefsPropsUtil.getString(
\r
251 companyId, PropsKeys.LDAP_BASE_DN + postfix);
\r
253 // Process LDAP auth search filter
\r
255 String filter = LDAPSettingsUtil.getAuthSearchFilter(
\r
256 ldapServerId, companyId, emailAddress, screenName,
\r
257 String.valueOf(userId));
\r
259 Properties userMappings = LDAPSettingsUtil.getUserMappings(
\r
260 ldapServerId, companyId);
\r
262 String userMappingsScreenName = GetterUtil.getString(
\r
263 userMappings.getProperty("screenName")).toLowerCase();
\r
265 SearchControls searchControls = new SearchControls(
\r
266 SearchControls.SUBTREE_SCOPE, 1, 0,
\r
267 new String[] {userMappingsScreenName}, false, false);
\r
270 SearchControls cons = new SearchControls(
\r
271 SearchControls.SUBTREE_SCOPE, 1, 0, null, false, false);
\r
273 enu = PortalLDAPUtil.doContextSearch(
\r
274 companyId, baseDN, filter, cons);
\r
276 if (enu.hasMoreElements()) {
\r
277 if (_log.isDebugEnabled()) {
\r
278 _log.debug("Search filter returned at least one result");
\r
281 SearchResult result = enu.nextElement();
\r
283 String fullUserDN = PortalLDAPUtil.getNameInNamespace(
\r
284 companyId, result);
\r
286 Attributes attributes = PortalLDAPUtil.getUserAttributes(
\r
287 companyId, fullUserDN);
\r
289 LDAPAuthResult ldapAuthResult = authenticate(
\r
290 ldapContext, companyId, attributes, fullUserDN, password);
\r
292 // Process LDAP failure codes
\r
294 String errorMessage = ldapAuthResult.getErrorMessage();
\r
296 if (errorMessage != null) {
\r
297 int pos = errorMessage.indexOf(
\r
298 PrefsPropsUtil.getString(
\r
299 companyId, PropsKeys.LDAP_ERROR_USER_LOCKOUT));
\r
302 throw new UserLockoutException();
\r
305 pos = errorMessage.indexOf(
\r
306 PrefsPropsUtil.getString(
\r
307 companyId, PropsKeys.LDAP_ERROR_PASSWORD_EXPIRED));
\r
310 throw new PasswordExpiredException();
\r
314 if (!ldapAuthResult.isAuthenticated()) {
\r
318 // Get user or create from LDAP
\r
320 /**************************
\r
321 * MIGRATION MODIFICATION *
\r
322 **************************/
\r
323 //User user = PortalLDAPImporterUtil.importLDAPUser(
\r
324 // ldapServerId, companyId, ldapContext, attributes, password);
\r
326 //User user = SynchronizationManager.synchronizeUser(companyId, attributes, password, false);
\r
328 // Process LDAP success codes
\r
330 String resultCode = ldapAuthResult.getResponseControl();
\r
332 // if (resultCode.equals(LDAPAuth.RESULT_PASSWORD_RESET)) {
\r
333 // UserLocalServiceUtil.updatePasswordReset(
\r
334 // user.getUserId(), true);
\r
338 if (_log.isDebugEnabled()) {
\r
339 _log.debug("Search filter did not return any results");
\r
345 catch (Exception e) {
\r
346 if (e instanceof PasswordExpiredException ||
\r
347 e instanceof UserLockoutException) {
\r
352 _log.error("Problem accessing LDAP server", e);
\r
361 if (ldapContext != null) {
\r
362 ldapContext.close();
\r
369 protected int authenticate(
\r
370 long companyId, String emailAddress, String screenName, long userId,
\r
374 if (!AuthSettingsUtil.isLDAPAuthEnabled(companyId)) {
\r
375 if (_log.isDebugEnabled()) {
\r
376 _log.debug("Authenticator is not enabled");
\r
382 if (_log.isDebugEnabled()) {
\r
383 _log.debug("Authenticator is enabled");
\r
386 long[] ldapServerIds = StringUtil.split(
\r
387 PrefsPropsUtil.getString(companyId, "ldap.server.ids"), 0L);
\r
389 for (long ldapServerId : ldapServerIds) {
\r
390 int result = authenticate(
\r
391 companyId, ldapServerId, emailAddress, screenName, userId,
\r
394 if (result == SUCCESS) {
\r
399 for (int ldapServerId = 0;; ldapServerId++) {
\r
400 String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);
\r
402 String providerUrl = PrefsPropsUtil.getString(
\r
403 companyId, PropsKeys.LDAP_BASE_PROVIDER_URL + postfix);
\r
405 if (Validator.isNull(providerUrl)) {
\r
409 int result = authenticate(
\r
410 companyId, ldapServerId, emailAddress, screenName, userId,
\r
413 if (result == SUCCESS) {
\r
418 return authenticateRequired(
\r
419 companyId, userId, emailAddress, screenName, true, FAILURE);
\r
422 protected int authenticateOmniadmin(
\r
423 long companyId, String emailAddress, String screenName, long userId)
\r
426 // Only allow omniadmin if Liferay password checking is enabled
\r
428 if (PropsValues.AUTH_PIPELINE_ENABLE_LIFERAY_CHECK) {
\r
430 if (OmniadminUtil.isOmniadmin(userId)) {
\r
434 else if (Validator.isNotNull(emailAddress)) {
\r
436 User user = UserLocalServiceUtil.getUserByEmailAddress(
\r
437 companyId, emailAddress);
\r
439 if (OmniadminUtil.isOmniadmin(user.getUserId())) {
\r
443 catch (NoSuchUserException nsue) {
\r
446 else if (Validator.isNotNull(screenName)) {
\r
448 User user = UserLocalServiceUtil.getUserByScreenName(
\r
449 companyId, screenName);
\r
451 if (OmniadminUtil.isOmniadmin(user.getUserId())) {
\r
455 catch (NoSuchUserException nsue) {
\r
463 protected int authenticateRequired(
\r
464 long companyId, long userId, String emailAddress, String screenName,
\r
465 boolean allowOmniadmin, int failureCode)
\r
468 // Make exceptions for omniadmins so that if they break the LDAP
\r
469 // configuration, they can still login to fix the problem
\r
471 if (allowOmniadmin &&
\r
472 (authenticateOmniadmin(
\r
473 companyId, emailAddress, screenName, userId) == SUCCESS)) {
\r
478 if (PrefsPropsUtil.getBoolean(
\r
479 companyId, PropsKeys.LDAP_AUTH_REQUIRED)) {
\r
481 return failureCode;
\r
488 private static Log _log = LogFactoryUtil.getLog(LDAPAuth.class);
\r