Skip to content

Commit 72109e2

Browse files
igorpelejzheaux
authored andcommitted
PermitAllSupport supports AuthorizeHttpRequestsConfigurer
PermitAllSupport supports either an ExpressionUrlAuthorizationConfigurer or an AuthorizeHttpRequestsConfigurer. If none or both are configured an error message is thrown. Closes gh-10482
1 parent 78857c6 commit 72109e2

File tree

5 files changed

+157
-10
lines changed

5 files changed

+157
-10
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 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.
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.security.config.annotation.web.configurers;
1818

19+
import java.util.LinkedHashMap;
1920
import java.util.List;
2021

2122
import jakarta.servlet.http.HttpServletRequest;
@@ -46,6 +47,9 @@
4647
public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>
4748
extends AbstractHttpConfigurer<AuthorizeHttpRequestsConfigurer<H>, H> {
4849

50+
static final AuthorizationManager<RequestAuthorizationContext> permitAllAuthorizationManager = (a,
51+
o) -> new AuthorizationDecision(true);
52+
4953
private final AuthorizationManagerRequestMatcherRegistry registry;
5054

5155
/**
@@ -81,6 +85,12 @@ private AuthorizationManagerRequestMatcherRegistry addMapping(List<? extends Req
8185
return this.registry;
8286
}
8387

88+
AuthorizationManagerRequestMatcherRegistry addFirst(RequestMatcher matcher,
89+
AuthorizationManager<RequestAuthorizationContext> manager) {
90+
this.registry.addFirst(matcher, manager);
91+
return this.registry;
92+
}
93+
8494
/**
8595
* Registry for mapping a {@link RequestMatcher} to an {@link AuthorizationManager}.
8696
*
@@ -106,6 +116,19 @@ private void addMapping(RequestMatcher matcher, AuthorizationManager<RequestAuth
106116
this.mappingCount++;
107117
}
108118

119+
private void addFirst(RequestMatcher matcher, AuthorizationManager<RequestAuthorizationContext> manager) {
120+
this.unmappedMatchers = null;
121+
this.managerBuilder.mappings((m) -> {
122+
LinkedHashMap<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> reorderedMap = new LinkedHashMap<>(
123+
m.size() + 1);
124+
reorderedMap.put(matcher, manager);
125+
reorderedMap.putAll(m);
126+
m.clear();
127+
m.putAll(reorderedMap);
128+
});
129+
this.mappingCount++;
130+
}
131+
109132
private AuthorizationManager<HttpServletRequest> createAuthorizationManager() {
110133
Assert.state(this.unmappedMatchers == null,
111134
() -> "An incomplete mapping was found for " + this.unmappedMatchers
@@ -209,7 +232,7 @@ protected List<? extends RequestMatcher> getMatchers() {
209232
* customizations
210233
*/
211234
public AuthorizationManagerRequestMatcherRegistry permitAll() {
212-
return access((a, o) -> new AuthorizationDecision(true));
235+
return access(permitAllAuthorizationManager);
213236
}
214237

215238
/**

config/src/main/java/org/springframework/security/config/annotation/web/configurers/PermitAllSupport.java

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2021 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.
@@ -48,11 +48,22 @@ static void permitAll(HttpSecurityBuilder<? extends HttpSecurityBuilder<?>> http
4848
RequestMatcher... requestMatchers) {
4949
ExpressionUrlAuthorizationConfigurer<?> configurer = http
5050
.getConfigurer(ExpressionUrlAuthorizationConfigurer.class);
51-
Assert.state(configurer != null, "permitAll only works with HttpSecurity.authorizeRequests()");
51+
AuthorizeHttpRequestsConfigurer<?> httpConfigurer = http.getConfigurer(AuthorizeHttpRequestsConfigurer.class);
52+
53+
boolean oneConfigurerPresent = configurer == null ^ httpConfigurer == null;
54+
Assert.state(oneConfigurerPresent,
55+
"permitAll only works with either HttpSecurity.authorizeRequests() or HttpSecurity.authorizeHttpRequests(). "
56+
+ "Please define one or the other but not both.");
57+
5258
for (RequestMatcher matcher : requestMatchers) {
5359
if (matcher != null) {
54-
configurer.getRegistry().addMapping(0, new UrlMapping(matcher,
55-
SecurityConfig.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
60+
if (configurer != null) {
61+
configurer.getRegistry().addMapping(0, new UrlMapping(matcher,
62+
SecurityConfig.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
63+
}
64+
else {
65+
httpConfigurer.addFirst(matcher, AuthorizeHttpRequestsConfigurer.permitAllAuthorizationManager);
66+
}
5667
}
5768
}
5869
}

config/src/test/java/org/springframework/security/config/annotation/web/configurers/PermitAllSupportTests.java

+63-3
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-2021 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.
@@ -61,11 +61,32 @@ public void performWhenUsingPermitAllExactUrlRequestMatcherThenMatchesExactUrl()
6161
this.mvc.perform(getWithCsrf).andExpect(status().isFound());
6262
}
6363

64+
@Test
65+
public void performWhenUsingPermitAllExactUrlRequestMatcherThenMatchesExactUrlWithAuthorizeHttp() throws Exception {
66+
this.spring.register(PermitAllConfigAuthorizeHttpRequests.class).autowire();
67+
MockHttpServletRequestBuilder request = get("/app/xyz").contextPath("/app");
68+
this.mvc.perform(request).andExpect(status().isNotFound());
69+
MockHttpServletRequestBuilder getWithQuery = get("/app/xyz?def").contextPath("/app");
70+
this.mvc.perform(getWithQuery).andExpect(status().isFound());
71+
MockHttpServletRequestBuilder postWithQueryAndCsrf = post("/app/abc?def").with(csrf()).contextPath("/app");
72+
this.mvc.perform(postWithQueryAndCsrf).andExpect(status().isNotFound());
73+
MockHttpServletRequestBuilder getWithCsrf = get("/app/abc").with(csrf()).contextPath("/app");
74+
this.mvc.perform(getWithCsrf).andExpect(status().isFound());
75+
}
76+
6477
@Test
6578
public void configureWhenNotAuthorizeRequestsThenException() {
6679
assertThatExceptionOfType(BeanCreationException.class)
67-
.isThrownBy(() -> this.spring.register(NoAuthorizedUrlsConfig.class).autowire())
68-
.withMessageContaining("permitAll only works with HttpSecurity.authorizeRequests");
80+
.isThrownBy(() -> this.spring.register(NoAuthorizedUrlsConfig.class).autowire()).withMessageContaining(
81+
"permitAll only works with either HttpSecurity.authorizeRequests() or HttpSecurity.authorizeHttpRequests()");
82+
}
83+
84+
@Test
85+
public void configureWhenBothAuthorizeRequestsAndAuthorizeHttpRequestsThenException() {
86+
assertThatExceptionOfType(BeanCreationException.class)
87+
.isThrownBy(() -> this.spring.register(PermitAllConfigWithBothConfigs.class).autowire())
88+
.withMessageContaining(
89+
"permitAll only works with either HttpSecurity.authorizeRequests() or HttpSecurity.authorizeHttpRequests()");
6990
}
7091

7192
@EnableWebSecurity
@@ -86,6 +107,45 @@ protected void configure(HttpSecurity http) throws Exception {
86107

87108
}
88109

110+
@EnableWebSecurity
111+
static class PermitAllConfigAuthorizeHttpRequests extends WebSecurityConfigurerAdapter {
112+
113+
@Override
114+
protected void configure(HttpSecurity http) throws Exception {
115+
// @formatter:off
116+
http
117+
.authorizeHttpRequests()
118+
.anyRequest().authenticated()
119+
.and()
120+
.formLogin()
121+
.loginPage("/xyz").permitAll()
122+
.loginProcessingUrl("/abc?def").permitAll();
123+
// @formatter:on
124+
}
125+
126+
}
127+
128+
@EnableWebSecurity
129+
static class PermitAllConfigWithBothConfigs extends WebSecurityConfigurerAdapter {
130+
131+
@Override
132+
protected void configure(HttpSecurity http) throws Exception {
133+
// @formatter:off
134+
http
135+
.authorizeRequests()
136+
.anyRequest().authenticated()
137+
.and()
138+
.authorizeHttpRequests()
139+
.anyRequest().authenticated()
140+
.and()
141+
.formLogin()
142+
.loginPage("/xyz").permitAll()
143+
.loginProcessingUrl("/abc?def").permitAll();
144+
// @formatter:on
145+
}
146+
147+
}
148+
89149
@EnableWebSecurity
90150
static class NoAuthorizedUrlsConfig extends WebSecurityConfigurerAdapter {
91151

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

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 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.
@@ -18,6 +18,7 @@
1818

1919
import java.util.LinkedHashMap;
2020
import java.util.Map;
21+
import java.util.function.Consumer;
2122
import java.util.function.Supplier;
2223

2324
import jakarta.servlet.http.HttpServletRequest;
@@ -112,6 +113,20 @@ public Builder add(RequestMatcher matcher, AuthorizationManager<RequestAuthoriza
112113
return this;
113114
}
114115

116+
/**
117+
* Allows to configure the {@link RequestMatcher} to {@link AuthorizationManager}
118+
* mappings.
119+
* @param mappingsConsumer used to configure the {@link RequestMatcher} to
120+
* {@link AuthorizationManager} mappings.
121+
* @return the {@link Builder} for further customizations
122+
*/
123+
public Builder mappings(
124+
Consumer<Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>>> mappingsConsumer) {
125+
Assert.notNull(mappingsConsumer, "mappingsConsumer cannot be null");
126+
mappingsConsumer.accept(this.mappings);
127+
return this;
128+
}
129+
115130
/**
116131
* Creates a {@link RequestMatcherDelegatingAuthorizationManager} instance.
117132
* @return the {@link RequestMatcherDelegatingAuthorizationManager} instance

web/src/test/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManagerTests.java

+38
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222

2323
import org.springframework.mock.web.MockHttpServletRequest;
2424
import org.springframework.security.authentication.TestingAuthenticationToken;
25+
import org.springframework.security.authorization.AuthorityAuthorizationManager;
2526
import org.springframework.security.authorization.AuthorizationDecision;
2627
import org.springframework.security.core.Authentication;
2728
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
29+
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
2830

2931
import static org.assertj.core.api.Assertions.assertThat;
3032
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@@ -83,4 +85,40 @@ public void checkWhenMultipleMappingsConfiguredThenDelegatesMatchingManager() {
8385
assertThat(abstain).isNull();
8486
}
8587

88+
@Test
89+
public void checkWhenMultipleMappingsConfiguredWithConsumerThenDelegatesMatchingManager() {
90+
RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()
91+
.mappings((m) -> {
92+
m.put(new MvcRequestMatcher(null, "/grant"), (a, o) -> new AuthorizationDecision(true));
93+
m.put(AnyRequestMatcher.INSTANCE, AuthorityAuthorizationManager.hasRole("ADMIN"));
94+
m.put(new MvcRequestMatcher(null, "/deny"), (a, o) -> new AuthorizationDecision(false));
95+
m.put(new MvcRequestMatcher(null, "/afterAny"), (a, o) -> new AuthorizationDecision(true));
96+
}).build();
97+
98+
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
99+
100+
AuthorizationDecision grant = manager.check(authentication, new MockHttpServletRequest(null, "/grant"));
101+
assertThat(grant).isNotNull();
102+
assertThat(grant.isGranted()).isTrue();
103+
104+
AuthorizationDecision deny = manager.check(authentication, new MockHttpServletRequest(null, "/deny"));
105+
assertThat(deny).isNotNull();
106+
assertThat(deny.isGranted()).isFalse();
107+
108+
AuthorizationDecision afterAny = manager.check(authentication, new MockHttpServletRequest(null, "/afterAny"));
109+
assertThat(afterAny).isNotNull();
110+
assertThat(afterAny.isGranted()).isFalse();
111+
112+
AuthorizationDecision unmapped = manager.check(authentication, new MockHttpServletRequest(null, "/unmapped"));
113+
assertThat(unmapped).isNotNull();
114+
assertThat(unmapped.isGranted()).isFalse();
115+
}
116+
117+
@Test
118+
public void addWhenMappingsConsumerNullThenException() {
119+
assertThatIllegalArgumentException()
120+
.isThrownBy(() -> RequestMatcherDelegatingAuthorizationManager.builder().mappings(null).build())
121+
.withMessage("mappingsConsumer cannot be null");
122+
}
123+
86124
}

0 commit comments

Comments
 (0)