001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.jetspeed.security.spi.impl;
018:
019: import java.sql.Timestamp;
020: import java.util.ArrayList;
021: import java.util.Collection;
022: import java.util.Date;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.Set;
026:
027: import org.apache.commons.logging.Log;
028: import org.apache.commons.logging.LogFactory;
029: import org.apache.jetspeed.security.AlgorithmUpgradePasswordEncodingService;
030: import org.apache.jetspeed.security.InvalidNewPasswordException;
031: import org.apache.jetspeed.security.InvalidPasswordException;
032: import org.apache.jetspeed.security.PasswordAlreadyUsedException;
033: import org.apache.jetspeed.security.SecurityException;
034: import org.apache.jetspeed.security.om.InternalCredential;
035: import org.apache.jetspeed.security.om.InternalUserPrincipal;
036: import org.apache.jetspeed.security.om.impl.InternalCredentialImpl;
037: import org.apache.jetspeed.security.spi.CredentialHandler;
038: import org.apache.jetspeed.security.spi.AlgorithmUpgradeCredentialPasswordEncoder;
039: import org.apache.jetspeed.security.spi.InternalPasswordCredentialInterceptor;
040: import org.apache.jetspeed.security.spi.PasswordCredentialProvider;
041: import org.apache.jetspeed.security.spi.SecurityAccess;
042:
043: /**
044: * @see org.apache.jetspeed.security.spi.CredentialHandler
045: * @author <a href="mailto:dlestrat@apache.org">David Le Strat </a>
046: */
047: public class DefaultCredentialHandler implements CredentialHandler {
048: private static final Log log = LogFactory
049: .getLog(DefaultCredentialHandler.class);
050:
051: private SecurityAccess securityAccess;
052:
053: private PasswordCredentialProvider pcProvider;
054:
055: private InternalPasswordCredentialInterceptor ipcInterceptor;
056:
057: public DefaultCredentialHandler(SecurityAccess securityAccess,
058: PasswordCredentialProvider pcProvider,
059: InternalPasswordCredentialInterceptor ipcInterceptor) {
060: this .securityAccess = securityAccess;
061: this .pcProvider = pcProvider;
062: this .ipcInterceptor = ipcInterceptor;
063: }
064:
065: /**
066: * @see org.apache.jetspeed.security.spi.CredentialHandler#getPrivateCredentials(java.lang.String)
067: */
068: public Set getPrivateCredentials(String username) {
069: Set credentials = new HashSet();
070: InternalUserPrincipal internalUser = securityAccess
071: .getInternalUserPrincipal(username, false);
072: if (null != internalUser) {
073: InternalCredential credential = getPasswordCredential(
074: internalUser, username);
075: if (credential != null) {
076: try {
077: credentials.add(pcProvider.create(username,
078: credential));
079: } catch (SecurityException e) {
080: if (log.isErrorEnabled())
081: log.error(
082: "Failure creating a PasswordCredential for InternalCredential "
083: + credential, e);
084: }
085: }
086: }
087: return credentials;
088: }
089:
090: /**
091: * @see org.apache.jetspeed.security.spi.CredentialHandler#getPublicCredentials(java.lang.String)
092: */
093: public Set getPublicCredentials(String username) {
094: return new HashSet();
095: }
096:
097: private InternalCredential getPasswordCredential(
098: InternalUserPrincipal internalUser, String username) {
099: InternalCredential credential = null;
100:
101: Collection internalCredentials = internalUser.getCredentials();
102: if (internalCredentials != null) {
103: Iterator iter = internalCredentials.iterator();
104:
105: while (iter.hasNext()) {
106: credential = (InternalCredential) iter.next();
107: if (credential.getType() == InternalCredential.PRIVATE) {
108: if ((null != credential.getClassname())
109: && (credential.getClassname()
110: .equals(pcProvider
111: .getPasswordCredentialClass()
112: .getName()))) {
113: try {
114: if (ipcInterceptor != null
115: && ipcInterceptor.afterLoad(
116: pcProvider, username,
117: credential)) {
118: // update InternalUserPrincipal to save post processed data
119: securityAccess
120: .setInternalUserPrincipal(
121: internalUser,
122: internalUser
123: .isMappingOnly());
124: }
125: break;
126: } catch (SecurityException e) {
127: if (log.isErrorEnabled())
128: log.error(
129: "Failure loading InternalCredential "
130: + credential, e);
131: }
132: }
133: }
134: credential = null;
135: }
136: }
137: return credential;
138: }
139:
140: /**
141: * @see org.apache.jetspeed.security.spi.CredentialHandler#setPassword(java.lang.String,java.lang.String,java.lang.String)
142: */
143: public void setPassword(String userName, String oldPassword,
144: String newPassword) throws SecurityException {
145: setPassword(userName, oldPassword, newPassword, false);
146: }
147:
148: /**
149: * @see org.apache.jetspeed.security.spi.CredentialHandler#importPassword(java.lang.String,java.lang.String)
150: */
151: public void importPassword(String userName, String newPassword)
152: throws SecurityException {
153: setPassword(userName, null, newPassword, true);
154: }
155:
156: /**
157: * @see org.apache.jetspeed.security.spi.CredentialHandler#setPassword(java.lang.String,java.lang.String,java.lang.String, boolean)
158: */
159: protected void setPassword(String userName, String oldPassword,
160: String newPassword, boolean raw) throws SecurityException {
161: InternalUserPrincipal internalUser = securityAccess
162: .getInternalUserPrincipal(userName, false);
163: if (null == internalUser) {
164: throw new SecurityException(
165: SecurityException.USER_DOES_NOT_EXIST
166: .create(userName));
167: }
168:
169: Collection credentials = internalUser.getCredentials();
170: if (null == credentials) {
171: credentials = new ArrayList();
172: }
173:
174: InternalCredential credential = getPasswordCredential(
175: internalUser, userName);
176:
177: if (null != oldPassword) {
178: if (credential != null && credential.getValue() != null
179: && credential.isEncoded()
180: && pcProvider.getEncoder() != null) {
181: if (pcProvider.getEncoder() instanceof AlgorithmUpgradeCredentialPasswordEncoder) {
182: oldPassword = ((AlgorithmUpgradeCredentialPasswordEncoder) pcProvider
183: .getEncoder()).encode(userName,
184: oldPassword, credential);
185: } else {
186: oldPassword = pcProvider.getEncoder().encode(
187: userName, oldPassword);
188: }
189: }
190: }
191:
192: if (oldPassword != null
193: && (credential == null || credential.getValue() == null || !credential
194: .getValue().equals(oldPassword))) {
195: // supplied PasswordCredential not defined for this user
196: throw new InvalidPasswordException();
197: }
198: if (!raw) // bypass validation if raw
199: {
200: if (pcProvider.getValidator() != null) {
201: try {
202: pcProvider.getValidator().validate(newPassword);
203: } catch (InvalidPasswordException ipe) {
204: throw new InvalidNewPasswordException();
205: }
206: }
207: }
208: boolean encoded = false;
209: if (pcProvider.getEncoder() != null) {
210: if (!(raw)) // if raw just bypass encoding
211: newPassword = pcProvider.getEncoder().encode(userName,
212: newPassword);
213: encoded = true;
214: }
215:
216: boolean create = credential == null;
217:
218: if (create) {
219: credential = new InternalCredentialImpl(internalUser
220: .getPrincipalId(), newPassword,
221: InternalCredential.PRIVATE, pcProvider
222: .getPasswordCredentialClass().getName());
223: credential.setEncoded(encoded);
224: credentials.add(credential);
225: } else if (oldPassword == null) {
226: /* TODO: should only be allowed for admin
227: // User *has* an PasswordCredential: setting a new Credential without supplying
228: // its current one is not allowed
229: throw new SecurityException(SecurityException.PASSWORD_REQUIRED);
230: */
231: } else if (oldPassword.equals(newPassword)) {
232: throw new PasswordAlreadyUsedException();
233: }
234:
235: if (ipcInterceptor != null) {
236: if (create) {
237: ipcInterceptor.beforeCreate(internalUser, credentials,
238: userName, credential, newPassword);
239: } else {
240: ipcInterceptor.beforeSetPassword(internalUser,
241: credentials, userName, credential, newPassword,
242: oldPassword != null);
243: }
244: }
245:
246: if (!create) {
247: credential.setValue(newPassword);
248: credential.setEncoded(encoded);
249: credential.setUpdateRequired(false);
250: }
251:
252: long time = new Date().getTime();
253:
254: if (oldPassword == null) {
255: // non-user (admin) modified the password
256:
257: if (encoded
258: && pcProvider.getEncoder() instanceof AlgorithmUpgradePasswordEncodingService) {
259: // set current time in previous auth date, and clear last authentication date
260: // !!! While this might be a bit strange logic, it is *required* for the AlgorithmUpgradePBEPasswordEncodingService
261: // to be able to distinguise password changes from other changes
262: credential.setPreviousAuthenticationDate(new Timestamp(
263: new Date().getTime()));
264: credential.setLastAuthenticationDate(null);
265: }
266: } else {
267: // authenticated password change (by user itself)
268: credential.setPreviousAuthenticationDate(credential
269: .getLastAuthenticationDate());
270: credential.setLastAuthenticationDate(new Timestamp(time));
271: }
272:
273: credential.setModifiedDate(new Timestamp(time));
274: internalUser.setModifiedDate(new Timestamp(time));
275: internalUser.setCredentials(credentials);
276: // Set the user with the new credentials.
277: securityAccess.setInternalUserPrincipal(internalUser, false);
278: }
279:
280: /**
281: * @see org.apache.jetspeed.security.spi.CredentialHandler#setPasswordEnabled(java.lang.String, boolean)
282: */
283: public void setPasswordEnabled(String userName, boolean enabled)
284: throws SecurityException {
285: InternalUserPrincipal internalUser = securityAccess
286: .getInternalUserPrincipal(userName, false);
287: if (null != internalUser) {
288: InternalCredential credential = getPasswordCredential(
289: internalUser, userName);
290: if (credential != null && !credential.isExpired()
291: && credential.isEnabled() != enabled) {
292: long time = new Date().getTime();
293: credential.setEnabled(enabled);
294: credential.setAuthenticationFailures(0);
295: credential.setModifiedDate(new Timestamp(time));
296: internalUser.setModifiedDate(new Timestamp(time));
297: securityAccess.setInternalUserPrincipal(internalUser,
298: false);
299: }
300: } else {
301: throw new SecurityException(
302: SecurityException.USER_DOES_NOT_EXIST
303: .create(userName));
304: }
305: }
306:
307: /**
308: * @see org.apache.jetspeed.security.spi.CredentialHandler#setPasswordUpdateRequired(java.lang.String, boolean)
309: */
310: public void setPasswordUpdateRequired(String userName,
311: boolean updateRequired) throws SecurityException {
312: InternalUserPrincipal internalUser = securityAccess
313: .getInternalUserPrincipal(userName, false);
314: if (null != internalUser) {
315: InternalCredential credential = getPasswordCredential(
316: internalUser, userName);
317: if (credential != null && !credential.isExpired()
318: && credential.isUpdateRequired() != updateRequired) {
319: // only allow setting updateRequired off if (non-Encoded) password is valid
320: if (!updateRequired && !credential.isEncoded()
321: && pcProvider.getValidator() != null) {
322: pcProvider.getValidator().validate(
323: credential.getValue());
324: }
325: credential.setUpdateRequired(updateRequired);
326: long time = new Date().getTime();
327: credential.setModifiedDate(new Timestamp(time));
328: // temporary hack for now to support setting passwordUpdateRequired = false
329: // for users never authenticated yet.
330: // The current InternalPasswordCredentialStateHandlingInterceptor.afterLoad()
331: // logic will only set it (back) to true if both prev and last auth. date is null
332: credential.setPreviousAuthenticationDate(new Timestamp(
333: time));
334: credential.setModifiedDate(new Timestamp(time));
335: internalUser.setModifiedDate(new Timestamp(time));
336: securityAccess.setInternalUserPrincipal(internalUser,
337: false);
338: }
339: } else {
340: throw new SecurityException(
341: SecurityException.USER_DOES_NOT_EXIST
342: .create(userName));
343: }
344: }
345:
346: /**
347: * @see org.apache.jetspeed.security.spi.CredentialHandler#setPasswordExpiration(java.lang.String, java.sql.Date)
348: */
349: public void setPasswordExpiration(String userName,
350: java.sql.Date expirationDate) throws SecurityException {
351: InternalUserPrincipal internalUser = securityAccess
352: .getInternalUserPrincipal(userName, false);
353: if (null != internalUser) {
354: InternalCredential credential = getPasswordCredential(
355: internalUser, userName);
356: if (credential != null) {
357: long time = new Date().getTime();
358: if (expirationDate != null
359: && new java.sql.Date(time)
360: .after(expirationDate)) {
361: credential.setExpired(true);
362: } else {
363: credential.setExpired(false);
364: }
365: credential.setExpirationDate(expirationDate);
366:
367: credential.setModifiedDate(new Timestamp(time));
368: internalUser.setModifiedDate(new Timestamp(time));
369: securityAccess.setInternalUserPrincipal(internalUser,
370: false);
371: }
372: } else {
373: throw new SecurityException(
374: SecurityException.USER_DOES_NOT_EXIST
375: .create(userName));
376: }
377: }
378:
379: /**
380: * @see org.apache.jetspeed.security.spi.CredentialHandler#authenticate(java.lang.String, java.lang.String)
381: */
382: public boolean authenticate(String userName, String password)
383: throws SecurityException {
384: boolean authenticated = false;
385: InternalUserPrincipal internalUser = securityAccess
386: .getInternalUserPrincipal(userName, false);
387: if (null != internalUser) {
388: InternalCredential credential = getPasswordCredential(
389: internalUser, userName);
390: if (credential != null && credential.isEnabled()
391: && !credential.isExpired()) {
392: String encodedPassword = password;
393: if (pcProvider.getEncoder() != null
394: && credential.isEncoded()) {
395: if (pcProvider.getEncoder() instanceof AlgorithmUpgradeCredentialPasswordEncoder) {
396: encodedPassword = ((AlgorithmUpgradeCredentialPasswordEncoder) pcProvider
397: .getEncoder()).encode(userName,
398: password, credential);
399: } else {
400: encodedPassword = pcProvider.getEncoder()
401: .encode(userName, password);
402: }
403: }
404:
405: authenticated = credential.getValue().equals(
406: encodedPassword);
407: boolean update = false;
408:
409: if (ipcInterceptor != null) {
410: update = ipcInterceptor.afterAuthenticated(
411: internalUser, userName, credential,
412: authenticated);
413: if (update
414: && (!credential.isEnabled() || credential
415: .isExpired())) {
416: authenticated = false;
417: }
418: }
419: long time = new Date().getTime();
420:
421: if (authenticated) {
422: credential.setAuthenticationFailures(0);
423:
424: if (pcProvider.getEncoder() != null
425: && pcProvider.getEncoder() instanceof AlgorithmUpgradeCredentialPasswordEncoder) {
426: ((AlgorithmUpgradeCredentialPasswordEncoder) pcProvider
427: .getEncoder()).recodeIfNeeded(userName,
428: password, credential);
429: }
430:
431: credential.setPreviousAuthenticationDate(credential
432: .getLastAuthenticationDate());
433: credential.setLastAuthenticationDate(new Timestamp(
434: time));
435: update = true;
436: }
437:
438: if (update) {
439: credential.setModifiedDate(new Timestamp(time));
440: internalUser.setModifiedDate(new Timestamp(time));
441: securityAccess.setInternalUserPrincipal(
442: internalUser, false);
443: }
444: }
445: } else {
446: throw new SecurityException(
447: SecurityException.USER_DOES_NOT_EXIST
448: .create(userName));
449: }
450: return authenticated;
451: }
452: }
|