Просмотр исходного кода

SEC-272: Completion of JDBC manager implementation.

Luke Taylor 17 лет назад
Родитель
Сommit
0459fc5477

+ 83 - 0
core/src/main/java/org/springframework/security/userdetails/GroupManager.java

@@ -0,0 +1,83 @@
+package org.springframework.security.userdetails;
+
+import org.springframework.security.GrantedAuthority;
+
+/**
+ * Allows management of groups of authorities and their members.
+ * <p>
+ * Typically this will be used to supplement the functionality of a {@link UserDetailsManager}
+ * in situations where the organization of application granted authorities into groups is preferred over
+ * a straight mapping of users to roles.
+ * <p>
+ * With this scenario, users are allocated to groups and take on the list of authorities which
+ * are assigned to the group, providing more flexible administration options.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ */
+public interface GroupManager {
+
+    /**
+     * Returns the names of all groups that this group manager controls.
+     */
+    String[] findAllGroups();
+
+    /**
+     * Locates the users who are members of a group
+     *
+     * @param groupName the group whose members are required
+     * @return the usernames of the group members
+     */
+    String[] findUsersInGroup(String groupName);
+
+    /**
+     * Creates a new group with the specified list of authorities.
+     *
+     * @param groupName the name for the new group
+     * @param authorities the authorities which are to be allocated to this group.
+     */
+    void createGroup(String groupName, GrantedAuthority[] authorities);
+
+    /**
+     * Removes a group, including all members and authorities.
+     *
+     * @param groupName the group to remove.
+     */
+    void deleteGroup(String groupName);
+
+    /**
+     * Changes the name of a group without altering the assigned authorities or members.
+     */
+    void renameGroup(String oldName, String newName);
+
+    /**
+     * Makes a user a member of a particular group.
+     *
+     * @param username the user to be given membership.
+     * @param group the name of the group to which the user will be added.
+     */
+    void addUserToGroup(String username, String group);
+
+    /**
+     * Deletes a user's membership of a group.
+     *
+     * @param username the user
+     * @param groupName the group to remove them from
+     */
+    void removeUserFromGroup(String username, String groupName);
+
+    /**
+     * Obtains the list of authorities which are assigned to a group.
+     */
+    GrantedAuthority[] findGroupAuthorities(String groupName);
+
+    /**
+     * Assigns a new authority to a group.
+     */
+    void addGroupAuthority(String groupName, GrantedAuthority authority);
+
+    /**
+     * Deletes an authority from those assigned to a group
+     */
+    void removeGroupAuthority(String groupName, GrantedAuthority authority);
+}

+ 12 - 12
core/src/main/java/org/springframework/security/userdetails/jdbc/JdbcDaoImpl.java

@@ -125,14 +125,6 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService {
      */
      */
     protected void addCustomAuthorities(String username, List authorities) {}
     protected void addCustomAuthorities(String username, List authorities) {}
 
 
-    public String getAuthoritiesByUsernameQuery() {
-        return authoritiesByUsernameQuery;
-    }
-
-    public String getRolePrefix() {
-        return rolePrefix;
-    }
-
     public String getUsersByUsernameQuery() {
     public String getUsersByUsernameQuery() {
         return usersByUsernameQuery;
         return usersByUsernameQuery;
     }
     }
@@ -151,10 +143,6 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService {
         this.groupAuthoritiesByUsernameMapping = new GroupAuthoritiesByUsernameMapping(getDataSource());
         this.groupAuthoritiesByUsernameMapping = new GroupAuthoritiesByUsernameMapping(getDataSource());
     }
     }
 
 
-    public boolean isUsernameBasedPrimaryKey() {
-        return usernameBasedPrimaryKey;
-    }
-
     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
         List users = usersByUsernameMapping.execute(username);
         List users = usersByUsernameMapping.execute(username);
 
 
@@ -208,6 +196,10 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService {
         authoritiesByUsernameQuery = queryString;
         authoritiesByUsernameQuery = queryString;
     }
     }
 
 
+    protected String getAuthoritiesByUsernameQuery() {
+        return authoritiesByUsernameQuery;
+    }
+
     /**
     /**
      * Allows the default query string used to retrieve group authorities based on username to be overriden, if
      * Allows the default query string used to retrieve group authorities based on username to be overriden, if
      * default table or column names need to be changed. The default query is {@link
      * default table or column names need to be changed. The default query is {@link
@@ -232,6 +224,10 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService {
         this.rolePrefix = rolePrefix;
         this.rolePrefix = rolePrefix;
     }
     }
 
 
+    protected String getRolePrefix() {
+        return rolePrefix;
+    }
+
     /**
     /**
      * If <code>true</code> (the default), indicates the {@link #getUsersByUsernameQuery()} returns a username
      * If <code>true</code> (the default), indicates the {@link #getUsersByUsernameQuery()} returns a username
      * in response to a query. If <code>false</code>, indicates that a primary key is used instead. If set to
      * in response to a query. If <code>false</code>, indicates that a primary key is used instead. If set to
@@ -246,6 +242,10 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService {
         this.usernameBasedPrimaryKey = usernameBasedPrimaryKey;
         this.usernameBasedPrimaryKey = usernameBasedPrimaryKey;
     }
     }
 
 
+    protected boolean isUsernameBasedPrimaryKey() {
+        return usernameBasedPrimaryKey;
+    }
+
     /**
     /**
      * Allows the default query string used to retrieve users based on username to be overriden, if default
      * Allows the default query string used to retrieve users based on username to be overriden, if default
      * table or column names need to be changed. The default query is {@link #DEF_USERS_BY_USERNAME_QUERY}; when
      * table or column names need to be changed. The default query is {@link #DEF_USERS_BY_USERNAME_QUERY}; when

+ 132 - 21
core/src/main/java/org/springframework/security/userdetails/jdbc/JdbcUserDetailsManager.java

@@ -5,13 +5,15 @@ import org.springframework.security.Authentication;
 import org.springframework.security.AuthenticationException;
 import org.springframework.security.AuthenticationException;
 import org.springframework.security.AuthenticationManager;
 import org.springframework.security.AuthenticationManager;
 import org.springframework.security.GrantedAuthority;
 import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.util.AuthorityUtils;
 import org.springframework.security.context.SecurityContextHolder;
 import org.springframework.security.context.SecurityContextHolder;
 import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
 import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
 import org.springframework.security.providers.dao.UserCache;
 import org.springframework.security.providers.dao.UserCache;
 import org.springframework.security.providers.dao.cache.NullUserCache;
 import org.springframework.security.providers.dao.cache.NullUserCache;
 import org.springframework.security.userdetails.UserDetails;
 import org.springframework.security.userdetails.UserDetails;
 import org.springframework.security.userdetails.UserDetailsManager;
 import org.springframework.security.userdetails.UserDetailsManager;
-import org.springframework.security.userdetails.GroupsManager;
+import org.springframework.security.userdetails.GroupManager;
 import org.springframework.context.ApplicationContextException;
 import org.springframework.context.ApplicationContextException;
 import org.springframework.jdbc.core.SqlParameter;
 import org.springframework.jdbc.core.SqlParameter;
 import org.springframework.jdbc.object.MappingSqlQuery;
 import org.springframework.jdbc.object.MappingSqlQuery;
@@ -35,7 +37,7 @@ import java.util.List;
  * @version $Id$
  * @version $Id$
  * @since 2.0
  * @since 2.0
  */
  */
-public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsManager, GroupsManager {
+public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsManager, GroupManager {
     //~ Static fields/initializers =====================================================================================
     //~ Static fields/initializers =====================================================================================
 
 
     // UserDetailsManager SQL
     // UserDetailsManager SQL
@@ -54,7 +56,7 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
     public static final String DEF_CHANGE_PASSWORD_SQL =
     public static final String DEF_CHANGE_PASSWORD_SQL =
             "update users set password = ? where username = ?";
             "update users set password = ? where username = ?";
 
 
-    // GroupsManager SQL
+    // GroupManager SQL
     public static final String DEF_FIND_GROUPS_SQL =
     public static final String DEF_FIND_GROUPS_SQL =
             "select group_name from groups";
             "select group_name from groups";
     public static final String DEF_FIND_USERS_IN_GROUP_SQL =
     public static final String DEF_FIND_USERS_IN_GROUP_SQL =
@@ -77,6 +79,15 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
             "update groups set group_name = ? where group_name = ?";
             "update groups set group_name = ? where group_name = ?";
     public static final String DEF_INSERT_GROUP_MEMBER_SQL =
     public static final String DEF_INSERT_GROUP_MEMBER_SQL =
             "insert into group_members (group_id, username) values (?,?)";
             "insert into group_members (group_id, username) values (?,?)";
+    public static final String DEF_DELETE_GROUP_MEMBER_SQL =
+            "delete from group_members where group_id = ? and username = ?";
+    public static final String DEF_GROUP_AUTHORITIES_QUERY_SQL =
+            "select g.id, g.group_name, ga.authority " +
+            "from groups g, group_authorities ga " +
+            "where g.group_name = ? " +
+            "and g.id = ga.group_id ";
+    public static final String DEF_DELETE_GROUP_AUTHORITY_SQL =
+            "delete from group_authorities where group_id = ? and authority = ?";
 
 
 
 
     //~ Instance fields ================================================================================================
     //~ Instance fields ================================================================================================
@@ -101,6 +112,9 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
     private String deleteGroupMembersSql = DEF_DELETE_GROUP_MEMBERS_SQL;
     private String deleteGroupMembersSql = DEF_DELETE_GROUP_MEMBERS_SQL;
     private String renameGroupSql = DEF_RENAME_GROUP_SQL;
     private String renameGroupSql = DEF_RENAME_GROUP_SQL;
     private String insertGroupMemberSql = DEF_INSERT_GROUP_MEMBER_SQL;
     private String insertGroupMemberSql = DEF_INSERT_GROUP_MEMBER_SQL;
+    private String deleteGroupMemberSql = DEF_DELETE_GROUP_MEMBER_SQL;
+    private String groupAuthoritiesSql = DEF_GROUP_AUTHORITIES_QUERY_SQL;
+    private String deleteGroupAuthoritySql = DEF_DELETE_GROUP_AUTHORITY_SQL;
 
 
     protected SqlUpdate insertUser;
     protected SqlUpdate insertUser;
     protected SqlUpdate deleteUser;
     protected SqlUpdate deleteUser;
@@ -120,6 +134,9 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
     protected SqlUpdate deleteGroupAuthorities;
     protected SqlUpdate deleteGroupAuthorities;
     protected SqlUpdate renameGroup;
     protected SqlUpdate renameGroup;
     protected SqlUpdate insertGroupMember;
     protected SqlUpdate insertGroupMember;
+    protected SqlUpdate deleteGroupMember;
+    protected SqlQuery groupAuthoritiesQuery;
+    protected SqlUpdate deleteGroupAuthority;
 
 
     private AuthenticationManager authenticationManager;
     private AuthenticationManager authenticationManager;
 
 
@@ -129,7 +146,7 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
 
 
     protected void initDao() throws ApplicationContextException {
     protected void initDao() throws ApplicationContextException {
         if (authenticationManager == null) {
         if (authenticationManager == null) {
-            logger.info("No authentication manager set. Reauthentication of users when changing passwords will" +
+            logger.info("No authentication manager set. Reauthentication of users when changing passwords will " +
                     "not be performed.");
                     "not be performed.");
         }
         }
 
 
@@ -151,6 +168,9 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
         deleteGroupMembers = new DeleteGroupMembers(getDataSource());
         deleteGroupMembers = new DeleteGroupMembers(getDataSource());
         renameGroup = new RenameGroup(getDataSource());
         renameGroup = new RenameGroup(getDataSource());
         insertGroupMember = new InsertGroupMember(getDataSource());
         insertGroupMember = new InsertGroupMember(getDataSource());
+        deleteGroupMember = new DeleteGroupMember (getDataSource());
+        groupAuthoritiesQuery = new GroupAuthoritiesByGroupNameMapping(getDataSource());
+        deleteGroupAuthority = new DeleteGroupAuthority(getDataSource());
 
 
         super.initDao();
         super.initDao();
     }
     }
@@ -158,22 +178,24 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
     //~ UserDetailsManager implementation ==============================================================================
     //~ UserDetailsManager implementation ==============================================================================
 
 
     public void createUser(UserDetails user) {
     public void createUser(UserDetails user) {
+        validateUserDetails(user);
         insertUser.update(new Object[] {user.getUsername(), user.getPassword(), Boolean.valueOf(user.isEnabled())});
         insertUser.update(new Object[] {user.getUsername(), user.getPassword(), Boolean.valueOf(user.isEnabled())});
-
-        for (int i=0; i < user.getAuthorities().length; i++) {
-            insertAuthority.update(user.getUsername(), user.getAuthorities()[i].getAuthority());
-        }
+        insertUserAuthorities(user);
     }
     }
 
 
     public void updateUser(UserDetails user) {
     public void updateUser(UserDetails user) {
+        validateUserDetails(user);
         updateUser.update(new Object[] {user.getPassword(), Boolean.valueOf(user.isEnabled()), user.getUsername()});
         updateUser.update(new Object[] {user.getPassword(), Boolean.valueOf(user.isEnabled()), user.getUsername()});
         deleteUserAuthorities.update(user.getUsername());
         deleteUserAuthorities.update(user.getUsername());
+        insertUserAuthorities(user);
+
+        userCache.removeUserFromCache(user.getUsername());
+    }
 
 
+    private void insertUserAuthorities(UserDetails user) {
         for (int i=0; i < user.getAuthorities().length; i++) {
         for (int i=0; i < user.getAuthorities().length; i++) {
             insertAuthority.update(user.getUsername(), user.getAuthorities()[i].getAuthority());
             insertAuthority.update(user.getUsername(), user.getAuthorities()[i].getAuthority());
         }
         }
-
-        userCache.removeUserFromCache(user.getUsername());
     }
     }
 
 
     public void deleteUser(String username) {
     public void deleteUser(String username) {
@@ -233,28 +255,32 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
 
 
     //~ GroupManager implementation ====================================================================================
     //~ GroupManager implementation ====================================================================================
 
 
-    public List findAllGroups() {
-        return findAllGroupsQuery.execute();
+    public String[] findAllGroups() {
+        return (String[]) findAllGroupsQuery.execute().toArray(new String[0]);
     }
     }
 
 
-    public List findUsersInGroup(String groupName) {
+    public String[] findUsersInGroup(String groupName) {
         Assert.hasText(groupName);
         Assert.hasText(groupName);
-        return findUsersInGroupQuery.execute(groupName);
+        return (String[]) findUsersInGroupQuery.execute(groupName).toArray(new String[0]);
     }
     }
 
 
     public void createGroup(String groupName, GrantedAuthority[] authorities) {
     public void createGroup(String groupName, GrantedAuthority[] authorities) {
         Assert.hasText(groupName);
         Assert.hasText(groupName);
         Assert.notNull(authorities);
         Assert.notNull(authorities);
 
 
+        logger.debug("Creating new group '" + groupName + "' with authorities " +
+                    AuthorityUtils.authorityArrayToSet(authorities));
+
         insertGroup.update(groupName);
         insertGroup.update(groupName);
-        Integer key = (Integer) findGroupIdQuery.findObject(groupName);
+        Integer id = (Integer) findGroupIdQuery.findObject(groupName);
 
 
         for (int i=0; i < authorities.length; i++) {
         for (int i=0; i < authorities.length; i++) {
-            insertGroupAuthority.update( new Object[] {key, authorities[i].getAuthority()});
+            insertGroupAuthority.update( new Object[] {id, authorities[i].getAuthority()});
         }
         }
     }
     }
 
 
     public void deleteGroup(String groupName) {
     public void deleteGroup(String groupName) {
+        logger.debug("Deleting group '" + groupName + "'");
         Assert.hasText(groupName);
         Assert.hasText(groupName);
 
 
         int id = ((Integer) findGroupIdQuery.findObject(groupName)).intValue();
         int id = ((Integer) findGroupIdQuery.findObject(groupName)).intValue();
@@ -264,19 +290,58 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
     }
     }
 
 
     public void renameGroup(String oldName, String newName) {
     public void renameGroup(String oldName, String newName) {
+        logger.debug("Changing group name from '" + oldName + "' to '" + newName + "'");
         Assert.hasText(oldName);
         Assert.hasText(oldName);
         Assert.hasText(newName);
         Assert.hasText(newName);
-        
-        renameGroup.update(newName, oldName);        
+
+        renameGroup.update(newName, oldName);
     }
     }
 
 
     public void addUserToGroup(String username, String groupName) {
     public void addUserToGroup(String username, String groupName) {
+        logger.debug("Adding user '" + username + "' to group '" + groupName + "'");
         Assert.hasText(username);
         Assert.hasText(username);
         Assert.hasText(groupName);
         Assert.hasText(groupName);
 
 
-        Integer key = (Integer) findGroupIdQuery.findObject(groupName);
+        Integer id = (Integer) findGroupIdQuery.findObject(groupName);
+
+        insertGroupMember.update(new Object[] {id, username});
+        userCache.removeUserFromCache(username);
+    }
+
+    public void removeUserFromGroup(String username, String groupName) {
+        logger.debug("Removing user '" + username + "' to group '" + groupName + "'");
+        Assert.hasText(username);
+        Assert.hasText(groupName);
+
+        Integer id = (Integer) findGroupIdQuery.findObject(groupName);
+
+        deleteGroupMember.update(new Object[] {id, username});
+        userCache.removeUserFromCache(username);
+    }
+
+    public GrantedAuthority[] findGroupAuthorities(String groupName) {
+        logger.debug("Loading authorities for group '" + groupName + "'");
+        Assert.hasText(groupName);
+
+        return (GrantedAuthority[]) groupAuthoritiesQuery.execute(groupName).toArray(new GrantedAuthority[0]);
+    }
+
+    public void removeGroupAuthority(String groupName, GrantedAuthority authority) {
+        logger.debug("Removing authority '" + authority + "' from group '" + groupName + "'");
+        Assert.hasText(groupName);
+        Assert.notNull(authority);
+
+        Integer id = (Integer) findGroupIdQuery.findObject(groupName);
+        deleteGroupAuthority.update(new Object[] {id, authority});
+    }
+
+    public void addGroupAuthority(String groupName, GrantedAuthority authority) {
+        logger.debug("Adding authority '" + authority + "' to group '" + groupName + "'");
+        Assert.hasText(groupName);
+        Assert.notNull(authority);
 
 
-        insertGroupMember.update(new Object[] {key, username});
+        Integer id = (Integer) findGroupIdQuery.findObject(groupName);
+        insertGroupAuthority.update(new Object[] {id, authority.getAuthority()});
     }
     }
 
 
     public void setAuthenticationManager(AuthenticationManager authenticationManager) {
     public void setAuthenticationManager(AuthenticationManager authenticationManager) {
@@ -333,6 +398,20 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
         this.userCache = userCache;
         this.userCache = userCache;
     }
     }
 
 
+    private void validateUserDetails(UserDetails user) {
+        Assert.hasText(user.getUsername(), "Username may not be empty or null");
+        validateAuthorities(user.getAuthorities());
+    }
+
+    private void validateAuthorities(GrantedAuthority[] authorities) {
+        Assert.notNull(authorities, "Authorities list must not be null");
+
+        for (int i=0; i < authorities.length; i++) {
+            Assert.notNull(authorities[i], "Authorities list contains a null entry");
+            Assert.hasText(authorities[i].getAuthority(), "getAuthority() method must return a non-empty string");
+        }
+    }
+
     //~ Inner Classes ==================================================================================================
     //~ Inner Classes ==================================================================================================
 
 
     protected class InsertUser extends SqlUpdate {
     protected class InsertUser extends SqlUpdate {
@@ -484,7 +563,7 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
             super(ds, renameGroupSql);
             super(ds, renameGroupSql);
             declareParameter(new SqlParameter(Types.VARCHAR));
             declareParameter(new SqlParameter(Types.VARCHAR));
             declareParameter(new SqlParameter(Types.VARCHAR));
             declareParameter(new SqlParameter(Types.VARCHAR));
-            compile();            
+            compile();
         }
         }
     }
     }
 
 
@@ -497,4 +576,36 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
         }
         }
     }
     }
 
 
+    private class DeleteGroupMember extends SqlUpdate {
+        public DeleteGroupMember(DataSource ds) {
+            super(ds, deleteGroupMemberSql);
+            declareParameter(new SqlParameter(Types.INTEGER));
+            declareParameter(new SqlParameter(Types.VARCHAR));
+            compile();
+        }
+    }
+
+    protected class GroupAuthoritiesByGroupNameMapping extends MappingSqlQuery {
+         protected GroupAuthoritiesByGroupNameMapping(DataSource ds) {
+             super(ds, groupAuthoritiesSql);
+             declareParameter(new SqlParameter(Types.VARCHAR));
+             compile();
+         }
+
+         protected Object mapRow(ResultSet rs, int rownum) throws SQLException {
+             String roleName = getRolePrefix() + rs.getString(3);
+             GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName);
+
+             return authority;
+         }
+    }
+
+    private class DeleteGroupAuthority extends SqlUpdate {
+        public DeleteGroupAuthority(DataSource ds) {
+            super(ds, deleteGroupAuthoritySql);
+            declareParameter(new SqlParameter(Types.INTEGER));
+            declareParameter(new SqlParameter(Types.VARCHAR));
+            compile();
+        }
+    }
 }
 }

+ 43 - 7
core/src/test/java/org/springframework/security/userdetails/jdbc/JdbcUserDetailsManagerTests.java

@@ -5,6 +5,8 @@ import org.springframework.security.Authentication;
 import org.springframework.security.BadCredentialsException;
 import org.springframework.security.BadCredentialsException;
 import org.springframework.security.MockAuthenticationManager;
 import org.springframework.security.MockAuthenticationManager;
 import org.springframework.security.PopulatedDatabase;
 import org.springframework.security.PopulatedDatabase;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
 import org.springframework.security.context.SecurityContextHolder;
 import org.springframework.security.context.SecurityContextHolder;
 import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
 import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
 import org.springframework.security.providers.dao.UserCache;
 import org.springframework.security.providers.dao.UserCache;
@@ -25,6 +27,8 @@ import java.util.Map;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Collections;
 import java.util.Collections;
+import java.util.ArrayList;
+import java.util.Arrays;
 
 
 /**
 /**
  * Tests for {@link JdbcUserDetailsManager}
  * Tests for {@link JdbcUserDetailsManager}
@@ -187,7 +191,7 @@ public class JdbcUserDetailsManagerTests {
 
 
     @Test
     @Test
     public void findAllGroupsReturnsExpectedGroupNames() {
     public void findAllGroupsReturnsExpectedGroupNames() {
-        List<String> groups = manager.findAllGroups();
+        List<String> groups = new ArrayList<String>(Arrays.asList(manager.findAllGroups()));
         assertEquals(4, groups.size());
         assertEquals(4, groups.size());
 
 
         Collections.sort(groups);
         Collections.sort(groups);
@@ -199,11 +203,11 @@ public class JdbcUserDetailsManagerTests {
 
 
     @Test
     @Test
     public void findGroupMembersReturnsCorrectData() {
     public void findGroupMembersReturnsCorrectData() {
-        List<String> groupMembers = manager.findUsersInGroup("GROUP_0");
-        assertEquals(1, groupMembers.size());
-        assertEquals("jerry", groupMembers.get(0));
+        String[] groupMembers = manager.findUsersInGroup("GROUP_0");
+        assertEquals(1, groupMembers.length);
+        assertEquals("jerry", groupMembers[0]);
         groupMembers = manager.findUsersInGroup("GROUP_1");
         groupMembers = manager.findUsersInGroup("GROUP_1");
-        assertEquals(2, groupMembers.size());
+        assertEquals(2, groupMembers.length);
     }
     }
 
 
     @Test
     @Test
@@ -227,7 +231,7 @@ public class JdbcUserDetailsManagerTests {
 
 
         assertEquals(0, template.queryForList("select * from group_authorities").size());
         assertEquals(0, template.queryForList("select * from group_authorities").size());
         assertEquals(0, template.queryForList("select * from group_members").size());
         assertEquals(0, template.queryForList("select * from group_members").size());
-        assertEquals(0, template.queryForList("select id from groups").size());        
+        assertEquals(0, template.queryForList("select id from groups").size());
     }
     }
 
 
     @Test
     @Test
@@ -244,6 +248,38 @@ public class JdbcUserDetailsManagerTests {
         assertEquals(2, template.queryForList("select username from group_members where group_id = 0").size());
         assertEquals(2, template.queryForList("select username from group_members where group_id = 0").size());
     }
     }
 
 
+    @Test
+    public void removeUserFromGroupDeletesGroupMemberRow() throws Exception {
+        manager.removeUserFromGroup("jerry", "GROUP_1");
+
+        assertEquals(1, template.queryForList("select group_id from group_members where username = 'jerry'").size());
+    }
+
+    @Test
+    public void findGroupAuthoritiesReturnsCorrectAuthorities() throws Exception {
+        GrantedAuthority[] authorities = manager.findGroupAuthorities("GROUP_0");
+
+        assertEquals("ROLE_A", authorities[0].getAuthority());
+    }
+
+    @Test
+    public void addGroupAuthorityInsertsCorrectGroupAuthorityRow() throws Exception {
+        GrantedAuthority auth = new GrantedAuthorityImpl("ROLE_X");
+        manager.addGroupAuthority("GROUP_0", auth);
+
+        template.queryForObject("select authority from group_authorities where authority = 'ROLE_X' and group_id = 0", String.class);
+    }
+
+    @Test
+    public void deleteGroupAuthorityRemovesCorrectRows() throws Exception {
+        GrantedAuthority auth = new GrantedAuthorityImpl("ROLE_A");
+        manager.removeGroupAuthority("GROUP_0", auth);
+        assertEquals(0, template.queryForList("select authority from group_authorities where group_id = 0").size());
+
+        manager.removeGroupAuthority("GROUP_2", auth);
+        assertEquals(2, template.queryForList("select authority from group_authorities where group_id = 2").size());        
+    }
+
     private Authentication authenticateJoe() {
     private Authentication authenticateJoe() {
         UsernamePasswordAuthenticationToken auth =
         UsernamePasswordAuthenticationToken auth =
                 new UsernamePasswordAuthenticationToken("joe","password", joe.getAuthorities());
                 new UsernamePasswordAuthenticationToken("joe","password", joe.getAuthorities());
@@ -261,7 +297,7 @@ public class JdbcUserDetailsManagerTests {
     }
     }
 
 
     private class MockUserCache implements UserCache {
     private class MockUserCache implements UserCache {
-        private Map cache = new HashMap();
+        private Map<String,UserDetails> cache = new HashMap<String,UserDetails>();
 
 
         public UserDetails getUserFromCache(String username) {
         public UserDetails getUserFromCache(String username) {
             return (User) cache.get(username);
             return (User) cache.get(username);