diff --git a/config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/ldap/LdapAuthenticationProviderConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/ldap/LdapAuthenticationProviderConfigurer.java index 441c5f72a1..f50aca3aae 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/ldap/LdapAuthenticationProviderConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/ldap/LdapAuthenticationProviderConfigurer.java @@ -15,11 +15,15 @@ */ package org.springframework.security.config.annotation.authentication.configurers.ldap; +import java.io.IOException; +import java.net.ServerSocket; + import org.springframework.ldap.core.support.BaseLdapPathContextSource; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.encoding.PasswordEncoder; import org.springframework.security.authentication.encoding.PlaintextPasswordEncoder; +import org.springframework.security.config.GrantedAuthorityDefaults; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.SecurityConfigurerAdapter; import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder; @@ -43,9 +47,6 @@ import org.springframework.security.ldap.userdetails.PersonContextMapper; import org.springframework.security.ldap.userdetails.UserDetailsContextMapper; import org.springframework.util.Assert; -import java.io.IOException; -import java.net.ServerSocket; - /** * Configures LDAP {@link AuthenticationProvider} in the {@link ProviderManagerBuilder}. * @@ -60,7 +61,7 @@ public class LdapAuthenticationProviderConfigurer rolePrefix(String rolePrefix) { - this.rolePrefix = rolePrefix; + this.rolePrefix = new GrantedAuthorityDefaults(rolePrefix); return this; } diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java index dcf5da7225..289d9b69ce 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -49,6 +49,7 @@ import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.access.intercept.AfterInvocationManager; import org.springframework.security.access.intercept.AfterInvocationProviderManager; import org.springframework.security.access.intercept.RunAsManager; +import org.springframework.security.access.intercept.RunAsManagerImpl; import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor; import org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor; import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource; @@ -63,6 +64,8 @@ import org.springframework.security.access.vote.RoleVoter; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; + +import org.springframework.security.config.GrantedAuthorityDefaults; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; @@ -74,6 +77,7 @@ import org.springframework.util.Assert; * {@link EnableGlobalMethodSecurity} annotation on the subclass. * * @author Rob Winch + * @author Eddú Meléndez * @since 3.2 * @see EnableGlobalMethodSecurity */ @@ -130,6 +134,14 @@ public class GlobalMethodSecurityConfiguration .setSecurityMetadataSource(methodSecurityMetadataSource()); RunAsManager runAsManager = runAsManager(); if (runAsManager != null) { + if (runAsManager instanceof RunAsManagerImpl) { + GrantedAuthorityDefaults grantedAuthorityDefaults = + getSingleBeanOrNull(GrantedAuthorityDefaults.class); + if (grantedAuthorityDefaults != null) { + ((RunAsManagerImpl) runAsManager).setRolePrefix( + grantedAuthorityDefaults.getRolePrefix()); + } + } methodSecurityInterceptor.setRunAsManager(runAsManager); } @@ -168,6 +180,13 @@ public class GlobalMethodSecurityConfiguration if (trustResolver != null) { this.defaultMethodExpressionHandler.setTrustResolver(trustResolver); } + + GrantedAuthorityDefaults grantedAuthorityDefaults = getSingleBeanOrNull( + GrantedAuthorityDefaults.class); + if (grantedAuthorityDefaults != null) { + this.defaultMethodExpressionHandler.setDefaultRolePrefix( + grantedAuthorityDefaults.getRolePrefix()); + } } private T getSingleBeanOrNull(Class type) { @@ -355,6 +374,12 @@ public class GlobalMethodSecurityConfiguration sources.add(new SecuredAnnotationSecurityMetadataSource()); } if (jsr250Enabled()) { + GrantedAuthorityDefaults grantedAuthorityDefaults = + getSingleBeanOrNull(GrantedAuthorityDefaults.class); + if (grantedAuthorityDefaults != null) { + this.jsr250MethodSecurityMetadataSource.setDefaultRolePrefix( + grantedAuthorityDefaults.getRolePrefix()); + } sources.add(jsr250MethodSecurityMetadataSource); } return new DelegatingMethodSecurityMetadataSource(sources); diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfigurationTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfigurationTests.groovy index 8116027ec1..e045ff3779 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfigurationTests.groovy +++ b/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfigurationTests.groovy @@ -17,7 +17,8 @@ package org.springframework.security.config.annotation.method.configuration import org.springframework.security.access.hierarchicalroles.RoleHierarchy; -import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; +import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl +import org.springframework.security.config.GrantedAuthorityDefaults; import java.lang.reflect.Proxy; @@ -496,4 +497,27 @@ public class GlobalMethodSecurityConfigurationTests extends BaseSpringSpec { return new RoleHierarchyImpl(hierarchy:"ROLE_USER > ROLE_ADMIN") } } + + def "GrantedAuthorityDefaults autowires"() { + when: + loadConfig(CustomGrantedAuthorityConfig) + def preAdviceVoter = context.getBean(MethodInterceptor).accessDecisionManager.decisionVoters.find { it instanceof PreInvocationAuthorizationAdviceVoter} + then: + preAdviceVoter.preAdvice.expressionHandler.defaultRolePrefix == "ROLE:" + } + + @EnableGlobalMethodSecurity(prePostEnabled = true) + static class CustomGrantedAuthorityConfig extends GlobalMethodSecurityConfiguration { + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + } + + @Bean + public GrantedAuthorityDefaults ga() { + return new GrantedAuthorityDefaults("ROLE:") + } + } } diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/NamespaceGlobalMethodSecurityTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/NamespaceGlobalMethodSecurityTests.groovy index 70246317cf..f709c5fe73 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/NamespaceGlobalMethodSecurityTests.groovy +++ b/config/src/test/groovy/org/springframework/security/config/annotation/method/configuration/NamespaceGlobalMethodSecurityTests.groovy @@ -15,7 +15,9 @@ */ package org.springframework.security.config.annotation.method.configuration +import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource import org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor +import org.springframework.security.config.GrantedAuthorityDefaults import static org.assertj.core.api.Assertions.assertThat import static org.junit.Assert.fail @@ -146,6 +148,39 @@ public class NamespaceGlobalMethodSecurityTests extends BaseSpringSpec { public static class Jsr250Config extends BaseMethodConfig { } + def "enable jsr250 with custom role prefix"() { + when: + context = new AnnotationConfigApplicationContext(Jsr250WithCustomRolePrefixConfig) + MethodSecurityService service = context.getBean(MethodSecurityService) + then: "@Secured and @PreAuthorize are ignored" + service.secured() == null + service.preAuthorize() == null + + when: "@DenyAll method invoked" + service.jsr250() + then: "access is denied" + thrown(AccessDeniedException) + when: "@PermitAll method invoked" + String jsr250PermitAll = service.jsr250PermitAll() + then: "access is allowed" + jsr250PermitAll == null + when: + Jsr250MethodSecurityMetadataSource jsr250MethodSecurity = context.getBean(Jsr250MethodSecurityMetadataSource) + then: + jsr250MethodSecurity.defaultRolePrefix == "ROLE:" + } + + @EnableGlobalMethodSecurity(jsr250Enabled = true) + @Configuration + public static class Jsr250WithCustomRolePrefixConfig extends BaseMethodConfig { + + @Bean + public GrantedAuthorityDefaults ga() { + return new GrantedAuthorityDefaults("ROLE:") + } + + } + // --- metadata-source-ref --- def "custom MethodSecurityMetadataSource can be used with higher priority than other sources"() { @@ -320,7 +355,7 @@ public class NamespaceGlobalMethodSecurityTests extends BaseSpringSpec { context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomRunAsManagerConfig) MethodSecurityService service = context.getBean(MethodSecurityService) then: - service.runAs().authorities.find { it.authority == "ROLE_RUN_AS_SUPER"} + service.runAs().authorities.find { it.authority == "ROLE:RUN_AS_SUPER"} } @EnableGlobalMethodSecurity(securedEnabled = true) @@ -331,6 +366,11 @@ public class NamespaceGlobalMethodSecurityTests extends BaseSpringSpec { runAsManager.setKey("some key") return runAsManager } + + @Bean + public GrantedAuthorityDefaults ga() { + return new GrantedAuthorityDefaults("ROLE:") + } } // --- secured-annotation --- diff --git a/core/src/main/java/org/springframework/security/config/GrantedAuthorityDefaults.java b/core/src/main/java/org/springframework/security/config/GrantedAuthorityDefaults.java new file mode 100644 index 0000000000..d8c990317d --- /dev/null +++ b/core/src/main/java/org/springframework/security/config/GrantedAuthorityDefaults.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002-2016 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; + +/** + * @author Eddú Meléndez + * @since 4.2.0 + */ +public class GrantedAuthorityDefaults { + + private String rolePrefix = "ROLE_"; + + public GrantedAuthorityDefaults(String rolePrefix) { + this.rolePrefix = rolePrefix; + } + + public String getRolePrefix() { + return this.rolePrefix; + } + + public void setRolePrefix(String rolePrefix) { + this.rolePrefix = rolePrefix; + } + +} diff --git a/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java b/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java index bffac728fd..82ef6202c6 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java +++ b/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java @@ -16,6 +16,10 @@ package org.springframework.security.ldap.userdetails; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.security.config.GrantedAuthorityDefaults; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.ldap.SpringSecurityLdapTemplate; @@ -97,7 +101,7 @@ import java.util.Set; * @author Luke Taylor * @author Filip Hanik */ -public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator { +public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator, ApplicationContextAware { // ~ Static fields/initializers // ===================================================================================== @@ -140,7 +144,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator /** * The role prefix that will be prepended to each role name */ - private String rolePrefix = "ROLE_"; + private GrantedAuthorityDefaults rolePrefix = new GrantedAuthorityDefaults("ROLE_"); /** * Should we convert the role name to uppercase */ @@ -250,7 +254,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator role = role.toUpperCase(); } - authorities.add(new SimpleGrantedAuthority(rolePrefix + role)); + authorities.add(new SimpleGrantedAuthority(rolePrefix.getRolePrefix() + role)); } return authorities; @@ -297,7 +301,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator */ public void setRolePrefix(String rolePrefix) { Assert.notNull(rolePrefix, "rolePrefix must not be null"); - this.rolePrefix = rolePrefix; + this.rolePrefix = new GrantedAuthorityDefaults(rolePrefix); } /** @@ -360,7 +364,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator * @see #setRolePrefix(String) */ protected final String getRolePrefix() { - return rolePrefix; + return this.rolePrefix.getRolePrefix(); } /** @@ -391,4 +395,14 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator private SearchControls getSearchControls() { return searchControls; } + + @Override + public void setApplicationContext(ApplicationContext context) throws + BeansException { + String[] beanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class); + if (beanNames.length == 1) { + this.rolePrefix = context.getBean(beanNames[0], GrantedAuthorityDefaults.class); + } + } + } diff --git a/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsMapper.java b/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsMapper.java index b8c9c218bb..250a072b96 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsMapper.java +++ b/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsMapper.java @@ -20,8 +20,13 @@ import java.util.Collection; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DirContextOperations; +import org.springframework.security.config.GrantedAuthorityDefaults; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; @@ -34,14 +39,15 @@ import org.springframework.util.Assert; * object. * * @author Luke Taylor + * @author Eddú Meléndez */ -public class LdapUserDetailsMapper implements UserDetailsContextMapper { +public class LdapUserDetailsMapper implements UserDetailsContextMapper, ApplicationContextAware { // ~ Instance fields // ================================================================================================ private final Log logger = LogFactory.getLog(LdapUserDetailsMapper.class); private String passwordAttributeName = "userPassword"; - private String rolePrefix = "ROLE_"; + private GrantedAuthorityDefaults rolePrefix = new GrantedAuthorityDefaults("ROLE_"); private String[] roleAttributes = null; private boolean convertToUpperCase = true; @@ -146,7 +152,7 @@ public class LdapUserDetailsMapper implements UserDetailsContextMapper { if (convertToUpperCase) { role = ((String) role).toUpperCase(); } - return new SimpleGrantedAuthority(rolePrefix + role); + return new SimpleGrantedAuthority(this.rolePrefix.getRolePrefix() + role); } return null; } @@ -188,6 +194,16 @@ public class LdapUserDetailsMapper implements UserDetailsContextMapper { * @param rolePrefix the prefix (defaults to "ROLE_"). */ public void setRolePrefix(String rolePrefix) { - this.rolePrefix = rolePrefix; + this.rolePrefix = new GrantedAuthorityDefaults(rolePrefix); } + + @Override + public void setApplicationContext(ApplicationContext context) throws + BeansException { + String[] beanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class); + if (beanNames.length == 1) { + this.rolePrefix = context.getBean(beanNames[0], GrantedAuthorityDefaults.class); + } + } + } diff --git a/ldap/src/test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java b/ldap/src/test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java new file mode 100644 index 0000000000..4f9f9e9c7c --- /dev/null +++ b/ldap/src/test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2016 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.ldap.userdetails; + +import org.junit.Test; + +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.ldap.core.ContextSource; +import org.springframework.security.config.GrantedAuthorityDefaults; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * @author Eddú Meléndez + */ +public class DefaultLdapAuthoritiesPopulatorTests { + + @Test + public void testDefaultRolePrefix() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(LdapAuthoritiesPopulatorConfiguration.class); + context.refresh(); + + DefaultLdapAuthoritiesPopulator ldapPopulator = context.getBean(DefaultLdapAuthoritiesPopulator.class); + assertThat(ldapPopulator.getRolePrefix()).isEqualTo("ROL_"); + } + + @Configuration + static class LdapAuthoritiesPopulatorConfiguration { + + @Bean + public GrantedAuthorityDefaults authorityDefaults() { + return new GrantedAuthorityDefaults("ROL_"); + } + + @Bean + public DefaultLdapAuthoritiesPopulator ldapAuthoritiesPopulator() { + ContextSource contextSource = mock(ContextSource.class); + return new DefaultLdapAuthoritiesPopulator(contextSource, "ou=groups"); + } + + } + +} diff --git a/ldap/src/test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsMapperTests.java b/ldap/src/test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsMapperTests.java index f3bb18061d..7da6005ff5 100644 --- a/ldap/src/test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsMapperTests.java +++ b/ldap/src/test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsMapperTests.java @@ -21,9 +21,14 @@ import javax.naming.directory.BasicAttributes; import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DistinguishedName; +import org.springframework.security.config.GrantedAuthorityDefaults; import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -94,4 +99,32 @@ public class LdapUserDetailsMapperTests { assertThat(user.getPassword()).isEqualTo("mypassword"); } + + @Test + public void testDefaultRolePrefix() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(LdapUserDetailsMapperConfiguration.class); + context.refresh(); + + LdapUserDetailsMapper ldapUserDetailsMapper = context.getBean(LdapUserDetailsMapper.class); + + GrantedAuthorityDefaults rolePrefix = (GrantedAuthorityDefaults) ReflectionTestUtils.getField(ldapUserDetailsMapper, "rolePrefix"); + assertThat(rolePrefix.getRolePrefix()).isEqualTo("ROL_"); + } + + @Configuration + static class LdapUserDetailsMapperConfiguration { + + @Bean + public GrantedAuthorityDefaults authorityDefaults() { + return new GrantedAuthorityDefaults("ROL_"); + } + + @Bean + public LdapUserDetailsMapper ldapUserDetailsMapper() { + return new LdapUserDetailsMapper(); + } + + } + } diff --git a/web/src/main/java/org/springframework/security/web/access/expression/DefaultWebSecurityExpressionHandler.java b/web/src/main/java/org/springframework/security/web/access/expression/DefaultWebSecurityExpressionHandler.java index df5fc22538..7cbeec399b 100644 --- a/web/src/main/java/org/springframework/security/web/access/expression/DefaultWebSecurityExpressionHandler.java +++ b/web/src/main/java/org/springframework/security/web/access/expression/DefaultWebSecurityExpressionHandler.java @@ -15,11 +15,14 @@ */ package org.springframework.security.web.access.expression; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; import org.springframework.security.access.expression.AbstractSecurityExpressionHandler; import org.springframework.security.access.expression.SecurityExpressionHandler; import org.springframework.security.access.expression.SecurityExpressionOperations; import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.authentication.AuthenticationTrustResolverImpl; +import org.springframework.security.config.GrantedAuthorityDefaults; import org.springframework.security.core.Authentication; import org.springframework.security.web.FilterInvocation; import org.springframework.util.Assert; @@ -27,6 +30,7 @@ import org.springframework.util.Assert; /** * * @author Luke Taylor + * @author Eddú Meléndez * @since 3.0 */ public class DefaultWebSecurityExpressionHandler extends @@ -34,7 +38,7 @@ public class DefaultWebSecurityExpressionHandler extends SecurityExpressionHandler { private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl(); - private String defaultRolePrefix = "ROLE_"; + private GrantedAuthorityDefaults defaultRolePrefix = new GrantedAuthorityDefaults("ROLE_"); @Override protected SecurityExpressionOperations createSecurityExpressionRoot( @@ -43,7 +47,7 @@ public class DefaultWebSecurityExpressionHandler extends root.setPermissionEvaluator(getPermissionEvaluator()); root.setTrustResolver(trustResolver); root.setRoleHierarchy(getRoleHierarchy()); - root.setDefaultRolePrefix(defaultRolePrefix); + root.setDefaultRolePrefix(this.defaultRolePrefix.getRolePrefix()); return root; } @@ -74,6 +78,16 @@ public class DefaultWebSecurityExpressionHandler extends * @param defaultRolePrefix the default prefix to add to roles. Default "ROLE_". */ public void setDefaultRolePrefix(String defaultRolePrefix) { - this.defaultRolePrefix = defaultRolePrefix; + this.defaultRolePrefix = new GrantedAuthorityDefaults(defaultRolePrefix); } + + @Override + public void setApplicationContext(ApplicationContext context) { + super.setApplicationContext(context); + String[] beanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class); + if (beanNames.length == 1) { + this.defaultRolePrefix = context.getBean(beanNames[0], GrantedAuthorityDefaults.class); + } + } + } \ No newline at end of file diff --git a/web/src/main/java/org/springframework/security/web/servletapi/SecurityContextHolderAwareRequestFilter.java b/web/src/main/java/org/springframework/security/web/servletapi/SecurityContextHolderAwareRequestFilter.java index 82cb48d27e..0043c3f599 100644 --- a/web/src/main/java/org/springframework/security/web/servletapi/SecurityContextHolderAwareRequestFilter.java +++ b/web/src/main/java/org/springframework/security/web/servletapi/SecurityContextHolderAwareRequestFilter.java @@ -27,9 +27,13 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.authentication.AuthenticationTrustResolverImpl; +import org.springframework.security.config.GrantedAuthorityDefaults; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.AuthenticationEntryPoint; @@ -71,12 +75,13 @@ import org.springframework.web.filter.GenericFilterBean; * @author Ben Alex * @author Luke Taylor * @author Rob Winch + * @author Eddú Meléndez */ -public class SecurityContextHolderAwareRequestFilter extends GenericFilterBean { +public class SecurityContextHolderAwareRequestFilter extends GenericFilterBean implements ApplicationContextAware { // ~ Instance fields // ================================================================================================ - private String rolePrefix = "ROLE_"; + private GrantedAuthorityDefaults rolePrefix = new GrantedAuthorityDefaults("ROLE_"); private HttpServletRequestFactory requestFactory; @@ -93,7 +98,7 @@ public class SecurityContextHolderAwareRequestFilter extends GenericFilterBean { public void setRolePrefix(String rolePrefix) { Assert.notNull(rolePrefix, "Role prefix must not be null"); - this.rolePrefix = rolePrefix; + this.rolePrefix = new GrantedAuthorityDefaults(rolePrefix); updateFactory(); } @@ -177,8 +182,9 @@ public class SecurityContextHolderAwareRequestFilter extends GenericFilterBean { } private void updateFactory() { - this.requestFactory = isServlet3() ? createServlet3Factory(this.rolePrefix) - : new HttpServlet25RequestFactory(this.trustResolver, this.rolePrefix); + String rolePrefix = this.rolePrefix.getRolePrefix(); + this.requestFactory = isServlet3() ? createServlet3Factory(rolePrefix) + : new HttpServlet25RequestFactory(this.trustResolver, rolePrefix); } /** @@ -210,4 +216,14 @@ public class SecurityContextHolderAwareRequestFilter extends GenericFilterBean { private boolean isServlet3() { return ClassUtils.hasMethod(ServletRequest.class, "startAsync"); } + + @Override + public void setApplicationContext(ApplicationContext context) throws + BeansException { + String[] beanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class); + if (beanNames.length == 1) { + this.rolePrefix = context.getBean(beanNames[0], GrantedAuthorityDefaults.class); + } + } + } diff --git a/web/src/test/java/org/springframework/security/web/access/expression/DefaultWebSecurityExpressionHandlerTests.java b/web/src/test/java/org/springframework/security/web/access/expression/DefaultWebSecurityExpressionHandlerTests.java index aca3d9f005..f0f1f658c4 100644 --- a/web/src/test/java/org/springframework/security/web/access/expression/DefaultWebSecurityExpressionHandlerTests.java +++ b/web/src/test/java/org/springframework/security/web/access/expression/DefaultWebSecurityExpressionHandlerTests.java @@ -33,6 +33,7 @@ import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.security.access.SecurityConfig; import org.springframework.security.authentication.AuthenticationTrustResolver; +import org.springframework.security.config.GrantedAuthorityDefaults; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.FilterInvocation; @@ -62,7 +63,7 @@ public class DefaultWebSecurityExpressionHandlerTests { } @Test - public void expressionPropertiesAreResolvedAgainsAppContextBeans() throws Exception { + public void expressionPropertiesAreResolvedAgainstAppContextBeans() throws Exception { StaticApplicationContext appContext = new StaticApplicationContext(); RootBeanDefinition bean = new RootBeanDefinition(SecurityConfig.class); bean.getConstructorArgumentValues().addGenericArgumentValue("ROLE_A"); @@ -95,4 +96,22 @@ public class DefaultWebSecurityExpressionHandlerTests { verify(trustResolver).isAnonymous(authentication); } + + @Test + public void testDefaultRolePrefix() { + StaticApplicationContext appContext = new StaticApplicationContext(); + RootBeanDefinition bean = new RootBeanDefinition(GrantedAuthorityDefaults.class); + bean.getConstructorArgumentValues().addGenericArgumentValue("ROL_"); + appContext.registerBeanDefinition("authorityDefaults", bean); + handler.setApplicationContext(appContext); + + EvaluationContext ctx = handler.createEvaluationContext( + mock(Authentication.class), mock(FilterInvocation.class)); + ExpressionParser parser = handler.getExpressionParser(); + assertThat(parser.parseExpression("@authorityDefaults.getRolePrefix() == 'ROL_'").getValue( + ctx, Boolean.class)).isTrue(); + assertThat(parser.parseExpression("@authorityDefaults.rolePrefix == 'ROL_'").getValue(ctx, + Boolean.class)).isTrue(); + } + } diff --git a/web/src/test/java/org/springframework/security/web/servletapi/SecurityContextHolderAwareRequestFilterTests.java b/web/src/test/java/org/springframework/security/web/servletapi/SecurityContextHolderAwareRequestFilterTests.java index f775f8d65f..62282fb082 100644 --- a/web/src/test/java/org/springframework/security/web/servletapi/SecurityContextHolderAwareRequestFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/servletapi/SecurityContextHolderAwareRequestFilterTests.java @@ -36,6 +36,9 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.reflect.internal.WhiteboxImpl; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.authentication.AuthenticationManager; @@ -43,11 +46,13 @@ import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.concurrent.DelegatingSecurityContextRunnable; +import org.springframework.security.config.GrantedAuthorityDefaults; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.logout.LogoutHandler; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.util.ClassUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -67,6 +72,7 @@ import static org.powermock.api.mockito.PowerMockito.when; * * @author Ben Alex * @author Rob Winch + * @author Eddú Meléndez */ @RunWith(PowerMockRunner.class) @PrepareForTest(ClassUtils.class) @@ -195,7 +201,7 @@ public class SecurityContextHolderAwareRequestFilterTests { // SEC-2296 @Test - public void loginWithExstingUser() throws Exception { + public void loginWithExistingUser() throws Exception { TestingAuthenticationToken expectedAuth = new TestingAuthenticationToken("user", "password", "ROLE_USER"); when(this.authenticationManager @@ -415,4 +421,31 @@ public class SecurityContextHolderAwareRequestFilterTests { return this.requestCaptor.getValue(); } + + @Test + public void testDefaultRolePrefix() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(FilterConfiguration.class); + context.refresh(); + + SecurityContextHolderAwareRequestFilter filter = context.getBean(SecurityContextHolderAwareRequestFilter.class); + GrantedAuthorityDefaults authorityDefaults = (GrantedAuthorityDefaults) ReflectionTestUtils.getField(filter, "rolePrefix"); + assertThat(authorityDefaults.getRolePrefix()).isEqualTo("ROL_"); + } + + @Configuration + static class FilterConfiguration { + + @Bean + public GrantedAuthorityDefaults authorityDefaults() { + return new GrantedAuthorityDefaults("ROL_"); + } + + @Bean + public SecurityContextHolderAwareRequestFilter requestFilter() { + return new SecurityContextHolderAwareRequestFilter(); + } + + } + }