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
78857c62f4
commit
72109e2921
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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;
|
package org.springframework.security.config.annotation.web.configurers;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
@ -46,6 +47,9 @@ import org.springframework.util.Assert;
|
||||||
public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>
|
public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
extends AbstractHttpConfigurer<AuthorizeHttpRequestsConfigurer<H>, H> {
|
extends AbstractHttpConfigurer<AuthorizeHttpRequestsConfigurer<H>, H> {
|
||||||
|
|
||||||
|
static final AuthorizationManager<RequestAuthorizationContext> permitAllAuthorizationManager = (a,
|
||||||
|
o) -> new AuthorizationDecision(true);
|
||||||
|
|
||||||
private final AuthorizationManagerRequestMatcherRegistry registry;
|
private final AuthorizationManagerRequestMatcherRegistry registry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,6 +85,12 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||||
return this.registry;
|
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}.
|
* Registry for mapping a {@link RequestMatcher} to an {@link AuthorizationManager}.
|
||||||
*
|
*
|
||||||
|
@ -106,6 +116,19 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||||
this.mappingCount++;
|
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() {
|
private AuthorizationManager<HttpServletRequest> createAuthorizationManager() {
|
||||||
Assert.state(this.unmappedMatchers == null,
|
Assert.state(this.unmappedMatchers == null,
|
||||||
() -> "An incomplete mapping was found for " + this.unmappedMatchers
|
() -> "An incomplete mapping was found for " + this.unmappedMatchers
|
||||||
|
@ -209,7 +232,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||||
* customizations
|
* customizations
|
||||||
*/
|
*/
|
||||||
public AuthorizationManagerRequestMatcherRegistry permitAll() {
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -48,11 +48,22 @@ final class PermitAllSupport {
|
||||||
RequestMatcher... requestMatchers) {
|
RequestMatcher... requestMatchers) {
|
||||||
ExpressionUrlAuthorizationConfigurer<?> configurer = http
|
ExpressionUrlAuthorizationConfigurer<?> configurer = http
|
||||||
.getConfigurer(ExpressionUrlAuthorizationConfigurer.class);
|
.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) {
|
for (RequestMatcher matcher : requestMatchers) {
|
||||||
if (matcher != null) {
|
if (matcher != null) {
|
||||||
configurer.getRegistry().addMapping(0, new UrlMapping(matcher,
|
if (configurer != null) {
|
||||||
SecurityConfig.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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());
|
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
|
@Test
|
||||||
public void configureWhenNotAuthorizeRequestsThenException() {
|
public void configureWhenNotAuthorizeRequestsThenException() {
|
||||||
assertThatExceptionOfType(BeanCreationException.class)
|
assertThatExceptionOfType(BeanCreationException.class)
|
||||||
.isThrownBy(() -> this.spring.register(NoAuthorizedUrlsConfig.class).autowire())
|
.isThrownBy(() -> this.spring.register(NoAuthorizedUrlsConfig.class).autowire()).withMessageContaining(
|
||||||
.withMessageContaining("permitAll only works with HttpSecurity.authorizeRequests");
|
"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
|
@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
|
@EnableWebSecurity
|
||||||
static class NoAuthorizedUrlsConfig extends WebSecurityConfigurerAdapter {
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
@ -112,6 +113,20 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho
|
||||||
return this;
|
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.
|
* Creates a {@link RequestMatcherDelegatingAuthorizationManager} instance.
|
||||||
* @return the {@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.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
|
import org.springframework.security.authorization.AuthorityAuthorizationManager;
|
||||||
import org.springframework.security.authorization.AuthorizationDecision;
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
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.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
@ -83,4 +85,40 @@ public class RequestMatcherDelegatingAuthorizationManagerTests {
|
||||||
assertThat(abstain).isNull();
|
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