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