diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java b/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java index c8923f7ef4..acc0f55f33 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -25,6 +25,7 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AnyRequestMatcher; import org.springframework.security.web.util.matcher.RegexRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.util.Assert; import org.springframework.web.servlet.handler.HandlerMappingIntrospector; import java.util.ArrayList; @@ -39,6 +40,7 @@ import java.util.List; * @param The object that is returned or Chained after creating the RequestMatcher * * @author Rob Winch + * @author Ankur Pathak * @since 3.2 */ public abstract class AbstractRequestMatcherRegistry { @@ -48,6 +50,8 @@ public abstract class AbstractRequestMatcherRegistry { private ApplicationContext context; + private boolean anyRequestConfigured = false; + protected final void setApplicationContext(ApplicationContext context) { this.context = context; } @@ -67,7 +71,10 @@ public abstract class AbstractRequestMatcherRegistry { * @return the object that is chained after creating the {@link RequestMatcher} */ public C anyRequest() { - return requestMatchers(ANY_REQUEST); + Assert.state(!this.anyRequestConfigured, "Can't configure anyRequest after itself"); + C configurer = requestMatchers(ANY_REQUEST); + this.anyRequestConfigured = true; + return configurer; } /** @@ -97,6 +104,7 @@ public abstract class AbstractRequestMatcherRegistry { * @return the object that is chained after creating the {@link RequestMatcher} */ public C antMatchers(HttpMethod method, String... antPatterns) { + Assert.state(!this.anyRequestConfigured, "Can't configure antMatchers after anyRequest"); return chainRequestMatchers(RequestMatchers.antMatchers(method, antPatterns)); } @@ -111,6 +119,7 @@ public abstract class AbstractRequestMatcherRegistry { * @return the object that is chained after creating the {@link RequestMatcher} */ public C antMatchers(String... antPatterns) { + Assert.state(!this.anyRequestConfigured, "Can't configure antMatchers after anyRequest"); return chainRequestMatchers(RequestMatchers.antMatchers(antPatterns)); } @@ -160,7 +169,7 @@ public abstract class AbstractRequestMatcherRegistry { */ protected final List createMvcMatchers(HttpMethod method, String... mvcPatterns) { - + Assert.state(!this.anyRequestConfigured, "Can't configure mvcMatchers after anyRequest"); ObjectPostProcessor opp = this.context.getBean(ObjectPostProcessor.class); if (!this.context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) { throw new NoSuchBeanDefinitionException("A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME +" of type " + HandlerMappingIntrospector.class.getName() @@ -195,6 +204,7 @@ public abstract class AbstractRequestMatcherRegistry { * @return the object that is chained after creating the {@link RequestMatcher} */ public C regexMatchers(HttpMethod method, String... regexPatterns) { + Assert.state(!this.anyRequestConfigured, "Can't configure regexMatchers after anyRequest"); return chainRequestMatchers(RequestMatchers.regexMatchers(method, regexPatterns)); } @@ -209,6 +219,7 @@ public abstract class AbstractRequestMatcherRegistry { * @return the object that is chained after creating the {@link RequestMatcher} */ public C regexMatchers(String... regexPatterns) { + Assert.state(!this.anyRequestConfigured, "Can't configure regexMatchers after anyRequest"); return chainRequestMatchers(RequestMatchers.regexMatchers(regexPatterns)); } @@ -221,6 +232,7 @@ public abstract class AbstractRequestMatcherRegistry { * @return the object that is chained after creating the {@link RequestMatcher} */ public C requestMatchers(RequestMatcher... requestMatchers) { + Assert.state(!this.anyRequestConfigured, "Can't configure requestMatchers after anyRequest"); return chainRequestMatchers(Arrays.asList(requestMatchers)); } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryAnyMatcherTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryAnyMatcherTests.java new file mode 100644 index 0000000000..de5c668388 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryAnyMatcherTests.java @@ -0,0 +1,122 @@ +/* + * Copyright 2002-2019 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.config.annotation.web; + +import org.junit.Test; + +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.mock.web.MockServletContext; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; + +/** + * Tests for {@link AbstractRequestMatcherRegistry}. + * + * @author Ankur Pathak + */ +public class AbstractRequestMatcherRegistryAnyMatcherTests{ + + @EnableWebSecurity + static class AntMatchersAfterAnyRequestConfig extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .antMatchers("/demo/**").permitAll(); + + } + } + + @Test(expected = BeanCreationException.class) + public void antMatchersCanNotWorkAfterAnyRequest(){ + loadConfig(AntMatchersAfterAnyRequestConfig.class); + } + + @EnableWebSecurity + static class MvcMatchersAfterAnyRequestConfig extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .mvcMatchers("/demo/**").permitAll(); + + } + } + + @Test(expected = BeanCreationException.class) + public void mvcMatchersCanNotWorkAfterAnyRequest() { + loadConfig(MvcMatchersAfterAnyRequestConfig.class); + } + + @EnableWebSecurity + static class RegexMatchersAfterAnyRequestConfig extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .regexMatchers(".*").permitAll(); + + } + } + + @Test(expected = BeanCreationException.class) + public void regexMatchersCanNotWorkAfterAnyRequest() { + loadConfig(RegexMatchersAfterAnyRequestConfig.class); + } + + @EnableWebSecurity + static class AnyRequestAfterItselfConfig extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .anyRequest().permitAll(); + + } + } + + @Test(expected = BeanCreationException.class) + public void anyRequestCanNotWorkAfterItself() { + loadConfig(AnyRequestAfterItselfConfig.class); + } + + @EnableWebSecurity + static class RequestMatchersAfterAnyRequestConfig extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .requestMatchers(new AntPathRequestMatcher("/**")).permitAll(); + + } + } + + @Test(expected = BeanCreationException.class) + public void requestMatchersCanNotWorkAfterAnyRequest() { + loadConfig(RequestMatchersAfterAnyRequestConfig.class); + } + + private void loadConfig(Class... configs) { + AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); + context.setAllowCircularReferences(false); + context.register(configs); + context.setServletContext(new MockServletContext()); + context.refresh(); + } +}