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/22 04:02:53 UTC

svn commit: r433464 - in /maven/shared/trunk/maven-user/maven-user-model: ./ src/main/java/org/apache/maven/user/model/ src/main/java/org/apache/maven/user/model/impl/ src/main/mdo/ src/main/resources/ src/main/resources/org/ src/main/resources/org/apa...

Author: carlos
Date: Mon Aug 21 19:02:52 2006
New Revision: 433464

URL: http://svn.apache.org/viewvc?rev=433464&view=rev
Log:
[CONTINUUM-800] Refactor methods to better suite the goal of the lib, add some new functionality
Submitted By: Joakim Erdfelt

Added:
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/Messages.java   (with props)
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordEncodingException.java   (with props)
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRule.java   (with props)
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRuleViolationException.java   (with props)
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRuleViolations.java   (with props)
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/AbstractJAASPasswordEncoder.java   (with props)
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/MustHavePasswordRule.java   (with props)
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/SHA1PasswordEncoder.java   (with props)
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/SHA256PasswordEncoder.java   (with props)
    maven/shared/trunk/maven-user/maven-user-model/src/main/resources/
    maven/shared/trunk/maven-user/maven-user-model/src/main/resources/org/
    maven/shared/trunk/maven-user/maven-user-model/src/main/resources/org/apache/
    maven/shared/trunk/maven-user/maven-user-model/src/main/resources/org/apache/maven/
    maven/shared/trunk/maven-user/maven-user-model/src/main/resources/org/apache/maven/user/
    maven/shared/trunk/maven-user/maven-user-model/src/main/resources/org/apache/maven/user/model/
    maven/shared/trunk/maven-user/maven-user-model/src/main/resources/org/apache/maven/user/model/messages.properties   (with props)
Modified:
    maven/shared/trunk/maven-user/maven-user-model/pom.xml
    maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordEncoder.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/mdo/user.xml
    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/pom.xml
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/pom.xml?rev=433464&r1=433463&r2=433464&view=diff
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/pom.xml (original)
+++ maven/shared/trunk/maven-user/maven-user-model/pom.xml Mon Aug 21 19:02:52 2006
@@ -37,6 +37,13 @@
       <artifactId>jpox</artifactId>
       <version>1.1.1</version>
       <scope>test</scope>
+      <exclusions>
+        <!-- targeting JDK 1.4 we don't need this -->
+        <exclusion>
+          <groupId>javax.sql</groupId>
+          <artifactId>jdbc-stdext</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
     <dependency>
       <groupId>hsqldb</groupId>

Added: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/Messages.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/Messages.java?rev=433464&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/Messages.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/Messages.java Mon Aug 21 19:02:52 2006
@@ -0,0 +1,92 @@
+package org.apache.maven.user.model;
+
+/*
+ * 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 java.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Localized Message Handling.
+ * 
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class Messages
+{
+    private static final String BUNDLE_NAME = "org.apache.maven.user.model.messages"; //$NON-NLS-1$
+
+    private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle( BUNDLE_NAME );
+
+    /**
+     * Get a Message as-is from the Resource Bundle.
+     * 
+     * @param key the key for the message to get.
+     * @return the value of the key, or "!key!" if the key is not found.
+     */
+    public static String getString( String key )
+    {
+        try
+        {
+            return RESOURCE_BUNDLE.getString( key );
+        }
+        catch ( MissingResourceException e )
+        {
+            return '!' + key + '!';
+        }
+    }
+
+    /**
+     * Gets a Message from the Resource Bundle, with {1} and {2} style arguments.
+     * 
+     * @param key the key for the message to get.
+     * @param arg the argument to pass in.
+     * @return the value of the key, or "!key!" if the key is not found.
+     */
+    public static String getString( String key, Object arg )
+    {
+        return getString( key, new Object[] { arg } );
+    }
+
+    /**
+     * Gets a Message from the Resource Bundle, with {1} and {2} style arguments.
+     * 
+     * @param key the key for the message to get.
+     * @param args the arguments to pass in.
+     * @return the value of the key, or "!key!" if the key is not found.
+     */
+    public static String getString( String key, Object args[] )
+    {
+        try
+        {
+            String pattern = RESOURCE_BUNDLE.getString( key );
+            return MessageFormat.format( pattern, args );
+        }
+        catch ( MissingResourceException e )
+        {
+            return '!' + key + '!';
+        }
+    }
+
+    /**
+     * Prevent Instantiation.
+     */
+    private Messages()
+    {
+    }
+}

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

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

Modified: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordEncoder.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordEncoder.java?rev=433464&r1=433463&r2=433464&view=diff
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordEncoder.java (original)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordEncoder.java Mon Aug 21 19:02:52 2006
@@ -26,6 +26,12 @@
 * @version $Id$
 */
 public interface PasswordEncoder {
+    
+    /**
+     * Provide the Plexus Component Role.
+     */
+    public static final String ROLE = PasswordEncoder.class.getName();
+    
    //~ Methods ========================================================================================================
 
    /**

Added: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordEncodingException.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordEncodingException.java?rev=433464&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordEncodingException.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordEncodingException.java Mon Aug 21 19:02:52 2006
@@ -0,0 +1,50 @@
+package org.apache.maven.user.model;
+
+/*
+ * 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.
+ */
+
+/**
+ * Password Encoding Exception.
+ * 
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class PasswordEncodingException
+    extends RuntimeException
+{
+    private static final long serialVersionUID = 7073213471284221645L;
+
+    public PasswordEncodingException()
+    {
+        super();
+    }
+
+    public PasswordEncodingException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+
+    public PasswordEncodingException( String message )
+    {
+        super( message );
+    }
+
+    public PasswordEncodingException( Throwable cause )
+    {
+        super( cause );
+    }
+
+}

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

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

Added: 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=433464&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRule.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRule.java Mon Aug 21 19:02:52 2006
@@ -0,0 +1,39 @@
+package org.apache.maven.user.model;
+
+/*
+ * 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.
+ */
+
+
+/**
+ * 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$
+ */
+public interface PasswordRule
+{
+    /**
+     * 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);
+}

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

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

Added: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRuleViolationException.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRuleViolationException.java?rev=433464&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRuleViolationException.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRuleViolationException.java Mon Aug 21 19:02:52 2006
@@ -0,0 +1,61 @@
+package org.apache.maven.user.model;
+
+/*
+ * 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.
+ */
+
+/**
+ * Password Rule Violations Exception
+ * 
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class PasswordRuleViolationException
+    extends Exception
+{
+    private static final long serialVersionUID = -4686338829234880328L;
+
+    private PasswordRuleViolations violations;
+
+    public PasswordRuleViolationException()
+    {
+        super();
+    }
+
+    public PasswordRuleViolationException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+
+    public PasswordRuleViolationException( String message )
+    {
+        super( message );
+    }
+
+    public PasswordRuleViolationException( Throwable cause )
+    {
+        super( cause );
+    }
+
+    public PasswordRuleViolations getViolations()
+    {
+        return violations;
+    }
+
+    public void setViolations( PasswordRuleViolations violations )
+    {
+        this.violations = violations;
+    }
+}

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

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

Added: 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=433464&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRuleViolations.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/PasswordRuleViolations.java Mon Aug 21 19:02:52 2006
@@ -0,0 +1,97 @@
+package org.apache.maven.user.model;
+
+/*
+ * 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 java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Password Rule Violations
+ * 
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class PasswordRuleViolations
+{
+    private List violations;
+
+    class MessageReference
+    {
+        String key;
+
+        String args[];
+    }
+
+    /**
+     * Construct a Password Rule Violations object.
+     */
+    public PasswordRuleViolations()
+    {
+        violations = new ArrayList();
+    }
+
+    /**
+     * Empty out the list of violations.
+     */
+    public void reset()
+    {
+        violations.clear();
+    }
+
+    /**
+     * Add a violation to the underlying list.
+     * 
+     * @param key the bundle/localization key for the message.
+     */
+    public void addViolation( String key )
+    {
+        MessageReference mesgref = new MessageReference();
+        mesgref.key = key;
+        mesgref.args = null;
+        violations.add( mesgref );
+    }
+
+    /**
+     * Get the List of Violations as localized and post-processed {@link String}s.
+     * 
+     * @return the List of {@link String} objects.
+     */
+    public List getLocalizedViolations()
+    {
+        List msgs = new ArrayList();
+
+        Iterator it = this.violations.iterator();
+        while ( it.hasNext() )
+        {
+            MessageReference msgref = (MessageReference) it.next();
+            msgs.add( Messages.getString( msgref.key, msgref.args ) );
+        }
+
+        return msgs;
+    }
+
+    /**
+     * Simple test to see if there are any violations.
+     * 
+     * @return true if there are any violations.
+     */
+    public boolean hasViolations()
+    {
+        return !violations.isEmpty();
+    }
+}

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

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

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=433464&r1=433463&r2=433464&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 Mon Aug 21 19:02:52 2006
@@ -26,6 +26,9 @@
  * 
  * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a>
  * @version $Id$
+ * 
+ * @todo policy: new user verification (no password, email verification link and/or uniq code, generate password)
+ * @todo policy: user self service password reset (request, send email, click on link and/or supply uniq code, password reset)
  */
 public interface UserManager
 {
@@ -42,8 +45,8 @@
      * @param user
      * @throws EntityExistsException if the user already exists
      */
-    void addUser( User user )
-        throws EntityExistsException;
+    User addUser( User user )
+        throws EntityExistsException, PasswordRuleViolationException;
 
     /**
      * Update user data. User password will be encoded using the {@link #getPasswordEncoder()}
@@ -53,7 +56,7 @@
      * @throws EntityNotFoundException if the user does not exist
      */
     void updateUser( User user )
-        throws EntityNotFoundException;
+        throws EntityNotFoundException, PasswordRuleViolationException;
 
     /**
      * Get all users. Users password won't be returned for security reasons.
@@ -63,37 +66,82 @@
     List getUsers();
 
     /**
+     * <p>
      * Get a user by id. User password won't be returned for security reasons.
+     * </p>
+     * 
+     * <p>
+     * NOTE: The User.accountId value is set by the underlying UserManager implementation.
+     * </p>
      * 
-     * @param userId
+     * @param accountId
      * @return null if the user doesn't exist
      */
-    User getUser( int userId );
+    User getUser( int accountId );
 
     /**
      * Get a user by name. User password won't be returned for security reasons.
      * 
      * @param username
      * @return null if the user doesn't exist
+     * @deprecated use {@link #getUser(String)} instead.
      */
     User getUserByUsername( String username );
+    
+    /**
+     * Get a user by name. User password won't be returned for security reasons.
+     * 
+     * @param username
+     * @return null if the user doesn't exist
+     */
+    User getUser( String username );
 
     /**
      * Get guest user
      * 
      * @return null if the user doesn't exist
      */
-    public User getGuestUser();
+    User getGuestUser();
 
     /**
      * Delete a user
      * 
-     * @param userId
+     * <p>
+     * NOTE: The User.accountId value is set by the underlying UserManager implementation.
+     * </p>
+     * 
+     * @param accountId
+     * @throws EntityNotFoundException if the user does not exist
+     */
+    void removeUser( int accountId )
+        throws EntityNotFoundException;
+    
+    /**
+     * Delete a user
+     * 
+     * @param username
      * @throws EntityNotFoundException if the user does not exist
      */
-    void removeUser( int userId )
+    void removeUser( String username )
         throws EntityNotFoundException;
 
+    // ----------------------------------------------------------------------
+    // Login
+    // ----------------------------------------------------------------------
+    
+    /**
+     * Perform login attempt to see if username and password are valid. 
+     * 
+     * @param username
+     * @param rawpassword
+     * @return true if user is able to log in. false if username or password is invalid.
+     */
+    boolean login(String username, String rawpassword);
+    
+    // ----------------------------------------------------------------------
+    // Passwords
+    // ----------------------------------------------------------------------
+    
     /**
      * Set the password encoder to be used for password operations 
      * 
@@ -107,6 +155,27 @@
      * @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
@@ -118,10 +187,11 @@
      * @param userGroup
      * @throws EntityExistsException if the user group already exists
      */
-    void addUserGroup( UserGroup userGroup )
+    UserGroup addUserGroup( UserGroup userGroup )
         throws EntityExistsException;
 
     /**
+     * Update an existing UserGroup.
      * 
      * @param userGroup
      * @throws EntityNotFoundException if the user group does not exist
@@ -130,19 +200,26 @@
         throws EntityNotFoundException;
 
     /**
+     * Get all UserGroup's.
      * 
      * @return all the user groups in the system
      */
     List getUserGroups();
 
     /**
+     * Get UserGroup by id.
      * 
+     * <p>
+     * NOTE: The UserGroup.id value is set by the underlying UserManager implementation.
+     * </p>
+     *  
      * @param userGroupId
      * @return null if the group doesn't exist
      */
     UserGroup getUserGroup( int userGroupId );
 
     /**
+     * Get the {@link UserGroup} by name
      * 
      * @param name
      * @return null if the group doesn't exist
@@ -150,11 +227,27 @@
     UserGroup getUserGroup( String name );
 
     /**
+     * <p>
+     * Remove the UserGroup using the UserGroup.id value.
+     * </p>
+     * 
+     * <p>
+     * NOTE: The UserGroup.id value is set by the underlying UserManager implementation.
+     * </p> 
      * 
      * @param userGroupId
      * @throws EntityNotFoundException if the user does not exist
      */
     void removeUserGroup( int userGroupId )
+        throws EntityNotFoundException;
+    
+    /**
+     * Remove the named UserGroup
+     * 
+     * @param name the user group name to remove
+     * @throws EntityNotFoundException if the user group name does not exist.
+     */
+    void removeUserGroup( String name )
         throws EntityNotFoundException;
 
     // ----------------------------------------------------------------------

Added: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/AbstractJAASPasswordEncoder.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/AbstractJAASPasswordEncoder.java?rev=433464&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/AbstractJAASPasswordEncoder.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/AbstractJAASPasswordEncoder.java Mon Aug 21 19:02:52 2006
@@ -0,0 +1,90 @@
+package org.apache.maven.user.model.impl;
+
+/*
+ * 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.Messages;
+import org.apache.maven.user.model.PasswordEncoder;
+import org.apache.maven.user.model.PasswordEncodingException;
+import org.codehaus.plexus.util.StringUtils;
+
+import sun.misc.BASE64Encoder;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Abstract Password Encoder that uses the {@link MessageDigest} from JAAS.
+ * 
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class AbstractJAASPasswordEncoder
+    implements PasswordEncoder
+{
+    private String algorithm;
+
+    public AbstractJAASPasswordEncoder( String algorithm )
+    {
+        this.algorithm = algorithm;
+    }
+
+    public String encodePassword( String rawPass, Object salt )
+    {
+        MessageDigest md = null;
+        try
+        {
+            md = MessageDigest.getInstance( this.algorithm );
+            if ( salt != null )
+            {
+                md.update( salt.toString().getBytes( "UTF-8" ) ); //$NON-NLS-1$
+            }
+            md.update( rawPass.getBytes( "UTF-8" ) ); //$NON-NLS-1$
+
+            byte raw[] = md.digest();
+            String hash = ( new BASE64Encoder() ).encode( raw );
+            return hash;
+        }
+        catch ( NoSuchAlgorithmException e )
+        {
+            throw new PasswordEncodingException( Messages
+                .getString( "password.encoder.no.such.algoritm", this.algorithm ), e ); //$NON-NLS-1$
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+            throw new PasswordEncodingException( Messages.getString( "password.encoder.unsupported.encoding" ), e ); //$NON-NLS-1$
+        }
+    }
+
+    public boolean isPasswordValid( String encPass, String rawPass, Object salt )
+    {
+        if ( StringUtils.isEmpty( encPass ) )
+        {
+            // TODO: Throw exception?
+            return false;
+        }
+
+        if ( StringUtils.isEmpty( rawPass ) )
+        {
+            // TODO: Throw exception?
+            return false;
+        }
+
+        String testPass = encodePassword( rawPass, salt );
+        return ( encPass.equals( testPass ) );
+    }
+}

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

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/AbstractJAASPasswordEncoder.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=433464&r1=433463&r2=433464&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 Mon Aug 21 19:02:52 2006
@@ -16,30 +16,36 @@
  * limitations under the License.
  */
 
-import java.util.List;
-import java.util.Collection;
-
-import javax.jdo.PersistenceManager;
-import javax.jdo.PersistenceManagerFactory;
-import javax.jdo.Query;
-import javax.jdo.Extent;
-import javax.jdo.Transaction;
-
-import javax.persistence.EntityExistsException;
-import javax.persistence.EntityNotFoundException;
-
+import org.apache.maven.user.model.Messages;
 import org.apache.maven.user.model.PasswordEncoder;
+import org.apache.maven.user.model.PasswordRule;
+import org.apache.maven.user.model.PasswordRuleViolationException;
+import org.apache.maven.user.model.PasswordRuleViolations;
+import org.apache.maven.user.model.Permission;
 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.Permission;
-
 import org.codehaus.plexus.jdo.JdoFactory;
 import org.codehaus.plexus.jdo.PlexusJdoUtils;
 import org.codehaus.plexus.jdo.PlexusObjectNotFoundException;
 import org.codehaus.plexus.jdo.PlexusStoreException;
 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
 import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
+import org.codehaus.plexus.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.jdo.Extent;
+import javax.jdo.PersistenceManager;
+import javax.jdo.PersistenceManagerFactory;
+import javax.jdo.Query;
+import javax.jdo.Transaction;
+import javax.persistence.EntityExistsException;
+import javax.persistence.EntityNotFoundException;
 
 /**
  * Default implementation of the {@link UserManager} interface.
@@ -53,11 +59,27 @@
     implements UserManager, Initializable
 {
     /**
-    * @plexus.requirement
-    */
+     * @plexus.requirement
+     */
     private JdoFactory jdoFactory;
     
+    /**
+     * @plexus.requirement role-hint="sha256"
+     */
+    private PasswordEncoder passwordEncoder;
+    
+    /**
+     * @plexus.configuration default-value="Step doog ekam Skravdraa"
+     */
+    private String salt;
+    
+    /**
+     * The List of {@link PasswordRule} objects.
+     */
+    private List rules;
+    
     private PersistenceManagerFactory pmf;
+    
        
     // ----------------------------------------------------------------------
     // Component Lifecycle
@@ -67,46 +89,118 @@
         throws InitializationException
     {
         pmf = jdoFactory.getPersistenceManagerFactory();
+        rules = new ArrayList();
+        
+        // TODO: Find way to have plexus initialize this list with only 1 item.
+        addPasswordRule(new MustHavePasswordRule());
     }
-   
-    public void addUser( User user )
-        throws EntityExistsException
+    
+    public boolean login( String username, String rawPassword )
     {
-        addObject( user );
+        User user = getUser( username );
+        if ( user == null )
+        {
+            return false;
+        }
+
+        return this.passwordEncoder.isPasswordValid( user.getEncodedPassword(), rawPassword, salt );
+    }
+    
+    
+    public User addUser( User user )
+        throws EntityExistsException, PasswordRuleViolationException
+    {
+        if(user.getAccountId() > 0)
+        {
+            throw new IllegalStateException( Messages.getString("user.manager.cannot.add.user.with.accountId") ); //$NON-NLS-1$
+        }
+        
+        processPasswordChange( user );
+        
+        return (User) addObject( user );
     }
 
-    public void addUserGroup( UserGroup userGroup )
+    private void processPasswordChange( User user )
+        throws PasswordRuleViolationException
+    {
+        validatePassword( user );
+        
+        user.setEncodedPassword( this.passwordEncoder.encodePassword( user.getPassword(), salt ) );
+        user.setPassword( null );
+        
+        user.setLastPasswordChange( new Date() ); // update timestamp to now.
+    }
+
+    private void validatePassword( User user )
+        throws PasswordRuleViolationException
+    {
+        PasswordRuleViolations violations = new PasswordRuleViolations();
+
+        Iterator it = this.rules.iterator();
+        while ( it.hasNext() )
+        {
+            PasswordRule rule = (PasswordRule) it.next();
+            rule.testPassword( violations, user );
+        }
+
+        if ( violations.hasViolations() )
+        {
+            PasswordRuleViolationException exception = new PasswordRuleViolationException();
+            exception.setViolations( violations );
+            throw exception;
+        }
+    }
+
+    public UserGroup addUserGroup( UserGroup userGroup )
         throws EntityExistsException
     {
-        addObject( userGroup );
+        if(userGroup.getId() > 0)
+        {
+            throw new IllegalStateException( Messages.getString("user.manager.cannot.add.group.with.id") ); //$NON-NLS-1$
+        }
+        
+        return (UserGroup) addObject( userGroup );
     }
 
     public PasswordEncoder getPasswordEncoder()
     {
-        return null;
+        return passwordEncoder;
     }
 
-    public User getUser( int userId )
+    public User getUser( int accountId )
     {
         User user = null;
         
         try
         {
-            user = ( User ) getObjectById( User.class, userId );
+            user = (User) getObjectById( User.class, accountId );
         }
         catch ( PlexusStoreException pse )
         {
-            // TODO log exception
+            //log exception
         }
         catch ( EntityNotFoundException eee )
         {
             return null;
         }
         return user;
-    }
 
+    }
+    
+    /**
+     * Get a user by name. User password won't be returned for security reasons.
+     * 
+     * @param username
+     * @return null if the user doesn't exist
+     * @deprecated use {@link #getUser(String)} instead.
+     */
     public User getUserByUsername( String username )
     {
+        return getUser( username );
+    }
+    
+    public User getUser( String username )
+    {
         PersistenceManager pm = getPersistenceManager();
 
         Transaction tx = pm.currentTransaction();
@@ -119,11 +213,11 @@
 
             Query query = pm.newQuery( extent );
 
-            query.declareImports( "import java.lang.String" );
+            query.declareImports( "import java.lang.String" ); //$NON-NLS-1$
 
-            query.declareParameters( "String username" );
+            query.declareParameters( "String username" ); //$NON-NLS-1$
 
-            query.setFilter( "this.username == username" );
+            query.setFilter( "this.username == username" ); //$NON-NLS-1$
 
             Collection result = (Collection) query.execute( username );
 
@@ -144,7 +238,7 @@
         {
             rollback( tx );
         }
-    }
+    }    
 
     public User getGuestUser()
     {
@@ -160,7 +254,7 @@
 
             Query query = pm.newQuery( extent );
 
-            query.setFilter( "this.guest == true" );
+            query.setFilter( "this.guest == true" ); //$NON-NLS-1$
 
             Collection result = (Collection) query.execute();
 
@@ -216,11 +310,11 @@
 
             Query query = pm.newQuery( extent );
 
-            query.declareImports( "import java.lang.String" );
+            query.declareImports( "import java.lang.String" ); //$NON-NLS-1$
 
-            query.declareParameters( "String name" );
+            query.declareParameters( "String name" ); //$NON-NLS-1$
 
-            query.setFilter( "this.name == name" );
+            query.setFilter( "this.name == name" ); //$NON-NLS-1$
 
             Collection result = (Collection) query.execute( name );
 
@@ -261,6 +355,14 @@
         removeObject( user );
     }
 
+    public void removeUser( String username )
+        throws EntityNotFoundException
+    {
+        User user = getUser( username );
+        
+        removeObject( user );
+    }
+    
     public void removeUserGroup( int userGroupId )
         throws EntityNotFoundException
     {
@@ -268,18 +370,48 @@
         
         removeObject( userGroup );
     }
+    
+    public void removeUserGroup( String userGroupName )
+        throws EntityNotFoundException
+    {
+        UserGroup userGroup = getUserGroup( userGroupName );
+
+        removeObject( userGroup );
+    }
 
     public void setPasswordEncoder( PasswordEncoder passwordEncoder )
     {
-        // TODO Auto-generated method stub
+        this.passwordEncoder = passwordEncoder;
+    }
+    
+    public void addPasswordRule( PasswordRule rule )
+    {
+        // TODO: check for duplicates?
+        
+        this.rules.add( rule );
+    }
+
+    public List getPasswordRules()
+    {
+        return this.rules;
+    }
 
+    public void setPasswordRules( List rules )
+    {
+        this.rules = rules;
     }
 
     public void updateUser( User user )
-        throws EntityNotFoundException
+        throws EntityNotFoundException, PasswordRuleViolationException
     {
         try
         {
+            // If password is supplied, assume changing of password.
+            if ( !StringUtils.isEmpty( user.getPassword() ) )
+            {
+                processPasswordChange( user );
+            }
+
             updateObject( user );
         }
         catch ( PlexusStoreException pse )
@@ -321,11 +453,11 @@
 
             Query query = pm.newQuery( extent );
 
-            query.declareImports( "import java.lang.String" );
+            query.declareImports( "import java.lang.String" ); //$NON-NLS-1$
 
-            query.declareParameters( "String name" );
+            query.declareParameters( "String name" ); //$NON-NLS-1$
 
-            query.setFilter( "this.name == name" );
+            query.setFilter( "this.name == name" ); //$NON-NLS-1$
 
             Collection result = (Collection) query.execute( name );
 
@@ -350,6 +482,11 @@
 
     public Permission addPermission( Permission perm )
     {
+        if(perm.getId() > 0)
+        {
+            throw new IllegalStateException( Messages.getString("user.manager.cannot.add.permission.with.id") ); //$NON-NLS-1$
+        }
+        
         return (Permission) addObject( perm );
     }
     

Added: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/MustHavePasswordRule.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/MustHavePasswordRule.java?rev=433464&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/MustHavePasswordRule.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/MustHavePasswordRule.java Mon Aug 21 19:02:52 2006
@@ -0,0 +1,49 @@
+package org.apache.maven.user.model.impl;
+
+/*
+ * 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.codehaus.plexus.util.StringUtils;
+
+/**
+ * Basic Password Rule, Checks for non-empty Passwords.
+ * 
+ * @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 boolean isValidPassword( User user )
+    {
+        return !StringUtils.isEmpty( user.getPassword() );
+    }
+
+    public void testPassword( PasswordRuleViolations violations, User user )
+    {
+        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/impl/MustHavePasswordRule.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/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/impl/SHA1PasswordEncoder.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/SHA1PasswordEncoder.java?rev=433464&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/SHA1PasswordEncoder.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/SHA1PasswordEncoder.java Mon Aug 21 19:02:52 2006
@@ -0,0 +1,37 @@
+package org.apache.maven.user.model.impl;
+
+/*
+ * 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.PasswordEncoder;
+
+/**
+ * SHA-1 Password Encoder.
+ * 
+ * @plexus.component role="org.apache.maven.user.model.PasswordEncoder" role-hint="sha1"
+ * 
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class SHA1PasswordEncoder
+    extends AbstractJAASPasswordEncoder
+    implements PasswordEncoder
+{
+    public SHA1PasswordEncoder()
+    {
+        super( "SHA-1" ); //$NON-NLS-1$
+    }
+}

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

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

Added: maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/SHA256PasswordEncoder.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/SHA256PasswordEncoder.java?rev=433464&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/SHA256PasswordEncoder.java (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/java/org/apache/maven/user/model/impl/SHA256PasswordEncoder.java Mon Aug 21 19:02:52 2006
@@ -0,0 +1,37 @@
+package org.apache.maven.user.model.impl;
+
+/*
+ * 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.PasswordEncoder;
+
+/**
+ * SHA-256 Password Encoder.
+ * 
+ * @plexus.component role="org.apache.maven.user.model.PasswordEncoder" role-hint="sha256"
+ * 
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class SHA256PasswordEncoder
+    extends AbstractJAASPasswordEncoder
+    implements PasswordEncoder
+{
+    public SHA256PasswordEncoder()
+    {
+        super( "SHA-256" );
+    }
+}

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

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

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=433464&r1=433463&r2=433464&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 Mon Aug 21 19:02:52 2006
@@ -19,6 +19,17 @@
           <identifier>true</identifier>
           <version>1.0.0+</version>
           <type>int</type>
+          <description>ID.
+          
+WARNING: The UserGroup.id field is managed by the underlying 
+UserManager implementation.
+
+Do not set this field, as any value you set will be 
+overridden by the UserManager implementation
+upon a call to the {@link 
+org.apache.maven.user.model.UserManager#addUserGroup(UserGroup)} 
+method.
+          </description>
         </field>
         <field>
           <name>name</name>
@@ -33,7 +44,7 @@
         <field>
           <name>permissions</name>
           <version>1.0.0+</version>
-          <association stash.part="false">
+          <association stash.part="true">
             <type>Permission</type>
             <multiplicity>*</multiplicity>
           </association>
@@ -49,6 +60,17 @@
           <version>1.0.0+</version>
           <type>int</type>
           <identifier>true</identifier>
+          <description>Account ID.
+          
+WARNING: The User.accountId field is managed by the 
+underlying UserManager implementation.
+
+Do not set this field, as any value you set will be 
+overridden by the UserManager implementation upon a 
+call to the {@link 
+org.apache.maven.user.model.UserManager#addUser(User)} 
+method.
+          </description>
         </field>
         <field>
           <name>username</name>
@@ -67,6 +89,11 @@
           <type>String</type>
         </field>
         <field>
+          <name>encodedPassword</name>
+          <version>1.0.0+</version>
+          <type>String</type>
+        </field>
+        <field>
           <name>fullName</name>
           <version>1.0.0+</version>
           <type>String</type>
@@ -77,9 +104,19 @@
           <type>String</type>
         </field>
         <field>
+          <name>lastPasswordChange</name>
+          <version>1.0.0+</version>
+          <type>Date</type>
+        </field>
+        <field>
+          <name>lastLogin</name>
+          <version>1.0.0+</version>
+          <type>Date</type>
+        </field>
+        <field>
           <name>group</name>
           <version>1.0.0+</version>
-          <association stash.part="false">
+          <association stash.part="true">
             <type>UserGroup</type>
           </association> 
         </field>
@@ -95,6 +132,17 @@
           <identifier>true</identifier>
           <version>1.0.0+</version>
           <type>int</type>
+          <description>ID.
+          
+WARNING: The Permission.id field is managed by the 
+underlying UserManager implementation.
+
+Do not set this field, as any value you set will be 
+overridden by the UserManager implementation
+upon a call to the {@link 
+org.apache.maven.user.model.UserManager#addPermission(Permission)} 
+method.
+          </description>
         </field>
         <field>
           <name>name</name>

Added: 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=433464&view=auto
==============================================================================
--- maven/shared/trunk/maven-user/maven-user-model/src/main/resources/org/apache/maven/user/model/messages.properties (added)
+++ maven/shared/trunk/maven-user/maven-user-model/src/main/resources/org/apache/maven/user/model/messages.properties Mon Aug 21 19:02:52 2006
@@ -0,0 +1,7 @@
+user.manager.cannot.add.user.with.accountId=User.accountId cannot be supplied on an .addUser() request.
+user.manager.cannot.add.user.without.password=User.password must be supplied on an .addUser() request.
+user.manager.cannot.add.group.with.id=UserGroup.id cannot be supplied on an .addUserGroup() request.
+user.manager.cannot.add.permission.with.id=Permission.id cannot be supplied on an .addPermission() request.
+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.

Propchange: maven/shared/trunk/maven-user/maven-user-model/src/main/resources/org/apache/maven/user/model/messages.properties
------------------------------------------------------------------------------
    svn:eol-style = native

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

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=433464&r1=433463&r2=433464&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 Mon Aug 21 19:02:52 2006
@@ -16,6 +16,7 @@
  * limitations under the License.
  */
 
+import org.apache.maven.user.model.PasswordRuleViolationException;
 import org.apache.maven.user.model.Permission;
 import org.apache.maven.user.model.User;
 import org.apache.maven.user.model.UserGroup;
@@ -34,11 +35,13 @@
 
 import javax.jdo.PersistenceManager;
 import javax.jdo.PersistenceManagerFactory;
+import javax.persistence.EntityExistsException;
 
 /**
  * Test Cases for the Default User Manager.
  * 
  * @version $Id$
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
  */
 public class DefaultUserManagerTest
     extends PlexusTestCase
@@ -56,21 +59,21 @@
         ConfigurableJdoFactory jdoFactory = (ConfigurableJdoFactory) lookup( JdoFactory.ROLE );
         assertEquals( DefaultConfigurableJdoFactory.class.getName(), jdoFactory.getClass().getName() );
 
-        jdoFactory.setPersistenceManagerFactoryClass( "org.jpox.PersistenceManagerFactoryImpl" );
+        jdoFactory.setPersistenceManagerFactoryClass( "org.jpox.PersistenceManagerFactoryImpl" ); //$NON-NLS-1$
 
-        jdoFactory.setDriverName( "org.hsqldb.jdbcDriver" );
+        jdoFactory.setDriverName( "org.hsqldb.jdbcDriver" ); //$NON-NLS-1$
 
-        jdoFactory.setUrl( "jdbc:hsqldb:mem:" + getName() );
+        jdoFactory.setUrl( "jdbc:hsqldb:mem:" + getName() ); //$NON-NLS-1$
 
-        jdoFactory.setUserName( "sa" );
+        jdoFactory.setUserName( "sa" ); //$NON-NLS-1$
 
-        jdoFactory.setPassword( "" );
+        jdoFactory.setPassword( "" ); //$NON-NLS-1$
 
-        jdoFactory.setProperty( "org.jpox.transactionIsolation", "READ_UNCOMMITTED" );
+        jdoFactory.setProperty( "org.jpox.transactionIsolation", "READ_UNCOMMITTED" ); //$NON-NLS-1$ //$NON-NLS-2$
 
-        jdoFactory.setProperty( "org.jpox.poid.transactionIsolation", "READ_UNCOMMITTED" );
+        jdoFactory.setProperty( "org.jpox.poid.transactionIsolation", "READ_UNCOMMITTED" ); //$NON-NLS-1$ //$NON-NLS-2$
 
-        jdoFactory.setProperty( "org.jpox.autoCreateSchema", "true" );
+        jdoFactory.setProperty( "org.jpox.autoCreateSchema", "true" ); //$NON-NLS-1$ //$NON-NLS-2$
 
         Properties properties = jdoFactory.getProperties();
 
@@ -81,7 +84,7 @@
             System.setProperty( (String) entry.getKey(), (String) entry.getValue() );
         }
 
-        SchemaTool.createSchemaTables( new URL[] { getClass().getResource( "/META-INF/package.jdo" ) }, null, false );
+        SchemaTool.createSchemaTables( new URL[] { getClass().getResource( "/META-INF/package.jdo" ) }, null, false ); //$NON-NLS-1$
 
         PersistenceManagerFactory pmf = jdoFactory.getPersistenceManagerFactory();
 
@@ -94,128 +97,136 @@
         usermanager = (UserManager) lookup( UserManager.ROLE );
     }
 
-    public void testAddGetUser()
+    public void testAddGetUserById()
         throws Exception
     {
         assertNotNull( usermanager );
 
-        assertEquals( "New UserManager should contain no users.", 0, usermanager.getUsers().size() );
+        assertEquals( "New UserManager should contain no users.", 0, usermanager.getUsers().size() ); //$NON-NLS-1$
 
         User smcqueen = new User();
-        smcqueen.setUsername( "smcqueen" );
-        smcqueen.setFullName( "Steve McQueen" );
-        usermanager.addUser( smcqueen );
+        smcqueen.setUsername( "smcqueen" ); //$NON-NLS-1$
+        smcqueen.setFullName( "Steve McQueen" ); //$NON-NLS-1$
+        smcqueen.setPassword( "the cooler king" ); //$NON-NLS-1$
+
+        /* Keep a reference to the object that was added.
+         * Since it has the actual accountId that was managed by jpox/jdo.
+         */
+        User added = usermanager.addUser( smcqueen );
 
         assertEquals( 1, usermanager.getUsers().size() );
 
-        User actual = usermanager.getUser( 1 );
+        /* Fetch user from usermanager using accountId returned earlier */
+        User actual = usermanager.getUser( added.getAccountId() );
         assertEquals( smcqueen, actual );
     }
 
-    public void testUpdateUser()
+    public void testAddGetUserByName()
+        throws Exception
     {
         assertNotNull( usermanager );
 
-        User jgarner = new User();
-        jgarner.setUsername( "jgarner" );
-        jgarner.setFullName( "James Garner" );
-        usermanager.addUser( jgarner );
+        assertEquals( "New UserManager should contain no users.", 0, usermanager.getUsers().size() ); //$NON-NLS-1$
 
-        User fetched = usermanager.getUserByUsername( "jgarner" );
-        fetched.setFullName( "The Scrounger" );
+        User smcqueen = new User();
+        smcqueen.setUsername( "smcqueen" ); //$NON-NLS-1$
+        smcqueen.setFullName( "Steve McQueen" ); //$NON-NLS-1$
+        smcqueen.setPassword( "the cooler king" ); //$NON-NLS-1$
+        usermanager.addUser( smcqueen );
 
-        usermanager.updateUser( fetched );
+        assertEquals( 1, usermanager.getUsers().size() );
 
-        User actual = usermanager.getUserByUsername( "jgarner" );
+        User actual = usermanager.getUser( "smcqueen" ); //$NON-NLS-1$
 
-        assertEquals( "The Scrounger", actual.getFullName() );
+        assertNotNull( "Should return the smcqueen user.", actual ); //$NON-NLS-1$
+        assertEquals( smcqueen, actual );
     }
 
-    public void testGetUser()
+    public void testUpdateUser() throws EntityExistsException, PasswordRuleViolationException
     {
         assertNotNull( usermanager );
 
-        assertEquals( "New UserManager should contain no users.", 0, usermanager.getUsers().size() );
+        assertEquals( "New UserManager should contain no users.", 0, usermanager.getUsers().size() ); //$NON-NLS-1$
 
-        User rattenborough = new User();
-        rattenborough.setUsername( "rattenborough" );
-        rattenborough.setFullName( "Richard Attenborough" );
-        usermanager.addUser( rattenborough );
+        User jgarner = new User();
+        jgarner.setUsername( "jgarner" ); //$NON-NLS-1$
+        jgarner.setFullName( "James Garner" ); //$NON-NLS-1$
+        jgarner.setPassword( "the scrounger" ); //$NON-NLS-1$
+        usermanager.addUser( jgarner );
 
-        User dpleasence = new User();
-        dpleasence.setUsername( "dpleasence" );
-        dpleasence.setFullName( "Donald Pleasence" );
-        usermanager.addUser( dpleasence );
+        User fetched = usermanager.getUser( "jgarner" ); //$NON-NLS-1$
+        assertNotNull( "User should not be null.", fetched ); //$NON-NLS-1$
+        fetched.setFullName( "Flight Lt. Hendley" ); //$NON-NLS-1$
 
-        assertEquals( 2, usermanager.getUsers().size() );
+        usermanager.updateUser( fetched );
 
-        User actual = usermanager.getUser( 1 );
-        assertEquals( rattenborough, actual );
+        User actual = usermanager.getUser( "jgarner" ); //$NON-NLS-1$
 
-        User actual2 = usermanager.getUser( 2 );
-        assertEquals( dpleasence, actual2 );
+        assertEquals( "Flight Lt. Hendley", actual.getFullName() ); //$NON-NLS-1$
     }
 
-    public void testRemoveUser()
+    public void testRemoveUser() throws EntityExistsException, PasswordRuleViolationException
     {
         assertNotNull( usermanager );
 
-        assertEquals( "New UserManager should contain no users.", 0, usermanager.getUsers().size() );
+        assertEquals( "New UserManager should contain no users.", 0, usermanager.getUsers().size() ); //$NON-NLS-1$
 
         User rattenborough = new User();
-        rattenborough.setUsername( "rattenborough" );
-        rattenborough.setFullName( "Richard Attenborough" );
+        rattenborough.setUsername( "rattenborough" ); //$NON-NLS-1$
+        rattenborough.setFullName( "Richard Attenborough" ); //$NON-NLS-1$
+        rattenborough.setPassword( "the big x" ); //$NON-NLS-1$
         usermanager.addUser( rattenborough );
 
         User dpleasence = new User();
-        dpleasence.setUsername( "dpleasence" );
-        dpleasence.setFullName( "Donald Pleasence" );
+        dpleasence.setUsername( "dpleasence" ); //$NON-NLS-1$
+        dpleasence.setFullName( "Donald Pleasence" ); //$NON-NLS-1$
+        dpleasence.setPassword( "the forger" ); //$NON-NLS-1$
         usermanager.addUser( dpleasence );
 
         assertEquals( 2, usermanager.getUsers().size() );
 
-        User actual = usermanager.getUser( 1 );
+        User actual = usermanager.getUser( "rattenborough" ); //$NON-NLS-1$
         assertEquals( rattenborough, actual );
 
-        usermanager.removeUser( 1 );
+        usermanager.removeUser( "rattenborough" ); //$NON-NLS-1$
 
         try
         {
-            actual = usermanager.getUser( 1 );
+            actual = usermanager.getUser( "rattenborough" ); //$NON-NLS-1$
         }
         catch ( Exception e )
         {
-            fail( "UserManager.getUser(int) should not throw an Exception: " + e.getClass().getName() + " - "
+            fail( "UserManager.getUser(int) should not throw an Exception: " + e.getClass().getName() + " - " //$NON-NLS-1$ //$NON-NLS-2$
                 + e.getMessage() );
         }
-        assertNull( "removed user should no longer be returned.", actual );
+        assertNull( "removed user should no longer be returned.", actual ); //$NON-NLS-1$
 
-        User actual2 = usermanager.getUser( 2 );
-        assertEquals( "removed user should not affect existing user ids.", dpleasence, actual2 );
+        User actual2 = usermanager.getUser( "dpleasence" ); //$NON-NLS-1$
+        assertEquals( "removed user should not affect existing user ids.", dpleasence, actual2 ); //$NON-NLS-1$
     }
 
     public void testAddGetUserGroupByName()
     {
         assertNotNull( usermanager );
 
-        assertEquals( "New UserManager should contain no users.", 0, usermanager.getUsers().size() );
-        assertEquals( "New UserManager should contain no groups.", 0, usermanager.getUserGroups().size() );
+        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$
 
         UserGroup british = new UserGroup();
-        british.setName( "raf" );
-        british.setDescription( "Royal Air Force" );
+        british.setName( "raf" ); //$NON-NLS-1$
+        british.setDescription( "Royal Air Force" ); //$NON-NLS-1$
 
         usermanager.addUserGroup( british );
 
         UserGroup american = new UserGroup();
-        american.setName( "usaaf" );
-        american.setDescription( "United States Army Air Forces" );
+        american.setName( "usaaf" ); //$NON-NLS-1$
+        american.setDescription( "United States Army Air Forces" ); //$NON-NLS-1$
 
         usermanager.addUserGroup( american );
 
         assertEquals( 2, usermanager.getUserGroups().size() );
 
-        UserGroup actual = usermanager.getUserGroup( "raf" );
+        UserGroup actual = usermanager.getUserGroup( "raf" ); //$NON-NLS-1$
         assertEquals( british, actual );
     }
 
@@ -223,48 +234,56 @@
     {
         assertNotNull( usermanager );
 
-        assertEquals( "New UserManager should contain no users.", 0, usermanager.getUsers().size() );
-        assertEquals( "New UserManager should contain no groups.", 0, usermanager.getUserGroups().size() );
+        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$
 
         UserGroup british = new UserGroup();
-        british.setName( "raf" );
-        british.setDescription( "Royal Air Force" );
+        british.setName( "raf" ); //$NON-NLS-1$
+        british.setDescription( "Royal Air Force" ); //$NON-NLS-1$
 
-        usermanager.addUserGroup( british );
+        /* Keep a reference to the object that was added.
+         * Since it has the actual ID that was managed by jpox/jdo.
+         */
+        UserGroup added = usermanager.addUserGroup( british );
 
+        // Add a second UserGroup to ensure that previous ID doesn't get changed.
         UserGroup american = new UserGroup();
-        american.setName( "usaaf" );
-        american.setDescription( "United States Army Air Forces" );
+        american.setName( "usaaf" ); //$NON-NLS-1$
+        american.setDescription( "United States Army Air Forces" ); //$NON-NLS-1$
 
         usermanager.addUserGroup( american );
 
         assertEquals( 2, usermanager.getUserGroups().size() );
 
-        UserGroup actualviaName = usermanager.getUserGroup( 1 );
-        assertEquals( british, actualviaName );
+        /* Fetch UserGroup from usermanager using ID returned earlier */
+        UserGroup actual = usermanager.getUserGroup( added.getId() );
+        assertNotNull( "UserGroup id:1918 should exist.", actual ); //$NON-NLS-1$
+        assertEquals( british, actual );
     }
 
     public void testUpdateUserGroup()
     {
         assertNotNull( usermanager );
 
-        assertEquals( "New UserManager should contain no users.", 0, usermanager.getUsers().size() );
-        assertEquals( "New UserManager should contain no groups.", 0, usermanager.getUserGroups().size() );
+        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$
 
         UserGroup british = new UserGroup();
-        british.setName( "raf" );
-        british.setDescription( "Royal Air Force" );
+        british.setName( "raf" ); //$NON-NLS-1$
+        british.setDescription( "Royal Air Force" ); //$NON-NLS-1$
 
         usermanager.addUserGroup( british );
 
+        assertNotNull( usermanager.getUserGroups() );
         assertEquals( 1, usermanager.getUserGroups().size() );
 
-        UserGroup raf = usermanager.getUserGroup( "raf" );
-        raf.setDescription( "Royal Air Force, British" );
+        UserGroup raf = usermanager.getUserGroup( "raf" ); //$NON-NLS-1$
+        assertNotNull( raf );
+        raf.setDescription( "Royal Air Force, British" ); //$NON-NLS-1$
 
         usermanager.updateUserGroup( raf );
 
-        UserGroup actual = usermanager.getUserGroup( "raf" );
+        UserGroup actual = usermanager.getUserGroup( "raf" ); //$NON-NLS-1$
         assertEquals( raf, actual );
     }
 
@@ -272,18 +291,18 @@
     {
         assertNotNull( usermanager );
 
-        assertEquals( "New UserManager should contain no users.", 0, usermanager.getUsers().size() );
-        assertEquals( "New UserManager should contain no groups.", 0, usermanager.getUserGroups().size() );
+        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$
 
         UserGroup british = new UserGroup();
-        british.setName( "raf" );
-        british.setDescription( "Royal Air Force" );
+        british.setName( "raf" ); //$NON-NLS-1$
+        british.setDescription( "Royal Air Force" ); //$NON-NLS-1$
 
         usermanager.addUserGroup( british );
 
         UserGroup american = new UserGroup();
-        american.setName( "usaaf" );
-        american.setDescription( "United States Army Air Forces" );
+        american.setName( "usaaf" ); //$NON-NLS-1$
+        american.setDescription( "United States Army Air Forces" ); //$NON-NLS-1$
 
         usermanager.addUserGroup( american );
 
@@ -296,77 +315,147 @@
     {
         assertNotNull( usermanager );
 
-        assertEquals( "New UserManager should contain no users.", 0, usermanager.getUsers().size() );
-        assertEquals( "New UserManager should contain no groups.", 0, usermanager.getUserGroups().size() );
+        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$
 
         UserGroup british = new UserGroup();
-        british.setName( "raf" );
-        british.setDescription( "Royal Air Force" );
+        british.setName( "raf" ); //$NON-NLS-1$
+        british.setDescription( "Royal Air Force" ); //$NON-NLS-1$
 
         usermanager.addUserGroup( british );
 
         UserGroup american = new UserGroup();
-        american.setName( "usaaf" );
-        american.setDescription( "United States Army Air Forces" );
+        american.setName( "usaaf" ); //$NON-NLS-1$
+        american.setDescription( "United States Army Air Forces" ); //$NON-NLS-1$
 
         usermanager.addUserGroup( american );
 
         assertEquals( 2, usermanager.getUserGroups().size() );
 
-        UserGroup actual = usermanager.getUserGroup( "raf" );
+        UserGroup actual = usermanager.getUserGroup( "raf" ); //$NON-NLS-1$
         assertEquals( british, actual );
 
-        usermanager.removeUserGroup( 1 );
+        usermanager.removeUserGroup( "raf" ); //$NON-NLS-1$
 
         try
         {
-            actual = usermanager.getUserGroup( "raf" );
+            actual = usermanager.getUserGroup( "raf" ); //$NON-NLS-1$
         }
         catch ( Exception e )
         {
-            fail( "UserManager.getUserGroup(int) should not throw an Exception: " + e.getClass().getName() + " - "
+            fail( "UserManager.getUserGroup(int) should not throw an Exception: " + e.getClass().getName() + " - " //$NON-NLS-1$ //$NON-NLS-2$
                 + e.getMessage() );
         }
-        assertNull( "removed user group should no longer be returned.", actual );
+        assertNull( "removed user group should no longer be returned.", actual ); //$NON-NLS-1$
+
+        UserGroup actual2 = usermanager.getUserGroup( "usaaf" ); //$NON-NLS-1$
+        assertEquals( "removed user should not affect existing user ids.", american, actual2 ); //$NON-NLS-1$
+    }
+
+    public void testGetSetUserGroupInUserLoose() throws EntityExistsException, PasswordRuleViolationException
+    {
+        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$
+
+        UserGroup british = new UserGroup();
+        british.setName( "raf" ); //$NON-NLS-1$
+        british.setDescription( "Royal Air Force" ); //$NON-NLS-1$
+
+        /* Forget to add UserGroup to usermanager (loose technique) */
+
+        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$
+        rattenborough.setGroup( british );
+
+        /* Add new user with new usergroup (Shouldn't work) */
+        User added = usermanager.addUser( rattenborough );
+        assertNotNull( "Added UserGroup should not by null.", added.getGroup() ); //$NON-NLS-1$
+
+        assertEquals( 1, usermanager.getUsers().size() );
+        assertEquals( 1, usermanager.getUserGroups().size() );
+
+        User actual = usermanager.getUser( "rattenborough" ); //$NON-NLS-1$
+        assertEquals( added, actual );
+        assertNotNull( "Actual UserGroup should not be null.", actual.getGroup() ); //$NON-NLS-1$
+        assertEquals( added.getGroup(), actual.getGroup() );
+    }
+
+    public void testGetSetUserGroupInUserPreloaded() throws EntityExistsException, PasswordRuleViolationException
+    {
+        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$
+
+        UserGroup british = new UserGroup();
+        british.setName( "raf" ); //$NON-NLS-1$
+        british.setDescription( "Royal Air Force" ); //$NON-NLS-1$
+        
+        /* Add UserGroup to usermanager (preload technique) */
+        UserGroup addedGroup = usermanager.addUserGroup( british );
+
+        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$
+        
+        /* Use the (resolved) addedGroup from above.
+         * If you use the (unresolved) british UserGroup here, you will
+         * inadvertently create 2 UserGroups with the same name, but different IDs.
+         */
+        rattenborough.setGroup( addedGroup ); 
+        User added = usermanager.addUser( rattenborough );
+        assertNotNull( "Added UserGroup should not by null.", added.getGroup() ); //$NON-NLS-1$
+
+        assertEquals( 1, usermanager.getUsers().size() );
+        assertEquals( 1, usermanager.getUserGroups().size() );
 
-        UserGroup actual2 = usermanager.getUserGroup( "usaaf" );
-        assertEquals( "removed user should not affect existing user ids.", american, actual2 );
+        User actual = usermanager.getUser( "rattenborough" ); //$NON-NLS-1$
+        assertEquals( added, actual );
+        assertNotNull( "Actual UserGroup should not be null.", actual.getGroup() ); //$NON-NLS-1$
+        assertEquals( added.getGroup(), actual.getGroup() );
     }
 
     public void testGetSetPermissions()
     {
         assertNotNull( usermanager );
 
-        assertEquals( "New UserManager should contain no users.", 0, usermanager.getUsers().size() );
-        assertEquals( "New UserManager should contain no groups.", 0, usermanager.getUserGroups().size() );
+        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$
 
         Permission canFly = new Permission();
-        canFly.setName( "can_fly" );
-        canFly.setDescription( "Allows for flight." );
+        canFly.setName( "can_fly" ); //$NON-NLS-1$
+        canFly.setDescription( "Allows for flight." ); //$NON-NLS-1$
 
         Permission canBomb = new Permission();
-        canBomb.setName( "can_bomb" );
-        canBomb.setDescription( "Allows for bombing." );
+        canBomb.setName( "can_bomb" ); //$NON-NLS-1$
+        canBomb.setDescription( "Allows for bombing." ); //$NON-NLS-1$
 
         UserGroup british = new UserGroup();
-        british.setName( "raf" );
-        british.setDescription( "Royal Air Force" );
+        british.setName( "raf" ); //$NON-NLS-1$
+        british.setDescription( "Royal Air Force" ); //$NON-NLS-1$
         british.addPermission( canFly );
         british.addPermission( canBomb );
 
         usermanager.addUserGroup( british );
 
         UserGroup american = new UserGroup();
-        american.setName( "usaaf" );
-        american.setDescription( "United States Army Air Forces" );
+        american.setName( "usaaf" ); //$NON-NLS-1$
+        american.setDescription( "United States Army Air Forces" ); //$NON-NLS-1$
         american.addPermission( canFly );
 
         usermanager.addUserGroup( american );
 
         assertEquals( 2, usermanager.getUserGroups().size() );
 
-        UserGroup actual = usermanager.getUserGroup( "raf" );
+        UserGroup actual = usermanager.getUserGroup( "raf" ); //$NON-NLS-1$
 
+        assertNotNull( actual );
+        assertNotNull( actual.getPermissions() );
         assertEquals( 2, actual.getPermissions().size() );
     }
 }