diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityConfiguration.java index f6d9aa6cce..fd7367805a 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 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. @@ -16,6 +16,7 @@ package org.springframework.security.config.annotation.method.configuration; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -28,17 +29,21 @@ import org.springframework.security.access.method.AbstractMethodSecurityMetadata import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource; import org.springframework.security.access.prepost.PrePostAdviceReactiveMethodInterceptor; import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource; +import org.springframework.security.config.core.GrantedAuthorityDefaults; import java.util.Arrays; /** * @author Rob Winch + * @author Tadaya Tsuyukubo * @since 5.0 */ @Configuration class ReactiveMethodSecurityConfiguration implements ImportAware { private int advisorOrder; + private GrantedAuthorityDefaults grantedAuthorityDefaults; + @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public MethodSecurityMetadataSourceAdvisor methodSecurityInterceptor(AbstractMethodSecurityMetadataSource source) throws Exception { @@ -49,9 +54,9 @@ class ReactiveMethodSecurityConfiguration implements ImportAware { } @Bean - public DelegatingMethodSecurityMetadataSource methodMetadataSource() { + public DelegatingMethodSecurityMetadataSource methodMetadataSource(MethodSecurityExpressionHandler methodSecurityExpressionHandler) { ExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory( - new DefaultMethodSecurityExpressionHandler()); + methodSecurityExpressionHandler); PrePostAnnotationSecurityMetadataSource prePostSource = new PrePostAnnotationSecurityMetadataSource( attributeFactory); return new DelegatingMethodSecurityMetadataSource(Arrays.asList(prePostSource)); @@ -70,11 +75,21 @@ class ReactiveMethodSecurityConfiguration implements ImportAware { @Bean public DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler() { - return new DefaultMethodSecurityExpressionHandler(); + DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler(); + if (this.grantedAuthorityDefaults != null) { + handler.setDefaultRolePrefix(this.grantedAuthorityDefaults.getRolePrefix()); + } + return handler; } @Override public void setImportMetadata(AnnotationMetadata importMetadata) { this.advisorOrder = (int) importMetadata.getAnnotationAttributes(EnableReactiveMethodSecurity.class.getName()).get("order"); } + + @Autowired(required = false) + void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) { + this.grantedAuthorityDefaults = grantedAuthorityDefaults; + } + } diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityConfigurationTests.java new file mode 100644 index 0000000000..97f8294df3 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityConfigurationTests.java @@ -0,0 +1,91 @@ +/* + * 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 + * + * https://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.method.configuration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import org.junit.Rule; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.expression.EvaluationContext; +import org.springframework.security.access.expression.SecurityExpressionRoot; +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.access.intercept.method.MockMethodInvocation; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.config.core.GrantedAuthorityDefaults; +import org.springframework.security.config.test.SpringTestRule; + +/** + * @author Tadaya Tsuyukubo + */ +public class ReactiveMethodSecurityConfigurationTests { + + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Autowired + DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler; + + @Test + public void rolePrefixWithGrantedAuthorityDefaults() { + this.spring.register(WithRolePrefixConfiguration.class).autowire(); + + TestingAuthenticationToken authentication = new TestingAuthenticationToken( + "principal", "credential", "CUSTOM_ABC"); + MockMethodInvocation methodInvocation = mock(MockMethodInvocation.class); + + EvaluationContext context = this.methodSecurityExpressionHandler + .createEvaluationContext(authentication, methodInvocation); + SecurityExpressionRoot root = (SecurityExpressionRoot) context.getRootObject() + .getValue(); + + assertThat(root.hasRole("ROLE_ABC")).isFalse(); + assertThat(root.hasRole("ROLE_CUSTOM_ABC")).isFalse(); + assertThat(root.hasRole("CUSTOM_ABC")).isTrue(); + assertThat(root.hasRole("ABC")).isTrue(); + } + + @Test + public void rolePrefixWithDefaultConfig() { + this.spring.register(ReactiveMethodSecurityConfiguration.class).autowire(); + + TestingAuthenticationToken authentication = new TestingAuthenticationToken( + "principal", "credential", "ROLE_ABC"); + MockMethodInvocation methodInvocation = mock(MockMethodInvocation.class); + + EvaluationContext context = this.methodSecurityExpressionHandler + .createEvaluationContext(authentication, methodInvocation); + SecurityExpressionRoot root = (SecurityExpressionRoot) context.getRootObject() + .getValue(); + + assertThat(root.hasRole("ROLE_ABC")).isTrue(); + assertThat(root.hasRole("ABC")).isTrue(); + } + + @Configuration + @EnableReactiveMethodSecurity // this imports ReactiveMethodSecurityConfiguration + static class WithRolePrefixConfiguration { + @Bean + GrantedAuthorityDefaults grantedAuthorityDefaults() { + return new GrantedAuthorityDefaults("CUSTOM_"); + } + } + +}