Polish RoleHierachyImpl#of

- Change to #fromHierarchy to match naming convention
- Keep existing test methods the same
- Deprecate setHierarchy and default constructor
- Add private Map constructor
- Change Adjust RoleHierarchyBuilder to use Map constructor

Issue gh-13788
This commit is contained in:
Josh Cummings 2023-12-08 10:45:26 -07:00
parent c1b3351569
commit 92be497d24
4 changed files with 112 additions and 86 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,7 +21,6 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -77,31 +76,46 @@ import org.springframework.util.Assert;
* your intentions clearer. * your intentions clearer.
* *
* @author Michael Mayr * @author Michael Mayr
* @author Josh Cummings
*/ */
public class RoleHierarchyImpl implements RoleHierarchy { public class RoleHierarchyImpl implements RoleHierarchy {
private static final Log logger = LogFactory.getLog(RoleHierarchyImpl.class); private static final Log logger = LogFactory.getLog(RoleHierarchyImpl.class);
/**
* Raw hierarchy configuration where each line represents single or multiple level
* role chain.
*/
private String roleHierarchyStringRepresentation = null;
/**
* {@code 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 (i.e. parsed
* {@link #roleHierarchyStringRepresentation} grouped by the higher role)
*/
private Map<String, Set<GrantedAuthority>> rolesReachableInOneStepMap = null;
/** /**
* {@code rolesReachableInOneOrMoreStepsMap} is a Map that under the key of a specific * {@code 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 * role name contains a set of all roles reachable from this role in 1 or more steps
* (i.e. fully resolved hierarchy from {@link #rolesReachableInOneStepMap})
*/ */
private Map<String, Set<GrantedAuthority>> rolesReachableInOneOrMoreStepsMap = null; private Map<String, Set<GrantedAuthority>> rolesReachableInOneOrMoreStepsMap = null;
/**
* @deprecated Use {@link RoleHierarchyImpl#fromHierarchy} instead
*/
@Deprecated
public RoleHierarchyImpl() {
}
private RoleHierarchyImpl(Map<String, Set<GrantedAuthority>> hierarchy) {
this.rolesReachableInOneOrMoreStepsMap = buildRolesReachableInOneOrMoreStepsMap(hierarchy);
}
/**
* Create a role hierarchy instance with the given definition, similar to the
* following:
*
* <pre>
* ROLE_A &gt; ROLE_B
* ROLE_B &gt; ROLE_AUTHENTICATED
* ROLE_AUTHENTICATED &gt; ROLE_UNAUTHENTICATED
* </pre>
* @param hierarchy the role hierarchy to use
* @return a {@link RoleHierarchyImpl} that uses the given {@code hierarchy}
*/
public static RoleHierarchyImpl fromHierarchy(String hierarchy) {
return new RoleHierarchyImpl(buildRolesReachableInOneStepMap(hierarchy));
}
/** /**
* Factory method that creates a {@link Builder} instance with the default role prefix * Factory method that creates a {@link Builder} instance with the default role prefix
* "ROLE_" * "ROLE_"
@ -125,17 +139,6 @@ public class RoleHierarchyImpl implements RoleHierarchy {
return new Builder(rolePrefix); return new Builder(rolePrefix);
} }
/**
* Create a role hierarchy instance with the given definition
* @param roleHierarchyStringRepresentation String definition of the role hierarchy.
* @return role hierarchy instance
*/
public static RoleHierarchyImpl of(String roleHierarchyStringRepresentation) {
RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
hierarchy.setHierarchy(roleHierarchyStringRepresentation);
return hierarchy;
}
/** /**
* Set the role hierarchy and pre-calculate for every role the set of all reachable * 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 * roles, i.e. all roles lower in the hierarchy of every given role. Pre-calculation
@ -143,13 +146,15 @@ public class RoleHierarchyImpl implements RoleHierarchy {
* time). During pre-calculation, cycles in role hierarchy are detected and will cause * time). During pre-calculation, cycles in role hierarchy are detected and will cause
* a <tt>CycleInRoleHierarchyException</tt> to be thrown. * a <tt>CycleInRoleHierarchyException</tt> to be thrown.
* @param roleHierarchyStringRepresentation - String definition of the role hierarchy. * @param roleHierarchyStringRepresentation - String definition of the role hierarchy.
* @deprecated Use {@link RoleHierarchyImpl#fromHierarchy} instead
*/ */
@Deprecated
public void setHierarchy(String roleHierarchyStringRepresentation) { public void setHierarchy(String roleHierarchyStringRepresentation) {
this.roleHierarchyStringRepresentation = roleHierarchyStringRepresentation;
logger.debug(LogMessage.format("setHierarchy() - The following role hierarchy was set: %s", logger.debug(LogMessage.format("setHierarchy() - The following role hierarchy was set: %s",
roleHierarchyStringRepresentation)); roleHierarchyStringRepresentation));
buildRolesReachableInOneStepMap(); Map<String, Set<GrantedAuthority>> hierarchy = buildRolesReachableInOneStepMap(
buildRolesReachableInOneOrMoreStepsMap(); roleHierarchyStringRepresentation);
this.rolesReachableInOneOrMoreStepsMap = buildRolesReachableInOneOrMoreStepsMap(hierarchy);
} }
@Override @Override
@ -193,21 +198,21 @@ public class RoleHierarchyImpl implements RoleHierarchy {
* Parse input and build the map for the roles reachable in one step: the higher role * 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. * will become a key that references a set of the reachable lower roles.
*/ */
private void buildRolesReachableInOneStepMap() { private static Map<String, Set<GrantedAuthority>> buildRolesReachableInOneStepMap(String hierarchy) {
this.rolesReachableInOneStepMap = new HashMap<>(); Map<String, Set<GrantedAuthority>> rolesReachableInOneStepMap = new HashMap<>();
for (String line : this.roleHierarchyStringRepresentation.split("\n")) { for (String line : hierarchy.split("\n")) {
// Split on > and trim excessive whitespace // Split on > and trim excessive whitespace
String[] roles = line.trim().split("\\s+>\\s+"); String[] roles = line.trim().split("\\s+>\\s+");
for (int i = 1; i < roles.length; i++) { for (int i = 1; i < roles.length; i++) {
String higherRole = roles[i - 1]; String higherRole = roles[i - 1];
GrantedAuthority lowerRole = new SimpleGrantedAuthority(roles[i]); GrantedAuthority lowerRole = new SimpleGrantedAuthority(roles[i]);
Set<GrantedAuthority> rolesReachableInOneStepSet; Set<GrantedAuthority> rolesReachableInOneStepSet;
if (!this.rolesReachableInOneStepMap.containsKey(higherRole)) { if (!rolesReachableInOneStepMap.containsKey(higherRole)) {
rolesReachableInOneStepSet = new HashSet<>(); rolesReachableInOneStepSet = new HashSet<>();
this.rolesReachableInOneStepMap.put(higherRole, rolesReachableInOneStepSet); rolesReachableInOneStepMap.put(higherRole, rolesReachableInOneStepSet);
} }
else { else {
rolesReachableInOneStepSet = this.rolesReachableInOneStepMap.get(higherRole); rolesReachableInOneStepSet = rolesReachableInOneStepMap.get(higherRole);
} }
rolesReachableInOneStepSet.add(lowerRole); rolesReachableInOneStepSet.add(lowerRole);
logger.debug(LogMessage.format( logger.debug(LogMessage.format(
@ -215,6 +220,7 @@ public class RoleHierarchyImpl implements RoleHierarchy {
higherRole, lowerRole)); higherRole, lowerRole));
} }
} }
return rolesReachableInOneStepMap;
} }
/** /**
@ -223,31 +229,31 @@ public class RoleHierarchyImpl implements RoleHierarchy {
* CycleInRoleHierarchyException if a cycle in the role hierarchy definition is * CycleInRoleHierarchyException if a cycle in the role hierarchy definition is
* detected) * detected)
*/ */
private void buildRolesReachableInOneOrMoreStepsMap() { private static Map<String, Set<GrantedAuthority>> buildRolesReachableInOneOrMoreStepsMap(
this.rolesReachableInOneOrMoreStepsMap = new HashMap<>(); Map<String, Set<GrantedAuthority>> hierarchy) {
Map<String, Set<GrantedAuthority>> rolesReachableInOneOrMoreStepsMap = new HashMap<>();
// iterate over all higher roles from rolesReachableInOneStepMap // iterate over all higher roles from rolesReachableInOneStepMap
for (String roleName : this.rolesReachableInOneStepMap.keySet()) { for (String roleName : hierarchy.keySet()) {
Set<GrantedAuthority> rolesToVisitSet = new HashSet<>(this.rolesReachableInOneStepMap.get(roleName)); Set<GrantedAuthority> rolesToVisitSet = new HashSet<>(hierarchy.get(roleName));
Set<GrantedAuthority> visitedRolesSet = new HashSet<>(); Set<GrantedAuthority> visitedRolesSet = new HashSet<>();
while (!rolesToVisitSet.isEmpty()) { while (!rolesToVisitSet.isEmpty()) {
// take a role from the rolesToVisit set // take a role from the rolesToVisit set
GrantedAuthority lowerRole = rolesToVisitSet.iterator().next(); GrantedAuthority lowerRole = rolesToVisitSet.iterator().next();
rolesToVisitSet.remove(lowerRole); rolesToVisitSet.remove(lowerRole);
if (!visitedRolesSet.add(lowerRole) if (!visitedRolesSet.add(lowerRole) || !hierarchy.containsKey(lowerRole.getAuthority())) {
|| !this.rolesReachableInOneStepMap.containsKey(lowerRole.getAuthority())) {
continue; // Already visited role or role with missing hierarchy continue; // Already visited role or role with missing hierarchy
} }
else if (roleName.equals(lowerRole.getAuthority())) { else if (roleName.equals(lowerRole.getAuthority())) {
throw new CycleInRoleHierarchyException(); throw new CycleInRoleHierarchyException();
} }
rolesToVisitSet.addAll(this.rolesReachableInOneStepMap.get(lowerRole.getAuthority())); rolesToVisitSet.addAll(hierarchy.get(lowerRole.getAuthority()));
} }
this.rolesReachableInOneOrMoreStepsMap.put(roleName, visitedRolesSet); rolesReachableInOneOrMoreStepsMap.put(roleName, visitedRolesSet);
logger.debug(LogMessage.format( logger.debug(LogMessage.format(
"buildRolesReachableInOneOrMoreStepsMap() - From role %s one can reach %s in one or more steps.", "buildRolesReachableInOneOrMoreStepsMap() - From role %s one can reach %s in one or more steps.",
roleName, visitedRolesSet)); roleName, visitedRolesSet));
} }
return rolesReachableInOneOrMoreStepsMap;
} }
/** /**
@ -261,7 +267,7 @@ public class RoleHierarchyImpl implements RoleHierarchy {
private final String rolePrefix; private final String rolePrefix;
private final Map<String, List<String>> hierarchy; private final Map<String, Set<GrantedAuthority>> hierarchy;
private Builder(String rolePrefix) { private Builder(String rolePrefix) {
this.rolePrefix = rolePrefix; this.rolePrefix = rolePrefix;
@ -285,16 +291,13 @@ public class RoleHierarchyImpl implements RoleHierarchy {
* @return a {@link RoleHierarchyImpl} * @return a {@link RoleHierarchyImpl}
*/ */
public RoleHierarchyImpl build() { public RoleHierarchyImpl build() {
String roleHierarchyRepresentation = RoleHierarchyUtils.roleHierarchyFromMap(this.hierarchy); return new RoleHierarchyImpl(this.hierarchy);
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy(roleHierarchyRepresentation);
return roleHierarchy;
} }
private Builder addHierarchy(String role, String... impliedRoles) { private Builder addHierarchy(String role, String... impliedRoles) {
List<String> withPrefix = new ArrayList<>(); Set<GrantedAuthority> withPrefix = new HashSet<>();
for (String impliedRole : impliedRoles) { for (String impliedRole : impliedRoles) {
withPrefix.add(this.rolePrefix.concat(impliedRole)); withPrefix.add(new SimpleGrantedAuthority(this.rolePrefix.concat(impliedRole)));
} }
this.hierarchy.put(this.rolePrefix.concat(role), withPrefix); this.hierarchy.put(this.rolePrefix.concat(role), withPrefix);
return this; return this;

View File

@ -40,7 +40,8 @@ public class RoleHierarchyImplTests {
public void testRoleHierarchyWithNullOrEmptyAuthorities() { public void testRoleHierarchyWithNullOrEmptyAuthorities() {
List<GrantedAuthority> authorities0 = null; List<GrantedAuthority> authorities0 = null;
List<GrantedAuthority> authorities1 = new ArrayList<>(); List<GrantedAuthority> authorities1 = new ArrayList<>();
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.of("ROLE_A > ROLE_B"); RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B");
assertThat(roleHierarchyImpl.getReachableGrantedAuthorities(authorities0)).isNotNull(); assertThat(roleHierarchyImpl.getReachableGrantedAuthorities(authorities0)).isNotNull();
assertThat(roleHierarchyImpl.getReachableGrantedAuthorities(authorities0)).isEmpty(); assertThat(roleHierarchyImpl.getReachableGrantedAuthorities(authorities0)).isEmpty();
assertThat(roleHierarchyImpl.getReachableGrantedAuthorities(authorities1)).isNotNull(); assertThat(roleHierarchyImpl.getReachableGrantedAuthorities(authorities1)).isNotNull();
@ -52,7 +53,8 @@ public class RoleHierarchyImplTests {
List<GrantedAuthority> authorities0 = AuthorityUtils.createAuthorityList("ROLE_0"); List<GrantedAuthority> authorities0 = AuthorityUtils.createAuthorityList("ROLE_0");
List<GrantedAuthority> authorities1 = AuthorityUtils.createAuthorityList("ROLE_A"); List<GrantedAuthority> authorities1 = AuthorityUtils.createAuthorityList("ROLE_A");
List<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B"); List<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B");
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.of("ROLE_A > ROLE_B"); RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B");
assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities( assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(
roleHierarchyImpl.getReachableGrantedAuthorities(authorities0), authorities0)) roleHierarchyImpl.getReachableGrantedAuthorities(authorities0), authorities0))
.isTrue(); .isTrue();
@ -70,10 +72,8 @@ public class RoleHierarchyImplTests {
List<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B", "ROLE_C"); List<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B", "ROLE_C");
List<GrantedAuthority> authorities3 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B", "ROLE_C", List<GrantedAuthority> authorities3 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B", "ROLE_C",
"ROLE_D"); "ROLE_D");
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.of(""" RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
ROLE_A > ROLE_B roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_C");
ROLE_B > ROLE_C
""");
assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities( assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(
roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2)) roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2))
.isTrue(); .isTrue();
@ -94,13 +94,8 @@ public class RoleHierarchyImplTests {
List<GrantedAuthority> authoritiesOutput3 = AuthorityUtils.createAuthorityList("ROLE_C", "ROLE_D"); List<GrantedAuthority> authoritiesOutput3 = AuthorityUtils.createAuthorityList("ROLE_C", "ROLE_D");
List<GrantedAuthority> authoritiesInput4 = AuthorityUtils.createAuthorityList("ROLE_D"); List<GrantedAuthority> authoritiesInput4 = AuthorityUtils.createAuthorityList("ROLE_D");
List<GrantedAuthority> authoritiesOutput4 = AuthorityUtils.createAuthorityList("ROLE_D"); List<GrantedAuthority> authoritiesOutput4 = AuthorityUtils.createAuthorityList("ROLE_D");
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
.of(""" roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_A > ROLE_C\nROLE_C > ROLE_D\nROLE_B > ROLE_D");
ROLE_A > ROLE_B
ROLE_A > ROLE_C
ROLE_C > ROLE_D
ROLE_B > ROLE_D
""");
assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities( assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(
roleHierarchyImpl.getReachableGrantedAuthorities(authoritiesInput1), authoritiesOutput1)) roleHierarchyImpl.getReachableGrantedAuthorities(authoritiesInput1), authoritiesOutput1))
.isTrue(); .isTrue();
@ -143,7 +138,8 @@ public class RoleHierarchyImplTests {
List<GrantedAuthority> authorities0 = HierarchicalRolesTestHelper.createAuthorityList("ROLE_0"); List<GrantedAuthority> authorities0 = HierarchicalRolesTestHelper.createAuthorityList("ROLE_0");
List<GrantedAuthority> authorities1 = HierarchicalRolesTestHelper.createAuthorityList("ROLE_A"); List<GrantedAuthority> authorities1 = HierarchicalRolesTestHelper.createAuthorityList("ROLE_A");
List<GrantedAuthority> authorities2 = HierarchicalRolesTestHelper.createAuthorityList("ROLE_A", "ROLE_B"); List<GrantedAuthority> authorities2 = HierarchicalRolesTestHelper.createAuthorityList("ROLE_A", "ROLE_B");
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.of("ROLE_A > ROLE_B"); RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B");
assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthoritiesCompareByAuthorityString( assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthoritiesCompareByAuthorityString(
roleHierarchyImpl.getReachableGrantedAuthorities(authorities0), authorities0)) roleHierarchyImpl.getReachableGrantedAuthorities(authorities0), authorities0))
.isTrue(); .isTrue();
@ -161,22 +157,12 @@ public class RoleHierarchyImplTests {
List<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList("ROLE A", "ROLE B", "ROLE>C"); List<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList("ROLE A", "ROLE B", "ROLE>C");
List<GrantedAuthority> authorities3 = AuthorityUtils.createAuthorityList("ROLE A", "ROLE B", "ROLE>C", List<GrantedAuthority> authorities3 = AuthorityUtils.createAuthorityList("ROLE A", "ROLE B", "ROLE>C",
"ROLE D"); "ROLE D");
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.of(""" RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
ROLE A > ROLE B roleHierarchyImpl.setHierarchy("ROLE A > ROLE B\nROLE B > ROLE>C");
ROLE B > ROLE>C
""");
assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities( assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(
roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2)) roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2))
.isTrue(); .isTrue();
roleHierarchyImpl.setHierarchy("ROLE A > ROLE B\nROLE B > ROLE>C\nROLE>C > ROLE D"); roleHierarchyImpl.setHierarchy("ROLE A > ROLE B\nROLE B > ROLE>C\nROLE>C > ROLE D");
assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(
roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2))
.isTrue();
roleHierarchyImpl.setHierarchy("""
ROLE A > ROLE B
ROLE B > ROLE>C
ROLE>C > ROLE D
""");
assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities( assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(
roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities3)) roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities3))
.isTrue(); .isTrue();
@ -214,12 +200,48 @@ public class RoleHierarchyImplTests {
List<GrantedAuthority> flatAuthorities = AuthorityUtils.createAuthorityList("ROLE_HIGHEST"); List<GrantedAuthority> flatAuthorities = AuthorityUtils.createAuthorityList("ROLE_HIGHEST");
List<GrantedAuthority> allAuthorities = AuthorityUtils.createAuthorityList("ROLE_HIGHEST", "ROLE_HIGHER", List<GrantedAuthority> allAuthorities = AuthorityUtils.createAuthorityList("ROLE_HIGHEST", "ROLE_HIGHER",
"ROLE_LOW", "ROLE_LOWER"); "ROLE_LOW", "ROLE_LOWER");
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
.of("ROLE_HIGHEST > ROLE_HIGHER > ROLE_LOW > ROLE_LOWER"); roleHierarchyImpl.setHierarchy("ROLE_HIGHEST > ROLE_HIGHER > ROLE_LOW > ROLE_LOWER");
assertThat(roleHierarchyImpl.getReachableGrantedAuthorities(flatAuthorities)) assertThat(roleHierarchyImpl.getReachableGrantedAuthorities(flatAuthorities))
.containsExactlyInAnyOrderElementsOf(allAuthorities); .containsExactlyInAnyOrderElementsOf(allAuthorities);
} }
@Test
public void testFromHierarchyWithTextBlock() {
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.fromHierarchy("""
ROLE_A > ROLE_B
ROLE_B > ROLE_C
ROLE_B > ROLE_D
""");
List<GrantedAuthority> flatAuthorities = AuthorityUtils.createAuthorityList("ROLE_A");
List<GrantedAuthority> allAuthorities = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B", "ROLE_C",
"ROLE_D");
assertThat(roleHierarchyImpl).isNotNull();
assertThat(roleHierarchyImpl.getReachableGrantedAuthorities(flatAuthorities))
.containsExactlyInAnyOrderElementsOf(allAuthorities);
}
@Test
public void testFromHierarchyNoCycles() {
assertThatNoException().isThrownBy(() -> RoleHierarchyImpl
.fromHierarchy("ROLE_A > ROLE_B\nROLE_A > ROLE_C\nROLE_C > ROLE_D\nROLE_B > ROLE_D"));
}
@Test
public void testFromHierarchyCycles() {
assertThatExceptionOfType(CycleInRoleHierarchyException.class)
.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy("ROLE_A > ROLE_A"));
assertThatExceptionOfType(CycleInRoleHierarchyException.class)
.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_A"));
assertThatExceptionOfType(CycleInRoleHierarchyException.class)
.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_C\nROLE_C > ROLE_A"));
assertThatExceptionOfType(CycleInRoleHierarchyException.class).isThrownBy(() -> RoleHierarchyImpl
.fromHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_C\nROLE_C > ROLE_E\nROLE_E > ROLE_D\nROLE_D > ROLE_B"));
assertThatExceptionOfType(CycleInRoleHierarchyException.class)
.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy("ROLE_C > ROLE_B\nROLE_B > ROLE_A\nROLE_A > ROLE_B"));
}
@Test @Test
public void testBuilderWithDefaultRolePrefix() { public void testBuilderWithDefaultRolePrefix() {
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.withDefaultRolePrefix() RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.withDefaultRolePrefix()

View File

@ -273,14 +273,14 @@ Xml::
[source,java,role="secondary"] [source,java,role="secondary"]
---- ----
<bean id="roleHierarchy" <bean id="roleHierarchy"
class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl"> class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl" factory-method="fromHierarchy">
<property name="hierarchy"> <constructor-arg>
<value> <value>
ROLE_ADMIN > ROLE_STAFF ROLE_ADMIN > ROLE_STAFF
ROLE_STAFF > ROLE_USER ROLE_STAFF > ROLE_USER
ROLE_USER > ROLE_GUEST ROLE_USER > ROLE_GUEST
</value> </value>
</property> </constructor-arg>
</bean> </bean>
<!-- and, if using method security also add --> <!-- and, if using method security also add -->

View File

@ -233,7 +233,7 @@ Java::
---- ----
@Bean @Bean
static RoleHierarchy roleHierarchy() { static RoleHierarchy roleHierarchy() {
return new RoleHierarchyImpl("ROLE_ADMIN > permission:read"); return RoleHierarchyImpl.fromHierarchy("ROLE_ADMIN > permission:read");
} }
---- ----
@ -244,7 +244,7 @@ Kotlin::
companion object { companion object {
@Bean @Bean
fun roleHierarchy(): RoleHierarchy { fun roleHierarchy(): RoleHierarchy {
return RoleHierarchyImpl("ROLE_ADMIN > permission:read") return RoleHierarchyImpl.fromHierarchy("ROLE_ADMIN > permission:read")
} }
} }
---- ----
@ -253,7 +253,8 @@ Xml::
+ +
[source,xml,role="secondary"] [source,xml,role="secondary"]
---- ----
<bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl"> <bean id="roleHierarchy"
class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl" factory-method="fromHierarchy">
<constructor-arg value="ROLE_ADMIN > permission:read"/> <constructor-arg value="ROLE_ADMIN > permission:read"/>
</bean> </bean>
---- ----