diff --git a/core/src/main/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyImpl.java b/core/src/main/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyImpl.java index 191a2b9a58..b1ce26e159 100755 --- a/core/src/main/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyImpl.java +++ b/core/src/main/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyImpl.java @@ -1,198 +1,233 @@ -/* - * 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. - */ - -package org.springframework.security.access.hierarchicalroles; - - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.GrantedAuthorityImpl; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import java.util.*; - -/** - *

- * This class defines a role hierarchy for use with the UserDetailsServiceWrapper. - *

- *

- * Here is an example configuration of a role hierarchy (hint: read the ">" sign as "includes"): -

-        <property name="hierarchy">
-            <value>
-                ROLE_A > ROLE_B
-                ROLE_B > ROLE_AUTHENTICATED
-                ROLE_AUTHENTICATED > ROLE_UNAUTHENTICATED
-            </value>
-        </property>
-
-

- *

- * Explanation of the above:
- * In effect every user with ROLE_A also has ROLE_B, ROLE_AUTHENTICATED and ROLE_UNAUTHENTICATED;
- * every user with ROLE_B also has ROLE_AUTHENTICATED and ROLE_UNAUTHENTICATED;
- * every user with ROLE_AUTHENTICATED also has ROLE_UNAUTHENTICATED. - *

- *

- * Hierarchical Roles will dramatically shorten your access rules (and also make the access rules much more elegant). - *

- *

- * Consider this access rule for Spring Security's RoleVoter (background: every user that is authenticated should be - * able to log out):
- * /logout.html=ROLE_A,ROLE_B,ROLE_AUTHENTICATED
- * With hierarchical roles this can now be shortened to:
- * /logout.html=ROLE_AUTHENTICATED
- * In addition to shorter rules this will also make your access rules more readable and your intentions clearer. - *

- * - * @author Michael Mayr - * - */ -public class RoleHierarchyImpl implements RoleHierarchy { - - private static final Log logger = LogFactory.getLog(RoleHierarchyImpl.class); - - private String roleHierarchyStringRepresentation = null; - - /** - * rolesReachableInOneStepMap is a Map that under the key of a specific role name contains a set of all roles - * reachable from this role in 1 step - */ - private Map> rolesReachableInOneStepMap = null; - - /** - * rolesReachableInOneOrMoreStepsMap is a Map that under the key of a specific role name contains a set of all - * roles reachable from this role in 1 or more steps - */ - private Map> rolesReachableInOneOrMoreStepsMap = null; - - /** - * Set the role hierarchy and pre-calculate for every role the set of all reachable roles, i.e. all roles lower in - * the hierarchy of every given role. Pre-calculation is done for performance reasons (reachable roles can then be - * calculated in O(1) time). - * During pre-calculation, cycles in role hierarchy are detected and will cause a - * CycleInRoleHierarchyException to be thrown. - * - * @param roleHierarchyStringRepresentation - String definition of the role hierarchy. - */ - public void setHierarchy(String roleHierarchyStringRepresentation) { - this.roleHierarchyStringRepresentation = roleHierarchyStringRepresentation; - - logger.debug("setHierarchy() - The following role hierarchy was set: " + roleHierarchyStringRepresentation); - - buildRolesReachableInOneStepMap(); - buildRolesReachableInOneOrMoreStepsMap(); - } - - public List getReachableGrantedAuthorities(List authorities) { - if (authorities == null || authorities.isEmpty()) { - return null; - } - - Set reachableRoles = new HashSet(); - - for (GrantedAuthority authority : authorities) { - reachableRoles.add(authority); - Set additionalReachableRoles = rolesReachableInOneOrMoreStepsMap.get(authority); - if (additionalReachableRoles != null) { - reachableRoles.addAll(additionalReachableRoles); - } - } - - if (logger.isDebugEnabled()) { - logger.debug("getReachableGrantedAuthorities() - From the roles " + authorities - + " one can reach " + reachableRoles + " in zero or more steps."); - } - - List reachableRoleList = new ArrayList(reachableRoles.size()); - reachableRoleList.addAll(reachableRoles); - - return reachableRoleList; - } - - /** - * Parse input and build the map for the roles reachable in one step: the higher role will become a key that - * references a set of the reachable lower roles. - */ - private void buildRolesReachableInOneStepMap() { - Pattern pattern = Pattern.compile("(\\s*([^\\s>]+)\\s*\\>\\s*([^\\s>]+))"); - - Matcher roleHierarchyMatcher = pattern.matcher(roleHierarchyStringRepresentation); - rolesReachableInOneStepMap = new HashMap>(); - - while (roleHierarchyMatcher.find()) { - GrantedAuthority higherRole = new GrantedAuthorityImpl(roleHierarchyMatcher.group(2)); - GrantedAuthority lowerRole = new GrantedAuthorityImpl(roleHierarchyMatcher.group(3)); - Set rolesReachableInOneStepSet = null; - - if (!rolesReachableInOneStepMap.containsKey(higherRole)) { - rolesReachableInOneStepSet = new HashSet(); - rolesReachableInOneStepMap.put(higherRole, rolesReachableInOneStepSet); - } else { - rolesReachableInOneStepSet = rolesReachableInOneStepMap.get(higherRole); - } - rolesReachableInOneStepSet.add(lowerRole); - - logger.debug("buildRolesReachableInOneStepMap() - From role " - + higherRole + " one can reach role " + lowerRole + " in one step."); - } - } - - /** - * For every higher role from rolesReachableInOneStepMap store all roles that are reachable from it in the map of - * roles reachable in one or more steps. (Or throw a CycleInRoleHierarchyException if a cycle in the role - * hierarchy definition is detected) - */ - private void buildRolesReachableInOneOrMoreStepsMap() { - rolesReachableInOneOrMoreStepsMap = new HashMap>(); - // iterate over all higher roles from rolesReachableInOneStepMap - - for(GrantedAuthority role : rolesReachableInOneStepMap.keySet()) { - Set rolesToVisitSet = new HashSet(); - - if (rolesReachableInOneStepMap.containsKey(role)) { - rolesToVisitSet.addAll(rolesReachableInOneStepMap.get(role)); - } - - Set visitedRolesSet = new HashSet(); - - while (!rolesToVisitSet.isEmpty()) { - // take a role from the rolesToVisit set - GrantedAuthority aRole = (GrantedAuthority) rolesToVisitSet.iterator().next(); - rolesToVisitSet.remove(aRole); - visitedRolesSet.add(aRole); - if (rolesReachableInOneStepMap.containsKey(aRole)) { - Set newReachableRoles = rolesReachableInOneStepMap.get(aRole); - - // definition of a cycle: you can reach the role you are starting from - if (rolesToVisitSet.contains(role) || visitedRolesSet.contains(role)) { - throw new CycleInRoleHierarchyException(); - } else { - // no cycle - rolesToVisitSet.addAll(newReachableRoles); - } - } - } - rolesReachableInOneOrMoreStepsMap.put(role, visitedRolesSet); - - logger.debug("buildRolesReachableInOneOrMoreStepsMap() - From role " - + role + " one can reach " + visitedRolesSet + " in one or more steps."); - } - - } - -} +/* + * 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. + */ + +package org.springframework.security.access.hierarchicalroles; + + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.GrantedAuthorityImpl; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import java.util.*; + +/** + *

+ * This class defines a role hierarchy for use with the UserDetailsServiceWrapper. + *

+ *

+ * Here is an example configuration of a role hierarchy (hint: read the ">" sign as "includes"): +

+        <property name="hierarchy">
+            <value>
+                ROLE_A > ROLE_B
+                ROLE_B > ROLE_AUTHENTICATED
+                ROLE_AUTHENTICATED > ROLE_UNAUTHENTICATED
+            </value>
+        </property>
+
+

+ *

+ * Explanation of the above:
+ * In effect every user with ROLE_A also has ROLE_B, ROLE_AUTHENTICATED and ROLE_UNAUTHENTICATED;
+ * every user with ROLE_B also has ROLE_AUTHENTICATED and ROLE_UNAUTHENTICATED;
+ * every user with ROLE_AUTHENTICATED also has ROLE_UNAUTHENTICATED. + *

+ *

+ * Hierarchical Roles will dramatically shorten your access rules (and also make the access rules much more elegant). + *

+ *

+ * Consider this access rule for Spring Security's RoleVoter (background: every user that is authenticated should be + * able to log out):
+ * /logout.html=ROLE_A,ROLE_B,ROLE_AUTHENTICATED
+ * With hierarchical roles this can now be shortened to:
+ * /logout.html=ROLE_AUTHENTICATED
+ * In addition to shorter rules this will also make your access rules more readable and your intentions clearer. + *

+ * + * @author Michael Mayr + * + */ +public class RoleHierarchyImpl implements RoleHierarchy { + + private static final Log logger = LogFactory.getLog(RoleHierarchyImpl.class); + + private String roleHierarchyStringRepresentation = null; + + /** + * rolesReachableInOneStepMap is a Map that under the key of a specific role name contains a set of all roles + * reachable from this role in 1 step + */ + private Map> rolesReachableInOneStepMap = null; + + /** + * rolesReachableInOneOrMoreStepsMap is a Map that under the key of a specific role name contains a set of all + * roles reachable from this role in 1 or more steps + */ + private Map> rolesReachableInOneOrMoreStepsMap = null; + + /** + * Set the role hierarchy and pre-calculate for every role the set of all reachable roles, i.e. all roles lower in + * the hierarchy of every given role. Pre-calculation is done for performance reasons (reachable roles can then be + * calculated in O(1) time). + * During pre-calculation, cycles in role hierarchy are detected and will cause a + * CycleInRoleHierarchyException to be thrown. + * + * @param roleHierarchyStringRepresentation - String definition of the role hierarchy. + */ + public void setHierarchy(String roleHierarchyStringRepresentation) { + this.roleHierarchyStringRepresentation = roleHierarchyStringRepresentation; + + logger.debug("setHierarchy() - The following role hierarchy was set: " + roleHierarchyStringRepresentation); + + buildRolesReachableInOneStepMap(); + buildRolesReachableInOneOrMoreStepsMap(); + } + + public List getReachableGrantedAuthorities(List authorities) { + if (authorities == null || authorities.isEmpty()) { + return null; + } + + Set reachableRoles = new HashSet(); + + for (GrantedAuthority authority : authorities) { + addReachableRoles(reachableRoles, authority); + Set additionalReachableRoles = getRolesReachableInOneOrMoreSteps(authority); + if (additionalReachableRoles != null) { + reachableRoles.addAll(additionalReachableRoles); + } + } + + if (logger.isDebugEnabled()) { + logger.debug("getReachableGrantedAuthorities() - From the roles " + authorities + + " one can reach " + reachableRoles + " in zero or more steps."); + } + + List reachableRoleList = new ArrayList(reachableRoles.size()); + reachableRoleList.addAll(reachableRoles); + + return reachableRoleList; + } + + // SEC-863 + private void addReachableRoles(Set reachableRoles, + GrantedAuthority authority) { + + Iterator iterator = reachableRoles.iterator(); + while (iterator.hasNext()) { + GrantedAuthority testAuthority = iterator.next(); + String testKey = testAuthority.getAuthority(); + if ((testKey != null) && (testKey.equals(authority.getAuthority()))) { + return; + } + } + reachableRoles.add(authority); + } + + // SEC-863 + private Set getRolesReachableInOneOrMoreSteps( + GrantedAuthority authority) { + + if (authority.getAuthority() == null) { + return null; + } + + Iterator iterator = rolesReachableInOneOrMoreStepsMap.keySet().iterator(); + while (iterator.hasNext()) { + GrantedAuthority testAuthority = iterator.next(); + String testKey = testAuthority.getAuthority(); + if ((testKey != null) && (testKey.equals(authority.getAuthority()))) { + return rolesReachableInOneOrMoreStepsMap.get(testAuthority); + } + } + + return null; + } + + /** + * Parse input and build the map for the roles reachable in one step: the higher role will become a key that + * references a set of the reachable lower roles. + */ + private void buildRolesReachableInOneStepMap() { + Pattern pattern = Pattern.compile("(\\s*([^\\s>]+)\\s*\\>\\s*([^\\s>]+))"); + + Matcher roleHierarchyMatcher = pattern.matcher(roleHierarchyStringRepresentation); + rolesReachableInOneStepMap = new HashMap>(); + + while (roleHierarchyMatcher.find()) { + GrantedAuthority higherRole = new GrantedAuthorityImpl(roleHierarchyMatcher.group(2)); + GrantedAuthority lowerRole = new GrantedAuthorityImpl(roleHierarchyMatcher.group(3)); + Set rolesReachableInOneStepSet = null; + + if (!rolesReachableInOneStepMap.containsKey(higherRole)) { + rolesReachableInOneStepSet = new HashSet(); + rolesReachableInOneStepMap.put(higherRole, rolesReachableInOneStepSet); + } else { + rolesReachableInOneStepSet = rolesReachableInOneStepMap.get(higherRole); + } + addReachableRoles(rolesReachableInOneStepSet, lowerRole); + + logger.debug("buildRolesReachableInOneStepMap() - From role " + + higherRole + " one can reach role " + lowerRole + " in one step."); + } + } + + /** + * For every higher role from rolesReachableInOneStepMap store all roles that are reachable from it in the map of + * roles reachable in one or more steps. (Or throw a CycleInRoleHierarchyException if a cycle in the role + * hierarchy definition is detected) + */ + private void buildRolesReachableInOneOrMoreStepsMap() { + rolesReachableInOneOrMoreStepsMap = new HashMap>(); + // iterate over all higher roles from rolesReachableInOneStepMap + + for(GrantedAuthority role : rolesReachableInOneStepMap.keySet()) { + Set rolesToVisitSet = new HashSet(); + + if (rolesReachableInOneStepMap.containsKey(role)) { + rolesToVisitSet.addAll(rolesReachableInOneStepMap.get(role)); + } + + Set visitedRolesSet = new HashSet(); + + while (!rolesToVisitSet.isEmpty()) { + // take a role from the rolesToVisit set + GrantedAuthority aRole = (GrantedAuthority) rolesToVisitSet.iterator().next(); + rolesToVisitSet.remove(aRole); + addReachableRoles(visitedRolesSet, aRole); + if (rolesReachableInOneStepMap.containsKey(aRole)) { + Set newReachableRoles = rolesReachableInOneStepMap.get(aRole); + + // definition of a cycle: you can reach the role you are starting from + if (rolesToVisitSet.contains(role) || visitedRolesSet.contains(role)) { + throw new CycleInRoleHierarchyException(); + } else { + // no cycle + rolesToVisitSet.addAll(newReachableRoles); + } + } + } + rolesReachableInOneOrMoreStepsMap.put(role, visitedRolesSet); + + logger.debug("buildRolesReachableInOneOrMoreStepsMap() - From role " + + role + " one can reach " + visitedRolesSet + " in one or more steps."); + } + + } + +} diff --git a/core/src/test/java/org/springframework/security/access/hierarchicalroles/HierarchicalRolesTestHelper.java b/core/src/test/java/org/springframework/security/access/hierarchicalroles/HierarchicalRolesTestHelper.java index 21fcb9f72c..0d363f4270 100755 --- a/core/src/test/java/org/springframework/security/access/hierarchicalroles/HierarchicalRolesTestHelper.java +++ b/core/src/test/java/org/springframework/security/access/hierarchicalroles/HierarchicalRolesTestHelper.java @@ -1,40 +1,92 @@ -/* - * 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. - */ - -package org.springframework.security.access.hierarchicalroles; - -import java.util.List; - -import org.springframework.security.core.GrantedAuthority; -import org.apache.commons.collections.CollectionUtils; - -/** - * Test helper class for the hierarchical roles tests. - * - * @author Michael Mayr - */ -public abstract class HierarchicalRolesTestHelper { - - public static boolean containTheSameGrantedAuthorities(List authorities1, List authorities2) { - if (authorities1 == null && authorities2 == null) { - return true; - } - - if (authorities1 == null || authorities2 == null) { - return false; - } - return CollectionUtils.isEqualCollection(authorities1, authorities2); - } - -} +/* + * 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. + */ + +package org.springframework.security.access.hierarchicalroles; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.security.core.GrantedAuthority; +import org.apache.commons.collections.CollectionUtils; + +/** + * Test helper class for the hierarchical roles tests. + * + * @author Michael Mayr + */ +public abstract class HierarchicalRolesTestHelper { + + public static boolean containTheSameGrantedAuthorities(List authorities1, List authorities2) { + if (authorities1 == null && authorities2 == null) { + return true; + } + + if (authorities1 == null || authorities2 == null) { + return false; + } + return CollectionUtils.isEqualCollection(authorities1, authorities2); + } + + public static boolean containTheSameGrantedAuthoritiesCompareByAuthorityString(List authorities1, List authorities2) { + if (authorities1 == null && authorities2 == null) { + return true; + } + + if (authorities1 == null || authorities2 == null) { + return false; + } + return CollectionUtils.isEqualCollection(toListOfAuthorityStrings(authorities1), toListOfAuthorityStrings(authorities2)); + } + + public static List toListOfAuthorityStrings(List authorities) { + if (authorities == null) { + return null; + } + + List result = new ArrayList(authorities.size()); + for (GrantedAuthority authority : authorities) { + result.add(authority.getAuthority()); + } + return result; + } + + public static List createAuthorityList(final String... roles) { + List authorities = new ArrayList(roles.length); + + for (final String role : roles) { + // Use non GrantedAuthorityImpl (SEC-863) + authorities.add(new GrantedAuthority() { + public String getAuthority() { + return role; + } + + public int compareTo(GrantedAuthority ga) { + if (ga != null) { + String rhsRole = ga.getAuthority(); + + if (rhsRole == null) { + return -1; + } + + return role.compareTo(rhsRole); + } + return -1; + } + }); + } + + return authorities; + } + +} diff --git a/core/src/test/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyImplTests.java b/core/src/test/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyImplTests.java index 266e7a5987..060e02baeb 100755 --- a/core/src/test/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyImplTests.java +++ b/core/src/test/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyImplTests.java @@ -1,114 +1,128 @@ -/* -* 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. -*/ - -package org.springframework.security.access.hierarchicalroles; - -import java.util.List; - -import junit.framework.TestCase; - -import org.springframework.security.access.hierarchicalroles.CycleInRoleHierarchyException; -import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; - -/** - * Tests for {@link RoleHierarchyImpl}. - * - * @author Michael Mayr - */ -public class RoleHierarchyImplTests extends TestCase { - - public void testSimpleRoleHierarchy() { - - List authorities0 = AuthorityUtils.createAuthorityList("ROLE_0"); - List authorities1 = AuthorityUtils.createAuthorityList("ROLE_A"); - List authorities2 = AuthorityUtils.createAuthorityList("ROLE_A","ROLE_B"); - - RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl(); - roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B"); - - assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authorities0), authorities0)); - assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2)); - assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authorities2), authorities2)); - } - - public void testTransitiveRoleHierarchies() { - List authorities1 = AuthorityUtils.createAuthorityList("ROLE_A"); - List authorities2 = AuthorityUtils.createAuthorityList("ROLE_A","ROLE_B","ROLE_C"); - List authorities3 = AuthorityUtils.createAuthorityList("ROLE_A","ROLE_B","ROLE_C","ROLE_D"); - - RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl(); - - roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_C"); - assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2)); - - roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_C\nROLE_C > ROLE_D"); - assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities3)); - } - - public void testComplexRoleHierarchy() { - List authoritiesInput1 = AuthorityUtils.createAuthorityList("ROLE_A"); - List authoritiesOutput1 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B","ROLE_C", "ROLE_D"); - List authoritiesInput2 = AuthorityUtils.createAuthorityList("ROLE_B"); - List authoritiesOutput2 = AuthorityUtils.createAuthorityList("ROLE_B","ROLE_D"); - List authoritiesInput3 = AuthorityUtils.createAuthorityList("ROLE_C"); - List authoritiesOutput3 = AuthorityUtils.createAuthorityList("ROLE_C","ROLE_D"); - List authoritiesInput4 = AuthorityUtils.createAuthorityList("ROLE_D"); - List authoritiesOutput4 = AuthorityUtils.createAuthorityList("ROLE_D"); - - RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl(); - roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_A > ROLE_C\nROLE_C > ROLE_D\nROLE_B > ROLE_D"); - - assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authoritiesInput1), authoritiesOutput1)); - assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authoritiesInput2), authoritiesOutput2)); - assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authoritiesInput3), authoritiesOutput3)); - assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authoritiesInput4), authoritiesOutput4)); - } - - public void testCyclesInRoleHierarchy() { - RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl(); - - try { - roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_A"); - fail("Cycle in role hierarchy was not detected!"); - } catch (CycleInRoleHierarchyException e) {} - - try { - roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_A"); - fail("Cycle in role hierarchy was not detected!"); - } catch (CycleInRoleHierarchyException e) {} - - try { - roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_C\nROLE_C > ROLE_A"); - fail("Cycle in role hierarchy was not detected!"); - } catch (CycleInRoleHierarchyException e) {} - - try { - roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_C\nROLE_C > ROLE_E\nROLE_E > ROLE_D\nROLE_D > ROLE_B"); - fail("Cycle in role hierarchy was not detected!"); - } catch (CycleInRoleHierarchyException e) {} - } - - public void testNoCyclesInRoleHierarchy() { - RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl(); - - try { - roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_A > ROLE_C\nROLE_C > ROLE_D\nROLE_B > ROLE_D"); - } catch (CycleInRoleHierarchyException e) { - fail("A cycle in role hierarchy was incorrectly detected!"); - } - } - -} +/* +* 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. +*/ + +package org.springframework.security.access.hierarchicalroles; + +import java.util.List; + +import junit.framework.TestCase; + +import org.springframework.security.access.hierarchicalroles.CycleInRoleHierarchyException; +import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; + +/** + * Tests for {@link RoleHierarchyImpl}. + * + * @author Michael Mayr + */ +public class RoleHierarchyImplTests extends TestCase { + + public void testSimpleRoleHierarchy() { + + List authorities0 = AuthorityUtils.createAuthorityList("ROLE_0"); + List authorities1 = AuthorityUtils.createAuthorityList("ROLE_A"); + List authorities2 = AuthorityUtils.createAuthorityList("ROLE_A","ROLE_B"); + + RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl(); + roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B"); + + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authorities0), authorities0)); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2)); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authorities2), authorities2)); + } + + public void testTransitiveRoleHierarchies() { + List authorities1 = AuthorityUtils.createAuthorityList("ROLE_A"); + List authorities2 = AuthorityUtils.createAuthorityList("ROLE_A","ROLE_B","ROLE_C"); + List authorities3 = AuthorityUtils.createAuthorityList("ROLE_A","ROLE_B","ROLE_C","ROLE_D"); + + RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl(); + + roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_C"); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2)); + + roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_C\nROLE_C > ROLE_D"); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities3)); + } + + public void testComplexRoleHierarchy() { + List authoritiesInput1 = AuthorityUtils.createAuthorityList("ROLE_A"); + List authoritiesOutput1 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B","ROLE_C", "ROLE_D"); + List authoritiesInput2 = AuthorityUtils.createAuthorityList("ROLE_B"); + List authoritiesOutput2 = AuthorityUtils.createAuthorityList("ROLE_B","ROLE_D"); + List authoritiesInput3 = AuthorityUtils.createAuthorityList("ROLE_C"); + List authoritiesOutput3 = AuthorityUtils.createAuthorityList("ROLE_C","ROLE_D"); + List authoritiesInput4 = AuthorityUtils.createAuthorityList("ROLE_D"); + List authoritiesOutput4 = AuthorityUtils.createAuthorityList("ROLE_D"); + + RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl(); + roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_A > ROLE_C\nROLE_C > ROLE_D\nROLE_B > ROLE_D"); + + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authoritiesInput1), authoritiesOutput1)); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authoritiesInput2), authoritiesOutput2)); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authoritiesInput3), authoritiesOutput3)); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(roleHierarchyImpl.getReachableGrantedAuthorities(authoritiesInput4), authoritiesOutput4)); + } + + public void testCyclesInRoleHierarchy() { + RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl(); + + try { + roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_A"); + fail("Cycle in role hierarchy was not detected!"); + } catch (CycleInRoleHierarchyException e) {} + + try { + roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_A"); + fail("Cycle in role hierarchy was not detected!"); + } catch (CycleInRoleHierarchyException e) {} + + try { + roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_C\nROLE_C > ROLE_A"); + fail("Cycle in role hierarchy was not detected!"); + } catch (CycleInRoleHierarchyException e) {} + + try { + roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_C\nROLE_C > ROLE_E\nROLE_E > ROLE_D\nROLE_D > ROLE_B"); + fail("Cycle in role hierarchy was not detected!"); + } catch (CycleInRoleHierarchyException e) {} + } + + public void testNoCyclesInRoleHierarchy() { + RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl(); + + try { + roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_A > ROLE_C\nROLE_C > ROLE_D\nROLE_B > ROLE_D"); + } catch (CycleInRoleHierarchyException e) { + fail("A cycle in role hierarchy was incorrectly detected!"); + } + } + + // SEC-863 + public void testSimpleRoleHierarchyWithCustomGrantedAuthorityImplementation() { + + List authorities0 = HierarchicalRolesTestHelper.createAuthorityList("ROLE_0"); + List authorities1 = HierarchicalRolesTestHelper.createAuthorityList("ROLE_A"); + List authorities2 = HierarchicalRolesTestHelper.createAuthorityList("ROLE_A","ROLE_B"); + + RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl(); + roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B"); + + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthoritiesCompareByAuthorityString(roleHierarchyImpl.getReachableGrantedAuthorities(authorities0), authorities0)); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthoritiesCompareByAuthorityString(roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2)); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthoritiesCompareByAuthorityString(roleHierarchyImpl.getReachableGrantedAuthorities(authorities2), authorities2)); + } +} diff --git a/core/src/test/java/org/springframework/security/access/hierarchicalroles/TestHelperTests.java b/core/src/test/java/org/springframework/security/access/hierarchicalroles/TestHelperTests.java index 4e3087ccc7..fbe292b7e6 100755 --- a/core/src/test/java/org/springframework/security/access/hierarchicalroles/TestHelperTests.java +++ b/core/src/test/java/org/springframework/security/access/hierarchicalroles/TestHelperTests.java @@ -1,55 +1,143 @@ -/* - * 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. - */ - -package org.springframework.security.access.hierarchicalroles; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.List; - -import org.junit.Test; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; - -/** - * Tests for {@link HierarchicalRolesTestHelper}. - * - * @author Michael Mayr - */ -public class TestHelperTests { - - @Test - public void testContainTheSameGrantedAuthorities() { - List authorities1 = AuthorityUtils.createAuthorityList("ROLE_A","ROLE_B"); - List authorities2 = AuthorityUtils.createAuthorityList("ROLE_B","ROLE_A"); - List authorities3 = AuthorityUtils.createAuthorityList("ROLE_A","ROLE_C"); - List authorities4 = AuthorityUtils.createAuthorityList("ROLE_A"); - List authorities5 = AuthorityUtils.createAuthorityList("ROLE_A","ROLE_A"); - - assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(null, null)); - assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities1)); - assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities2)); - assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities2, authorities1)); - - assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(null, authorities1)); - assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, null)); - assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities3)); - assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities3, authorities1)); - assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities4)); - assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities4, authorities1)); - assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities4, authorities5)); - } - -} +/* + * 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. + */ + +package org.springframework.security.access.hierarchicalroles; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.collections.CollectionUtils; +import org.junit.Test; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; + +/** + * Tests for {@link HierarchicalRolesTestHelper}. + * + * @author Michael Mayr + */ +public class TestHelperTests { + + @Test + public void testContainTheSameGrantedAuthorities() { + List authorities1 = AuthorityUtils.createAuthorityList("ROLE_A","ROLE_B"); + List authorities2 = AuthorityUtils.createAuthorityList("ROLE_B","ROLE_A"); + List authorities3 = AuthorityUtils.createAuthorityList("ROLE_A","ROLE_C"); + List authorities4 = AuthorityUtils.createAuthorityList("ROLE_A"); + List authorities5 = AuthorityUtils.createAuthorityList("ROLE_A","ROLE_A"); + + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(null, null)); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities1)); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities2)); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities2, authorities1)); + + assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(null, authorities1)); + assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, null)); + assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities3)); + assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities3, authorities1)); + assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities4)); + assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities4, authorities1)); + assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities4, authorities5)); + } + + // SEC-863 + @Test + public void testToListOfAuthorityStrings() { + List authorities1 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B"); + List authorities2 = AuthorityUtils.createAuthorityList("ROLE_B", "ROLE_A"); + List authorities3 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_C"); + List authorities4 = AuthorityUtils.createAuthorityList("ROLE_A"); + List authorities5 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_A"); + + List authoritiesStrings1 = new ArrayList(); + authoritiesStrings1.add("ROLE_A"); + authoritiesStrings1.add("ROLE_B"); + + List authoritiesStrings2 = new ArrayList(); + authoritiesStrings2.add("ROLE_B"); + authoritiesStrings2.add("ROLE_A"); + + List authoritiesStrings3 = new ArrayList(); + authoritiesStrings3.add("ROLE_A"); + authoritiesStrings3.add("ROLE_C"); + + List authoritiesStrings4 = new ArrayList(); + authoritiesStrings4.add("ROLE_A"); + + List authoritiesStrings5 = new ArrayList(); + authoritiesStrings5.add("ROLE_A"); + authoritiesStrings5.add("ROLE_A"); + + assertTrue(CollectionUtils.isEqualCollection( + HierarchicalRolesTestHelper.toListOfAuthorityStrings(authorities1), authoritiesStrings1)); + + assertTrue(CollectionUtils.isEqualCollection( + HierarchicalRolesTestHelper.toListOfAuthorityStrings(authorities2), authoritiesStrings2)); + + assertTrue(CollectionUtils.isEqualCollection( + HierarchicalRolesTestHelper.toListOfAuthorityStrings(authorities3), authoritiesStrings3)); + + assertTrue(CollectionUtils.isEqualCollection( + HierarchicalRolesTestHelper.toListOfAuthorityStrings(authorities4), authoritiesStrings4)); + + assertTrue(CollectionUtils.isEqualCollection( + HierarchicalRolesTestHelper.toListOfAuthorityStrings(authorities5), authoritiesStrings5)); + } + + // SEC-863 + @Test + public void testContainTheSameGrantedAuthoritiesCompareByAuthorityString() { + List authorities1 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B"); + List authorities2 = AuthorityUtils.createAuthorityList("ROLE_B", "ROLE_A"); + List authorities3 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_C"); + List authorities4 = AuthorityUtils.createAuthorityList("ROLE_A"); + List authorities5 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_A"); + + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(null, null)); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities1)); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities2)); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities2, authorities1)); + + assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(null, authorities1)); + assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, null)); + assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities3)); + assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities3, authorities1)); + assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities4)); + assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities4, authorities1)); + assertFalse(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities4, authorities5)); + } + + // SEC-863 + @Test + public void testContainTheSameGrantedAuthoritiesCompareByAuthorityStringWithAuthorityLists() { + List authorities1 = HierarchicalRolesTestHelper.createAuthorityList("ROLE_A", "ROLE_B"); + List authorities2 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B"); + assertTrue(HierarchicalRolesTestHelper.containTheSameGrantedAuthoritiesCompareByAuthorityString(authorities1, authorities2)); + } + + // SEC-863 + @Test + public void testCreateAuthorityList() { + List authorities1 = HierarchicalRolesTestHelper.createAuthorityList("ROLE_A"); + assertEquals(authorities1.size(), 1); + assertEquals("ROLE_A", authorities1.get(0).getAuthority()); + + List authorities2 = HierarchicalRolesTestHelper.createAuthorityList("ROLE_A", "ROLE_C"); + assertEquals(authorities2.size(), 2); + assertEquals("ROLE_A", authorities2.get(0).getAuthority()); + assertEquals("ROLE_C", authorities2.get(1).getAuthority()); + } +}