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
This commit is contained in:
parent
f838b7cb1d
commit
a3a9de1b9b
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -46,6 +47,9 @@ import org.springframework.util.Assert;
|
|||
public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
extends AbstractHttpConfigurer<AuthorizeHttpRequestsConfigurer<H>, H> {
|
||||
|
||||
static final AuthorizationManager<RequestAuthorizationContext> permitAllAuthorizationManager = (a,
|
||||
o) -> new AuthorizationDecision(true);
|
||||
|
||||
private final AuthorizationManagerRequestMatcherRegistry registry;
|
||||
|
||||
/**
|
||||
|
@ -81,6 +85,12 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||
return this.registry;
|
||||
}
|
||||
|
||||
AuthorizationManagerRequestMatcherRegistry addFirst(RequestMatcher matcher,
|
||||
AuthorizationManager<RequestAuthorizationContext> manager) {
|
||||
this.registry.addFirst(matcher, manager);
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registry for mapping a {@link RequestMatcher} to an {@link AuthorizationManager}.
|
||||
*
|
||||
|
@ -106,6 +116,19 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||
this.mappingCount++;
|
||||
}
|
||||
|
||||
private void addFirst(RequestMatcher matcher, AuthorizationManager<RequestAuthorizationContext> manager) {
|
||||
this.unmappedMatchers = null;
|
||||
this.managerBuilder.mappings((m) -> {
|
||||
LinkedHashMap<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> reorderedMap = new LinkedHashMap<>(
|
||||
m.size() + 1);
|
||||
reorderedMap.put(matcher, manager);
|
||||
reorderedMap.putAll(m);
|
||||
m.clear();
|
||||
m.putAll(reorderedMap);
|
||||
});
|
||||
this.mappingCount++;
|
||||
}
|
||||
|
||||
private AuthorizationManager<HttpServletRequest> createAuthorizationManager() {
|
||||
Assert.state(this.unmappedMatchers == null,
|
||||
() -> "An incomplete mapping was found for " + this.unmappedMatchers
|
||||
|
@ -209,7 +232,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||
* customizations
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry permitAll() {
|
||||
return access((a, o) -> new AuthorizationDecision(true));
|
||||
return access(permitAllAuthorizationManager);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -48,12 +48,23 @@ final class PermitAllSupport {
|
|||
RequestMatcher... requestMatchers) {
|
||||
ExpressionUrlAuthorizationConfigurer<?> configurer = http
|
||||
.getConfigurer(ExpressionUrlAuthorizationConfigurer.class);
|
||||
Assert.state(configurer != null, "permitAll only works with HttpSecurity.authorizeRequests()");
|
||||
AuthorizeHttpRequestsConfigurer<?> httpConfigurer = http.getConfigurer(AuthorizeHttpRequestsConfigurer.class);
|
||||
|
||||
boolean oneConfigurerPresent = configurer == null ^ httpConfigurer == null;
|
||||
Assert.state(oneConfigurerPresent,
|
||||
"permitAll only works with either HttpSecurity.authorizeRequests() or HttpSecurity.authorizeHttpRequests(). "
|
||||
+ "Please define one or the other but not both.");
|
||||
|
||||
for (RequestMatcher matcher : requestMatchers) {
|
||||
if (matcher != null) {
|
||||
if (configurer != null) {
|
||||
configurer.getRegistry().addMapping(0, new UrlMapping(matcher,
|
||||
SecurityConfig.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
|
||||
}
|
||||
else {
|
||||
httpConfigurer.addFirst(matcher, AuthorizeHttpRequestsConfigurer.permitAllAuthorizationManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -61,11 +61,32 @@ public class PermitAllSupportTests {
|
|||
this.mvc.perform(getWithCsrf).andExpect(status().isFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performWhenUsingPermitAllExactUrlRequestMatcherThenMatchesExactUrlWithAuthorizeHttp() throws Exception {
|
||||
this.spring.register(PermitAllConfigAuthorizeHttpRequests.class).autowire();
|
||||
MockHttpServletRequestBuilder request = get("/app/xyz").contextPath("/app");
|
||||
this.mvc.perform(request).andExpect(status().isNotFound());
|
||||
MockHttpServletRequestBuilder getWithQuery = get("/app/xyz?def").contextPath("/app");
|
||||
this.mvc.perform(getWithQuery).andExpect(status().isFound());
|
||||
MockHttpServletRequestBuilder postWithQueryAndCsrf = post("/app/abc?def").with(csrf()).contextPath("/app");
|
||||
this.mvc.perform(postWithQueryAndCsrf).andExpect(status().isNotFound());
|
||||
MockHttpServletRequestBuilder getWithCsrf = get("/app/abc").with(csrf()).contextPath("/app");
|
||||
this.mvc.perform(getWithCsrf).andExpect(status().isFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenNotAuthorizeRequestsThenException() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.spring.register(NoAuthorizedUrlsConfig.class).autowire())
|
||||
.withMessageContaining("permitAll only works with HttpSecurity.authorizeRequests");
|
||||
.isThrownBy(() -> this.spring.register(NoAuthorizedUrlsConfig.class).autowire()).withMessageContaining(
|
||||
"permitAll only works with either HttpSecurity.authorizeRequests() or HttpSecurity.authorizeHttpRequests()");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenBothAuthorizeRequestsAndAuthorizeHttpRequestsThenException() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.spring.register(PermitAllConfigWithBothConfigs.class).autowire())
|
||||
.withMessageContaining(
|
||||
"permitAll only works with either HttpSecurity.authorizeRequests() or HttpSecurity.authorizeHttpRequests()");
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
|
@ -86,6 +107,45 @@ public class PermitAllSupportTests {
|
|||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class PermitAllConfigAuthorizeHttpRequests extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeHttpRequests()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
.loginPage("/xyz").permitAll()
|
||||
.loginProcessingUrl("/abc?def").permitAll();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class PermitAllConfigWithBothConfigs extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.authorizeHttpRequests()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
.loginPage("/xyz").permitAll()
|
||||
.loginProcessingUrl("/abc?def").permitAll();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class NoAuthorizedUrlsConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -18,6 +18,7 @@ package org.springframework.security.web.access.intercept;
|
|||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -112,6 +113,20 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to configure the {@link RequestMatcher} to {@link AuthorizationManager}
|
||||
* mappings.
|
||||
* @param mappingsConsumer used to configure the {@link RequestMatcher} to
|
||||
* {@link AuthorizationManager} mappings.
|
||||
* @return the {@link Builder} for further customizations
|
||||
*/
|
||||
public Builder mappings(
|
||||
Consumer<Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>>> mappingsConsumer) {
|
||||
Assert.notNull(mappingsConsumer, "mappingsConsumer cannot be null");
|
||||
mappingsConsumer.accept(this.mappings);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link RequestMatcherDelegatingAuthorizationManager} instance.
|
||||
* @return the {@link RequestMatcherDelegatingAuthorizationManager} instance
|
||||
|
|
|
@ -22,9 +22,11 @@ import org.junit.jupiter.api.Test;
|
|||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.authorization.AuthorityAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
@ -83,4 +85,40 @@ public class RequestMatcherDelegatingAuthorizationManagerTests {
|
|||
assertThat(abstain).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkWhenMultipleMappingsConfiguredWithConsumerThenDelegatesMatchingManager() {
|
||||
RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()
|
||||
.mappings((m) -> {
|
||||
m.put(new MvcRequestMatcher(null, "/grant"), (a, o) -> new AuthorizationDecision(true));
|
||||
m.put(AnyRequestMatcher.INSTANCE, AuthorityAuthorizationManager.hasRole("ADMIN"));
|
||||
m.put(new MvcRequestMatcher(null, "/deny"), (a, o) -> new AuthorizationDecision(false));
|
||||
m.put(new MvcRequestMatcher(null, "/afterAny"), (a, o) -> new AuthorizationDecision(true));
|
||||
}).build();
|
||||
|
||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||
|
||||
AuthorizationDecision grant = manager.check(authentication, new MockHttpServletRequest(null, "/grant"));
|
||||
assertThat(grant).isNotNull();
|
||||
assertThat(grant.isGranted()).isTrue();
|
||||
|
||||
AuthorizationDecision deny = manager.check(authentication, new MockHttpServletRequest(null, "/deny"));
|
||||
assertThat(deny).isNotNull();
|
||||
assertThat(deny.isGranted()).isFalse();
|
||||
|
||||
AuthorizationDecision afterAny = manager.check(authentication, new MockHttpServletRequest(null, "/afterAny"));
|
||||
assertThat(afterAny).isNotNull();
|
||||
assertThat(afterAny.isGranted()).isFalse();
|
||||
|
||||
AuthorizationDecision unmapped = manager.check(authentication, new MockHttpServletRequest(null, "/unmapped"));
|
||||
assertThat(unmapped).isNotNull();
|
||||
assertThat(unmapped.isGranted()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addWhenMappingsConsumerNullThenException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> RequestMatcherDelegatingAuthorizationManager.builder().mappings(null).build())
|
||||
.withMessage("mappingsConsumer cannot be null");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue