Author: ate
Date: Sun Jul  2 08:10:18 2006
New Revision: 418604

URL: http://svn.apache.org/viewvc?rev=418604&view=rev
Log:
JS2-550: A new Two-way password encoding service allowing decoding of encoded 
passwords
This set of changes provides a further enhancement providing a way to lazy 
upgrade from one encoding scheme to another.
Furthermore, a few improvements in handling of InternalCredential timestamp 
properties.
See: http://issues.apache.org/jira/browse/JS2-550#action_12418846

Added:
    
portals/jetspeed-2/trunk/components/security/src/java/org/apache/jetspeed/security/spi/AlgorithmUpgradeCredentialPasswordEncoder.java
    
portals/jetspeed-2/trunk/components/security/src/java/org/apache/jetspeed/security/spi/impl/AlgorithmUpgradePBEPasswordService.java
    
portals/jetspeed-2/trunk/jetspeed-api/src/java/org/apache/jetspeed/security/AlgorithmUpgradePasswordEncodingService.java
Modified:
    
portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/security/impl/PasswordCredentialValveImpl.java
    
portals/jetspeed-2/trunk/components/security/src/java/org/apache/jetspeed/security/spi/impl/DefaultCredentialHandler.java
    portals/jetspeed-2/trunk/src/webapp/WEB-INF/assembly/security-spi-atn.xml

Modified: 
portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/security/impl/PasswordCredentialValveImpl.java
URL: 
http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/security/impl/PasswordCredentialValveImpl.java?rev=418604&r1=418603&r2=418604&view=diff
==============================================================================
--- 
portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/security/impl/PasswordCredentialValveImpl.java
 (original)
+++ 
portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/security/impl/PasswordCredentialValveImpl.java
 Sun Jul  2 08:10:18 2006
@@ -102,6 +102,7 @@
                     {
                         request.setSessionAttribute(CHECKED_KEY,Boolean.TRUE);
                         if ( pwdCredential.getPreviousAuthenticationDate() != 
null && 
+                                pwdCredential.getLastAuthenticationDate() != 
null &&
                                 pwdCredential.getExpirationDate() != null )
                         {
                             long expirationTime = 
pwdCredential.getExpirationDate().getTime();

Added: 
portals/jetspeed-2/trunk/components/security/src/java/org/apache/jetspeed/security/spi/AlgorithmUpgradeCredentialPasswordEncoder.java
URL: 
http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/components/security/src/java/org/apache/jetspeed/security/spi/AlgorithmUpgradeCredentialPasswordEncoder.java?rev=418604&view=auto
==============================================================================
--- 
portals/jetspeed-2/trunk/components/security/src/java/org/apache/jetspeed/security/spi/AlgorithmUpgradeCredentialPasswordEncoder.java
 (added)
+++ 
portals/jetspeed-2/trunk/components/security/src/java/org/apache/jetspeed/security/spi/AlgorithmUpgradeCredentialPasswordEncoder.java
 Sun Jul  2 08:10:18 2006
@@ -0,0 +1,42 @@
+/* Copyright 2004 Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.apache.jetspeed.security.spi;
+
+import org.apache.jetspeed.security.PasswordCredential;
+import org.apache.jetspeed.security.SecurityException;
+import org.apache.jetspeed.security.om.InternalCredential;
+
+/**
+ * <p>
+ * AlgorithmUpgradeCredentialPasswordEncoder which is provided with the 
InternalCredential as well
+ * to allow for migrating between two different encoding schemes.
+ * </p>
+ * <p>
+ * The extended encode method is *only* called in the context of validating an 
existing (old) password,
+ * and not used for creating or updating to a new password directl!
+ * </p>
+ * <p>
+ * After successfull authentication, the recodeIfNeeded method will be called 
allowing to migrate to the new encryption scheme.
+ * </p>
+ * 
+ * @author <a href="mailto:[EMAIL PROTECTED]">Ate Douma</a>
+ * @version $Id$
+ */
+public interface AlgorithmUpgradeCredentialPasswordEncoder extends 
CredentialPasswordEncoder
+{
+    String encode(String userName, String clearTextPassword, 
InternalCredential credential) throws SecurityException;
+    void recodeIfNeeded(String userName, String clearTextPassword, 
InternalCredential credential) throws SecurityException;
+    boolean usesOldEncodingAlgorithm(PasswordCredential credential);
+}

Added: 
portals/jetspeed-2/trunk/components/security/src/java/org/apache/jetspeed/security/spi/impl/AlgorithmUpgradePBEPasswordService.java
URL: 
http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/components/security/src/java/org/apache/jetspeed/security/spi/impl/AlgorithmUpgradePBEPasswordService.java?rev=418604&view=auto
==============================================================================
--- 
portals/jetspeed-2/trunk/components/security/src/java/org/apache/jetspeed/security/spi/impl/AlgorithmUpgradePBEPasswordService.java
 (added)
+++ 
portals/jetspeed-2/trunk/components/security/src/java/org/apache/jetspeed/security/spi/impl/AlgorithmUpgradePBEPasswordService.java
 Sun Jul  2 08:10:18 2006
@@ -0,0 +1,112 @@
+/* Copyright 2004 Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jetspeed.security.spi.impl;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.jetspeed.security.AlgorithmUpgradePasswordEncodingService;
+import org.apache.jetspeed.security.PasswordCredential;
+import org.apache.jetspeed.security.SecurityException;
+import org.apache.jetspeed.security.om.InternalCredential;
+import 
org.apache.jetspeed.security.spi.AlgorithmUpgradeCredentialPasswordEncoder;
+import org.apache.jetspeed.security.spi.CredentialPasswordEncoder;
+
+/**
+ * <p>
+ * MessageDigestToPBEPasswordUpgradeService allows for migrating from a 
MessageDigestCredentialPasswordEncoder
+ * to the PBEPasswordService
+ * </p>
+ * 
+ * @author <a href="mailto:[EMAIL PROTECTED]">Ate Douma</a>
+ * @version $Id:$
+ */
+public class AlgorithmUpgradePBEPasswordService extends PBEPasswordService 
implements AlgorithmUpgradeCredentialPasswordEncoder, 
AlgorithmUpgradePasswordEncodingService
+{
+    private CredentialPasswordEncoder oldEncoder;
+    private Timestamp startPBEPasswordEncoding;
+    
+    public AlgorithmUpgradePBEPasswordService(String pbePassword, 
CredentialPasswordEncoder oldEncoder, String startPBEPasswordEncoding) throws 
InvalidKeySpecException,
+            NoSuchAlgorithmException, ParseException
+    {
+        super(pbePassword);
+        this.oldEncoder = oldEncoder;
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        this.startPBEPasswordEncoding = new 
Timestamp(df.parse(startPBEPasswordEncoding).getTime());
+    }
+    
+    /* (non-Javadoc)
+     * @see 
org.apache.jetspeed.security.AlgorithmUpgradePasswordEncodingService#usesOldEncodingAlgorithm(org.apache.jetspeed.security.PasswordCredential)
+     */
+    public boolean usesOldEncodingAlgorithm(PasswordCredential credential)
+    {
+        return usesOldEncodingAlgorithm(credential.isEnabled(), 
credential.getLastAuthenticationDate(), 
credential.getPreviousAuthenticationDate());
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.jetspeed.security.spi.AlgorithmUpgradeCredentialPasswordEncoder#encode(java.lang.String,
 java.lang.String, org.apache.jetspeed.security.om.InternalCredential)
+     */
+    public String encode(String userName, String clearTextPassword, 
InternalCredential credential) throws SecurityException
+    {
+        if ( usesOldEncodingAlgorithm(credential.isEnabled(), 
credential.getLastAuthenticationDate(), 
credential.getPreviousAuthenticationDate()))
+        {
+            return oldEncoder.encode(userName, clearTextPassword);
+        }
+        else
+        {
+            return encode(userName, clearTextPassword);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.jetspeed.security.spi.AlgorithmUpgradeCredentialPasswordEncoder#recodeIfNeeded(java.lang.String,
 java.lang.String, org.apache.jetspeed.security.om.InternalCredential)
+     */
+    public void recodeIfNeeded(String userName, String clearTextPassword, 
InternalCredential credential) throws SecurityException
+    {
+        if ( usesOldEncodingAlgorithm(credential.isEnabled(), 
credential.getLastAuthenticationDate(), 
credential.getPreviousAuthenticationDate()))
+        {
+            credential.setValue(encode(userName, clearTextPassword));
+        }
+    }
+    
+    private boolean usesOldEncodingAlgorithm(boolean encoded, Timestamp 
lastAuthDate, Timestamp prevAuthDate )
+    {
+        if ( encoded )
+        {
+            if ( lastAuthDate != null )
+            {
+                return lastAuthDate.before(startPBEPasswordEncoding);
+            }
+            else if ( prevAuthDate != null )
+            {
+                // password was created, but the user is not authenticated yet
+                return prevAuthDate.before(startPBEPasswordEncoding);
+            }
+            else
+            {
+                // not yet upgraded encoded password
+                return true;
+            }
+        }
+        else
+        {
+            return false;
+        }
+    }
+}

Modified: 
portals/jetspeed-2/trunk/components/security/src/java/org/apache/jetspeed/security/spi/impl/DefaultCredentialHandler.java
URL: 
http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/components/security/src/java/org/apache/jetspeed/security/spi/impl/DefaultCredentialHandler.java?rev=418604&r1=418603&r2=418604&view=diff
==============================================================================
--- 
portals/jetspeed-2/trunk/components/security/src/java/org/apache/jetspeed/security/spi/impl/DefaultCredentialHandler.java
 (original)
+++ 
portals/jetspeed-2/trunk/components/security/src/java/org/apache/jetspeed/security/spi/impl/DefaultCredentialHandler.java
 Sun Jul  2 08:10:18 2006
@@ -32,6 +32,7 @@
 import org.apache.jetspeed.security.om.InternalUserPrincipal;
 import org.apache.jetspeed.security.om.impl.InternalCredentialImpl;
 import org.apache.jetspeed.security.spi.CredentialHandler;
+import 
org.apache.jetspeed.security.spi.AlgorithmUpgradeCredentialPasswordEncoder;
 import org.apache.jetspeed.security.spi.InternalPasswordCredentialInterceptor;
 import org.apache.jetspeed.security.spi.PasswordCredentialProvider;
 import org.apache.jetspeed.security.spi.SecurityAccess;
@@ -157,7 +158,14 @@
                     credential.isEncoded() && 
                     pcProvider.getEncoder() != null )
             {
-                oldPassword = pcProvider.getEncoder().encode(userName, 
oldPassword);
+                if ( pcProvider.getEncoder() instanceof 
AlgorithmUpgradeCredentialPasswordEncoder )
+                {
+                    oldPassword = 
((AlgorithmUpgradeCredentialPasswordEncoder)pcProvider.getEncoder()).encode(userName,oldPassword,
 credential);
+                }
+                else
+                {
+                    oldPassword = 
pcProvider.getEncoder().encode(userName,oldPassword);
+                }
             }
         }
         
@@ -219,6 +227,7 @@
                 ipcInterceptor.beforeSetPassword(internalUser, credentials, 
userName, credential, newPassword, oldPassword != null );
             }
         }
+        
         if (!create)
         {
             credential.setValue(newPassword);
@@ -226,7 +235,27 @@
             credential.setUpdateRequired(false);
         }
                 
-        internalUser.setModifiedDate(new Timestamp(new Date().getTime()));
+        long time = new Date().getTime();
+        
+        if ( oldPassword == null )
+        {
+            // non-user (admin) modified the password
+            
+            // set current time in previous auth date, and clear last 
authentication date
+            // !!! While this might be a bit strange logic, it is *required* 
for the AlgorithmUpgradePBEPasswordEncodingService
+            // to be able to distinguise password changes from other changes
+            credential.setPreviousAuthenticationDate(new Timestamp(new 
Date().getTime()));
+            credential.setLastAuthenticationDate(null);
+        }
+        else
+        {
+            // authenticated password change (by user itself)
+            
credential.setPreviousAuthenticationDate(credential.getLastAuthenticationDate());
+            credential.setLastAuthenticationDate(new Timestamp(time));
+        }
+        
+        credential.setModifiedDate(new Timestamp(time));
+        internalUser.setModifiedDate(new Timestamp(time));
         internalUser.setCredentials(credentials);
         // Set the user with the new credentials.
         securityAccess.setInternalUserPrincipal(internalUser, false);
@@ -244,9 +273,11 @@
             InternalCredential credential = 
getPasswordCredential(internalUser, userName );
             if ( credential != null && !credential.isExpired() && 
credential.isEnabled() != enabled )
             {
+                long time = new Date().getTime();
                 credential.setEnabled(enabled);
                 credential.setAuthenticationFailures(0);
-                internalUser.setModifiedDate(new Timestamp(new 
Date().getTime()));
+                credential.setModifiedDate(new Timestamp(time));
+                internalUser.setModifiedDate(new Timestamp(time));
                 securityAccess.setInternalUserPrincipal(internalUser, false);
             }
         }
@@ -280,6 +311,7 @@
                 // The current 
InternalPasswordCredentialStateHandlingInterceptor.afterLoad()
                 // logic will only set it (back) to true if both prev and last 
auth. date is null
                 credential.setPreviousAuthenticationDate(new Timestamp(time));
+                credential.setModifiedDate(new Timestamp(time));
                 internalUser.setModifiedDate(new Timestamp(time));
                 securityAccess.setInternalUserPrincipal(internalUser, false);
             }
@@ -335,12 +367,20 @@
             InternalCredential credential = 
getPasswordCredential(internalUser, userName );
             if ( credential != null && credential.isEnabled() && 
!credential.isExpired())
             {
+                String encodedPassword = password;
                 if ( pcProvider.getEncoder() != null && credential.isEncoded())
                 {
-                    password = 
pcProvider.getEncoder().encode(userName,password);
+                    if ( pcProvider.getEncoder() instanceof 
AlgorithmUpgradeCredentialPasswordEncoder )
+                    {
+                        encodedPassword = 
((AlgorithmUpgradeCredentialPasswordEncoder)pcProvider.getEncoder()).encode(userName,password,
 credential);
+                    }
+                    else
+                    {
+                        encodedPassword = 
pcProvider.getEncoder().encode(userName,password);
+                    }
                 }
 
-                authenticated = credential.getValue().equals(password);
+                authenticated = credential.getValue().equals(encodedPassword);
                 boolean update = false;
 
                 if ( ipcInterceptor != null )
@@ -351,17 +391,26 @@
                         authenticated = false;
                     }
                 }
+                long time = new Date().getTime();
+                
                 if ( authenticated )
                 {
                     credential.setAuthenticationFailures(0);
+
+                    if ( pcProvider.getEncoder() != null && 
pcProvider.getEncoder() instanceof AlgorithmUpgradeCredentialPasswordEncoder)
+                    {
+                        
((AlgorithmUpgradeCredentialPasswordEncoder)pcProvider.getEncoder()).recodeIfNeeded(userName,password,credential);
+                    }
+                    
                     
credential.setPreviousAuthenticationDate(credential.getLastAuthenticationDate());
-                    credential.setLastAuthenticationDate(new 
Timestamp(System.currentTimeMillis()));
+                    credential.setLastAuthenticationDate(new Timestamp(time));
                     update = true;
                 }
                 
                 if ( update )
                 {
-                    internalUser.setModifiedDate(new 
Timestamp(System.currentTimeMillis()));
+                    credential.setModifiedDate(new Timestamp(time));
+                    internalUser.setModifiedDate(new Timestamp(time));
                     securityAccess.setInternalUserPrincipal(internalUser, 
false);
                 }
             }

Added: 
portals/jetspeed-2/trunk/jetspeed-api/src/java/org/apache/jetspeed/security/AlgorithmUpgradePasswordEncodingService.java
URL: 
http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/jetspeed-api/src/java/org/apache/jetspeed/security/AlgorithmUpgradePasswordEncodingService.java?rev=418604&view=auto
==============================================================================
--- 
portals/jetspeed-2/trunk/jetspeed-api/src/java/org/apache/jetspeed/security/AlgorithmUpgradePasswordEncodingService.java
 (added)
+++ 
portals/jetspeed-2/trunk/jetspeed-api/src/java/org/apache/jetspeed/security/AlgorithmUpgradePasswordEncodingService.java
 Sun Jul  2 08:10:18 2006
@@ -0,0 +1,30 @@
+/* Copyright 2004 Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.apache.jetspeed.security;
+
+/**
+ * <p>
+ * AlgorithmUpgradePasswordEncodingService allows checking a specific 
PasswordCredential if it uses the provided Encoding Algorithm.
+ * </p>
+ * <p>
+ * This service can be used for gradually migrating from a one-way encoding to 
a two-way encoding algoritmn.
+ * </p>
+ * @author <a href="mailto:[EMAIL PROTECTED]">Ate Douma</a>
+ * @version $Id$
+ */
+public interface AlgorithmUpgradePasswordEncodingService extends 
PasswordEncodingService
+{
+    boolean usesOldEncodingAlgorithm(PasswordCredential passwordCredential);
+}

Modified: 
portals/jetspeed-2/trunk/src/webapp/WEB-INF/assembly/security-spi-atn.xml
URL: 
http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/src/webapp/WEB-INF/assembly/security-spi-atn.xml?rev=418604&r1=418603&r2=418604&view=diff
==============================================================================
--- portals/jetspeed-2/trunk/src/webapp/WEB-INF/assembly/security-spi-atn.xml 
(original)
+++ portals/jetspeed-2/trunk/src/webapp/WEB-INF/assembly/security-spi-atn.xml 
Sun Jul  2 08:10:18 2006
@@ -47,6 +47,32 @@
   </bean>       
 -->
 
+ <!-- A Two-way encoding password service which also implements 
CredentialPasswordEncoder
+       Furthermore, this extension of the PBEPasswordService supports lazy 
upgrading from an old CredentialPasswordEncoder
+       like the default provided MessageDigestCredentialPasswordEncoder
+  ->
+  <bean id="org.apache.jetspeed.security.PasswordEncodingService"
+        name="org.apache.jetspeed.security.spi.CredentialPasswordEncoder"
+        
class="org.apache.jetspeed.security.spi.impl.AlgorithmUpgradePBEPasswordService">
+    <constructor-arg index="0">
+      <!- secret PBE key password ->
+      <value>********</value>
+    </constructor-arg>
+    <constructor-arg index="1">
+      <!- old MessageDigestCredentialPasswordEncoder to be upgrading from, 
using SHA-1 ->
+      <bean 
class="org.apache.jetspeed.security.spi.impl.MessageDigestCredentialPasswordEncoder">
+       <constructor-arg index="0"><value>SHA-1</value></constructor-arg>       
+      </bean>       
+    </constructor-arg>
+    <constructor-arg index="2">
+      <!- startPBEPasswordEncodingService: date before which old encoded 
passwords need to be recoded (on authentication)
+           (SimpleDateFormat) format: yyyy-MM-dd HH:mm:ss
+      ->
+      <value>2006-07-02 15:00:00</value>
+    </constructor-arg>
+  </bean>
+-->
+
   <!-- allow multiple InternalPasswordCredentialInterceptors to be used for 
DefaultCredentialHandler --> 
   <bean 
id="org.apache.jetspeed.security.spi.InternalPasswordCredentialInterceptor"
        
class="org.apache.jetspeed.security.spi.impl.InternalPasswordCredentialInterceptorsProxy">



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to