1
22
23 package com.liferay.portal.security.auth;
24
25 import com.liferay.portal.NoSuchUserException;
26 import com.liferay.portal.PasswordExpiredException;
27 import com.liferay.portal.UserLockoutException;
28 import com.liferay.portal.kernel.log.Log;
29 import com.liferay.portal.kernel.log.LogFactoryUtil;
30 import com.liferay.portal.kernel.util.StringPool;
31 import com.liferay.portal.kernel.util.Validator;
32 import com.liferay.portal.model.User;
33 import com.liferay.portal.security.ldap.PortalLDAPUtil;
34 import com.liferay.portal.security.pwd.PwdEncryptor;
35 import com.liferay.portal.service.UserLocalServiceUtil;
36 import com.liferay.portal.util.PrefsPropsUtil;
37 import com.liferay.portal.util.PropsKeys;
38 import com.liferay.portal.util.PropsValues;
39 import com.liferay.portlet.admin.util.OmniadminUtil;
40
41 import java.util.Hashtable;
42 import java.util.Map;
43
44 import javax.naming.Context;
45 import javax.naming.NamingEnumeration;
46 import javax.naming.directory.Attribute;
47 import javax.naming.directory.Attributes;
48 import javax.naming.directory.SearchControls;
49 import javax.naming.directory.SearchResult;
50 import javax.naming.ldap.Control;
51 import javax.naming.ldap.InitialLdapContext;
52 import javax.naming.ldap.LdapContext;
53
54
61 public class LDAPAuth implements Authenticator {
62
63 public static final String AUTH_METHOD_BIND = "bind";
64
65 public static final String AUTH_METHOD_PASSWORD_COMPARE =
66 "password-compare";
67
68 public static final String RESULT_PASSWORD_RESET =
69 "2.16.840.1.113730.3.4.4";
70
71 public static final String RESULT_PASSWORD_EXP_WARNING =
72 "2.16.840.1.113730.3.4.5";
73
74 public int authenticateByEmailAddress(
75 long companyId, String emailAddress, String password,
76 Map<String, String[]> headerMap, Map<String, String[]> parameterMap)
77 throws AuthException {
78
79 try {
80 return authenticate(
81 companyId, emailAddress, StringPool.BLANK, 0, password);
82 }
83 catch (Exception e) {
84 _log.error(e, e);
85
86 throw new AuthException(e);
87 }
88 }
89
90 public int authenticateByScreenName(
91 long companyId, String screenName, String password,
92 Map<String, String[]> headerMap, Map<String, String[]> parameterMap)
93 throws AuthException {
94
95 try {
96 return authenticate(
97 companyId, StringPool.BLANK, screenName, 0, password);
98 }
99 catch (Exception e) {
100 _log.error(e, e);
101
102 throw new AuthException(e);
103 }
104 }
105
106 public int authenticateByUserId(
107 long companyId, long userId, String password,
108 Map<String, String[]> headerMap, Map<String, String[]> parameterMap)
109 throws AuthException {
110
111 try {
112 return authenticate(
113 companyId, StringPool.BLANK, StringPool.BLANK, userId,
114 password);
115 }
116 catch (Exception e) {
117 _log.error(e, e);
118
119 throw new AuthException(e);
120 }
121 }
122
123 protected int authenticate(
124 long companyId, String emailAddress, String screenName, long userId,
125 String password)
126 throws Exception {
127
128 if (!PortalLDAPUtil.isAuthEnabled(companyId)) {
129 if (_log.isDebugEnabled()) {
130 _log.debug("Authenticator is not enabled");
131 }
132
133 return SUCCESS;
134 }
135
136 if (_log.isDebugEnabled()) {
137 _log.debug("Authenticator is enabled");
138 }
139
140
143 if (authenticateOmniadmin(
144 companyId, emailAddress, screenName, userId) == SUCCESS) {
145
146 return SUCCESS;
147 }
148
149 String baseDN = PrefsPropsUtil.getString(
150 companyId, PropsKeys.LDAP_BASE_DN);
151
152 LdapContext ctx = PortalLDAPUtil.getContext(companyId);
153
154 if (ctx == null) {
155 return authenticateRequired(
156 companyId, userId, emailAddress, FAILURE);
157 }
158
159
161 String filter = PortalLDAPUtil.getAuthSearchFilter(
162 companyId, emailAddress, screenName, String.valueOf(userId));
163
164 try {
165 SearchControls cons = new SearchControls(
166 SearchControls.SUBTREE_SCOPE, 1, 0, null, false, false);
167
168 NamingEnumeration<SearchResult> enu = ctx.search(
169 baseDN, filter, cons);
170
171 if (enu.hasMoreElements()) {
172 if (_log.isDebugEnabled()) {
173 _log.debug("Search filter returned at least one result");
174 }
175
176 SearchResult result = enu.nextElement();
177
178 String fullUserDN = PortalLDAPUtil.getNameInNamespace(
179 companyId, result);
180
181 Attributes attrs = PortalLDAPUtil.getUserAttributes(
182 companyId, ctx, fullUserDN);
183
184 LDAPAuthResult ldapAuthResult = authenticate(
185 ctx, companyId, attrs, fullUserDN, password);
186
187
189 String errorMessage = ldapAuthResult.getErrorMessage();
190
191 if (errorMessage != null) {
192 if (errorMessage.indexOf(PrefsPropsUtil.getString(
193 companyId, PropsKeys.LDAP_ERROR_USER_LOCKOUT))
194 != -1) {
195
196 throw new UserLockoutException();
197 }
198 else if (errorMessage.indexOf(PrefsPropsUtil.getString(
199 companyId, PropsKeys.LDAP_ERROR_PASSWORD_EXPIRED))
200 != -1) {
201
202 throw new PasswordExpiredException();
203 }
204 }
205
206 if (!ldapAuthResult.isAuthenticated()) {
207 return authenticateRequired(
208 companyId, userId, emailAddress, FAILURE);
209 }
210
211
213 User user = PortalLDAPUtil.importLDAPUser(
214 companyId, ctx, attrs, password, true);
215
216
218 String resultCode = ldapAuthResult.getResponseControl();
219
220 if (resultCode.equals(LDAPAuth.RESULT_PASSWORD_RESET)) {
221 UserLocalServiceUtil.updatePasswordReset(
222 user.getUserId(), true);
223 }
224 else if (
225 resultCode.equals(LDAPAuth.RESULT_PASSWORD_EXP_WARNING)) {
226
227 UserLocalServiceUtil.updatePasswordReset(
228 user.getUserId(), true);
229 }
230 }
231 else {
232 if (_log.isDebugEnabled()) {
233 _log.debug("Search filter did not return any results");
234 }
235
236 return authenticateRequired(
237 companyId, userId, emailAddress, DNE);
238 }
239
240 enu.close();
241 }
242 catch (Exception e) {
243 _log.error("Problem accessing LDAP server: " + e.getMessage());
244
245 if (authenticateRequired(
246 companyId, userId, emailAddress, FAILURE) == FAILURE) {
247
248 throw e;
249 }
250 }
251 finally {
252 if (ctx != null) {
253 ctx.close();
254 }
255 }
256
257 return SUCCESS;
258 }
259
260 protected LDAPAuthResult authenticate(
261 LdapContext ctx, long companyId, Attributes attrs, String userDN,
262 String password)
263 throws Exception {
264
265 LDAPAuthResult ldapAuthResult = new LDAPAuthResult();
266
267
271 String authMethod = PrefsPropsUtil.getString(
272 companyId, PropsKeys.LDAP_AUTH_METHOD);
273 InitialLdapContext innerCtx = null;
274
275 if (authMethod.equals(AUTH_METHOD_BIND)) {
276 try {
277 Hashtable<String, Object> env =
278 (Hashtable<String, Object>)ctx.getEnvironment();
279
280 env.put(Context.SECURITY_PRINCIPAL, userDN);
281 env.put(Context.SECURITY_CREDENTIALS, password);
282 env.put(
283 Context.REFERRAL,
284 PrefsPropsUtil.getString(
285 companyId, PropsKeys.LDAP_REFERRAL));
286
287
289 env.put("com.sun.jndi.ldap.connect.pool", "false");
290
291 innerCtx = new InitialLdapContext(env, null);
292
293
295 Control[] responseControls = innerCtx.getResponseControls();
296
297 ldapAuthResult.setAuthenticated(true);
298 ldapAuthResult.setResponseControl(responseControls);
299 }
300 catch (Exception e) {
301 if (_log.isDebugEnabled()) {
302 _log.debug(
303 "Failed to bind to the LDAP server with userDN "
304 + userDN + " and password " + password);
305 }
306
307 _log.error(
308 "Failed to bind to the LDAP server: " + e.getMessage());
309
310 ldapAuthResult.setAuthenticated(false);
311 ldapAuthResult.setErrorMessage(e.getMessage());
312 }
313 finally {
314 if (innerCtx != null) {
315 innerCtx.close();
316 }
317 }
318 }
319 else if (authMethod.equals(AUTH_METHOD_PASSWORD_COMPARE)) {
320 Attribute userPassword = attrs.get("userPassword");
321
322 if (userPassword != null) {
323 String ldapPassword = new String((byte[])userPassword.get());
324
325 String encryptedPassword = password;
326
327 String algorithm = PrefsPropsUtil.getString(
328 companyId,
329 PropsKeys.LDAP_AUTH_PASSWORD_ENCRYPTION_ALGORITHM);
330
331 if (Validator.isNotNull(algorithm)) {
332 encryptedPassword =
333 "{" + algorithm + "}" +
334 PwdEncryptor.encrypt(
335 algorithm, password, ldapPassword);
336 }
337
338 if (ldapPassword.equals(encryptedPassword)) {
339 ldapAuthResult.setAuthenticated(true);
340 }
341 else {
342 ldapAuthResult.setAuthenticated(false);
343
344 if (_log.isWarnEnabled()) {
345 _log.warn(
346 "Passwords do not match for userDN " + userDN);
347 }
348 }
349 }
350 }
351
352 return ldapAuthResult;
353 }
354
355 protected int authenticateOmniadmin(
356 long companyId, String emailAddress, String screenName, long userId)
357 throws Exception {
358
359
361 if (PropsValues.AUTH_PIPELINE_ENABLE_LIFERAY_CHECK) {
362 if (userId > 0) {
363 if (OmniadminUtil.isOmniadmin(userId)) {
364 return SUCCESS;
365 }
366 }
367 else if (Validator.isNotNull(emailAddress)) {
368 try {
369 User user = UserLocalServiceUtil.getUserByEmailAddress(
370 companyId, emailAddress);
371
372 if (OmniadminUtil.isOmniadmin(user.getUserId())) {
373 return SUCCESS;
374 }
375 }
376 catch (NoSuchUserException nsue) {
377 }
378 }
379 else if (Validator.isNotNull(screenName)) {
380 try {
381 User user = UserLocalServiceUtil.getUserByScreenName(
382 companyId, screenName);
383
384 if (OmniadminUtil.isOmniadmin(user.getUserId())) {
385 return SUCCESS;
386 }
387 }
388 catch (NoSuchUserException nsue) {
389 }
390 }
391 }
392
393 return FAILURE;
394 }
395
396 protected int authenticateRequired(
397 long companyId, long userId, String emailAddress, int failureCode)
398 throws Exception {
399
400 if (PrefsPropsUtil.getBoolean(
401 companyId, PropsKeys.LDAP_AUTH_REQUIRED)) {
402
403 return failureCode;
404 }
405 else {
406 return SUCCESS;
407 }
408 }
409
410 private static Log _log = LogFactoryUtil.getLog(LDAPAuth.class);
411
412 }