Skip to content

Configure permissionEvaluator and roleHierarchy by default #4115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import org.springframework.beans.factory.config.BeanPostProcessor
import org.springframework.context.ApplicationListener
import org.springframework.context.annotation.Bean
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.PermissionEvaluator
import org.springframework.security.access.hierarchicalroles.RoleHierarchy
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl
import org.springframework.security.access.event.AuthorizedEvent
import org.springframework.security.access.vote.AffirmativeBased
import org.springframework.security.authentication.RememberMeAuthenticationToken
Expand All @@ -35,6 +38,7 @@ 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.configurers.ExpressionUrlAuthorizationConfigurerConfigs.CustomExpressionRootConfig
import org.springframework.security.core.Authentication
import org.springframework.security.core.authority.AuthorityUtils
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor

Expand Down Expand Up @@ -574,4 +578,119 @@ public class ExpressionUrlAuthorizationConfigurerTests extends BaseSpringSpec {
}

}

def "permissionEvaluator autowired"() {
setup:
loadConfig(PermissionEvaluatorConfig)
when: "invoke hasPermission expression that allows access"
super.setup()
login()
request.servletPath = "/allow/1"
springSecurityFilterChain.doFilter(request, response, chain)
then: "permissionEvaluator with id and type works - allows access"
response.status == HttpServletResponse.SC_OK
when: "invoke hasPermission expression that denies access"
super.setup()
login()
request.servletPath = "/deny/1"
springSecurityFilterChain.doFilter(request, response, chain)
then: "permissionEvaluator with id and type works - denies access"
response.status == HttpServletResponse.SC_FORBIDDEN
when: "invoke hasPermission expression that allows access"
super.setup()
login()
request.servletPath = "/allowObject/1"
springSecurityFilterChain.doFilter(request, response, chain)
then: "permissionEvaluator with object works - allows access"
response.status == HttpServletResponse.SC_OK
when: "invoke hasPermission expression that denies access"
super.setup()
login()
request.servletPath = "/denyObject/1"
springSecurityFilterChain.doFilter(request, response, chain)
then: "permissionEvaluator with object works - denies access"
response.status == HttpServletResponse.SC_FORBIDDEN
}

@EnableWebSecurity
static class PermissionEvaluatorConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/allow/**").access("hasPermission('ID', 'TYPE', 'PERMISSION')")
.antMatchers("/allowObject/**").access("hasPermission('TESTOBJ', 'PERMISSION')")
.antMatchers("/deny/**").access("hasPermission('ID', 'TYPE', 'NO PERMISSION')")
.antMatchers("/denyObject/**").access("hasPermission('TESTOBJ', 'NO PERMISSION')")
.anyRequest().permitAll();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
}

@Bean
public PermissionEvaluator permissionEvaluator(){
return new PermissionEvaluator(){
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
return "TESTOBJ".equals(targetDomainObject) && "PERMISSION".equals(permission);
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,
Object permission) {
return "ID".equals(targetId) && "TYPE".equals(targetType) && "PERMISSION".equals(permission);
}
};
}

}

@EnableWebSecurity
static class RoleHierarchyConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/allow/**").access("hasRole('XXX')")
.antMatchers("/deny/**").access("hasRole('NOPE')")
.anyRequest().permitAll();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
}

@Bean
public RoleHierarchy roleHierarchy(){
return new RoleHierarchyImpl("USER > XXX");
}

}

def "roleHierarchy autowired"() {
setup:
loadConfig(PermissionEvaluatorConfig)
when: "invoke roleHierarchy expression that allows access"
super.setup()
login()
request.servletPath = "/allow/1"
springSecurityFilterChain.doFilter(request, response, chain)
then: "permissionEvaluator with id and type works - allows access"
response.status == HttpServletResponse.SC_OK
when: "invoke roleHierarchy expression that denies access"
super.setup()
login()
request.servletPath = "/deny/1"
springSecurityFilterChain.doFilter(request, response, chain)
then: "permissionEvaluator with id and type works - denies access"
response.status == HttpServletResponse.SC_FORBIDDEN
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ public abstract class AbstractSecurityExpressionHandler<T> implements
SecurityExpressionHandler<T>, ApplicationContextAware {
private ExpressionParser expressionParser = new SpelExpressionParser();
private BeanResolver br;
private ApplicationContext context;
private RoleHierarchy roleHierarchy;
private PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();
private boolean roleHierarchySet = false;
private boolean permissionEvaluatorSet = false;


public final ExpressionParser getExpressionParser() {
return expressionParser;
Expand Down Expand Up @@ -101,23 +105,52 @@ protected StandardEvaluationContext createEvaluationContextInternal(
protected abstract SecurityExpressionOperations createSecurityExpressionRoot(
Authentication authentication, T invocation);

private boolean roleHerarchyNotSetForValidContext() {
return ! roleHierarchySet && context != null;
}

protected RoleHierarchy getRoleHierarchy() {
if(roleHerarchyNotSetForValidContext()) {
RoleHierarchy contextRoleHierarchy = getSingleBeanOrNull(RoleHierarchy.class);
if(contextRoleHierarchy != null){
roleHierarchy = contextRoleHierarchy;
}
roleHierarchySet = true;
}
return roleHierarchy;
}

public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
roleHierarchySet = true;
this.roleHierarchy = roleHierarchy;
}

protected PermissionEvaluator getPermissionEvaluator() {
if(! permissionEvaluatorSet && context != null) {
PermissionEvaluator contextPermissionEvaluator = getSingleBeanOrNull(PermissionEvaluator.class);
if(contextPermissionEvaluator != null){
permissionEvaluator = contextPermissionEvaluator;
}
permissionEvaluatorSet = true;
}
return permissionEvaluator;
}

public void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {
permissionEvaluatorSet = true;
this.permissionEvaluator = permissionEvaluator;
}

public void setApplicationContext(ApplicationContext applicationContext) {
br = new BeanFactoryResolver(applicationContext);
this.context = applicationContext;
}

private <T> T getSingleBeanOrNull(Class<T> type) {
String[] beanNamesForType = context.getBeanNamesForType(type);
if (beanNamesForType == null || beanNamesForType.length != 1) {
return null;
}
return context.getBean(beanNamesForType[0], type);
}
}