Skip to content

Commit bb6b8ae

Browse files
committed
Add AllAuthoritiesReactiveAuthorizationManager
Issue spring-projectsgh-17916
1 parent 096dfd4 commit bb6b8ae

File tree

7 files changed

+333
-8
lines changed

7 files changed

+333
-8
lines changed

core/src/main/java/org/springframework/security/authorization/AllAuthoritiesAuthorizationManager.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.ArrayList;
2020
import java.util.Arrays;
21+
import java.util.Collections;
2122
import java.util.List;
2223
import java.util.function.Supplier;
2324

@@ -36,7 +37,7 @@
3637
*
3738
* @author Rob Winch
3839
* @since 7.0
39-
* @see AuthoritiesAuthorizationManager
40+
* @see AuthorityAuthorizationManager
4041
*/
4142
public final class AllAuthoritiesAuthorizationManager<T> implements AuthorizationManager<T> {
4243

@@ -83,6 +84,9 @@ public AuthorityAuthorizationDecision authorize(Supplier<? extends @Nullable Aut
8384
}
8485

8586
private List<String> getGrantedAuthorities(Authentication authentication) {
87+
if (authentication == null || !authentication.isAuthenticated()) {
88+
return Collections.emptyList();
89+
}
8690
return this.roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities())
8791
.stream()
8892
.map(GrantedAuthority::getAuthority)
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* Copyright 2004-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.authorization;
18+
19+
import java.util.ArrayList;
20+
import java.util.Arrays;
21+
import java.util.Collections;
22+
import java.util.List;
23+
import java.util.function.Supplier;
24+
25+
import reactor.core.publisher.Mono;
26+
27+
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
28+
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
29+
import org.springframework.security.core.Authentication;
30+
import org.springframework.security.core.GrantedAuthority;
31+
import org.springframework.security.core.authority.AuthorityUtils;
32+
import org.springframework.util.Assert;
33+
34+
/**
35+
* A {@link ReactiveAuthorizationManager} that determines if the current user is
36+
* authorized by evaluating if the {@link Authentication} contains all the specified
37+
* authorities.
38+
*
39+
* @author Rob Winch
40+
* @since 7.0
41+
* @see AuthorityReactiveAuthorizationManager
42+
*/
43+
public final class AllAuthoritiesReactiveAuthorizationManager<T> implements ReactiveAuthorizationManager<T> {
44+
45+
private static final String ROLE_PREFIX = "ROLE_";
46+
47+
private RoleHierarchy roleHierarchy = new NullRoleHierarchy();
48+
49+
private final List<String> requiredAuthorities;
50+
51+
private final AuthorityAuthorizationDecision defaultDecision;
52+
53+
/**
54+
* Creates a new instance.
55+
* @param requiredAuthorities the authorities that are required.
56+
*/
57+
private AllAuthoritiesReactiveAuthorizationManager(String... requiredAuthorities) {
58+
Assert.notEmpty(requiredAuthorities, "requiredAuthorities cannot be empty");
59+
this.requiredAuthorities = Arrays.asList(requiredAuthorities);
60+
this.defaultDecision = new AuthorityAuthorizationDecision(false,
61+
AuthorityUtils.createAuthorityList(this.requiredAuthorities));
62+
}
63+
64+
/**
65+
* Sets the {@link RoleHierarchy} to be used. Default is {@link NullRoleHierarchy}.
66+
* Cannot be null.
67+
* @param roleHierarchy the {@link RoleHierarchy} to use
68+
*/
69+
public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
70+
Assert.notNull(roleHierarchy, "roleHierarchy cannot be null");
71+
this.roleHierarchy = roleHierarchy;
72+
}
73+
74+
/**
75+
* Determines if the current user is authorized by evaluating if the
76+
* {@link Authentication} contains any of specified authorities.
77+
* @param authentication the {@link Supplier} of the {@link Authentication} to check
78+
* @param object the object to check authorization on (not used).
79+
* @return an {@link AuthorityAuthorizationDecision}
80+
*/
81+
@Override
82+
public Mono<AuthorizationResult> authorize(Mono<Authentication> authentication, T object) {
83+
// @formatter:off
84+
return authentication
85+
.filter(Authentication::isAuthenticated)
86+
.map(this::getGrantedAuthorities)
87+
.defaultIfEmpty(Collections.emptyList())
88+
.map((authenticatedAuthorities) -> {
89+
List<String> missingAuthorities = new ArrayList<>(this.requiredAuthorities);
90+
missingAuthorities.removeIf(authenticatedAuthorities::contains);
91+
return new AuthorityAuthorizationDecision(missingAuthorities.isEmpty(),
92+
AuthorityUtils.createAuthorityList(missingAuthorities));
93+
});
94+
// @formatter:on
95+
96+
}
97+
98+
private List<String> getGrantedAuthorities(Authentication authentication) {
99+
return this.roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities())
100+
.stream()
101+
.map(GrantedAuthority::getAuthority)
102+
.toList();
103+
}
104+
105+
/**
106+
* Creates an instance of {@link AllAuthoritiesReactiveAuthorizationManager} with the
107+
* provided authorities.
108+
* @param roles the authorities to check for prefixed with "ROLE_". Each role should
109+
* not start with "ROLE_" since it is automatically prepended already.
110+
* @param <T> the type of object being authorized
111+
* @return the new instance
112+
*/
113+
public static <T> AllAuthoritiesReactiveAuthorizationManager<T> hasAllRoles(String... roles) {
114+
return hasAllPrefixedAuthorities(ROLE_PREFIX, roles);
115+
}
116+
117+
/**
118+
* Creates an instance of {@link AllAuthoritiesReactiveAuthorizationManager} with the
119+
* provided authorities.
120+
* @param prefix the prefix for <code>authorities</code>
121+
* @param authorities the authorities to check for prefixed with <code>prefix</code>
122+
* @param <T> the type of object being authorized
123+
* @return the new instance
124+
*/
125+
public static <T> AllAuthoritiesReactiveAuthorizationManager<T> hasAllPrefixedAuthorities(String prefix,
126+
String... authorities) {
127+
Assert.notNull(prefix, "rolePrefix cannot be null");
128+
Assert.notEmpty(authorities, "roles cannot be empty");
129+
Assert.noNullElements(authorities, "roles cannot contain null values");
130+
return hasAllAuthorities(toNamedRolesArray(prefix, authorities));
131+
}
132+
133+
/**
134+
* Creates an instance of {@link AllAuthoritiesReactiveAuthorizationManager} with the
135+
* provided authorities.
136+
* @param authorities the authorities to check for
137+
* @param <T> the type of object being authorized
138+
* @return the new instance
139+
*/
140+
public static <T> AllAuthoritiesReactiveAuthorizationManager<T> hasAllAuthorities(String... authorities) {
141+
Assert.notEmpty(authorities, "authorities cannot be empty");
142+
Assert.noNullElements(authorities, "authorities cannot contain null values");
143+
return new AllAuthoritiesReactiveAuthorizationManager<>(authorities);
144+
}
145+
146+
private static String[] toNamedRolesArray(String rolePrefix, String[] roles) {
147+
String[] result = new String[roles.length];
148+
for (int i = 0; i < roles.length; i++) {
149+
String role = roles[i];
150+
Assert.isTrue(rolePrefix.isEmpty() || !role.startsWith(rolePrefix), () -> role + " should not start with "
151+
+ rolePrefix + " since " + rolePrefix
152+
+ " is automatically prepended when using hasAnyRole. Consider using hasAnyAuthority instead.");
153+
result[i] = rolePrefix + role;
154+
}
155+
return result;
156+
}
157+
158+
}

core/src/main/java/org/springframework/security/authorization/AuthoritiesAuthorizationManager.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
*
3535
* @author Evgeniy Cheban
3636
* @since 6.1
37-
* @see AllAuthoritiesAuthorizationManager
3837
*/
3938
public final class AuthoritiesAuthorizationManager implements AuthorizationManager<Collection<String>> {
4039

core/src/main/java/org/springframework/security/authorization/AuthorityAuthorizationManager.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
* @param <T> the type of object being authorized.
3434
* @author Evgeniy Cheban
3535
* @since 5.5
36+
* @see AllAuthoritiesAuthorizationManager
3637
*/
3738
public final class AuthorityAuthorizationManager<T> implements AuthorizationManager<T> {
3839

core/src/test/java/org/springframework/security/authorization/AllAuthoritiesAuthorizationManagerTests.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,24 @@ void hasAllAuthortiesWhenEmptyAuthoritiesThenIllegalArgumentException() {
6767
@Test
6868
void authorizeWhenGranted() {
6969
Authentication authentication = new TestingAuthenticationToken("user", "password", ROLE_USER);
70-
AllAuthoritiesAuthorizationManager manager = AllAuthoritiesAuthorizationManager.hasAllAuthorities(ROLE_USER);
70+
AllAuthoritiesAuthorizationManager<Object> manager = AllAuthoritiesAuthorizationManager
71+
.hasAllAuthorities(ROLE_USER);
7172
assertThat(manager.authorize(() -> authentication, "").isGranted()).isTrue();
7273
}
7374

75+
@Test
76+
void authorizeWhenNotAuthenticated() {
77+
Authentication authentication = new TestingAuthenticationToken("user", "password", ROLE_USER);
78+
authentication.setAuthenticated(false);
79+
AllAuthoritiesAuthorizationManager<Object> manager = AllAuthoritiesAuthorizationManager
80+
.hasAllAuthorities(ROLE_USER);
81+
assertThat(manager.authorize(() -> authentication, "").isGranted()).isFalse();
82+
}
83+
7484
@Test
7585
void hasAllRolesAuthorizeWhenGranted() {
7686
Authentication authentication = new TestingAuthenticationToken("user", "password", ROLE_USER);
77-
AllAuthoritiesAuthorizationManager manager = AllAuthoritiesAuthorizationManager.hasAllRoles("USER");
87+
AllAuthoritiesAuthorizationManager<Object> manager = AllAuthoritiesAuthorizationManager.hasAllRoles("USER");
7888
assertThat(manager.authorize(() -> authentication, "").isGranted()).isTrue();
7989
}
8090

@@ -85,23 +95,24 @@ void hasAllPrefixedAuthoritiesAuthorizeWhenGranted() {
8595
String authority2 = "AUTHORITY2";
8696
Authentication authentication = new TestingAuthenticationToken("user", "password", prefix + authority1,
8797
prefix + authority2);
88-
AllAuthoritiesAuthorizationManager manager = AllAuthoritiesAuthorizationManager
98+
AllAuthoritiesAuthorizationManager<Object> manager = AllAuthoritiesAuthorizationManager
8999
.hasAllPrefixedAuthorities(prefix, authority1, authority2);
90100
assertThat(manager.authorize(() -> authentication, "").isGranted()).isTrue();
91101
}
92102

93103
@Test
94104
void authorizeWhenSingleMissingThenDenied() {
95105
Authentication authentication = new TestingAuthenticationToken("user", "password", ROLE_USER);
96-
AllAuthoritiesAuthorizationManager manager = AllAuthoritiesAuthorizationManager.hasAllAuthorities(ROLE_ADMIN);
106+
AllAuthoritiesAuthorizationManager<Object> manager = AllAuthoritiesAuthorizationManager
107+
.hasAllAuthorities(ROLE_ADMIN);
97108
assertThat(manager.authorize(() -> authentication, "").isGranted()).isFalse();
98109
}
99110

100111
@Test
101112
void authorizeWhenMultipleMissingOneThenDenied() {
102113
Authentication authentication = new TestingAuthenticationToken("user", "password", ROLE_USER);
103-
AllAuthoritiesAuthorizationManager manager = AllAuthoritiesAuthorizationManager.hasAllAuthorities(ROLE_ADMIN,
104-
ROLE_USER);
114+
AllAuthoritiesAuthorizationManager<Object> manager = AllAuthoritiesAuthorizationManager
115+
.hasAllAuthorities(ROLE_ADMIN, ROLE_USER);
105116
AuthorityAuthorizationDecision result = manager.authorize(() -> authentication, "");
106117
assertThat(result.isGranted()).isFalse();
107118
assertThat(result.getAuthorities()).hasSize(1);

0 commit comments

Comments
 (0)