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:[EMAIL PROTECTED]">Joakim Erdfelt</a> * @version $Id$ */ @@ -31,9 +28,9 @@ * Provide the Plexus Component Role. */ public static final String ROLE = PasswordRule.class.getName(); - + /** * Tests the [EMAIL PROTECTED] 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 [EMAIL PROTECTED] 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 [EMAIL PROTECTED] #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 [EMAIL PROTECTED] #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 [EMAIL PROTECTED] PasswordRule} objects. - */ - void setPasswordRules( List rules ); - - /** - * Get the Password Rules List. - * - * @return the list of [EMAIL PROTECTED] 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 [EMAIL PROTECTED] 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 [EMAIL PROTECTED] PasswordRule} objects. + */ public List getPasswordRules() { return this.rules; } + /** + * Set the Password Rules List. + * + * @param rules the list of [EMAIL PROTECTED] 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:[EMAIL PROTECTED]">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 [EMAIL PROTECTED] #setMinimumCount(int)} of + * alpha characters contained within. + * + * @plexus.component role="org.apache.maven.user.model.PasswordRule" role-hint="alpha-count" + * + * @author <a href="mailto:[EMAIL PROTECTED]">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 [EMAIL PROTECTED] #setMinimumCharacters(int)} and + * [EMAIL PROTECTED] #setMaximumCharacters(int)} characters in length. + * + * @plexus.component role="org.apache.maven.user.model.PasswordRule" role-hint="character-length" + * + * @author <a href="mailto:[EMAIL PROTECTED]">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:[EMAIL PROTECTED]">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 [EMAIL PROTECTED] #setMinimumCount(int)} of + * numerical characters contained within. + * + * @plexus.component role="org.apache.maven.user.model.PasswordRule" role-hint="numerical-count" + * + * @author <a href="mailto:[EMAIL PROTECTED]">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 [EMAIL PROTECTED] User#getPassword()} against + * the [EMAIL PROTECTED] 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:[EMAIL PROTECTED]">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() ); } }