You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ca...@apache.org on 2006/08/30 01:57:40 UTC

svn commit: r438300 - in /maven/shared/trunk/maven-user/maven-user-model/src: main/java/org/apache/maven/user/model/ main/java/org/apache/maven/user/model/impl/ main/java/org/apache/maven/user/model/rules/ main/java/org/apache/maven/user/model/store/im...

Author: carlos
Date: Tue Aug 29 16:57:39 2006
New Revision: 438300

URL: http://svn.apache.org/viewvc?rev=438300&view=rev
Log:
[CONTINUUM-793, CONTINUUM-795, CONTINUUM-796] Password validation
Submitted By: Joakime Erdfelt

Added:
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/UserSecurityPolicy.java   (with props)
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/DefaultUserSecurityPolicy.java   (with props)
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/AlphaPasswordRule.java   (with props)
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/CharacterLengthPasswordRule.java   (with props)
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/MustHavePasswordRule.java   (with props)
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/NumericalPasswordRule.java   (with props)
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/ReusePasswordRule.java   (with props)
Removed:
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/MustHavePasswordRule.java
Modified:
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRule.java
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRuleViolations.java
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/UserManager.java
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/DefaultUserManager.java
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/store/impl/DefaultUserStore.java
    maven/shared/trunk/maven-user/maven-user-model/src/main/mdo/user.xml
    maven/shared/trunk/maven-user/maven-user-model/src/main/resources/org/apache/maven/user/model/messages.properties
    maven/shared/trunk/maven-user/maven-user-model/src/test/java/org/apache/maven/user/model/impl/DefaultUserManagerTest.java

Modified: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRule.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRule.java?rev=438300&r1=438299&r2=438300&view=diff
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRule.java (original)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRule.java Tue Aug 29 16:57:39 2006
@@ -16,12 +16,9 @@
  * limitations under the License.
  */
 
-
 /**
  * A Password Rule
  * 
- * @TODO don't rely too much into this until we leverage commons-validation.
- * 
  * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
  * @version $Id$
  */
@@ -31,9 +28,9 @@
      * Provide the Plexus Component Role.
      */
     public static final String ROLE = PasswordRule.class.getName();
-    
+
     /**
      * Tests the {@link User#getPassword()} for a valid password, based on rule.
      */
-    void testPassword(PasswordRuleViolations violations, User user);
+    void testPassword( PasswordRuleViolations violations, User user, UserSecurityPolicy securityPolicy );
 }

Modified: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRuleViolations.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRuleViolations.java?rev=438300&r1=438299&r2=438300&view=diff
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRuleViolations.java (original)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRuleViolations.java Tue Aug 29 16:57:39 2006
@@ -34,7 +34,7 @@
     {
         String key;
 
-        String args[];
+        Object args[];
     }
 
     /**
@@ -60,12 +60,23 @@
      */
     public void addViolation( String key )
     {
+        addViolation(key, null);
+    }
+    
+    /**
+     * Add a violation to the underlying list.
+     * 
+     * @param key the bundle/localization key for the message.
+     * @param args the arguments for the message.
+     */
+    public void addViolation( String key, Object args[] )
+    {
         MessageReference mesgref = new MessageReference();
         mesgref.key = key;
-        mesgref.args = null;
+        mesgref.args = args;
         violations.add( mesgref );
     }
-
+    
     /**
      * Get the List of Violations as localized and post-processed {@link String}s.
      * 

Modified: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/UserManager.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/UserManager.java?rev=438300&r1=438299&r2=438300&view=diff
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/UserManager.java (original)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/UserManager.java Tue Aug 29 16:57:39 2006
@@ -36,8 +36,7 @@
     // ----------------------------------------------------------------------
 
     /**
-     * Add a new user. User password will be encoded using the {@link #getPasswordEncoder()}
-     * before storing it.
+     * Add a new user. User password may be encoded before storing it.
      * 
      * @param user
      */
@@ -45,8 +44,7 @@
         throws PasswordRuleViolationException;
 
     /**
-     * Update user data. User password will be encoded using the {@link #getPasswordEncoder()}
-     * before storing it.
+     * Update user data. User password may be encoded before storing it.
      * 
      * @param user
      */
@@ -129,45 +127,6 @@
      */
     boolean login(String username, String rawpassword);
     
-    // ----------------------------------------------------------------------
-    // Passwords
-    // ----------------------------------------------------------------------
-    
-    /**
-     * Set the password encoder to be used for password operations 
-     * 
-     * @param passwordEncoder
-     */
-    void setPasswordEncoder( PasswordEncoder passwordEncoder );
-
-    /**
-     * Get the password encoder to be used for password operations
-     * 
-     * @return the encoder
-     */
-    PasswordEncoder getPasswordEncoder();
-    
-    /**
-     * Set the Password Rules List.
-     * 
-     * @param rules the list of {@link PasswordRule} objects.
-     */
-    void setPasswordRules( List rules );
-    
-    /**
-     * Get the Password Rules List.
-     * 
-     * @return the list of {@link PasswordRule} objects.
-     */
-    List getPasswordRules();
-
-    /**
-     * Add a Specific Rule to the Password Rules List.
-     * 
-     * @param rule the rule to add. 
-     */
-    void addPasswordRule( PasswordRule rule );
-
     // ----------------------------------------------------------------------
     // User Group
     // ----------------------------------------------------------------------

Added: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/UserSecurityPolicy.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/UserSecurityPolicy.java?rev=438300&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/UserSecurityPolicy.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/UserSecurityPolicy.java Tue Aug 29 16:57:39 2006
@@ -0,0 +1,36 @@
+package org.apache.maven.user.model;
+
+/*
+ * Copyright 2001-2006 The 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.
+ */
+
+/**
+ * User Security Policy Settings.  
+ */
+public interface UserSecurityPolicy
+{
+    public static final String ROLE = UserSecurityPolicy.class.getName();
+
+    public int getAllowedLoginAttempts();
+
+    public int getPreviousPasswordsCount();
+
+    /**
+     * Salt to be used in addiotion to the algorithm when encoding a password
+     * 
+     * @return the salt
+     */
+    public String getSalt();
+}

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/UserSecurityPolicy.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/UserSecurityPolicy.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Modified: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/DefaultUserManager.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/DefaultUserManager.java?rev=438300&r1=438299&r2=438300&view=diff
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/DefaultUserManager.java (original)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/DefaultUserManager.java Tue Aug 29 16:57:39 2006
@@ -30,6 +30,8 @@
 import org.apache.maven.user.model.User;
 import org.apache.maven.user.model.UserGroup;
 import org.apache.maven.user.model.UserManager;
+import org.apache.maven.user.model.UserSecurityPolicy;
+import org.apache.maven.user.model.rules.MustHavePasswordRule;
 import org.apache.maven.user.model.store.UserStore;
 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
 import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
@@ -57,10 +59,10 @@
     private PasswordEncoder passwordEncoder;
 
     /**
-     * @plexus.configuration default-value="Step doog ekam Skravdraa"
+     * @plexus.requirement
      */
-    private String salt;
-
+    private UserSecurityPolicy securityPolicy;
+    
     /**
      * The List of {@link PasswordRule} objects.
      */
@@ -86,8 +88,63 @@
         {
             return false;
         }
+        
+        if ( user.isLocked() )
+        {
+            return false;
+        }
 
-        return this.passwordEncoder.isPasswordValid( user.getEncodedPassword(), rawPassword, salt );
+        // Ensure that user cannot set password during login.
+        user.setPassword( null );
+        
+        boolean validPassword = this.passwordEncoder.isPasswordValid( user.getEncodedPassword(), rawPassword,
+                                                                      securityPolicy.getSalt() );
+        
+        if ( validPassword )
+        {
+            // successful login. reset any failed login attempts counter.
+            user.setFailedLoginAttempts( 0 );
+        }
+        else
+        {
+            // failed login. increment and test.
+            if ( user.incrementFailedLoginAttempts() >= securityPolicy.getAllowedLoginAttempts() )
+            {
+                user.setLocked( true );
+            }
+            
+            try
+            {
+                this.updateUser( user );
+            }
+            catch ( PasswordRuleViolationException e )
+            {
+                // not possible here.
+                throw new RuntimeException( e );
+            }
+        } 
+        
+        return validPassword;
+    }
+    
+    /**
+     * Sets the Security Policy to use.
+     * 
+     * @param policy the policy to use.
+     */
+    public void setSecurityPolicy( UserSecurityPolicy policy )
+    {
+        this.securityPolicy = policy;
+    }
+
+    /**
+     * Gets the Security Policy to use.
+     * 
+     * @return the security policy.
+     */
+    public UserSecurityPolicy getSecurityPolicy()
+    {
+        return securityPolicy;
     }
 
     public User addUser( User user )
@@ -106,17 +163,30 @@
     private void processPasswordChange( User user )
         throws PasswordRuleViolationException
     {
-        validatePassword( user );
-
         if ( user.isGuest() )
         {
-            //TODO we shouldn't allow password changes for guest users, throw exception before getting here
             user.setEncodedPassword( null );
+            //TODO we shouldn't allow password changes for guest users, throw exception before getting here
+            return;
         }
-        else
+        
+        validatePassword( user );
+        
+        // remember the previous password.
+        List previousPasswords = new ArrayList();
+        previousPasswords.add( user.getEncodedPassword() );
+        
+        if ( ( user.getPreviousEncodedPasswords() != null ) && !user.getPreviousEncodedPasswords().isEmpty() )
         {
-            user.setEncodedPassword( this.passwordEncoder.encodePassword( user.getPassword(), salt ) );
+            int oldCount = Math.min( securityPolicy.getPreviousPasswordsCount() - 1, 
+                                     user.getPreviousEncodedPasswords().size() );
+            List sublist = user.getPreviousEncodedPasswords().subList( 1, oldCount );
+            previousPasswords.addAll( sublist );
         }
+        user.setPreviousEncodedPasswords( previousPasswords );
+        
+        // set the current encoded password.
+        user.setEncodedPassword( encodePassword( user.getPassword() ) );
         user.setPassword( null );
 
         user.setLastPasswordChange( new Date() ); // update timestamp to now.
@@ -125,13 +195,16 @@
     private void validatePassword( User user )
         throws PasswordRuleViolationException
     {
+        // Trim password.
+        user.setPassword( StringUtils.trim( user.getPassword() ) );
+        
         PasswordRuleViolations violations = new PasswordRuleViolations();
 
         Iterator it = this.rules.iterator();
         while ( it.hasNext() )
         {
             PasswordRule rule = (PasswordRule) it.next();
-            rule.testPassword( violations, user );
+            rule.testPassword( violations, user, securityPolicy );
         }
 
         if ( violations.hasViolations() )
@@ -152,11 +225,6 @@
         return userStore.addUserGroup( userGroup );
     }
 
-    public PasswordEncoder getPasswordEncoder()
-    {
-        return passwordEncoder;
-    }
-
     public User getUser( int accountId )
     {
         return userStore.getUser( accountId );
@@ -224,23 +292,64 @@
         userStore.removeUserGroup( userGroupName );
     }
 
+    /**
+     * Set the password encoder to be used for password operations 
+     * 
+     * @param passwordEncoder
+     */
     public void setPasswordEncoder( PasswordEncoder passwordEncoder )
     {
         this.passwordEncoder = passwordEncoder;
     }
 
+    /**
+     * Get the password encoder to be used for password operations
+     * 
+     * @return the encoder
+     */
+    public PasswordEncoder getPasswordEncoder()
+    {
+        return passwordEncoder;
+    }
+
+    /**
+     * Encode arbitrary password using configured encoder and salt.
+     * 
+     * @param rawpassword the raw password to encode.
+     * @return the encoded form of the password.
+     */
+    public String encodePassword( String rawpassword )
+    {
+        return this.passwordEncoder.encodePassword( rawpassword, securityPolicy.getSalt() );
+    }
+
+    /**
+     * Add a Specific Rule to the Password Rules List.
+     * 
+     * @param rule the rule to add. 
+     */
     public void addPasswordRule( PasswordRule rule )
     {
-        // TODO: check for duplicates?
+        // TODO: check for duplicates? if so, check should only be based on Rule class name.
 
         this.rules.add( rule );
     }
 
+    /**
+     * Get the Password Rules List.
+     * 
+     * @return the list of {@link PasswordRule} objects.
+     */
     public List getPasswordRules()
     {
         return this.rules;
     }
 
+    /**
+     * Set the Password Rules List.
+     * 
+     * @param rules the list of {@link PasswordRule} objects.
+     */
     public void setPasswordRules( List rules )
     {
         this.rules = rules;

Added: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/DefaultUserSecurityPolicy.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/DefaultUserSecurityPolicy.java?rev=438300&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/DefaultUserSecurityPolicy.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/DefaultUserSecurityPolicy.java Tue Aug 29 16:57:39 2006
@@ -0,0 +1,76 @@
+package org.apache.maven.user.model.impl;
+
+/*
+ * Copyright 2001-2006 The 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.
+ */
+
+import org.apache.maven.user.model.UserSecurityPolicy;
+
+/**
+ * User Security Policy. 
+ * 
+ * @plexus.component role="org.apache.maven.user.model.UserSecurityPolicy"
+ * 
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class DefaultUserSecurityPolicy
+    implements UserSecurityPolicy
+{
+    /**
+     * @plexus.configuration default-value="3"
+     */
+    private int allowedLoginAttempts;
+
+    /**
+     * @plexus.configuration default-value="6"
+     */
+    private int previousPasswordsCount;
+
+    /**
+     * @plexus.configuration default-value="Step doog ekam Skravdraa"
+     */
+    private String salt;
+
+    public int getAllowedLoginAttempts()
+    {
+        return allowedLoginAttempts;
+    }
+
+    public int getPreviousPasswordsCount()
+    {
+        return previousPasswordsCount;
+    }
+
+    public void setAllowedLoginAttempts( int allowedLoginAttempts )
+    {
+        this.allowedLoginAttempts = allowedLoginAttempts;
+    }
+
+    public void setPreviousPasswordsCount( int previousPasswordsCount )
+    {
+        this.previousPasswordsCount = previousPasswordsCount;
+    }
+
+    public void setSalt( String salt )
+    {
+        this.salt = salt;
+    }
+
+    public String getSalt()
+    {
+        return salt;
+    }
+}

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/DefaultUserSecurityPolicy.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/DefaultUserSecurityPolicy.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/AlphaPasswordRule.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/AlphaPasswordRule.java?rev=438300&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/AlphaPasswordRule.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/AlphaPasswordRule.java Tue Aug 29 16:57:39 2006
@@ -0,0 +1,84 @@
+package org.apache.maven.user.model.rules;
+
+/*
+ * Copyright 2001-2006 The 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.
+ */
+
+import org.apache.maven.user.model.PasswordRule;
+import org.apache.maven.user.model.PasswordRuleViolations;
+import org.apache.maven.user.model.User;
+import org.apache.maven.user.model.UserSecurityPolicy;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Basic Password Rule, Checks for non-empty passwords that have at least {@link #setMinimumCount(int)} of 
+ * alpha characters contained within. 
+ * 
+ * @plexus.component role="org.apache.maven.user.model.PasswordRule" role-hint="alpha-count"
+ * 
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class AlphaPasswordRule
+    implements PasswordRule
+{
+    private int minimumCount;
+
+    public AlphaPasswordRule()
+    {
+        minimumCount = 1;
+    }
+
+    public int getMinimumCount()
+    {
+        return minimumCount;
+    }
+
+    public void setMinimumCount( int minimumCount )
+    {
+        this.minimumCount = minimumCount;
+    }
+
+    public void testPassword( PasswordRuleViolations violations, User user, UserSecurityPolicy securityPolicy )
+    {
+        if ( countAlphaCharacters( user.getPassword() ) < this.minimumCount )
+        {
+            violations.addViolation( "user.password.violation.alpha", new Object[] { new Integer( minimumCount ) } ); //$NON-NLS-1$
+        }
+    }
+
+    private int countAlphaCharacters( String password )
+    {
+        int count = 0;
+
+        if ( StringUtils.isEmpty( password ) )
+        {
+            return count;
+        }
+
+        // Doing this via iteration of code points to take in account localized passwords.
+        for ( int i = 0; i < password.length(); i++ )
+        {
+            int codepoint = password.codePointAt( i );
+            if ( Character.isLetter( codepoint ) )
+            {
+                count++;
+            }
+        }
+
+        return count;
+    }
+
+}

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/AlphaPasswordRule.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/AlphaPasswordRule.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/CharacterLengthPasswordRule.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/CharacterLengthPasswordRule.java?rev=438300&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/CharacterLengthPasswordRule.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/CharacterLengthPasswordRule.java Tue Aug 29 16:57:39 2006
@@ -0,0 +1,88 @@
+package org.apache.maven.user.model.rules;
+
+/*
+ * Copyright 2001-2006 The 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.
+ */
+
+import org.apache.maven.user.model.PasswordRule;
+import org.apache.maven.user.model.PasswordRuleViolations;
+import org.apache.maven.user.model.User;
+import org.apache.maven.user.model.UserSecurityPolicy;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Basic Password Rule, Checks for non-empty passwords that have between {@link #setMinimumCharacters(int)} and 
+ * {@link #setMaximumCharacters(int)} characters in length. 
+ * 
+ * @plexus.component role="org.apache.maven.user.model.PasswordRule" role-hint="character-length"
+ * 
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class CharacterLengthPasswordRule
+    implements PasswordRule
+{
+    private int minimumCharacters;
+    private int maximumCharacters;
+    
+    public CharacterLengthPasswordRule()
+    {
+        minimumCharacters = 1;
+        maximumCharacters = 8;
+    }
+
+    public int getMaximumCharacters()
+    {
+        return maximumCharacters;
+    }
+
+    public int getMinimumCharacters()
+    {
+        return minimumCharacters;
+    }
+
+    public void setMaximumCharacters( int maximumCharacters )
+    {
+        this.maximumCharacters = maximumCharacters;
+    }
+
+    public void setMinimumCharacters( int minimumCharacters )
+    {
+        this.minimumCharacters = minimumCharacters;
+    }
+
+    public void testPassword( PasswordRuleViolations violations, User user, UserSecurityPolicy securityPolicy )
+    {
+        if(minimumCharacters > maximumCharacters)
+        {
+            /* this should caught up front during the configuration of the component */
+            // TODO: Throw runtime exception instead?
+            violations.addViolation( "user.password.violation.length.misconfigured", new Object[] {
+                new Integer( minimumCharacters ),
+                new Integer( maximumCharacters ) } ); //$NON-NLS-1$
+        }
+        
+        String password = user.getPassword();
+        
+        if ( StringUtils.isEmpty( password ) 
+            || password.length() < minimumCharacters
+            || password.length() > maximumCharacters )
+        {
+            violations.addViolation( "user.password.violation.length", new Object[] {
+                new Integer( minimumCharacters ),
+                new Integer( maximumCharacters ) } ); //$NON-NLS-1$
+        }
+    }
+}

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/CharacterLengthPasswordRule.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/CharacterLengthPasswordRule.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/MustHavePasswordRule.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/MustHavePasswordRule.java?rev=438300&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/MustHavePasswordRule.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/MustHavePasswordRule.java Tue Aug 29 16:57:39 2006
@@ -0,0 +1,44 @@
+package org.apache.maven.user.model.rules;
+
+/*
+ * Copyright 2006 The 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.
+ */
+
+import org.apache.maven.user.model.PasswordRule;
+import org.apache.maven.user.model.PasswordRuleViolations;
+import org.apache.maven.user.model.User;
+import org.apache.maven.user.model.UserSecurityPolicy;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Basic Password Rule, Checks for non-empty Passwords in non guest users.
+ * 
+ * @plexus.component role="org.apache.maven.user.model.PasswordRule" role-hint="must-have"
+ * 
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class MustHavePasswordRule
+    implements PasswordRule
+{
+    public void testPassword( PasswordRuleViolations violations, User user, UserSecurityPolicy securityPolicy )
+    {
+        if ( StringUtils.isEmpty( user.getPassword() ) )
+        {
+            violations.addViolation( "user.password.violation.missing" ); //$NON-NLS-1$
+        }
+    }
+
+}

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/MustHavePasswordRule.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/MustHavePasswordRule.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/NumericalPasswordRule.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/NumericalPasswordRule.java?rev=438300&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/NumericalPasswordRule.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/NumericalPasswordRule.java Tue Aug 29 16:57:39 2006
@@ -0,0 +1,84 @@
+package org.apache.maven.user.model.rules;
+
+/*
+ * Copyright 2001-2006 The 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.
+ */
+
+import org.apache.maven.user.model.PasswordRule;
+import org.apache.maven.user.model.PasswordRuleViolations;
+import org.apache.maven.user.model.User;
+import org.apache.maven.user.model.UserSecurityPolicy;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Basic Password Rule, Checks for non-empty passwords that have at least {@link #setMinimumCount(int)} of 
+ * numerical characters contained within. 
+ * 
+ * @plexus.component role="org.apache.maven.user.model.PasswordRule" role-hint="numerical-count"
+ * 
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class NumericalPasswordRule
+    implements PasswordRule
+{
+    private int minimumCount;
+
+    public NumericalPasswordRule()
+    {
+        this.minimumCount = 1;
+    }
+
+    public int getMinimumCount()
+    {
+        return minimumCount;
+    }
+
+    public void setMinimumCount( int minimumCount )
+    {
+        this.minimumCount = minimumCount;
+    }
+
+    public void testPassword( PasswordRuleViolations violations, User user, UserSecurityPolicy securityPolicy )
+    {
+        if ( countDigitCharacters( user.getPassword() ) < this.minimumCount )
+        {
+            violations.addViolation( "user.password.violation.digit", new Object[] { new Integer( minimumCount ) } ); //$NON-NLS-1$
+        }
+    }
+
+    private int countDigitCharacters( String password )
+    {
+        int count = 0;
+
+        if ( StringUtils.isEmpty( password ) )
+        {
+            return count;
+        }
+
+        // Doing this via iteration of code points to take in account localized passwords.
+        for ( int i = 0; i < password.length(); i++ )
+        {
+            int codepoint = password.codePointAt( i );
+            if ( Character.isDigit( codepoint ) )
+            {
+                count++;
+            }
+        }
+
+        return count;
+    }
+
+}

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/NumericalPasswordRule.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/NumericalPasswordRule.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/ReusePasswordRule.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/ReusePasswordRule.java?rev=438300&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/ReusePasswordRule.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/ReusePasswordRule.java Tue Aug 29 16:57:39 2006
@@ -0,0 +1,102 @@
+package org.apache.maven.user.model.rules;
+
+/*
+ * Copyright 2001-2006 The 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.
+ */
+
+import java.util.Iterator;
+
+import org.apache.maven.user.model.PasswordEncoder;
+import org.apache.maven.user.model.PasswordRule;
+import org.apache.maven.user.model.PasswordRuleViolations;
+import org.apache.maven.user.model.User;
+import org.apache.maven.user.model.UserSecurityPolicy;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Password Rule, Checks supplied password found at {@link User#getPassword()} against 
+ * the {@link User#getPreviousEncodedPasswords()} to ensure that a password is not reused.  
+ * 
+ * @plexus.component role="org.apache.maven.user.model.PasswordRule" role-hint="reuse"
+ * 
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class ReusePasswordRule
+    implements PasswordRule
+{
+    /**
+     * @plexus.requirement
+     */
+    private PasswordEncoder passwordEncoder;
+    
+    private int previousPasswordCount;
+
+    /**
+     * Create a rule that will check last 3 passwords
+     */
+    public ReusePasswordRule()
+    {
+        this.previousPasswordCount = 3;
+    }
+
+    public int getPreviousPasswordCount()
+    {
+        return previousPasswordCount;
+    }
+
+    public void setPreviousPasswordCount( int previousPasswordCount )
+    {
+        this.previousPasswordCount = previousPasswordCount;
+    }
+
+    private boolean hasReusedPassword( User user, String password, String salt )
+    {
+        if ( StringUtils.isEmpty( password ) )
+        {
+            return false;
+        }
+
+        String encodedPassword = passwordEncoder.encodePassword( password, salt );
+
+        int checkCount = previousPasswordCount;
+
+        Iterator it = user.getPreviousEncodedPasswords().iterator();
+
+        while ( it.hasNext() && ( checkCount >= 0 ) )
+        {
+            String prevEncodedPassword = (String) it.next();
+            if ( encodedPassword.equals( prevEncodedPassword ) )
+            {
+                return true;
+            }
+            checkCount--;
+        }
+
+        return false;
+    }
+
+    public void testPassword( PasswordRuleViolations violations, User user, UserSecurityPolicy securityPolicy )
+    {
+        String password = user.getPassword();
+
+        if ( hasReusedPassword( user, password, securityPolicy.getSalt() ) )
+        {
+            violations
+                .addViolation( "user.password.violation.reuse", new Object[] { new Integer( previousPasswordCount ) } ); //$NON-NLS-1$
+        }
+    }
+
+}

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/ReusePasswordRule.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/rules/ReusePasswordRule.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Modified: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/store/impl/DefaultUserStore.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/store/impl/DefaultUserStore.java?rev=438300&r1=438299&r2=438300&view=diff
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/store/impl/DefaultUserStore.java (original)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/store/impl/DefaultUserStore.java Tue Aug 29 16:57:39 2006
@@ -283,6 +283,7 @@
         catch ( PlexusStoreException pse )
         {
             //TODO log exception
+            throw new RuntimeException( pse.getMessage(), pse );
         }
     }
 
@@ -295,6 +296,7 @@
         catch ( PlexusStoreException pse )
         {
             //TODO log exception
+            throw new RuntimeException( pse.getMessage(), pse );
         }
     }
 

Modified: maven/shared/trunk/maven-user/maven-user-model/src/main/mdo/user.xml
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/mdo/user.xml?rev=438300&r1=438299&r2=438300&view=diff
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/mdo/user.xml (original)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/mdo/user.xml Tue Aug 29 16:57:39 2006
@@ -114,6 +114,25 @@
           <type>Date</type>
         </field>
         <field>
+          <name>failedLoginAttempts</name>
+          <version>1.0.0+</version>
+          <type>int</type>
+        </field>
+        <field>
+          <name>locked</name>
+          <version>1.0.0+</version>
+          <type>boolean</type>
+          <defaultValue>false</defaultValue>
+        </field>
+        <field>
+          <name>previousEncodedPasswords</name>
+          <version>1.0.0+</version>
+          <association stash.part="true">
+            <type>String</type>
+            <multiplicity>*</multiplicity>
+          </association>
+        </field>
+        <field>
           <name>group</name>
           <version>1.0.0+</version>
           <association stash.part="true">
@@ -121,6 +140,17 @@
           </association> 
         </field>
       </fields>
+      <codeSegments>
+        <codeSegment>
+          <version>1.0.0+</version>
+          <code><![CDATA[
+              public int incrementFailedLoginAttempts() 
+              {
+                 return failedLoginAttempts++;
+              }
+          ]]></code>
+        </codeSegment>
+      </codeSegments>
     </class>
 
     <class stash.storable="true">

Modified: maven/shared/trunk/maven-user/maven-user-model/src/main/resources/org/apache/maven/user/model/messages.properties
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/resources/org/apache/maven/user/model/messages.properties?rev=438300&r1=438299&r2=438300&view=diff
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/resources/org/apache/maven/user/model/messages.properties (original)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/resources/org/apache/maven/user/model/messages.properties Tue Aug 29 16:57:39 2006
@@ -5,3 +5,8 @@
 password.encoder.no.such.algoritm=The specified algorithm {1} is not available in the JAAS Implementation of this JVM.
 password.encoder.unsupported.encoding=The UTF-8 Encoding is not available in the JAAS Implementation of this JVM.
 user.password.violation.missing=You must provide a password.
+user.password.violation.length=You must provide a password between {1} and {2} characters in length.
+user.password.violation.length.misconfigured=Password Length Rule is misconfigured.  Specified minimum of ({1}) is larger than specified maximum of ({2}).  Rule disabled.
+user.password.violation.alpha=You must provide a password containing at least {1} alphabetic character(s).
+user.password.violation.numeric=You must provide a password containing at least {1} numeric character(s).
+user.password.violation.reuse=Your password cannot match any of your previous {1} password(s).

Modified: maven/shared/trunk/maven-user/maven-user-model/src/test/java/org/apache/maven/user/model/impl/DefaultUserManagerTest.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/test/java/org/apache/maven/user/model/impl/DefaultUserManagerTest.java?rev=438300&r1=438299&r2=438300&view=diff
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/test/java/org/apache/maven/user/model/impl/DefaultUserManagerTest.java (original)
+++ maven/shared/trunk/maven-user/maven-user-model/src/test/java/org/apache/maven/user/model/impl/DefaultUserManagerTest.java Tue Aug 29 16:57:39 2006
@@ -16,6 +16,15 @@
  * limitations under the License.
  */
 
+import java.net.URL;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.jdo.PersistenceManager;
+import javax.jdo.PersistenceManagerFactory;
+
 import org.apache.maven.user.model.Permission;
 import org.apache.maven.user.model.User;
 import org.apache.maven.user.model.UserGroup;
@@ -26,15 +35,6 @@
 import org.codehaus.plexus.jdo.JdoFactory;
 import org.jpox.SchemaTool;
 
-import java.net.URL;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-
-import javax.jdo.PersistenceManager;
-import javax.jdo.PersistenceManagerFactory;
-
 /**
  * Test Cases for the Default User Manager.
  * 
@@ -44,7 +44,7 @@
 public class DefaultUserManagerTest
     extends PlexusTestCase
 {
-    UserManager usermanager = null;
+    private DefaultUserManager usermanager = null;
 
     /**
      * Creates a new UserManager which contains no data.
@@ -92,7 +92,7 @@
 
         pm.close();
 
-        usermanager = (UserManager) lookup( UserManager.ROLE );
+        usermanager = (DefaultUserManager) lookup( UserManager.ROLE );
     }
 
     public void testAddGetUserById()
@@ -154,12 +154,17 @@
 
         User fetched = usermanager.getUser( "jgarner" ); //$NON-NLS-1$
         assertNotNull( "User should not be null.", fetched ); //$NON-NLS-1$
+        assertEquals( "James Garner", fetched.getFullName() ); //$NON-NLS-1$
+        
+        // Change the full name, and update the user.
         fetched.setFullName( "Flight Lt. Hendley" ); //$NON-NLS-1$
-
         usermanager.updateUser( fetched );
+        
+        // Should not change number of users being tracked.
+        assertEquals( 1, usermanager.getUsers().size() );
 
+        // Fetch the user and test for updated Full Name.
         User actual = usermanager.getUser( "jgarner" ); //$NON-NLS-1$
-
         assertEquals( "Flight Lt. Hendley", actual.getFullName() ); //$NON-NLS-1$
     }
 
@@ -455,5 +460,35 @@
         assertNotNull( actual );
         assertNotNull( actual.getPermissions() );
         assertEquals( 2, actual.getPermissions().size() );
+    }
+    
+    public void testPolicyLoginFailureLock() throws Exception
+    {
+        assertNotNull( usermanager );
+        
+        assertEquals( "New UserManager should contain no users.", 0, usermanager.getUsers().size() ); //$NON-NLS-1$
+        assertEquals( "New UserManager should contain no groups.", 0, usermanager.getUserGroups().size() ); //$NON-NLS-1$
+        assertNotNull( "New UserManager should have a Security Policy", usermanager.getSecurityPolicy() ); //$NON-NLS-1$
+        
+        User rattenborough = new User();
+        rattenborough.setUsername( "rattenborough" ); //$NON-NLS-1$
+        rattenborough.setFullName( "Richard Attenborough" ); //$NON-NLS-1$
+        rattenborough.setPassword( "the big x" ); //$NON-NLS-1$
+
+        usermanager.addUser( rattenborough );
+        
+        assertEquals( 1, usermanager.getUsers().size() );
+        
+        // Setup the policy.
+        ( (DefaultUserSecurityPolicy) usermanager.getSecurityPolicy() ).setAllowedLoginAttempts( 3 );
+        
+        assertFalse( usermanager.login( "rattenborough", "the big lebowski" ) );
+        assertFalse( usermanager.getUser( "rattenborough" ).isLocked() );
+        
+        assertFalse( usermanager.login( "rattenborough", "the big cheese" ) );
+        assertFalse( usermanager.getUser( "rattenborough" ).isLocked() );
+        
+        assertFalse( usermanager.login( "rattenborough", "big x" ) );
+        assertTrue( usermanager.getUser( "rattenborough" ).isLocked() );
     }
 }