Skip to content

Commit

Permalink
SEC-2758: Make ROLE_ consistent
Browse files Browse the repository at this point in the history
  • Loading branch information
Rob Winch committed Jan 29, 2015
1 parent 753fdca commit 6627f76
Show file tree
Hide file tree
Showing 17 changed files with 479 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public <T> T postProcess(T object) {
private AnnotationAttributes enableMethodSecurity;
private ApplicationContext context;
private MethodSecurityExpressionHandler expressionHandler;
private Jsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource;

/**
* Creates the default MethodInterceptor which is a MethodSecurityInterceptor using the following methods to
Expand Down Expand Up @@ -172,7 +173,6 @@ protected RunAsManager runAsManager() {
*
* @return
*/
@SuppressWarnings("rawtypes")
protected AccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<AccessDecisionVoter<? extends Object>>();
ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
Expand Down Expand Up @@ -282,14 +282,13 @@ public MethodSecurityMetadataSource methodSecurityMetadataSource() {
sources.add(customMethodSecurityMetadataSource);
}
if (prePostEnabled()) {
sources.add(new PrePostAnnotationSecurityMetadataSource(
attributeFactory));
sources.add(new PrePostAnnotationSecurityMetadataSource(attributeFactory));
}
if (securedEnabled()) {
sources.add(new SecuredAnnotationSecurityMetadataSource());
}
if (jsr250Enabled()) {
sources.add(new Jsr250MethodSecurityMetadataSource());
sources.add(jsr250MethodSecurityMetadataSource);
}
return new DelegatingMethodSecurityMetadataSource(sources);
}
Expand Down Expand Up @@ -344,6 +343,12 @@ public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcess
this.defaultMethodExpressionHandler = objectPostProcessor.postProcess(defaultMethodExpressionHandler);
}

@Autowired(required = false)
public void setJsr250MethodSecurityMetadataSource(
Jsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource) {
this.jsr250MethodSecurityMetadataSource = jsr250MethodSecurityMetadataSource;
}

@Autowired(required = false)
public void setPermissionEvaluator(List<PermissionEvaluator> permissionEvaluators) {
if(permissionEvaluators.size() != 1) {
Expand All @@ -352,6 +357,15 @@ public void setPermissionEvaluator(List<PermissionEvaluator> permissionEvaluator
this.defaultMethodExpressionHandler.setPermissionEvaluator(permissionEvaluators.get(0));
}

@Autowired(required = false)
public void setMethodSecurityExpressionHandler(List<MethodSecurityExpressionHandler> handlers) {
if(handlers.size() != 1) {
logger.debug("Not autwiring PermissionEvaluator since size != 1. Got " + handlers);
return;
}
this.expressionHandler = handlers.get(0);
}

@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package org.springframework.security.config.annotation.method.configuration;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.context.annotation.AdviceMode;
Expand Down Expand Up @@ -49,10 +51,20 @@ public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
AdviceMode mode = attributes.getEnum("mode");
String autoProxyClassName = AdviceMode.PROXY == mode ? AutoProxyRegistrar.class.getName()
: GlobalMethodSecurityAspectJAutoProxyRegistrar.class.getName();
if(skipMethodSecurityConfiguration) {
return new String[] { autoProxyClassName };

boolean jsr250Enabled = attributes.getBoolean("jsr250Enabled");

List<String> classNames = new ArrayList<String>(4);
classNames.add(autoProxyClassName);

if(!skipMethodSecurityConfiguration) {
classNames.add(GlobalMethodSecurityConfiguration.class.getName());
}
return new String[] { autoProxyClassName,
GlobalMethodSecurityConfiguration.class.getName()};

if(jsr250Enabled) {
classNames.add(Jsr250MetadataSourceConfiguration.class.getName());
}

return classNames.toArray(new String[0]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.springframework.security.config.annotation.method.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;

@Configuration
class Jsr250MetadataSourceConfiguration {

@Bean
public Jsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource() {
return new Jsr250MethodSecurityMetadataSource();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* Copyright 2002-2013 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.sec2758;

import javax.annotation.security.RolesAllowed;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.core.PriorityOrdered;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.TestingAuthenticationToken
import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.config.annotation.web.configuration.sec2377.a.*
import org.springframework.security.config.annotation.web.configuration.sec2377.b.*
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext

public class Sec2758Tests extends BaseSpringSpec {

def cleanup() {
SecurityContextHolder.clearContext()
}

def "SEC-2758: Verify Passivity Restored with Advice from JIRA"() {
setup:
SecurityContextHolder.context.authentication = new TestingAuthenticationToken("user", "pass", "USER")
loadConfig(SecurityConfig)
Service service = context.getBean(Service)

when:
findFilter(FilterSecurityInterceptor).doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(), new MockFilterChain())
then:
noExceptionThrown()

when:
service.doPreAuthorize()
then:
noExceptionThrown()

when:
service.doJsr250()
then:
noExceptionThrown()
}

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
static class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().hasAnyAuthority("USER");
}

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth
.inMemoryAuthentication()
.withUser("user").password("password").authorities("USER")
}

@Bean
Service service() {
return new ServiceImpl()
}

@Bean
static DefaultRolesPrefixPostProcessor defaultRolesPrefixPostProcessor() {
new DefaultRolesPrefixPostProcessor()
}
}

interface Service {
void doPreAuthorize()
void doJsr250()
}

static class ServiceImpl implements Service {
@PreAuthorize("hasRole('USER')")
void doPreAuthorize() {}

@RolesAllowed("USER")
void doJsr250() {}
}

static class DefaultRolesPrefixPostProcessor implements BeanPostProcessor, PriorityOrdered {

@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if(bean instanceof Jsr250MethodSecurityMetadataSource) {
((Jsr250MethodSecurityMetadataSource) bean).setDefaultRolePrefix(null);
}
if(bean instanceof DefaultMethodSecurityExpressionHandler) {
((DefaultMethodSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
}
if(bean instanceof DefaultWebSecurityExpressionHandler) {
((DefaultWebSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
}
return bean;
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}

@Override
public int getOrder() {
return PriorityOrdered.HIGHEST_PRECEDENCE;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,14 @@ public void targetShouldPreventProtectedMethodInvocationWithIncorrectRole() {

target.someAdminMethod();
}


@Test
public void hasAnyRoleAddsDefaultPrefix() {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
AuthorityUtils.createAuthorityList("ROLE_USER"));
SecurityContextHolder.getContext().setAuthentication(token);

target.rolesAllowedUser();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,24 @@
*/
public class Jsr250MethodSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource {

private String defaultRolePrefix = "ROLE_";

/**
* <p>
* Sets the default prefix to be added to {@link RolesAllowed}. For example, if {@code @RolesAllowed("ADMIN")} or {@code @RolesAllowed("ADMIN")} is used,
* then the role ROLE_ADMIN will be used when the defaultRolePrefix is "ROLE_" (default).
* </p>
*
* <p>
* If null or empty, then no default role prefix is used.
* </p>
*
* @param defaultRolePrefix the default prefix to add to roles. Default "ROLE_".
*/
public void setDefaultRolePrefix(String defaultRolePrefix) {
this.defaultRolePrefix = defaultRolePrefix;
}

protected Collection<ConfigAttribute> findAttributes(Class<?> clazz) {
return processAnnotations(clazz.getAnnotations());
}
Expand Down Expand Up @@ -69,11 +87,25 @@ private List<ConfigAttribute> processAnnotations(Annotation[] annotations) {
RolesAllowed ra = (RolesAllowed) a;

for (String allowed : ra.value()) {
attributes.add(new Jsr250SecurityConfig(allowed));
String defaultedAllowed = getRoleWithDefaultPrefix(allowed);
attributes.add(new Jsr250SecurityConfig(defaultedAllowed));
}
return attributes;
}
}
return null;
}

private String getRoleWithDefaultPrefix(String role) {
if(role == null) {
return role;
}
if(defaultRolePrefix == null || defaultRolePrefix.length() == 0) {
return role;
}
if(role.startsWith(defaultRolePrefix)) {
return role;
}
return defaultRolePrefix + role;
}
}
Loading

0 comments on commit 6627f76

Please sign in to comment.