1
1
/*
2
- * Copyright 2002-2019 the original author or authors.
2
+ * Copyright 2002-2023 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
21
21
import java .util .HashMap ;
22
22
import java .util .HashSet ;
23
23
import java .util .LinkedHashMap ;
24
- import java .util .List ;
25
24
import java .util .Map ;
26
25
import java .util .Set ;
27
26
77
76
* your intentions clearer.
78
77
*
79
78
* @author Michael Mayr
79
+ * @author Josh Cummings
80
80
*/
81
81
public class RoleHierarchyImpl implements RoleHierarchy {
82
82
83
83
private static final Log logger = LogFactory .getLog (RoleHierarchyImpl .class );
84
84
85
85
/**
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
88
88
*/
89
- private String roleHierarchyStringRepresentation = null ;
89
+ private Map < String , Set < GrantedAuthority >> rolesReachableInOneOrMoreStepsMap = null ;
90
90
91
91
/**
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
95
93
*/
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
+ }
97
102
98
103
/**
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 > ROLE_B
109
+ * ROLE_B > ROLE_AUTHENTICATED
110
+ * ROLE_AUTHENTICATED > ROLE_UNAUTHENTICATED
111
+ * </pre>
112
+ * @param hierarchy the role hierarchy to use
113
+ * @return a {@link RoleHierarchyImpl} that uses the given {@code hierarchy}
102
114
*/
103
- private Map <String , Set <GrantedAuthority >> rolesReachableInOneOrMoreStepsMap = null ;
115
+ public static RoleHierarchyImpl fromHierarchy (String hierarchy ) {
116
+ return new RoleHierarchyImpl (buildRolesReachableInOneStepMap (hierarchy ));
117
+ }
104
118
105
119
/**
106
120
* Factory method that creates a {@link Builder} instance with the default role prefix
@@ -125,31 +139,22 @@ public static Builder withRolePrefix(String rolePrefix) {
125
139
return new Builder (rolePrefix );
126
140
}
127
141
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
-
139
142
/**
140
143
* Set the role hierarchy and pre-calculate for every role the set of all reachable
141
144
* roles, i.e. all roles lower in the hierarchy of every given role. Pre-calculation
142
145
* is done for performance reasons (reachable roles can then be calculated in O(1)
143
146
* time). During pre-calculation, cycles in role hierarchy are detected and will cause
144
147
* a <tt>CycleInRoleHierarchyException</tt> to be thrown.
145
148
* @param roleHierarchyStringRepresentation - String definition of the role hierarchy.
149
+ * @deprecated Use {@link RoleHierarchyImpl#fromHierarchy} instead
146
150
*/
151
+ @ Deprecated
147
152
public void setHierarchy (String roleHierarchyStringRepresentation ) {
148
- this .roleHierarchyStringRepresentation = roleHierarchyStringRepresentation ;
149
153
logger .debug (LogMessage .format ("setHierarchy() - The following role hierarchy was set: %s" ,
150
154
roleHierarchyStringRepresentation ));
151
- buildRolesReachableInOneStepMap ();
152
- buildRolesReachableInOneOrMoreStepsMap ();
155
+ Map <String , Set <GrantedAuthority >> hierarchy = buildRolesReachableInOneStepMap (
156
+ roleHierarchyStringRepresentation );
157
+ this .rolesReachableInOneOrMoreStepsMap = buildRolesReachableInOneOrMoreStepsMap (hierarchy );
153
158
}
154
159
155
160
@ Override
@@ -193,28 +198,29 @@ public Collection<GrantedAuthority> getReachableGrantedAuthorities(
193
198
* Parse input and build the map for the roles reachable in one step: the higher role
194
199
* will become a key that references a set of the reachable lower roles.
195
200
*/
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 " )) {
199
204
// Split on > and trim excessive whitespace
200
205
String [] roles = line .trim ().split ("\\ s+>\\ s+" );
201
206
for (int i = 1 ; i < roles .length ; i ++) {
202
207
String higherRole = roles [i - 1 ];
203
208
GrantedAuthority lowerRole = new SimpleGrantedAuthority (roles [i ]);
204
209
Set <GrantedAuthority > rolesReachableInOneStepSet ;
205
- if (!this . rolesReachableInOneStepMap .containsKey (higherRole )) {
210
+ if (!rolesReachableInOneStepMap .containsKey (higherRole )) {
206
211
rolesReachableInOneStepSet = new HashSet <>();
207
- this . rolesReachableInOneStepMap .put (higherRole , rolesReachableInOneStepSet );
212
+ rolesReachableInOneStepMap .put (higherRole , rolesReachableInOneStepSet );
208
213
}
209
214
else {
210
- rolesReachableInOneStepSet = this . rolesReachableInOneStepMap .get (higherRole );
215
+ rolesReachableInOneStepSet = rolesReachableInOneStepMap .get (higherRole );
211
216
}
212
217
rolesReachableInOneStepSet .add (lowerRole );
213
218
logger .debug (LogMessage .format (
214
219
"buildRolesReachableInOneStepMap() - From role %s one can reach role %s in one step." ,
215
220
higherRole , lowerRole ));
216
221
}
217
222
}
223
+ return rolesReachableInOneStepMap ;
218
224
}
219
225
220
226
/**
@@ -223,31 +229,31 @@ private void buildRolesReachableInOneStepMap() {
223
229
* CycleInRoleHierarchyException if a cycle in the role hierarchy definition is
224
230
* detected)
225
231
*/
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 <>();
228
235
// 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 ));
231
238
Set <GrantedAuthority > visitedRolesSet = new HashSet <>();
232
239
while (!rolesToVisitSet .isEmpty ()) {
233
240
// take a role from the rolesToVisit set
234
241
GrantedAuthority lowerRole = rolesToVisitSet .iterator ().next ();
235
242
rolesToVisitSet .remove (lowerRole );
236
- if (!visitedRolesSet .add (lowerRole )
237
- || !this .rolesReachableInOneStepMap .containsKey (lowerRole .getAuthority ())) {
243
+ if (!visitedRolesSet .add (lowerRole ) || !hierarchy .containsKey (lowerRole .getAuthority ())) {
238
244
continue ; // Already visited role or role with missing hierarchy
239
245
}
240
246
else if (roleName .equals (lowerRole .getAuthority ())) {
241
247
throw new CycleInRoleHierarchyException ();
242
248
}
243
- rolesToVisitSet .addAll (this . rolesReachableInOneStepMap .get (lowerRole .getAuthority ()));
249
+ rolesToVisitSet .addAll (hierarchy .get (lowerRole .getAuthority ()));
244
250
}
245
- this . rolesReachableInOneOrMoreStepsMap .put (roleName , visitedRolesSet );
251
+ rolesReachableInOneOrMoreStepsMap .put (roleName , visitedRolesSet );
246
252
logger .debug (LogMessage .format (
247
253
"buildRolesReachableInOneOrMoreStepsMap() - From role %s one can reach %s in one or more steps." ,
248
254
roleName , visitedRolesSet ));
249
255
}
250
-
256
+ return rolesReachableInOneOrMoreStepsMap ;
251
257
}
252
258
253
259
/**
@@ -261,7 +267,7 @@ public static final class Builder {
261
267
262
268
private final String rolePrefix ;
263
269
264
- private final Map <String , List < String >> hierarchy ;
270
+ private final Map <String , Set < GrantedAuthority >> hierarchy ;
265
271
266
272
private Builder (String rolePrefix ) {
267
273
this .rolePrefix = rolePrefix ;
@@ -285,16 +291,13 @@ public ImpliedRoles role(String role) {
285
291
* @return a {@link RoleHierarchyImpl}
286
292
*/
287
293
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 );
292
295
}
293
296
294
297
private Builder addHierarchy (String role , String ... impliedRoles ) {
295
- List < String > withPrefix = new ArrayList <>();
298
+ Set < GrantedAuthority > withPrefix = new HashSet <>();
296
299
for (String impliedRole : impliedRoles ) {
297
- withPrefix .add (this .rolePrefix .concat (impliedRole ));
300
+ withPrefix .add (new SimpleGrantedAuthority ( this .rolePrefix .concat (impliedRole ) ));
298
301
}
299
302
this .hierarchy .put (this .rolePrefix .concat (role ), withPrefix );
300
303
return this ;
0 commit comments