Support AuthorizationManager for intercept-methods Element
Closes gh-11328
This commit is contained in:
parent
415a674edc
commit
74a007dc91
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,13 +16,22 @@
|
||||||
|
|
||||||
package org.springframework.security.config.method;
|
package org.springframework.security.config.method;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
|
import org.springframework.aop.ClassFilter;
|
||||||
|
import org.springframework.aop.MethodMatcher;
|
||||||
|
import org.springframework.aop.Pointcut;
|
||||||
import org.springframework.aop.config.AbstractInterceptorDrivenBeanDefinitionDecorator;
|
import org.springframework.aop.config.AbstractInterceptorDrivenBeanDefinitionDecorator;
|
||||||
|
import org.springframework.aop.support.AopUtils;
|
||||||
|
import org.springframework.aop.support.RootClassFilter;
|
||||||
|
import org.springframework.beans.BeanMetadataElement;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||||
|
@ -32,11 +41,24 @@ import org.springframework.beans.factory.support.ManagedMap;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
|
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
|
||||||
import org.springframework.beans.factory.xml.ParserContext;
|
import org.springframework.beans.factory.xml.ParserContext;
|
||||||
|
import org.springframework.expression.EvaluationContext;
|
||||||
|
import org.springframework.expression.Expression;
|
||||||
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
import org.springframework.security.access.SecurityConfig;
|
import org.springframework.security.access.SecurityConfig;
|
||||||
|
import org.springframework.security.access.expression.ExpressionUtils;
|
||||||
|
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||||
|
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||||
import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;
|
import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;
|
||||||
import org.springframework.security.access.method.MapBasedMethodSecurityMetadataSource;
|
import org.springframework.security.access.method.MapBasedMethodSecurityMetadataSource;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||||
import org.springframework.security.config.BeanIds;
|
import org.springframework.security.config.BeanIds;
|
||||||
import org.springframework.security.config.Elements;
|
import org.springframework.security.config.Elements;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.access.expression.ExpressionAuthorizationDecision;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.util.xml.DomUtils;
|
import org.springframework.util.xml.DomUtils;
|
||||||
|
|
||||||
|
@ -68,9 +90,76 @@ public class InterceptMethodsBeanDefinitionDecorator implements BeanDefinitionDe
|
||||||
|
|
||||||
private static final String ATT_ACCESS_MGR = "access-decision-manager-ref";
|
private static final String ATT_ACCESS_MGR = "access-decision-manager-ref";
|
||||||
|
|
||||||
|
private static final String ATT_USE_AUTHORIZATION_MGR = "use-authorization-manager";
|
||||||
|
|
||||||
|
private static final String ATT_AUTHORIZATION_MGR = "authorization-manager-ref";
|
||||||
|
|
||||||
|
private final ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BeanDefinition createInterceptorDefinition(Node node) {
|
protected BeanDefinition createInterceptorDefinition(Node node) {
|
||||||
Element interceptMethodsElt = (Element) node;
|
Element interceptMethodsElt = (Element) node;
|
||||||
|
if (Boolean.parseBoolean(interceptMethodsElt.getAttribute(ATT_USE_AUTHORIZATION_MGR))) {
|
||||||
|
return createAuthorizationManagerInterceptorDefinition(interceptMethodsElt);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(interceptMethodsElt.getAttribute(ATT_AUTHORIZATION_MGR))) {
|
||||||
|
return createAuthorizationManagerInterceptorDefinition(interceptMethodsElt);
|
||||||
|
}
|
||||||
|
return createMethodSecurityInterceptorDefinition(interceptMethodsElt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BeanDefinition createAuthorizationManagerInterceptorDefinition(Element interceptMethodsElt) {
|
||||||
|
BeanDefinitionBuilder interceptor = BeanDefinitionBuilder
|
||||||
|
.rootBeanDefinition(AuthorizationManagerBeforeMethodInterceptor.class);
|
||||||
|
interceptor.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
|
||||||
|
Map<Pointcut, BeanMetadataElement> managers = new ManagedMap<>();
|
||||||
|
List<Element> methods = DomUtils.getChildElementsByTagName(interceptMethodsElt, Elements.PROTECT);
|
||||||
|
for (Element protectElt : methods) {
|
||||||
|
managers.put(pointcut(interceptMethodsElt, protectElt),
|
||||||
|
authorizationManager(interceptMethodsElt, protectElt));
|
||||||
|
}
|
||||||
|
return interceptor.addConstructorArgValue(Pointcut.TRUE)
|
||||||
|
.addConstructorArgValue(authorizationManager(managers)).getBeanDefinition();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pointcut pointcut(Element interceptorElt, Element protectElt) {
|
||||||
|
String method = protectElt.getAttribute(ATT_METHOD);
|
||||||
|
Class<?> javaType = javaType(interceptorElt, method);
|
||||||
|
return new PrefixBasedMethodMatcher(javaType, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BeanMetadataElement authorizationManager(Element interceptMethodsElt, Element protectElt) {
|
||||||
|
String authorizationManager = interceptMethodsElt.getAttribute(ATT_AUTHORIZATION_MGR);
|
||||||
|
if (StringUtils.hasText(authorizationManager)) {
|
||||||
|
return new RuntimeBeanReference(authorizationManager);
|
||||||
|
}
|
||||||
|
String access = protectElt.getAttribute(ATT_ACCESS);
|
||||||
|
SpelExpressionParser parser = new SpelExpressionParser();
|
||||||
|
Expression expression = parser.parseExpression(access);
|
||||||
|
return BeanDefinitionBuilder.rootBeanDefinition(MethodExpressionAuthorizationManager.class)
|
||||||
|
.addConstructorArgValue(expression).getBeanDefinition();
|
||||||
|
}
|
||||||
|
|
||||||
|
private BeanMetadataElement authorizationManager(Map<Pointcut, BeanMetadataElement> managers) {
|
||||||
|
return BeanDefinitionBuilder.rootBeanDefinition(PointcutMatchingAuthorizationManager.class)
|
||||||
|
.addConstructorArgValue(managers).getBeanDefinition();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> javaType(Element interceptMethodsElt, String method) {
|
||||||
|
int lastDotIndex = method.lastIndexOf(".");
|
||||||
|
String parentBeanClass = ((Element) interceptMethodsElt.getParentNode()).getAttribute("class");
|
||||||
|
Assert.isTrue(lastDotIndex != -1 || StringUtils.hasText(parentBeanClass),
|
||||||
|
() -> "'" + method + "' is not a valid method name: format is FQN.methodName");
|
||||||
|
if (lastDotIndex == -1) {
|
||||||
|
return ClassUtils.resolveClassName(parentBeanClass, this.beanClassLoader);
|
||||||
|
}
|
||||||
|
String methodName = method.substring(lastDotIndex + 1);
|
||||||
|
Assert.hasText(methodName, () -> "Method not found for '" + method + "'");
|
||||||
|
String typeName = method.substring(0, lastDotIndex);
|
||||||
|
return ClassUtils.resolveClassName(typeName, this.beanClassLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BeanDefinition createMethodSecurityInterceptorDefinition(Element interceptMethodsElt) {
|
||||||
BeanDefinitionBuilder interceptor = BeanDefinitionBuilder
|
BeanDefinitionBuilder interceptor = BeanDefinitionBuilder
|
||||||
.rootBeanDefinition(MethodSecurityInterceptor.class);
|
.rootBeanDefinition(MethodSecurityInterceptor.class);
|
||||||
// Default to autowiring to pick up after invocation mgr
|
// Default to autowiring to pick up after invocation mgr
|
||||||
|
@ -83,7 +172,7 @@ public class InterceptMethodsBeanDefinitionDecorator implements BeanDefinitionDe
|
||||||
interceptor.addPropertyValue("authenticationManager",
|
interceptor.addPropertyValue("authenticationManager",
|
||||||
new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
|
new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
|
||||||
// Lookup parent bean information
|
// Lookup parent bean information
|
||||||
String parentBeanClass = ((Element) node.getParentNode()).getAttribute("class");
|
String parentBeanClass = ((Element) interceptMethodsElt.getParentNode()).getAttribute("class");
|
||||||
// Parse the included methods
|
// Parse the included methods
|
||||||
List<Element> methods = DomUtils.getChildElementsByTagName(interceptMethodsElt, Elements.PROTECT);
|
List<Element> methods = DomUtils.getChildElementsByTagName(interceptMethodsElt, Elements.PROTECT);
|
||||||
Map<String, BeanDefinition> mappings = new ManagedMap<>();
|
Map<String, BeanDefinition> mappings = new ManagedMap<>();
|
||||||
|
@ -108,4 +197,103 @@ public class InterceptMethodsBeanDefinitionDecorator implements BeanDefinitionDe
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class PrefixBasedMethodMatcher implements MethodMatcher, Pointcut {
|
||||||
|
|
||||||
|
private final ClassFilter classFilter;
|
||||||
|
|
||||||
|
private final Class<?> javaType;
|
||||||
|
|
||||||
|
private final String methodPrefix;
|
||||||
|
|
||||||
|
PrefixBasedMethodMatcher(Class<?> javaType, String methodPrefix) {
|
||||||
|
this.classFilter = new RootClassFilter(javaType);
|
||||||
|
this.javaType = javaType;
|
||||||
|
this.methodPrefix = methodPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassFilter getClassFilter() {
|
||||||
|
return this.classFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodMatcher getMethodMatcher() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(Method method, Class<?> targetClass) {
|
||||||
|
return matches(this.methodPrefix, method.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRuntime() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(Method method, Class<?> targetClass, Object... args) {
|
||||||
|
return matches(this.methodPrefix, method.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matches(String mappedName, String methodName) {
|
||||||
|
boolean equals = methodName.equals(mappedName);
|
||||||
|
return equals || prefixMatches(mappedName, methodName) || suffixMatches(mappedName, methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean prefixMatches(String mappedName, String methodName) {
|
||||||
|
return mappedName.endsWith("*") && methodName.startsWith(mappedName.substring(0, mappedName.length() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean suffixMatches(String mappedName, String methodName) {
|
||||||
|
return mappedName.startsWith("*") && methodName.endsWith(mappedName.substring(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PointcutMatchingAuthorizationManager implements AuthorizationManager<MethodInvocation> {
|
||||||
|
|
||||||
|
private final Map<Pointcut, AuthorizationManager<MethodInvocation>> managers;
|
||||||
|
|
||||||
|
PointcutMatchingAuthorizationManager(Map<Pointcut, AuthorizationManager<MethodInvocation>> managers) {
|
||||||
|
this.managers = managers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation object) {
|
||||||
|
for (Map.Entry<Pointcut, AuthorizationManager<MethodInvocation>> entry : this.managers.entrySet()) {
|
||||||
|
Class<?> targetClass = (object.getThis() != null) ? AopUtils.getTargetClass(object.getThis()) : null;
|
||||||
|
if (entry.getKey().getClassFilter().matches(targetClass)
|
||||||
|
&& entry.getKey().getMethodMatcher().matches(object.getMethod(), targetClass)) {
|
||||||
|
return entry.getValue().check(authentication, object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new AuthorizationDecision(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MethodExpressionAuthorizationManager implements AuthorizationManager<MethodInvocation> {
|
||||||
|
|
||||||
|
private final Expression expression;
|
||||||
|
|
||||||
|
private SecurityExpressionHandler<MethodInvocation> expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||||
|
|
||||||
|
MethodExpressionAuthorizationManager(Expression expression) {
|
||||||
|
this.expression = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) {
|
||||||
|
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, invocation);
|
||||||
|
boolean granted = ExpressionUtils.evaluateAsBoolean(this.expression, ctx);
|
||||||
|
return new ExpressionAuthorizationDecision(granted, this.expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setExpressionHandler(SecurityExpressionHandler<MethodInvocation> expressionHandler) {
|
||||||
|
this.expressionHandler = expressionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,7 +177,12 @@ intercept-methods =
|
||||||
intercept-methods.attlist &=
|
intercept-methods.attlist &=
|
||||||
## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.
|
## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.
|
||||||
attribute access-decision-manager-ref {xsd:token}?
|
attribute access-decision-manager-ref {xsd:token}?
|
||||||
|
intercept-methods.attlist &=
|
||||||
|
## Use the AuthorizationManager API instead of AccessDecisionManager (defaults to false)
|
||||||
|
attribute use-authorization-manager {xsd:boolean}?
|
||||||
|
intercept-methods.attlist &=
|
||||||
|
## Use this AuthorizationManager instead of the default (supercedes use-authorization-manager)
|
||||||
|
attribute authorization-manager-ref {xsd:token}?
|
||||||
|
|
||||||
protect =
|
protect =
|
||||||
## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix "protect" declarations with any services provided "global-method-security".
|
## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix "protect" declarations with any services provided "global-method-security".
|
||||||
|
|
|
@ -540,6 +540,19 @@
|
||||||
</xs:documentation>
|
</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="use-authorization-manager" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Use the AuthorizationManager API instead of AccessDecisionManager (defaults to false)
|
||||||
|
</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="authorization-manager-ref" type="xs:token">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Use this AuthorizationManager instead of the default (supercedes
|
||||||
|
use-authorization-manager)
|
||||||
|
</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
</xs:attributeGroup>
|
</xs:attributeGroup>
|
||||||
|
|
||||||
<xs:attributeGroup name="protect.attlist">
|
<xs:attributeGroup name="protect.attlist">
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.security.config.method;
|
package org.springframework.security.config.method;
|
||||||
|
|
||||||
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -32,6 +33,8 @@ import org.springframework.security.authentication.AuthenticationCredentialsNotF
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
import org.springframework.security.config.TestBusinessBean;
|
import org.springframework.security.config.TestBusinessBean;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
|
@ -41,6 +44,9 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
|
@ -57,6 +63,21 @@ public class InterceptMethodsBeanDefinitionDecoratorTests implements Application
|
||||||
@Qualifier("transactionalTarget")
|
@Qualifier("transactionalTarget")
|
||||||
private TestBusinessBean transactionalTarget;
|
private TestBusinessBean transactionalTarget;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("targetAuthorizationManager")
|
||||||
|
private TestBusinessBean targetAuthorizationManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("transactionalTargetAuthorizationManager")
|
||||||
|
private TestBusinessBean transactionalTargetAuthorizationManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("targetCustomAuthorizationManager")
|
||||||
|
private TestBusinessBean targetCustomAuthorizationManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthorizationManager<MethodInvocation> mockAuthorizationManager;
|
||||||
|
|
||||||
private ApplicationContext appContext;
|
private ApplicationContext appContext;
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
|
@ -72,10 +93,12 @@ public class InterceptMethodsBeanDefinitionDecoratorTests implements Application
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void targetDoesntLoseApplicationListenerInterface() {
|
public void targetDoesntLoseApplicationListenerInterface() {
|
||||||
assertThat(this.appContext.getBeansOfType(ApplicationListener.class)).hasSize(1);
|
assertThat(this.appContext.getBeansOfType(ApplicationListener.class)).isNotEmpty();
|
||||||
assertThat(this.appContext.getBeanNamesForType(ApplicationListener.class)).hasSize(1);
|
assertThat(this.appContext.getBeanNamesForType(ApplicationListener.class)).isNotEmpty();
|
||||||
this.appContext.publishEvent(new AuthenticationSuccessEvent(new TestingAuthenticationToken("user", "")));
|
this.appContext.publishEvent(new AuthenticationSuccessEvent(new TestingAuthenticationToken("user", "")));
|
||||||
assertThat(this.target).isInstanceOf(ApplicationListener.class);
|
assertThat(this.target).isInstanceOf(ApplicationListener.class);
|
||||||
|
assertThat(this.targetAuthorizationManager).isInstanceOf(ApplicationListener.class);
|
||||||
|
assertThat(this.targetCustomAuthorizationManager).isInstanceOf(ApplicationListener.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -110,6 +133,46 @@ public class InterceptMethodsBeanDefinitionDecoratorTests implements Application
|
||||||
assertThatExceptionOfType(AuthenticationException.class).isThrownBy(this.transactionalTarget::doSomething);
|
assertThatExceptionOfType(AuthenticationException.class).isThrownBy(this.transactionalTarget::doSomething);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void targetAuthorizationManagerShouldAllowUnprotectedMethodInvocationWithNoContext() {
|
||||||
|
this.targetAuthorizationManager.unprotected();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void targetAuthorizationManagerShouldPreventProtectedMethodInvocationWithNoContext() {
|
||||||
|
assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
|
||||||
|
.isThrownBy(this.targetAuthorizationManager::doSomething);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void targetAuthorizationManagerShouldAllowProtectedMethodInvocationWithCorrectRole() {
|
||||||
|
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated("Test",
|
||||||
|
"Password", AuthorityUtils.createAuthorityList("ROLE_USER"));
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(token);
|
||||||
|
this.targetAuthorizationManager.doSomething();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void targetAuthorizationManagerShouldPreventProtectedMethodInvocationWithIncorrectRole() {
|
||||||
|
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated("Test",
|
||||||
|
"Password", AuthorityUtils.createAuthorityList("ROLE_SOMEOTHERROLE"));
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(token);
|
||||||
|
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.targetAuthorizationManager::doSomething);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void transactionalAuthorizationManagerMethodsShouldBeSecured() {
|
||||||
|
assertThatExceptionOfType(AuthenticationException.class)
|
||||||
|
.isThrownBy(this.transactionalTargetAuthorizationManager::doSomething);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void targetCustomAuthorizationManagerUsed() {
|
||||||
|
given(this.mockAuthorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(true));
|
||||||
|
this.targetCustomAuthorizationManager.doSomething();
|
||||||
|
verify(this.mockAuthorizationManager).check(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||||
this.appContext = applicationContext;
|
this.appContext = applicationContext;
|
||||||
|
|
|
@ -30,6 +30,30 @@
|
||||||
</intercept-methods>
|
</intercept-methods>
|
||||||
</b:bean>
|
</b:bean>
|
||||||
|
|
||||||
|
<b:bean id="transactionalTargetAuthorizationManager" class="org.springframework.security.config.TransactionalTestBusinessBean">
|
||||||
|
<intercept-methods use-authorization-manager="true">
|
||||||
|
<protect method="*" access="hasRole('ROLE_USER')" />
|
||||||
|
</intercept-methods>
|
||||||
|
</b:bean>
|
||||||
|
|
||||||
|
|
||||||
|
<b:bean id="targetAuthorizationManager" class="org.springframework.security.config.TestBusinessBeanImpl">
|
||||||
|
<!-- This will add a security interceptor to the bean -->
|
||||||
|
<intercept-methods use-authorization-manager="true">
|
||||||
|
<protect method="org.springframework.security.config.TestBusinessBean.set*" access="hasRole('${admin.role}')" />
|
||||||
|
<protect method="get*" access="hasAnyRole('${admin.role}','ROLE_USER')" />
|
||||||
|
<protect method="doSomething" access="hasRole('ROLE_USER')" />
|
||||||
|
<protect method="*" access="permitAll"/>
|
||||||
|
</intercept-methods>
|
||||||
|
</b:bean>
|
||||||
|
|
||||||
|
<b:bean id="targetCustomAuthorizationManager" class="org.springframework.security.config.TestBusinessBeanImpl">
|
||||||
|
<!-- This will add a security interceptor to the bean -->
|
||||||
|
<intercept-methods authorization-manager-ref="authorization-manager">
|
||||||
|
<protect method="*" access="denyAll"/>
|
||||||
|
</intercept-methods>
|
||||||
|
</b:bean>
|
||||||
|
|
||||||
<authentication-manager>
|
<authentication-manager>
|
||||||
<authentication-provider>
|
<authentication-provider>
|
||||||
<user-service>
|
<user-service>
|
||||||
|
@ -39,4 +63,7 @@
|
||||||
</authentication-provider>
|
</authentication-provider>
|
||||||
</authentication-manager>
|
</authentication-manager>
|
||||||
|
|
||||||
|
<b:bean id="authorization-manager" class="org.mockito.Mockito" factory-method="mock">
|
||||||
|
<b:constructor-arg value="org.springframework.security.authorization.AuthorizationManager"/>
|
||||||
|
</b:bean>
|
||||||
</b:beans>
|
</b:beans>
|
||||||
|
|
|
@ -271,6 +271,13 @@ Can be used inside a bean definition to add a security interceptor to the bean a
|
||||||
[[nsa-intercept-methods-attributes]]
|
[[nsa-intercept-methods-attributes]]
|
||||||
=== <intercept-methods> Attributes
|
=== <intercept-methods> Attributes
|
||||||
|
|
||||||
|
[[nsa-intercept-methods-use-authorization-manager]]
|
||||||
|
* **use-authorization-manager**
|
||||||
|
Use AuthorizationManager API instead of AccessDecisionManager
|
||||||
|
|
||||||
|
[[nsa-intercept-methods-authorization-manager-ref]]
|
||||||
|
* **authorization-manager-ref**
|
||||||
|
Optional AuthorizationManager bean ID to be used instead of the default (supercedes use-authorization-manager)
|
||||||
|
|
||||||
[[nsa-intercept-methods-access-decision-manager-ref]]
|
[[nsa-intercept-methods-access-decision-manager-ref]]
|
||||||
* **access-decision-manager-ref**
|
* **access-decision-manager-ref**
|
||||||
|
|
Loading…
Reference in New Issue