浏览代码

SEC-272: Partial group manager implementation.

Luke Taylor 17 年之前
父节点
当前提交
bad58fe96a

+ 32 - 0
core/src/main/java/org/springframework/security/userdetails/GroupsManager.java

@@ -0,0 +1,32 @@
+package org.springframework.security.userdetails;
+
+import org.springframework.security.GrantedAuthority;
+
+import java.util.List;
+
+/**
+ * @author Luke Taylor
+ * @version $Id$
+ */
+public interface GroupsManager {
+
+    List findAllGroups();
+
+    List findUsersInGroup(String groupName);
+
+    void createGroup(String groupName, GrantedAuthority[] authorities);
+//
+//    void deleteGroup(String groupName);
+//
+//    void renameGroup(String oldName, String newName);
+//
+//    void addUserToGroup(String username, String group);
+//
+//    void removeUserFromGroup(String username, String groupName);
+//
+//    GrantedAuthority[] findGroupAuthorities(String groupName);
+//
+//    void removeGroupAuthority(String groupName, GrantedAuthority authority);
+//
+//    void addGroupAuthority(String groupName, GrantedAuthority authority);
+}

+ 118 - 2
core/src/main/java/org/springframework/security/userdetails/jdbc/JdbcUserDetailsManager.java

@@ -4,12 +4,14 @@ import org.springframework.security.AccessDeniedException;
 import org.springframework.security.Authentication;
 import org.springframework.security.AuthenticationException;
 import org.springframework.security.AuthenticationManager;
+import org.springframework.security.GrantedAuthority;
 import org.springframework.security.context.SecurityContextHolder;
 import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
 import org.springframework.security.providers.dao.UserCache;
 import org.springframework.security.providers.dao.cache.NullUserCache;
 import org.springframework.security.userdetails.UserDetails;
 import org.springframework.security.userdetails.UserDetailsManager;
+import org.springframework.security.userdetails.GroupsManager;
 import org.springframework.context.ApplicationContextException;
 import org.springframework.jdbc.core.SqlParameter;
 import org.springframework.jdbc.object.MappingSqlQuery;
@@ -31,10 +33,12 @@ import java.util.List;
  *
  * @author Luke Taylor
  * @version $Id$
+ * @since 2.0
  */
-public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsManager {
+public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsManager, GroupsManager {
     //~ Static fields/initializers =====================================================================================
 
+    // UserDetailsManager SQL
     public static final String DEF_CREATE_USER_SQL =
             "insert into users (username, password, enabled) values (?,?,?)";
     public static final String DEF_DELETE_USER_SQL =
@@ -50,6 +54,19 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
     public static final String DEF_CHANGE_PASSWORD_SQL =
             "update users set password = ? where username = ?";
 
+    // GroupsManager SQL
+    public static final String DEF_FIND_GROUPS_SQL =
+            "select group_name from groups";
+    public static final String DEF_FIND_USERS_IN_GROUP_SQL =
+            "select username from group_members gm, groups g " +
+            "where gm.group_id = g.id" +
+            " and g.group_name = ?";
+    public static final String DEF_INSERT_GROUP_SQL =
+            "insert into groups (group_name) values (?)";
+    public static final String DEF_FIND_GROUP_ID_SQL =
+            "select id from groups where group_name = ?";
+    public static final String DEF_INSERT_GROUP_AUTHORITY_SQL =
+            "insert into group_authorities (group_id, authority) values (?,?)";
 
     //~ Instance fields ================================================================================================
 
@@ -63,6 +80,12 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
     private String userExistsSql = DEF_USER_EXISTS_SQL;
     private String changePasswordSql = DEF_CHANGE_PASSWORD_SQL;
 
+    private String findAllGroupsSql = DEF_FIND_GROUPS_SQL;
+    private String findUsersInGroupSql = DEF_FIND_USERS_IN_GROUP_SQL;
+    private String insertGroupSql = DEF_INSERT_GROUP_SQL;
+    private String findGroupIdSql = DEF_FIND_GROUP_ID_SQL;
+    private String insertGroupAuthoritySql = DEF_INSERT_GROUP_AUTHORITY_SQL;
+
     protected SqlUpdate insertUser;
     protected SqlUpdate deleteUser;
     protected SqlUpdate updateUser;
@@ -71,6 +94,12 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
     protected SqlQuery  userExistsQuery;
     protected SqlUpdate changePassword;
 
+    protected SqlQuery  findAllGroupsQuery;
+    protected SqlQuery  findUsersInGroupQuery;
+    protected SqlUpdate insertGroup;
+    protected SqlQuery  findGroupIdQuery;
+    protected SqlUpdate insertGroupAuthority;
+
     private AuthenticationManager authenticationManager;
 
     private UserCache userCache = new NullUserCache();
@@ -90,9 +119,18 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
         deleteUserAuthorities = new DeleteUserAuthorities(getDataSource());
         userExistsQuery = new UserExistsQuery(getDataSource());
         changePassword = new ChangePassword(getDataSource());
+
+        findAllGroupsQuery = new AllGroupsQuery(getDataSource());
+        findUsersInGroupQuery = new GroupMembersQuery(getDataSource());
+        insertGroup = new InsertGroup(getDataSource());
+        findGroupIdQuery = new FindGroupIdQuery(getDataSource());
+        insertGroupAuthority = new InsertGroupAuthority(getDataSource());
+
         super.initDao();
     }
 
+    //~ UserDetailsManager implementation ==============================================================================
+
     public void createUser(UserDetails user) {
         insertUser.update(new Object[] {user.getUsername(), user.getPassword(), Boolean.valueOf(user.isEnabled())});
 
@@ -167,6 +205,29 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
         return users.size() == 1;
     }
 
+    //~ GroupManager implementation ====================================================================================
+
+    public List findAllGroups() {
+        return findAllGroupsQuery.execute();
+    }
+
+    public List findUsersInGroup(String groupName) {
+        Assert.hasText(groupName);
+        return findUsersInGroupQuery.execute(groupName);
+    }
+
+    public void createGroup(String groupName, GrantedAuthority[] authorities) {
+        Assert.hasText(groupName);
+        Assert.notNull(authorities);
+
+        insertGroup.update(groupName);
+        Integer key = (Integer) findGroupIdQuery.findObject(groupName);
+
+        for (int i=0; i < authorities.length; i++) {
+            insertGroupAuthority.update( new Object[] {key, authorities[i].getAuthority()});
+        }
+    }
+
     public void setAuthenticationManager(AuthenticationManager authenticationManager) {
         this.authenticationManager = authenticationManager;
     }
@@ -206,6 +267,10 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
         this.changePasswordSql = changePasswordSql;
     }
 
+    public void setFindAllGroupsSql(String findAllGroupsSql) {
+        this.findAllGroupsSql = findAllGroupsSql;
+    }
+
     /**
      * Optionally sets the UserCache if one is in use in the application.
      * This allows the user to be removed from the cache after updates have taken place to avoid stale data.
@@ -276,7 +341,6 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
 
 
     protected class UserExistsQuery extends MappingSqlQuery {
-
         public UserExistsQuery(DataSource ds) {
             super(ds, userExistsSql);
             declareParameter(new SqlParameter(Types.VARCHAR));
@@ -287,4 +351,56 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
             return rs.getString(1);
         }
     }
+
+    protected class AllGroupsQuery extends MappingSqlQuery {
+        public AllGroupsQuery(DataSource ds) {
+            super(ds, findAllGroupsSql);
+            compile();
+        }
+
+        protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
+            return rs.getString(1);
+        }
+    }
+
+    protected class GroupMembersQuery extends MappingSqlQuery {
+        public GroupMembersQuery(DataSource ds) {
+            super(ds, findUsersInGroupSql);
+            declareParameter(new SqlParameter(Types.VARCHAR));
+            compile();
+        }
+
+        protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
+            return rs.getString(1);
+        }
+    }
+
+    protected class InsertGroup extends SqlUpdate {
+        public InsertGroup(DataSource ds) {
+            super(ds, insertGroupSql);
+            declareParameter(new SqlParameter(Types.VARCHAR));
+            compile();
+        }
+    }
+
+    private class FindGroupIdQuery extends MappingSqlQuery {
+        public FindGroupIdQuery(DataSource ds) {
+            super(ds, findGroupIdSql);
+            declareParameter(new SqlParameter(Types.INTEGER));
+            compile();
+        }
+
+        protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
+            return Integer.valueOf(rs.getInt(1));
+        }
+    }
+
+    protected class InsertGroupAuthority extends SqlUpdate {
+        public InsertGroupAuthority(DataSource ds) {
+            super(ds, insertGroupAuthoritySql);
+            declareParameter(new SqlParameter(Types.INTEGER));
+            declareParameter(new SqlParameter(Types.VARCHAR));
+            compile();
+        }
+    }
 }

+ 11 - 4
core/src/test/java/org/springframework/security/PopulatedDatabase.java

@@ -99,6 +99,11 @@ public class PopulatedDatabase {
         template.execute("INSERT INTO acl_permission VALUES (null, 3, 'scott', 14);");
         template.execute("INSERT INTO acl_permission VALUES (null, 6, 'scott', 1);");
 
+        createGroupTables(template);
+        insertGroupData(template);
+    }
+
+    public static void createGroupTables(JdbcTemplate template) {
         //  Group tables and data
         template.execute(
                 "CREATE TABLE GROUPS(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 0) PRIMARY KEY, GROUP_NAME VARCHAR_IGNORECASE(50) NOT NULL)");
@@ -106,15 +111,17 @@ public class PopulatedDatabase {
                 "CREATE TABLE GROUP_AUTHORITIES(GROUP_ID BIGINT NOT NULL, AUTHORITY VARCHAR(50) NOT NULL, CONSTRAINT FK_GROUP_AUTHORITIES_GROUP FOREIGN KEY(GROUP_ID) REFERENCES GROUPS(ID))");
         template.execute(
                 "CREATE TABLE GROUP_MEMBERS(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 0) PRIMARY KEY, USERNAME VARCHAR(50) NOT NULL, GROUP_ID BIGINT NOT NULL, CONSTRAINT FK_GROUP_MEMBERS_GROUP FOREIGN KEY(GROUP_ID) REFERENCES GROUPS(ID))");
+    }
 
+    public static void insertGroupData(JdbcTemplate template) {
         template.execute("INSERT INTO USERS VALUES('jerry','password',TRUE)");
         template.execute("INSERT INTO USERS VALUES('tom','password',TRUE)");
 
-        template.execute("INSERT INTO GROUPS VALUES (0, 'GROUP_ZERO')");
-        template.execute("INSERT INTO GROUPS VALUES (1, 'GROUP_ONE')");
-        template.execute("INSERT INTO GROUPS VALUES (2, 'GROUP_TWO')");
+        template.execute("INSERT INTO GROUPS VALUES (0, 'GROUP_0')");
+        template.execute("INSERT INTO GROUPS VALUES (1, 'GROUP_1')");
+        template.execute("INSERT INTO GROUPS VALUES (2, 'GROUP_2')");
         // Group 3 isn't used
-        template.execute("INSERT INTO GROUPS VALUES (3, 'GROUP_THREE')");
+        template.execute("INSERT INTO GROUPS VALUES (3, 'GROUP_3')");
 
         template.execute("INSERT INTO GROUP_AUTHORITIES VALUES (0, 'ROLE_A')");
         template.execute("INSERT INTO GROUP_AUTHORITIES VALUES (1, 'ROLE_B')");

+ 44 - 1
core/src/test/java/org/springframework/security/userdetails/jdbc/JdbcUserDetailsManagerTests.java

@@ -4,6 +4,9 @@ import org.springframework.security.AccessDeniedException;
 import org.springframework.security.Authentication;
 import org.springframework.security.BadCredentialsException;
 import org.springframework.security.MockAuthenticationManager;
+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.providers.UsernamePasswordAuthenticationToken;
 import org.springframework.security.providers.dao.UserCache;
@@ -22,6 +25,8 @@ import org.junit.Test;
 
 import java.util.Map;
 import java.util.HashMap;
+import java.util.List;
+import java.util.Collections;
 
 /**
  * Tests for {@link JdbcUserDetailsManager}
@@ -43,7 +48,7 @@ public class JdbcUserDetailsManagerTests {
 
     @BeforeClass
     public static void createDataSource() {
-        dataSource = new DriverManagerDataSource("org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:tokenrepotest", "sa", "");
+        dataSource = new DriverManagerDataSource("org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:jdbcusermgrtest", "sa", "");
     }
 
     @AfterClass
@@ -71,12 +76,17 @@ public class JdbcUserDetailsManagerTests {
                 "password varchar(20) not null, enabled boolean not null)");
         template.execute("create table authorities (username varchar(20) not null, authority varchar(20) not null, " +
                 "constraint fk_authorities_users foreign key(username) references users(username))");
+        PopulatedDatabase.createGroupTables(template);
+        PopulatedDatabase.insertGroupData(template);
     }
 
     @After
     public void dropTablesAndClearContext() {
         template.execute("drop table authorities");
         template.execute("drop table users");
+        template.execute("drop table group_authorities");
+        template.execute("drop table group_members");
+        template.execute("drop table groups");
         SecurityContextHolder.clearContext();
     }
 
@@ -177,6 +187,39 @@ public class JdbcUserDetailsManagerTests {
         assertTrue(cache.getUserMap().containsKey("joe"));
     }
 
+    @Test
+    public void findAllGroupsReturnsExpectedGroupNames() {
+        List<String> groups = manager.findAllGroups();
+        assertEquals(4, groups.size());
+
+        Collections.sort(groups);
+        assertEquals("GROUP_0", groups.get(0));
+        assertEquals("GROUP_1", groups.get(1));
+        assertEquals("GROUP_2", groups.get(2));
+        assertEquals("GROUP_3", groups.get(3));
+    }
+
+    @Test
+    public void findGroupMembersReturnsCorrectData() {
+        List<String> groupMembers = manager.findUsersInGroup("GROUP_0");
+        assertEquals(1, groupMembers.size());
+        assertEquals("jerry", groupMembers.get(0));
+        groupMembers = manager.findUsersInGroup("GROUP_1");
+        assertEquals(2, groupMembers.size());
+    }
+
+    @Test
+    public void createGroupInsertsCorrectData() {
+        manager.createGroup("TEST_GROUP", AuthorityUtils.stringArrayToAuthorityArray(new String[] {"ROLE_X", "ROLE_Y"}));
+
+        List roles = template.queryForList(
+                "select ga.authority from groups g, group_authorities ga " +
+                "where ga.group_id = g.id" +
+                " and g.group_name = 'TEST_GROUP'");
+
+        assertEquals(2, roles.size());
+    }
+
     private Authentication authenticateJoe() {
         UsernamePasswordAuthenticationToken auth =
                 new UsernamePasswordAuthenticationToken("joe","password", joe.getAuthorities());