Skip to content

Commit 5264269

Browse files
committed
Simplify RequestMatcherDelegatingAuthorizationManager.Builder matcher registration
Closes gh-11624
1 parent 42cd19f commit 5264269

File tree

3 files changed

+390
-3
lines changed

3 files changed

+390
-3
lines changed

core/src/test/java/org/springframework/security/authentication/TestAuthentication.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 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.
@@ -17,15 +17,23 @@
1717
package org.springframework.security.authentication;
1818

1919
import org.springframework.security.core.Authentication;
20+
import org.springframework.security.core.authority.AuthorityUtils;
2021
import org.springframework.security.core.userdetails.PasswordEncodedUser;
2122
import org.springframework.security.core.userdetails.UserDetails;
2223

2324
/**
2425
* @author Rob Winch
26+
* @author Evgeniy Cheban
2527
* @since 5.0
2628
*/
2729
public class TestAuthentication extends PasswordEncodedUser {
2830

31+
private static final Authentication ANONYMOUS = new AnonymousAuthenticationToken("key", "anonymous",
32+
AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
33+
34+
private static final RememberMeAuthenticationToken REMEMBER_ME = new RememberMeAuthenticationToken("key", "user",
35+
AuthorityUtils.createAuthorityList("ROLE_USER"));
36+
2937
public static Authentication authenticatedAdmin() {
3038
return autheticated(admin());
3139
}
@@ -38,4 +46,12 @@ public static Authentication autheticated(UserDetails user) {
3846
return UsernamePasswordAuthenticationToken.authenticated(user, null, user.getAuthorities());
3947
}
4048

49+
public static Authentication anonymousUser() {
50+
return ANONYMOUS;
51+
}
52+
53+
public static Authentication rememberMeUser() {
54+
return REMEMBER_ME;
55+
}
56+
4157
}

web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java

Lines changed: 147 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 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.
@@ -26,9 +26,12 @@
2626
import org.apache.commons.logging.LogFactory;
2727

2828
import org.springframework.core.log.LogMessage;
29+
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
30+
import org.springframework.security.authorization.AuthorityAuthorizationManager;
2931
import org.springframework.security.authorization.AuthorizationDecision;
3032
import org.springframework.security.authorization.AuthorizationManager;
3133
import org.springframework.security.core.Authentication;
34+
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
3235
import org.springframework.security.web.util.matcher.RequestMatcher;
3336
import org.springframework.security.web.util.matcher.RequestMatcher.MatchResult;
3437
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
@@ -102,6 +105,8 @@ public static Builder builder() {
102105
*/
103106
public static final class Builder {
104107

108+
private boolean anyRequestConfigured;
109+
105110
private final List<RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>>> mappings = new ArrayList<>();
106111

107112
/**
@@ -111,6 +116,7 @@ public static final class Builder {
111116
* @return the {@link Builder} for further customizations
112117
*/
113118
public Builder add(RequestMatcher matcher, AuthorizationManager<RequestAuthorizationContext> manager) {
119+
Assert.state(!this.anyRequestConfigured, "Can't add mappings after anyRequest");
114120
Assert.notNull(matcher, "matcher cannot be null");
115121
Assert.notNull(manager, "manager cannot be null");
116122
this.mappings.add(new RequestMatcherEntry<>(matcher, manager));
@@ -127,11 +133,34 @@ public Builder add(RequestMatcher matcher, AuthorizationManager<RequestAuthoriza
127133
*/
128134
public Builder mappings(
129135
Consumer<List<RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>>>> mappingsConsumer) {
136+
Assert.state(!this.anyRequestConfigured, "Can't configure mappings after anyRequest");
130137
Assert.notNull(mappingsConsumer, "mappingsConsumer cannot be null");
131138
mappingsConsumer.accept(this.mappings);
132139
return this;
133140
}
134141

142+
/**
143+
* Maps any request.
144+
* @return the {@link AuthorizedUrl} for further customizations
145+
* @since 6.2
146+
*/
147+
public AuthorizedUrl anyRequest() {
148+
Assert.state(!this.anyRequestConfigured, "Can't configure anyRequest after itself");
149+
this.anyRequestConfigured = true;
150+
return new AuthorizedUrl(AnyRequestMatcher.INSTANCE);
151+
}
152+
153+
/**
154+
* Maps {@link RequestMatcher}s to {@link AuthorizationManager}.
155+
* @param matchers the {@link RequestMatcher}s to map
156+
* @return the {@link AuthorizedUrl} for further customizations
157+
* @since 6.2
158+
*/
159+
public AuthorizedUrl requestMatchers(RequestMatcher... matchers) {
160+
Assert.state(!this.anyRequestConfigured, "Can't configure requestMatchers after anyRequest");
161+
return new AuthorizedUrl(matchers);
162+
}
163+
135164
/**
136165
* Creates a {@link RequestMatcherDelegatingAuthorizationManager} instance.
137166
* @return the {@link RequestMatcherDelegatingAuthorizationManager} instance
@@ -140,6 +169,123 @@ public RequestMatcherDelegatingAuthorizationManager build() {
140169
return new RequestMatcherDelegatingAuthorizationManager(this.mappings);
141170
}
142171

172+
/**
173+
* An object that allows configuring the {@link AuthorizationManager} for
174+
* {@link RequestMatcher}s.
175+
*
176+
* @author Evgeniy Cheban
177+
* @since 6.2
178+
*/
179+
public final class AuthorizedUrl {
180+
181+
private final List<RequestMatcher> matchers;
182+
183+
private AuthorizedUrl(RequestMatcher... matchers) {
184+
this(List.of(matchers));
185+
}
186+
187+
private AuthorizedUrl(List<RequestMatcher> matchers) {
188+
this.matchers = matchers;
189+
}
190+
191+
/**
192+
* Specify that URLs are allowed by anyone.
193+
* @return the {@link Builder} for further customizations
194+
*/
195+
public Builder permitAll() {
196+
return access((a, o) -> new AuthorizationDecision(true));
197+
}
198+
199+
/**
200+
* Specify that URLs are not allowed by anyone.
201+
* @return the {@link Builder} for further customizations
202+
*/
203+
public Builder denyAll() {
204+
return access((a, o) -> new AuthorizationDecision(false));
205+
}
206+
207+
/**
208+
* Specify that URLs are allowed by any authenticated user.
209+
* @return the {@link Builder} for further customizations
210+
*/
211+
public Builder authenticated() {
212+
return access(AuthenticatedAuthorizationManager.authenticated());
213+
}
214+
215+
/**
216+
* Specify that URLs are allowed by users who have authenticated and were not
217+
* "remembered".
218+
* @return the {@link Builder} for further customization
219+
*/
220+
public Builder fullyAuthenticated() {
221+
return access(AuthenticatedAuthorizationManager.fullyAuthenticated());
222+
}
223+
224+
/**
225+
* Specify that URLs are allowed by users that have been remembered.
226+
* @return the {@link Builder} for further customization
227+
*/
228+
public Builder rememberMe() {
229+
return access(AuthenticatedAuthorizationManager.rememberMe());
230+
}
231+
232+
/**
233+
* Specify that URLs are allowed by anonymous users.
234+
* @return the {@link Builder} for further customization
235+
*/
236+
public Builder anonymous() {
237+
return access(AuthenticatedAuthorizationManager.anonymous());
238+
}
239+
240+
/**
241+
* Specifies a user requires a role.
242+
* @param role the role that should be required which is prepended with ROLE_
243+
* automatically (i.e. USER, ADMIN, etc). It should not start with ROLE_
244+
* @return {@link Builder} for further customizations
245+
*/
246+
public Builder hasRole(String role) {
247+
return access(AuthorityAuthorizationManager.hasRole(role));
248+
}
249+
250+
/**
251+
* Specifies that a user requires one of many roles.
252+
* @param roles the roles that the user should have at least one of (i.e.
253+
* ADMIN, USER, etc). Each role should not start with ROLE_ since it is
254+
* automatically prepended already
255+
* @return the {@link Builder} for further customizations
256+
*/
257+
public Builder hasAnyRole(String... roles) {
258+
return access(AuthorityAuthorizationManager.hasAnyRole(roles));
259+
}
260+
261+
/**
262+
* Specifies a user requires an authority.
263+
* @param authority the authority that should be required
264+
* @return the {@link Builder} for further customizations
265+
*/
266+
public Builder hasAuthority(String authority) {
267+
return access(AuthorityAuthorizationManager.hasAuthority(authority));
268+
}
269+
270+
/**
271+
* Specifies that a user requires one of many authorities.
272+
* @param authorities the authorities that the user should have at least one
273+
* of (i.e. ROLE_USER, ROLE_ADMIN, etc)
274+
* @return the {@link Builder} for further customizations
275+
*/
276+
public Builder hasAnyAuthority(String... authorities) {
277+
return access(AuthorityAuthorizationManager.hasAnyAuthority(authorities));
278+
}
279+
280+
private Builder access(AuthorizationManager<RequestAuthorizationContext> manager) {
281+
for (RequestMatcher matcher : this.matchers) {
282+
Builder.this.mappings.add(new RequestMatcherEntry<>(matcher, manager));
283+
}
284+
return Builder.this;
285+
}
286+
287+
}
288+
143289
}
144290

145291
}

0 commit comments

Comments
 (0)