View Javadoc

1   /* 
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.jetspeed.security.spi.impl;
18  
19  import java.sql.Timestamp;
20  import java.util.ArrayList;
21  import java.util.Collection;
22  import java.util.Date;
23  import java.util.HashSet;
24  import java.util.Iterator;
25  import java.util.Set;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.jetspeed.security.AlgorithmUpgradePasswordEncodingService;
30  import org.apache.jetspeed.security.InvalidNewPasswordException;
31  import org.apache.jetspeed.security.InvalidPasswordException;
32  import org.apache.jetspeed.security.PasswordAlreadyUsedException;
33  import org.apache.jetspeed.security.SecurityException;
34  import org.apache.jetspeed.security.om.InternalCredential;
35  import org.apache.jetspeed.security.om.InternalUserPrincipal;
36  import org.apache.jetspeed.security.om.impl.InternalCredentialImpl;
37  import org.apache.jetspeed.security.spi.CredentialHandler;
38  import org.apache.jetspeed.security.spi.AlgorithmUpgradeCredentialPasswordEncoder;
39  import org.apache.jetspeed.security.spi.InternalPasswordCredentialInterceptor;
40  import org.apache.jetspeed.security.spi.PasswordCredentialProvider;
41  import org.apache.jetspeed.security.spi.SecurityAccess;
42  
43  /***
44   * @see org.apache.jetspeed.security.spi.CredentialHandler
45   * @author <a href="mailto:dlestrat@apache.org">David Le Strat </a>
46   */
47  public class DefaultCredentialHandler implements CredentialHandler
48  {
49      private static final Log log = LogFactory.getLog(DefaultCredentialHandler.class);
50  
51      private SecurityAccess securityAccess;
52  
53      private PasswordCredentialProvider pcProvider;
54      
55      private InternalPasswordCredentialInterceptor ipcInterceptor;
56      
57      public DefaultCredentialHandler(SecurityAccess securityAccess, PasswordCredentialProvider pcProvider, 
58              InternalPasswordCredentialInterceptor ipcInterceptor)
59      {
60          this.securityAccess = securityAccess;
61          this.pcProvider = pcProvider;
62          this.ipcInterceptor = ipcInterceptor;
63      }
64      
65      /***
66       * @see org.apache.jetspeed.security.spi.CredentialHandler#getPrivateCredentials(java.lang.String)
67       */
68      public Set getPrivateCredentials(String username)
69      {
70          Set credentials = new HashSet();
71          InternalUserPrincipal internalUser = securityAccess.getInternalUserPrincipal(username, false);
72          if (null != internalUser)
73          {
74              InternalCredential credential = getPasswordCredential(internalUser, username );
75              if ( credential != null )
76              {
77                  try
78                  {
79                      credentials.add(pcProvider.create(username,credential));
80                  }
81                  catch (SecurityException e)
82                  {
83                      if ( log.isErrorEnabled() )
84                          log.error("Failure creating a PasswordCredential for InternalCredential "+credential, e);
85                  }
86              }
87          }
88          return credentials;
89      }
90  
91      /***
92       * @see org.apache.jetspeed.security.spi.CredentialHandler#getPublicCredentials(java.lang.String)
93       */
94      public Set getPublicCredentials(String username)
95      {
96          return new HashSet();
97      }
98      
99      private InternalCredential getPasswordCredential(InternalUserPrincipal internalUser, String username)
100     {
101         InternalCredential credential = null;
102         
103         Collection internalCredentials = internalUser.getCredentials();
104         if ( internalCredentials != null )
105         {
106             Iterator iter = internalCredentials.iterator();
107             
108             while (iter.hasNext())
109             {
110                 credential = (InternalCredential) iter.next();
111                 if (credential.getType() == InternalCredential.PRIVATE )
112                 {
113                     if ((null != credential.getClassname())
114                             && (credential.getClassname().equals(pcProvider.getPasswordCredentialClass().getName())))
115                     {
116                         try
117                         {
118                             if ( ipcInterceptor != null && ipcInterceptor.afterLoad(pcProvider, username, credential) )
119                             {
120                                 // update InternalUserPrincipal to save post processed data 
121                                 securityAccess.setInternalUserPrincipal(internalUser,internalUser.isMappingOnly());
122                             }
123                             break;
124                         }
125                         catch (SecurityException e)
126                         {
127                             if ( log.isErrorEnabled() )
128                                 log.error("Failure loading InternalCredential "+credential, e);
129                         }
130                     }
131                 }
132                 credential = null;
133             }
134         }
135         return credential;
136     }
137 
138     /***
139      * @see org.apache.jetspeed.security.spi.CredentialHandler#setPassword(java.lang.String,java.lang.String,java.lang.String)
140      */
141     public void setPassword(String userName, String oldPassword, String newPassword) throws SecurityException
142     {
143     	setPassword (userName, oldPassword, newPassword, false);
144     }
145   
146     /***
147      * @see org.apache.jetspeed.security.spi.CredentialHandler#importPassword(java.lang.String,java.lang.String)
148      */
149     public void importPassword(String userName, String newPassword) throws SecurityException
150     {
151     	setPassword (userName, null, newPassword, true);
152     }
153   
154     /***
155      * @see org.apache.jetspeed.security.spi.CredentialHandler#setPassword(java.lang.String,java.lang.String,java.lang.String, boolean)
156      */
157     protected void setPassword(String userName, String oldPassword, String newPassword, boolean raw) throws SecurityException
158     {
159         InternalUserPrincipal internalUser = securityAccess.getInternalUserPrincipal(userName, false);
160         if (null == internalUser)
161         {
162             throw new SecurityException(SecurityException.USER_DOES_NOT_EXIST.create(userName));
163         }
164         
165         Collection credentials = internalUser.getCredentials();
166         if (null == credentials)
167         {
168             credentials = new ArrayList();
169         }
170 
171         InternalCredential credential = getPasswordCredential(internalUser, userName );
172         
173         if (null != oldPassword)
174         {
175             if ( credential != null && 
176                     credential.getValue() != null && 
177                     credential.isEncoded() && 
178                     pcProvider.getEncoder() != null )
179             {
180                 if ( pcProvider.getEncoder() instanceof AlgorithmUpgradeCredentialPasswordEncoder )
181                 {
182                     oldPassword = ((AlgorithmUpgradeCredentialPasswordEncoder)pcProvider.getEncoder()).encode(userName,oldPassword, credential);
183                 }
184                 else
185                 {
186                     oldPassword = pcProvider.getEncoder().encode(userName,oldPassword);
187                 }
188             }
189         }
190         
191         if (oldPassword != null && (credential == null || credential.getValue() == null || !credential.getValue().equals(oldPassword)))
192         {
193             // supplied PasswordCredential not defined for this user
194             throw new InvalidPasswordException();
195         }
196         if (!raw) // bypass validation if raw 
197         {
198 	        if ( pcProvider.getValidator() != null )
199 	        {
200 	            try
201 	            {
202 	                pcProvider.getValidator().validate(newPassword);
203 	            }
204 	            catch (InvalidPasswordException ipe)
205 	            {
206 	                throw new InvalidNewPasswordException();
207 	            }
208 	        }
209         }
210         boolean encoded = false;
211         if ( pcProvider.getEncoder() != null )
212         {
213         	if (!(raw)) // if raw just bypass encoding
214         		newPassword = pcProvider.getEncoder().encode(userName, newPassword);
215             encoded = true;
216         }
217 
218         boolean create = credential == null;
219 
220         if ( create )
221         {
222             credential = new InternalCredentialImpl(internalUser.getPrincipalId(), newPassword, InternalCredential.PRIVATE,
223                             pcProvider.getPasswordCredentialClass().getName());
224             credential.setEncoded(encoded);
225             credentials.add(credential);
226         }
227         else if ( oldPassword == null )
228         {
229 /* TODO: should only be allowed for admin                     
230             // User *has* an PasswordCredential: setting a new Credential without supplying
231             // its current one is not allowed
232             throw new SecurityException(SecurityException.PASSWORD_REQUIRED);
233 */            
234         }
235         else if ( oldPassword.equals(newPassword) )
236         {
237             throw new PasswordAlreadyUsedException();
238         }
239 
240         if ( ipcInterceptor != null )
241         {
242             if ( create )
243             {
244                 ipcInterceptor.beforeCreate(internalUser, credentials, userName, credential, newPassword );
245             }
246             else
247             {
248                 ipcInterceptor.beforeSetPassword(internalUser, credentials, userName, credential, newPassword, oldPassword != null );
249             }
250         }
251         
252         if (!create)
253         {
254             credential.setValue(newPassword);
255             credential.setEncoded(encoded);
256             credential.setUpdateRequired(false);
257         }
258                 
259         long time = new Date().getTime();
260         
261         if ( oldPassword == null )
262         {
263             // non-user (admin) modified the password
264             
265             if ( encoded && pcProvider.getEncoder() instanceof AlgorithmUpgradePasswordEncodingService )
266             {
267                 // set current time in previous auth date, and clear last authentication date
268                 // !!! While this might be a bit strange logic, it is *required* for the AlgorithmUpgradePBEPasswordEncodingService
269                 // to be able to distinguise password changes from other changes
270                 credential.setPreviousAuthenticationDate(new Timestamp(new Date().getTime()));
271                 credential.setLastAuthenticationDate(null);
272             }
273         }
274         else
275         {
276             // authenticated password change (by user itself)
277             credential.setPreviousAuthenticationDate(credential.getLastAuthenticationDate());
278             credential.setLastAuthenticationDate(new Timestamp(time));
279         }
280         
281         credential.setModifiedDate(new Timestamp(time));
282         internalUser.setModifiedDate(new Timestamp(time));
283         internalUser.setCredentials(credentials);
284         // Set the user with the new credentials.
285         securityAccess.setInternalUserPrincipal(internalUser, false);
286     }
287     
288     
289     /***
290      * @see org.apache.jetspeed.security.spi.CredentialHandler#setPasswordEnabled(java.lang.String, boolean)
291      */
292     public void setPasswordEnabled(String userName, boolean enabled) throws SecurityException
293     {
294         InternalUserPrincipal internalUser = securityAccess.getInternalUserPrincipal(userName, false);
295         if (null != internalUser)
296         {
297             InternalCredential credential = getPasswordCredential(internalUser, userName );
298             if ( credential != null && !credential.isExpired() && credential.isEnabled() != enabled )
299             {
300                 long time = new Date().getTime();
301                 credential.setEnabled(enabled);
302                 credential.setAuthenticationFailures(0);
303                 credential.setModifiedDate(new Timestamp(time));
304                 internalUser.setModifiedDate(new Timestamp(time));
305                 securityAccess.setInternalUserPrincipal(internalUser, false);
306             }
307         }
308         else
309         {
310             throw new SecurityException(SecurityException.USER_DOES_NOT_EXIST.create(userName));
311         }
312     }
313   
314     /***
315      * @see org.apache.jetspeed.security.spi.CredentialHandler#setPasswordUpdateRequired(java.lang.String, boolean)
316      */
317     public void setPasswordUpdateRequired(String userName, boolean updateRequired) throws SecurityException
318     {
319         InternalUserPrincipal internalUser = securityAccess.getInternalUserPrincipal(userName, false);
320         if (null != internalUser)
321         {
322             InternalCredential credential = getPasswordCredential(internalUser, userName );
323             if ( credential != null && !credential.isExpired() && credential.isUpdateRequired() != updateRequired )
324             {
325                 // only allow setting updateRequired off if (non-Encoded) password is valid
326                 if ( !updateRequired && !credential.isEncoded() && pcProvider.getValidator() != null )
327                 {
328                     pcProvider.getValidator().validate(credential.getValue());
329                 }
330                 credential.setUpdateRequired(updateRequired);
331                 long time = new Date().getTime();
332                 credential.setModifiedDate(new Timestamp(time));
333                 // temporary hack for now to support setting passwordUpdateRequired = false
334                 // for users never authenticated yet.
335                 // The current InternalPasswordCredentialStateHandlingInterceptor.afterLoad()
336                 // logic will only set it (back) to true if both prev and last auth. date is null
337                 credential.setPreviousAuthenticationDate(new Timestamp(time));
338                 credential.setModifiedDate(new Timestamp(time));
339                 internalUser.setModifiedDate(new Timestamp(time));
340                 securityAccess.setInternalUserPrincipal(internalUser, false);
341             }
342         }
343         else
344         {
345             throw new SecurityException(SecurityException.USER_DOES_NOT_EXIST.create(userName));
346         }
347     }
348     
349     /***
350      * @see org.apache.jetspeed.security.spi.CredentialHandler#setPasswordExpiration(java.lang.String, java.sql.Date)
351      */
352     public void setPasswordExpiration(String userName, java.sql.Date expirationDate) throws SecurityException
353     {
354         InternalUserPrincipal internalUser = securityAccess.getInternalUserPrincipal(userName, false);
355         if (null != internalUser)
356         {
357             InternalCredential credential = getPasswordCredential(internalUser, userName );
358             if ( credential != null )
359             {
360                 long time = new Date().getTime();
361                 if ( expirationDate != null && new java.sql.Date(time).after(expirationDate))
362                 {
363                     credential.setExpired(true);
364                 }
365                 else
366                 {
367                     credential.setExpired(false);
368                 }
369                 credential.setExpirationDate(expirationDate);
370                 
371                 credential.setModifiedDate(new Timestamp(time));
372                 internalUser.setModifiedDate(new Timestamp(time));
373                 securityAccess.setInternalUserPrincipal(internalUser, false);
374             }
375         }
376         else
377         {
378             throw new SecurityException(SecurityException.USER_DOES_NOT_EXIST.create(userName));
379         }
380     }
381 
382     /***
383      * @see org.apache.jetspeed.security.spi.CredentialHandler#authenticate(java.lang.String, java.lang.String)
384      */
385     public boolean authenticate(String userName, String password) throws SecurityException
386     {
387         boolean authenticated = false;
388         InternalUserPrincipal internalUser = securityAccess.getInternalUserPrincipal(userName, false);
389         if (null != internalUser)
390         {
391             InternalCredential credential = getPasswordCredential(internalUser, userName );
392             if ( credential != null && credential.isEnabled() && !credential.isExpired())
393             {
394                 String encodedPassword = password;
395                 if ( pcProvider.getEncoder() != null && credential.isEncoded())
396                 {
397                     if ( pcProvider.getEncoder() instanceof AlgorithmUpgradeCredentialPasswordEncoder )
398                     {
399                         encodedPassword = ((AlgorithmUpgradeCredentialPasswordEncoder)pcProvider.getEncoder()).encode(userName,password, credential);
400                     }
401                     else
402                     {
403                         encodedPassword = pcProvider.getEncoder().encode(userName,password);
404                     }
405                 }
406 
407                 authenticated = credential.getValue().equals(encodedPassword);
408                 boolean update = false;
409 
410                 if ( ipcInterceptor != null )
411                 {
412                     update = ipcInterceptor.afterAuthenticated(internalUser, userName, credential, authenticated);
413                     if ( update && (!credential.isEnabled() || credential.isExpired()))
414                     {
415                         authenticated = false;
416                     }
417                 }
418                 long time = new Date().getTime();
419                 
420                 if ( authenticated )
421                 {
422                     credential.setAuthenticationFailures(0);
423 
424                     if ( pcProvider.getEncoder() != null && pcProvider.getEncoder() instanceof AlgorithmUpgradeCredentialPasswordEncoder)
425                     {
426                         ((AlgorithmUpgradeCredentialPasswordEncoder)pcProvider.getEncoder()).recodeIfNeeded(userName,password,credential);
427                     }
428                     
429                     credential.setPreviousAuthenticationDate(credential.getLastAuthenticationDate());
430                     credential.setLastAuthenticationDate(new Timestamp(time));
431                     update = true;
432                 }
433                 
434                 if ( update )
435                 {
436                     credential.setModifiedDate(new Timestamp(time));
437                     internalUser.setModifiedDate(new Timestamp(time));
438                     securityAccess.setInternalUserPrincipal(internalUser, false);
439                 }
440             }
441         }
442         else
443         {
444             throw new SecurityException(SecurityException.USER_DOES_NOT_EXIST.create(userName));
445         }
446         return authenticated;
447     }
448 }