Skip to content

Commit 92be497

Browse files
committed
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
1 parent c1b3351 commit 92be497

File tree

4 files changed

+109
-83
lines changed

4 files changed

+109
-83
lines changed

core/src/main/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyImpl.java

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,7 +21,6 @@
2121
import java.util.HashMap;
2222
import java.util.HashSet;
2323
import java.util.LinkedHashMap;
24-
import java.util.List;
2524
import java.util.Map;
2625
import java.util.Set;
2726

@@ -77,30 +76,45 @@
7776
* your intentions clearer.
7877
*
7978
* @author Michael Mayr
79+
* @author Josh Cummings
8080
*/
8181
public class RoleHierarchyImpl implements RoleHierarchy {
8282

8383
private static final Log logger = LogFactory.getLog(RoleHierarchyImpl.class);
8484

8585
/**
86-
* Raw hierarchy configuration where each line represents single or multiple level
87-
* role chain.
86+
* {@code rolesReachableInOneOrMoreStepsMap} is a Map that under the key of a specific
87+
* role name contains a set of all roles reachable from this role in 1 or more steps
8888
*/
89-
private String roleHierarchyStringRepresentation = null;
89+
private Map<String, Set<GrantedAuthority>> rolesReachableInOneOrMoreStepsMap = null;
9090

9191
/**
92-
* {@code rolesReachableInOneStepMap} is a Map that under the key of a specific role
93-
* name contains a set of all roles reachable from this role in 1 step (i.e. parsed
94-
* {@link #roleHierarchyStringRepresentation} grouped by the higher role)
92+
* @deprecated Use {@link RoleHierarchyImpl#fromHierarchy} instead
9593
*/
96-
private Map<String, Set<GrantedAuthority>> rolesReachableInOneStepMap = null;
94+
@Deprecated
95+
public RoleHierarchyImpl() {
96+
97+
}
98+
99+
private RoleHierarchyImpl(Map<String, Set<GrantedAuthority>> hierarchy) {
100+
this.rolesReachableInOneOrMoreStepsMap = buildRolesReachableInOneOrMoreStepsMap(hierarchy);
101+
}
97102

98103
/**
99-
* {@code rolesReachableInOneOrMoreStepsMap} is a Map that under the key of a specific
100-
* role name contains a set of all roles reachable from this role in 1 or more steps
101-
* (i.e. fully resolved hierarchy from {@link #rolesReachableInOneStepMap})
104+
* Create a role hierarchy instance with the given definition, similar to the
105+
* following:
106+
*
107+
* <pre>
108+
* ROLE_A &gt; ROLE_B
109+
* ROLE_B &gt; ROLE_AUTHENTICATED
110+
* ROLE_AUTHENTICATED &gt; ROLE_UNAUTHENTICATED
111+
* </pre>
112+
* @param hierarchy the role hierarchy to use
113+
* @return a {@link RoleHierarchyImpl} that uses the given {@code hierarchy}
102114
*/
103-
private Map<String, Set<GrantedAuthority>> rolesReachableInOneOrMoreStepsMap = null;
115+
public static RoleHierarchyImpl fromHierarchy(String hierarchy) {
116+
return new RoleHierarchyImpl(buildRolesReachableInOneStepMap(hierarchy));
117+
}
104118

105119
/**
106120
* Factory method that creates a {@link Builder} instance with the default role prefix
@@ -125,31 +139,22 @@ public static Builder withRolePrefix(String rolePrefix) {
125139
return new Builder(rolePrefix);
126140
}
127141

128-
/**
129-
* Create a role hierarchy instance with the given definition
130-
* @param roleHierarchyStringRepresentation String definition of the role hierarchy.
131-
* @return role hierarchy instance
132-
*/
133-
public static RoleHierarchyImpl of(String roleHierarchyStringRepresentation) {
134-
RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
135-
hierarchy.setHierarchy(roleHierarchyStringRepresentation);
136-
return hierarchy;
137-
}
138-
139142
/**
140143
* Set the role hierarchy and pre-calculate for every role the set of all reachable
141144
* roles, i.e. all roles lower in the hierarchy of every given role. Pre-calculation
142145
* is done for performance reasons (reachable roles can then be calculated in O(1)
143146
* time). During pre-calculation, cycles in role hierarchy are detected and will cause
144147
* a <tt>CycleInRoleHierarchyException</tt> to be thrown.
145148
* @param roleHierarchyStringRepresentation - String definition of the role hierarchy.
149+
* @deprecated Use {@link RoleHierarchyImpl#fromHierarchy} instead
146150
*/
151+
@Deprecated
147152
public void setHierarchy(String roleHierarchyStringRepresentation) {
148-
this.roleHierarchyStringRepresentation = roleHierarchyStringRepresentation;
149153
logger.debug(LogMessage.format("setHierarchy() - The following role hierarchy was set: %s",
150154
roleHierarchyStringRepresentation));
151-
buildRolesReachableInOneStepMap();
152-
buildRolesReachableInOneOrMoreStepsMap();
155+
Map<String, Set<GrantedAuthority>> hierarchy = buildRolesReachableInOneStepMap(
156+
roleHierarchyStringRepresentation);
157+
this.rolesReachableInOneOrMoreStepsMap = buildRolesReachableInOneOrMoreStepsMap(hierarchy);
153158
}
154159

155160
@Override
@@ -193,28 +198,29 @@ public Collection<GrantedAuthority> getReachableGrantedAuthorities(
193198
* Parse input and build the map for the roles reachable in one step: the higher role
194199
* will become a key that references a set of the reachable lower roles.
195200
*/
196-
private void buildRolesReachableInOneStepMap() {
197-
this.rolesReachableInOneStepMap = new HashMap<>();
198-
for (String line : this.roleHierarchyStringRepresentation.split("\n")) {
201+
private static Map<String, Set<GrantedAuthority>> buildRolesReachableInOneStepMap(String hierarchy) {
202+
Map<String, Set<GrantedAuthority>> rolesReachableInOneStepMap = new HashMap<>();
203+
for (String line : hierarchy.split("\n")) {
199204
// Split on > and trim excessive whitespace
200205
String[] roles = line.trim().split("\\s+>\\s+");
201206
for (int i = 1; i < roles.length; i++) {
202207
String higherRole = roles[i - 1];
203208
GrantedAuthority lowerRole = new SimpleGrantedAuthority(roles[i]);
204209
Set<GrantedAuthority> rolesReachableInOneStepSet;
205-
if (!this.rolesReachableInOneStepMap.containsKey(higherRole)) {
210+
if (!rolesReachableInOneStepMap.containsKey(higherRole)) {
206211
rolesReachableInOneStepSet = new HashSet<>();
207-
this.rolesReachableInOneStepMap.put(higherRole, rolesReachableInOneStepSet);
212+
rolesReachableInOneStepMap.put(higherRole, rolesReachableInOneStepSet);
208213
}
209214
else {
210-
rolesReachableInOneStepSet = this.rolesReachableInOneStepMap.get(higherRole);
215+
rolesReachableInOneStepSet = rolesReachableInOneStepMap.get(higherRole);
211216
}
212217
rolesReachableInOneStepSet.add(lowerRole);
213218
logger.debug(LogMessage.format(
214219
"buildRolesReachableInOneStepMap() - From role %s one can reach role %s in one step.",
215220
higherRole, lowerRole));
216221
}
217222
}
223+
return rolesReachableInOneStepMap;
218224
}
219225

220226
/**
@@ -223,31 +229,31 @@ private void buildRolesReachableInOneStepMap() {
223229
* CycleInRoleHierarchyException if a cycle in the role hierarchy definition is
224230
* detected)
225231
*/
226-
private void buildRolesReachableInOneOrMoreStepsMap() {
227-
this.rolesReachableInOneOrMoreStepsMap = new HashMap<>();
232+
private static Map<String, Set<GrantedAuthority>> buildRolesReachableInOneOrMoreStepsMap(
233+
Map<String, Set<GrantedAuthority>> hierarchy) {
234+
Map<String, Set<GrantedAuthority>> rolesReachableInOneOrMoreStepsMap = new HashMap<>();
228235
// iterate over all higher roles from rolesReachableInOneStepMap
229-
for (String roleName : this.rolesReachableInOneStepMap.keySet()) {
230-
Set<GrantedAuthority> rolesToVisitSet = new HashSet<>(this.rolesReachableInOneStepMap.get(roleName));
236+
for (String roleName : hierarchy.keySet()) {
237+
Set<GrantedAuthority> rolesToVisitSet = new HashSet<>(hierarchy.get(roleName));
231238
Set<GrantedAuthority> visitedRolesSet = new HashSet<>();
232239
while (!rolesToVisitSet.isEmpty()) {
233240
// take a role from the rolesToVisit set
234241
GrantedAuthority lowerRole = rolesToVisitSet.iterator().next();
235242
rolesToVisitSet.remove(lowerRole);
236-
if (!visitedRolesSet.add(lowerRole)
237-
|| !this.rolesReachableInOneStepMap.containsKey(lowerRole.getAuthority())) {
243+
if (!visitedRolesSet.add(lowerRole) || !hierarchy.containsKey(lowerRole.getAuthority())) {
238244
continue; // Already visited role or role with missing hierarchy
239245
}
240246
else if (roleName.equals(lowerRole.getAuthority())) {
241247
throw new CycleInRoleHierarchyException();
242248
}
243-
rolesToVisitSet.addAll(this.rolesReachableInOneStepMap.get(lowerRole.getAuthority()));
249+
rolesToVisitSet.addAll(hierarchy.get(lowerRole.getAuthority()));
244250
}
245-
this.rolesReachableInOneOrMoreStepsMap.put(roleName, visitedRolesSet);
251+
rolesReachableInOneOrMoreStepsMap.put(roleName, visitedRolesSet);
246252
logger.debug(LogMessage.format(
247253
"buildRolesReachableInOneOrMoreStepsMap() - From role %s one can reach %s in one or more steps.",
248254
roleName, visitedRolesSet));
249255
}
250-
256+
return rolesReachableInOneOrMoreStepsMap;
251257
}
252258

253259
/**
@@ -261,7 +267,7 @@ public static final class Builder {
261267

262268
private final String rolePrefix;
263269

264-
private final Map<String, List<String>> hierarchy;
270+
private final Map<String, Set<GrantedAuthority>> hierarchy;
265271

266272
private Builder(String rolePrefix) {
267273
this.rolePrefix = rolePrefix;
@@ -285,16 +291,13 @@ public ImpliedRoles role(String role) {
285291
* @return a {@link RoleHierarchyImpl}
286292
*/
287293
public RoleHierarchyImpl build() {
288-
String roleHierarchyRepresentation = RoleHierarchyUtils.roleHierarchyFromMap(this.hierarchy);
289-
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
290-
roleHierarchy.setHierarchy(roleHierarchyRepresentation);
291-
return roleHierarchy;
294+
return new RoleHierarchyImpl(this.hierarchy);
292295
}
293296

294297
private Builder addHierarchy(String role, String... impliedRoles) {
295-
List<String> withPrefix = new ArrayList<>();
298+
Set<GrantedAuthority> withPrefix = new HashSet<>();
296299
for (String impliedRole : impliedRoles) {
297-
withPrefix.add(this.rolePrefix.concat(impliedRole));
300+
withPrefix.add(new SimpleGrantedAuthority(this.rolePrefix.concat(impliedRole)));
298301
}
299302
this.hierarchy.put(this.rolePrefix.concat(role), withPrefix);
300303
return this;

core/src/test/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyImplTests.java

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ public class RoleHierarchyImplTests {
4040
public void testRoleHierarchyWithNullOrEmptyAuthorities() {
4141
List<GrantedAuthority> authorities0 = null;
4242
List<GrantedAuthority> authorities1 = new ArrayList<>();
43-
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.of("ROLE_A > ROLE_B");
43+
RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
44+
roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B");
4445
assertThat(roleHierarchyImpl.getReachableGrantedAuthorities(authorities0)).isNotNull();
4546
assertThat(roleHierarchyImpl.getReachableGrantedAuthorities(authorities0)).isEmpty();
4647
assertThat(roleHierarchyImpl.getReachableGrantedAuthorities(authorities1)).isNotNull();
@@ -52,7 +53,8 @@ public void testSimpleRoleHierarchy() {
5253
List<GrantedAuthority> authorities0 = AuthorityUtils.createAuthorityList("ROLE_0");
5354
List<GrantedAuthority> authorities1 = AuthorityUtils.createAuthorityList("ROLE_A");
5455
List<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B");
55-
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.of("ROLE_A > ROLE_B");
56+
RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
57+
roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B");
5658
assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(
5759
roleHierarchyImpl.getReachableGrantedAuthorities(authorities0), authorities0))
5860
.isTrue();
@@ -70,10 +72,8 @@ public void testTransitiveRoleHierarchies() {
7072
List<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B", "ROLE_C");
7173
List<GrantedAuthority> authorities3 = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B", "ROLE_C",
7274
"ROLE_D");
73-
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.of("""
74-
ROLE_A > ROLE_B
75-
ROLE_B > ROLE_C
76-
""");
75+
RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
76+
roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_C");
7777
assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(
7878
roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2))
7979
.isTrue();
@@ -94,13 +94,8 @@ public void testComplexRoleHierarchy() {
9494
List<GrantedAuthority> authoritiesOutput3 = AuthorityUtils.createAuthorityList("ROLE_C", "ROLE_D");
9595
List<GrantedAuthority> authoritiesInput4 = AuthorityUtils.createAuthorityList("ROLE_D");
9696
List<GrantedAuthority> authoritiesOutput4 = AuthorityUtils.createAuthorityList("ROLE_D");
97-
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl
98-
.of("""
99-
ROLE_A > ROLE_B
100-
ROLE_A > ROLE_C
101-
ROLE_C > ROLE_D
102-
ROLE_B > ROLE_D
103-
""");
97+
RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
98+
roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B\nROLE_A > ROLE_C\nROLE_C > ROLE_D\nROLE_B > ROLE_D");
10499
assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(
105100
roleHierarchyImpl.getReachableGrantedAuthorities(authoritiesInput1), authoritiesOutput1))
106101
.isTrue();
@@ -143,7 +138,8 @@ public void testSimpleRoleHierarchyWithCustomGrantedAuthorityImplementation() {
143138
List<GrantedAuthority> authorities0 = HierarchicalRolesTestHelper.createAuthorityList("ROLE_0");
144139
List<GrantedAuthority> authorities1 = HierarchicalRolesTestHelper.createAuthorityList("ROLE_A");
145140
List<GrantedAuthority> authorities2 = HierarchicalRolesTestHelper.createAuthorityList("ROLE_A", "ROLE_B");
146-
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.of("ROLE_A > ROLE_B");
141+
RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
142+
roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B");
147143
assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthoritiesCompareByAuthorityString(
148144
roleHierarchyImpl.getReachableGrantedAuthorities(authorities0), authorities0))
149145
.isTrue();
@@ -161,22 +157,12 @@ public void testWhitespaceRoleHierarchies() {
161157
List<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList("ROLE A", "ROLE B", "ROLE>C");
162158
List<GrantedAuthority> authorities3 = AuthorityUtils.createAuthorityList("ROLE A", "ROLE B", "ROLE>C",
163159
"ROLE D");
164-
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.of("""
165-
ROLE A > ROLE B
166-
ROLE B > ROLE>C
167-
""");
160+
RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
161+
roleHierarchyImpl.setHierarchy("ROLE A > ROLE B\nROLE B > ROLE>C");
168162
assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(
169163
roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2))
170164
.isTrue();
171165
roleHierarchyImpl.setHierarchy("ROLE A > ROLE B\nROLE B > ROLE>C\nROLE>C > ROLE D");
172-
assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(
173-
roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2))
174-
.isTrue();
175-
roleHierarchyImpl.setHierarchy("""
176-
ROLE A > ROLE B
177-
ROLE B > ROLE>C
178-
ROLE>C > ROLE D
179-
""");
180166
assertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(
181167
roleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities3))
182168
.isTrue();
@@ -214,12 +200,48 @@ public void singleLineLargeHierarchy() {
214200
List<GrantedAuthority> flatAuthorities = AuthorityUtils.createAuthorityList("ROLE_HIGHEST");
215201
List<GrantedAuthority> allAuthorities = AuthorityUtils.createAuthorityList("ROLE_HIGHEST", "ROLE_HIGHER",
216202
"ROLE_LOW", "ROLE_LOWER");
217-
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl
218-
.of("ROLE_HIGHEST > ROLE_HIGHER > ROLE_LOW > ROLE_LOWER");
203+
RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
204+
roleHierarchyImpl.setHierarchy("ROLE_HIGHEST > ROLE_HIGHER > ROLE_LOW > ROLE_LOWER");
205+
assertThat(roleHierarchyImpl.getReachableGrantedAuthorities(flatAuthorities))
206+
.containsExactlyInAnyOrderElementsOf(allAuthorities);
207+
}
208+
209+
@Test
210+
public void testFromHierarchyWithTextBlock() {
211+
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.fromHierarchy("""
212+
ROLE_A > ROLE_B
213+
ROLE_B > ROLE_C
214+
ROLE_B > ROLE_D
215+
""");
216+
List<GrantedAuthority> flatAuthorities = AuthorityUtils.createAuthorityList("ROLE_A");
217+
List<GrantedAuthority> allAuthorities = AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B", "ROLE_C",
218+
"ROLE_D");
219+
220+
assertThat(roleHierarchyImpl).isNotNull();
219221
assertThat(roleHierarchyImpl.getReachableGrantedAuthorities(flatAuthorities))
220222
.containsExactlyInAnyOrderElementsOf(allAuthorities);
221223
}
222224

225+
@Test
226+
public void testFromHierarchyNoCycles() {
227+
assertThatNoException().isThrownBy(() -> RoleHierarchyImpl
228+
.fromHierarchy("ROLE_A > ROLE_B\nROLE_A > ROLE_C\nROLE_C > ROLE_D\nROLE_B > ROLE_D"));
229+
}
230+
231+
@Test
232+
public void testFromHierarchyCycles() {
233+
assertThatExceptionOfType(CycleInRoleHierarchyException.class)
234+
.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy("ROLE_A > ROLE_A"));
235+
assertThatExceptionOfType(CycleInRoleHierarchyException.class)
236+
.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_A"));
237+
assertThatExceptionOfType(CycleInRoleHierarchyException.class)
238+
.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_C\nROLE_C > ROLE_A"));
239+
assertThatExceptionOfType(CycleInRoleHierarchyException.class).isThrownBy(() -> RoleHierarchyImpl
240+
.fromHierarchy("ROLE_A > ROLE_B\nROLE_B > ROLE_C\nROLE_C > ROLE_E\nROLE_E > ROLE_D\nROLE_D > ROLE_B"));
241+
assertThatExceptionOfType(CycleInRoleHierarchyException.class)
242+
.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy("ROLE_C > ROLE_B\nROLE_B > ROLE_A\nROLE_A > ROLE_B"));
243+
}
244+
223245
@Test
224246
public void testBuilderWithDefaultRolePrefix() {
225247
RoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.withDefaultRolePrefix()

docs/modules/ROOT/pages/servlet/authorization/architecture.adoc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,14 +273,14 @@ Xml::
273273
[source,java,role="secondary"]
274274
----
275275
<bean id="roleHierarchy"
276-
class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
277-
<property name="hierarchy">
276+
class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl" factory-method="fromHierarchy">
277+
<constructor-arg>
278278
<value>
279279
ROLE_ADMIN > ROLE_STAFF
280280
ROLE_STAFF > ROLE_USER
281281
ROLE_USER > ROLE_GUEST
282282
</value>
283-
</property>
283+
</constructor-arg>
284284
</bean>
285285
286286
<!-- and, if using method security also add -->

docs/modules/ROOT/pages/servlet/authorization/method-security.adoc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ Java::
233233
----
234234
@Bean
235235
static RoleHierarchy roleHierarchy() {
236-
return new RoleHierarchyImpl("ROLE_ADMIN > permission:read");
236+
return RoleHierarchyImpl.fromHierarchy("ROLE_ADMIN > permission:read");
237237
}
238238
----
239239
@@ -244,7 +244,7 @@ Kotlin::
244244
companion object {
245245
@Bean
246246
fun roleHierarchy(): RoleHierarchy {
247-
return RoleHierarchyImpl("ROLE_ADMIN > permission:read")
247+
return RoleHierarchyImpl.fromHierarchy("ROLE_ADMIN > permission:read")
248248
}
249249
}
250250
----
@@ -253,7 +253,8 @@ Xml::
253253
+
254254
[source,xml,role="secondary"]
255255
----
256-
<bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
256+
<bean id="roleHierarchy"
257+
class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl" factory-method="fromHierarchy">
257258
<constructor-arg value="ROLE_ADMIN > permission:read"/>
258259
</bean>
259260
----

0 commit comments

Comments
 (0)