Use Interceptors instead of Advice

- Interceptor is a more descriptive term for what
method security is doing
- This also allows the code to follow a delegate
pattern that unifies both before-method and after-
method authorization

Issue gh-9289
This commit is contained in:
Josh Cummings 2021-04-08 14:31:36 -06:00
parent 122346bd27
commit df8abcfae7
No known key found for this signature in database
GPG Key ID: 49EF60DD7FF83443
37 changed files with 1010 additions and 1244 deletions

View File

@ -16,20 +16,11 @@
package org.springframework.security.config.annotation.method.configuration;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.Pointcuts;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
@ -39,26 +30,16 @@ import org.springframework.context.annotation.ImportAware;
import org.springframework.context.annotation.Role;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PreFilter;
import org.springframework.security.authorization.method.AuthorizationManagerMethodAfterAdvice;
import org.springframework.security.authorization.method.AuthorizationManagerMethodBeforeAdvice;
import org.springframework.security.authorization.method.AuthorizationMethodAfterAdvice;
import org.springframework.security.authorization.method.AuthorizationMethodBeforeAdvice;
import org.springframework.security.authorization.method.AuthorizationMethodInterceptor;
import org.springframework.security.authorization.method.DelegatingAuthorizationMethodAfterAdvice;
import org.springframework.security.authorization.method.DelegatingAuthorizationMethodBeforeAdvice;
import org.springframework.security.authorization.method.AuthorizationMethodInterceptors;
import org.springframework.security.authorization.method.DelegatingAuthorizationMethodInterceptor;
import org.springframework.security.authorization.method.Jsr250AuthorizationManager;
import org.springframework.security.authorization.method.MethodAuthorizationContext;
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodAfterAdvice;
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor;
import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodBeforeAdvice;
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;
import org.springframework.security.authorization.method.SecuredAuthorizationManager;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.util.Assert;
@ -79,30 +60,19 @@ final class MethodSecurityConfiguration implements ImportAware, InitializingBean
private GrantedAuthorityDefaults grantedAuthorityDefaults;
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> authorizationMethodBeforeAdvice;
private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> authorizationMethodAfterAdvice;
private AuthorizationMethodInterceptor interceptor;
private AnnotationAttributes enableMethodSecurity;
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
DefaultPointcutAdvisor methodSecurityAdvisor(AuthorizationMethodInterceptor interceptor) {
AuthorizationMethodBeforeAdvice<?> beforeAdvice = getAuthorizationMethodBeforeAdvice();
AuthorizationMethodAfterAdvice<?> afterAdvice = getAuthorizationMethodAfterAdvice();
Pointcut pointcut = Pointcuts.union(beforeAdvice.getPointcut(), afterAdvice.getPointcut());
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, interceptor);
DefaultPointcutAdvisor methodSecurityAdvisor() {
AuthorizationMethodInterceptor interceptor = getInterceptor();
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(interceptor.getPointcut(), interceptor);
advisor.setOrder(order());
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
AuthorizationMethodInterceptor authorizationMethodInterceptor() {
return new AuthorizationMethodInterceptor(getAuthorizationMethodBeforeAdvice(),
getAuthorizationMethodAfterAdvice());
}
private MethodSecurityExpressionHandler getMethodSecurityExpressionHandler() {
if (this.methodSecurityExpressionHandler == null) {
DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
@ -124,15 +94,18 @@ final class MethodSecurityConfiguration implements ImportAware, InitializingBean
this.grantedAuthorityDefaults = grantedAuthorityDefaults;
}
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> getAuthorizationMethodBeforeAdvice() {
if (this.authorizationMethodBeforeAdvice == null) {
this.authorizationMethodBeforeAdvice = createDefaultAuthorizationMethodBeforeAdvice();
private AuthorizationMethodInterceptor getInterceptor() {
if (this.interceptor != null) {
return this.interceptor;
}
return this.authorizationMethodBeforeAdvice;
List<AuthorizationMethodInterceptor> interceptors = new ArrayList<>();
interceptors.addAll(createDefaultAuthorizationMethodBeforeAdvice());
interceptors.addAll(createDefaultAuthorizationMethodAfterAdvice());
return new DelegatingAuthorizationMethodInterceptor(interceptors);
}
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> createDefaultAuthorizationMethodBeforeAdvice() {
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> beforeAdvices = new ArrayList<>();
private List<AuthorizationMethodInterceptor> createDefaultAuthorizationMethodBeforeAdvice() {
List<AuthorizationMethodInterceptor> beforeAdvices = new ArrayList<>();
beforeAdvices.add(getPreFilterAuthorizationMethodBeforeAdvice());
beforeAdvices.add(getPreAuthorizeAuthorizationMethodBeforeAdvice());
if (securedEnabled()) {
@ -141,79 +114,55 @@ final class MethodSecurityConfiguration implements ImportAware, InitializingBean
if (jsr250Enabled()) {
beforeAdvices.add(getJsr250AuthorizationMethodBeforeAdvice());
}
return new DelegatingAuthorizationMethodBeforeAdvice<>(beforeAdvices);
return beforeAdvices;
}
private PreFilterAuthorizationMethodBeforeAdvice getPreFilterAuthorizationMethodBeforeAdvice() {
Pointcut pointcut = forAnnotation(PreFilter.class);
PreFilterAuthorizationMethodBeforeAdvice preFilterBeforeAdvice = new PreFilterAuthorizationMethodBeforeAdvice(
pointcut);
preFilterBeforeAdvice.setExpressionHandler(getMethodSecurityExpressionHandler());
return preFilterBeforeAdvice;
private PreFilterAuthorizationMethodInterceptor getPreFilterAuthorizationMethodBeforeAdvice() {
PreFilterAuthorizationMethodInterceptor interceptor = new PreFilterAuthorizationMethodInterceptor();
interceptor.setExpressionHandler(getMethodSecurityExpressionHandler());
return interceptor;
}
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> getPreAuthorizeAuthorizationMethodBeforeAdvice() {
Pointcut pointcut = forAnnotation(PreAuthorize.class);
private AuthorizationMethodInterceptor getPreAuthorizeAuthorizationMethodBeforeAdvice() {
PreAuthorizeAuthorizationManager authorizationManager = new PreAuthorizeAuthorizationManager();
authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler());
return new AuthorizationManagerMethodBeforeAdvice<>(pointcut, authorizationManager);
return AuthorizationMethodInterceptors.preAuthorize(authorizationManager);
}
private AuthorizationManagerMethodBeforeAdvice<MethodAuthorizationContext> getSecuredAuthorizationMethodBeforeAdvice() {
Pointcut pointcut = forAnnotation(Secured.class);
SecuredAuthorizationManager authorizationManager = new SecuredAuthorizationManager();
return new AuthorizationManagerMethodBeforeAdvice<>(pointcut, authorizationManager);
private AuthorizationMethodInterceptor getSecuredAuthorizationMethodBeforeAdvice() {
return AuthorizationMethodInterceptors.secured(new SecuredAuthorizationManager());
}
private AuthorizationManagerMethodBeforeAdvice<MethodAuthorizationContext> getJsr250AuthorizationMethodBeforeAdvice() {
Pointcut pointcut = new ComposablePointcut(forAnnotation(DenyAll.class)).union(forAnnotation(PermitAll.class))
.union(forAnnotation(RolesAllowed.class));
private AuthorizationMethodInterceptor getJsr250AuthorizationMethodBeforeAdvice() {
Jsr250AuthorizationManager authorizationManager = new Jsr250AuthorizationManager();
if (this.grantedAuthorityDefaults != null) {
authorizationManager.setRolePrefix(this.grantedAuthorityDefaults.getRolePrefix());
}
return new AuthorizationManagerMethodBeforeAdvice<>(pointcut, authorizationManager);
return AuthorizationMethodInterceptors.jsr250(authorizationManager);
}
@Autowired(required = false)
void setAuthorizationMethodBeforeAdvice(
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> authorizationMethodBeforeAdvice) {
this.authorizationMethodBeforeAdvice = authorizationMethodBeforeAdvice;
void setAuthorizationMethodInterceptor(AuthorizationMethodInterceptor interceptor) {
this.interceptor = interceptor;
}
private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> getAuthorizationMethodAfterAdvice() {
if (this.authorizationMethodAfterAdvice == null) {
this.authorizationMethodAfterAdvice = createDefaultAuthorizationMethodAfterAdvice();
}
return this.authorizationMethodAfterAdvice;
}
private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> createDefaultAuthorizationMethodAfterAdvice() {
List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> afterAdvices = new ArrayList<>();
private List<AuthorizationMethodInterceptor> createDefaultAuthorizationMethodAfterAdvice() {
List<AuthorizationMethodInterceptor> afterAdvices = new ArrayList<>();
afterAdvices.add(getPostFilterAuthorizationMethodAfterAdvice());
afterAdvices.add(getPostAuthorizeAuthorizationMethodAfterAdvice());
return new DelegatingAuthorizationMethodAfterAdvice<>(afterAdvices);
return afterAdvices;
}
private PostFilterAuthorizationMethodAfterAdvice getPostFilterAuthorizationMethodAfterAdvice() {
Pointcut pointcut = forAnnotation(PostFilter.class);
PostFilterAuthorizationMethodAfterAdvice postFilterAfterAdvice = new PostFilterAuthorizationMethodAfterAdvice(
pointcut);
postFilterAfterAdvice.setExpressionHandler(getMethodSecurityExpressionHandler());
return postFilterAfterAdvice;
private AuthorizationMethodInterceptor getPostFilterAuthorizationMethodAfterAdvice() {
PostFilterAuthorizationMethodInterceptor interceptor = new PostFilterAuthorizationMethodInterceptor();
interceptor.setExpressionHandler(getMethodSecurityExpressionHandler());
return interceptor;
}
private AuthorizationManagerMethodAfterAdvice<MethodAuthorizationContext> getPostAuthorizeAuthorizationMethodAfterAdvice() {
Pointcut pointcut = forAnnotation(PostAuthorize.class);
private AuthorizationMethodInterceptor getPostAuthorizeAuthorizationMethodAfterAdvice() {
PostAuthorizeAuthorizationManager authorizationManager = new PostAuthorizeAuthorizationManager();
authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler());
return new AuthorizationManagerMethodAfterAdvice<>(pointcut, authorizationManager);
}
@Autowired(required = false)
void setAuthorizationMethodAfterAdvice(
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> authorizationMethodAfterAdvice) {
this.authorizationMethodAfterAdvice = authorizationMethodAfterAdvice;
return AuthorizationMethodInterceptors.postAuthorize(authorizationManager);
}
@Override
@ -227,7 +176,7 @@ final class MethodSecurityConfiguration implements ImportAware, InitializingBean
if (!securedEnabled() && !jsr250Enabled()) {
return;
}
Assert.isNull(this.authorizationMethodBeforeAdvice,
Assert.isNull(this.interceptor,
"You have specified your own advice, meaning that the annotation attributes securedEnabled and jsr250Enabled will be ignored. Please choose one or the other.");
}
@ -243,9 +192,4 @@ final class MethodSecurityConfiguration implements ImportAware, InitializingBean
return this.enableMethodSecurity.getNumber("order");
}
private Pointcut forAnnotation(Class<? extends Annotation> annotationClass) {
return Pointcuts.union(new AnnotationMatchingPointcut(annotationClass, true),
new AnnotationMatchingPointcut(null, annotationClass, true));
}
}

View File

@ -18,9 +18,11 @@ package org.springframework.security.config.annotation.method.configuration;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -38,10 +40,8 @@ import org.springframework.security.access.expression.method.DefaultMethodSecuri
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.method.AuthorizationManagerMethodBeforeAdvice;
import org.springframework.security.authorization.method.AuthorizationMethodAfterAdvice;
import org.springframework.security.authorization.method.AuthorizationMethodBeforeAdvice;
import org.springframework.security.authorization.method.MethodAuthorizationContext;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.authorization.method.AuthorizationMethodInterceptor;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.Authentication;
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
@ -273,6 +273,43 @@ public class MethodSecurityConfigurationTests {
this.businessService.rolesAllowedUser();
}
@WithMockUser(roles = { "ADMIN", "USER" })
@Test
public void manyAnnotationsWhenMeetsConditionsThenReturnsFilteredList() throws Exception {
List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo");
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
List<String> filtered = this.methodSecurityService.manyAnnotations(new ArrayList<>(names));
assertThat(filtered).hasSize(2);
assertThat(filtered).containsExactly("harold", "jonathan");
}
@WithMockUser
@Test
public void manyAnnotationsWhenUserThenFails() {
List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo");
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));
}
@WithMockUser
@Test
public void manyAnnotationsWhenShortListThenFails() {
List<String> names = Arrays.asList("harold", "jonathan", "pete");
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));
}
@WithMockUser(roles = "ADMIN")
@Test
public void manyAnnotationsWhenAdminThenFails() {
List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo");
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));
}
@Test
public void configureWhenCustomAdviceAndSecureEnabledThenException() {
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> this.spring
@ -338,12 +375,12 @@ public class MethodSecurityConfigurationTests {
static class CustomAuthorizationManagerBeforeAdviceConfig {
@Bean
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> customBeforeAdvice() {
JdkRegexpMethodPointcut methodMatcher = new JdkRegexpMethodPointcut();
methodMatcher.setPattern(".*MethodSecurityServiceImpl.*securedUser");
AuthorizationManager<MethodAuthorizationContext> authorizationManager = (a,
AuthorizationMethodInterceptor customBeforeAdvice() {
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPattern(".*MethodSecurityServiceImpl.*securedUser");
AuthorizationManager<MethodInvocation> authorizationManager = (a,
o) -> new AuthorizationDecision("bob".equals(a.get().getName()));
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager);
return new AuthorizationManagerBeforeMethodInterceptor(pointcut, authorizationManager);
}
}
@ -352,18 +389,18 @@ public class MethodSecurityConfigurationTests {
static class CustomAuthorizationManagerAfterAdviceConfig {
@Bean
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> customAfterAdvice() {
AuthorizationMethodInterceptor customAfterAdvice() {
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPattern(".*MethodSecurityServiceImpl.*securedUser");
return new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
AuthorizationMethodInterceptor interceptor = new AuthorizationMethodInterceptor() {
@Override
public Pointcut getPointcut() {
return pointcut;
}
@Override
public Object after(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext, Object returnedObject) {
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) {
Authentication auth = authentication.get();
if ("bob".equals(auth.getName())) {
return "granted";
@ -371,6 +408,7 @@ public class MethodSecurityConfigurationTests {
throw new AccessDeniedException("Access Denied for User '" + auth.getName() + "'");
}
};
return interceptor;
}
}

View File

@ -16,12 +16,16 @@
package org.springframework.security.config.annotation.method.configuration;
import java.util.List;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PreFilter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.parameters.P;
@ -69,4 +73,11 @@ public interface MethodSecurityService {
@PostAuthorize("#o?.contains('grant')")
String postAnnotation(@P("o") String object);
@PreFilter("filterObject.length > 3")
@PreAuthorize("hasRole('ADMIN')")
@Secured("ROLE_USER")
@PostFilter("filterObject.length > 5")
@PostAuthorize("returnObject.size > 1")
List<String> manyAnnotations(List<String> array);
}

View File

@ -16,6 +16,8 @@
package org.springframework.security.config.annotation.method.configuration;
import java.util.List;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
@ -86,4 +88,9 @@ public class MethodSecurityServiceImpl implements MethodSecurityService {
return null;
}
@Override
public List<String> manyAnnotations(List<String> object) {
return object;
}
}

View File

@ -27,28 +27,25 @@ import org.springframework.lang.NonNull;
import org.springframework.security.authorization.AuthorizationManager;
/**
* An abstract registry which provides an {@link AuthorizationManager} for the
* {@link MethodInvocation}.
* For internal use only, as this contract is likely to change
*
* @author Evgeniy Cheban
* @since 5.5
*/
abstract class AbstractAuthorizationManagerRegistry {
static final AuthorizationManager<MethodAuthorizationContext> NULL_MANAGER = (a, o) -> null;
static final AuthorizationManager<MethodInvocation> NULL_MANAGER = (a, o) -> null;
private final Map<MethodClassKey, AuthorizationManager<MethodAuthorizationContext>> cachedManagers = new ConcurrentHashMap<>();
private final Map<MethodClassKey, AuthorizationManager<MethodInvocation>> cachedManagers = new ConcurrentHashMap<>();
/**
* Returns an {@link AuthorizationManager} for the {@link MethodAuthorizationContext}.
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to use
* Returns an {@link AuthorizationManager} for the
* {@link AuthorizationMethodInvocation}.
* @param methodInvocation the {@link AuthorizationMethodInvocation} to use
* @return an {@link AuthorizationManager} to use
*/
final AuthorizationManager<MethodAuthorizationContext> getManager(
MethodAuthorizationContext methodAuthorizationContext) {
MethodInvocation methodInvocation = methodAuthorizationContext.getMethodInvocation();
final AuthorizationManager<MethodInvocation> getManager(AuthorizationMethodInvocation methodInvocation) {
Method method = methodInvocation.getMethod();
Class<?> targetClass = methodAuthorizationContext.getTargetClass();
Class<?> targetClass = methodInvocation.getTargetClass();
MethodClassKey cacheKey = new MethodClassKey(method, targetClass);
return this.cachedManagers.computeIfAbsent(cacheKey, (k) -> resolveManager(method, targetClass));
}
@ -61,6 +58,6 @@ abstract class AbstractAuthorizationManagerRegistry {
* @return the non-null {@link AuthorizationManager}
*/
@NonNull
abstract AuthorizationManager<MethodAuthorizationContext> resolveManager(Method method, Class<?> targetClass);
abstract AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass);
}

View File

@ -20,31 +20,27 @@ import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.MethodClassKey;
import org.springframework.lang.NonNull;
/**
* An abstract registry which provides an {@link ExpressionAttribute} for the
* {@link MethodInvocation}.
* For internal use only, as this contract is likely to change
*
* @author Evgeniy Cheban
* @since 5.5
*/
abstract class AbstractExpressionAttributeRegistry<T extends ExpressionAttribute> {
private final Map<MethodClassKey, T> cachedAttributes = new ConcurrentHashMap<>();
/**
* Returns an {@link ExpressionAttribute} for the {@link MethodAuthorizationContext}.
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to use
* Returns an {@link ExpressionAttribute} for the
* {@link AuthorizationMethodInvocation}.
* @param mi the {@link AuthorizationMethodInvocation} to use
* @return the {@link ExpressionAttribute} to use
*/
final T getAttribute(MethodAuthorizationContext methodAuthorizationContext) {
MethodInvocation methodInvocation = methodAuthorizationContext.getMethodInvocation();
Method method = methodInvocation.getMethod();
Class<?> targetClass = methodAuthorizationContext.getTargetClass();
final T getAttribute(AuthorizationMethodInvocation mi) {
Method method = mi.getMethod();
Class<?> targetClass = mi.getTargetClass();
return getAttribute(method, targetClass);
}

View File

@ -18,7 +18,8 @@ package org.springframework.security.authorization.method;
import java.util.function.Supplier;
import org.springframework.aop.MethodMatcher;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.AuthorizationManager;
@ -26,28 +27,27 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationMethodAfterAdvice} which can determine if an
* {@link Authentication} has access to the {@link T} object using an
* {@link AuthorizationManager} if a {@link MethodMatcher} matches.
* An {@link AuthorizationMethodInterceptor} which can determine if an
* {@link Authentication} has access to the result of an {@link MethodInvocation} using an
* {@link AuthorizationManager}
*
* @param <T> the type of object that the authorization check is being done one.
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class AuthorizationManagerMethodAfterAdvice<T> implements AuthorizationMethodAfterAdvice<T> {
public final class AuthorizationManagerAfterMethodInterceptor implements AuthorizationMethodInterceptor {
private final Pointcut pointcut;
private final AfterMethodAuthorizationManager<T> authorizationManager;
private final AfterMethodAuthorizationManager<MethodInvocation> authorizationManager;
/**
* Creates an instance.
* @param pointcut the {@link Pointcut} to use
* @param authorizationManager the {@link AuthorizationManager} to use
*/
public AuthorizationManagerMethodAfterAdvice(Pointcut pointcut,
AfterMethodAuthorizationManager<T> authorizationManager) {
public AuthorizationManagerAfterMethodInterceptor(Pointcut pointcut,
AfterMethodAuthorizationManager<MethodInvocation> authorizationManager) {
Assert.notNull(pointcut, "pointcut cannot be null");
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
this.pointcut = pointcut;
@ -55,16 +55,17 @@ public final class AuthorizationManagerMethodAfterAdvice<T> implements Authoriza
}
/**
* Determine if an {@link Authentication} has access to the {@link T} object using the
* {@link AuthorizationManager}.
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
* using the {@link AuthorizationManager}.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
* @param mi the {@link MethodInvocation} to check
* @throws AccessDeniedException if access is not granted
*/
@Override
public Object after(Supplier<Authentication> authentication, T context, Object object) {
this.authorizationManager.verify(authentication, context, object);
return object;
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
Object result = mi.proceed();
this.authorizationManager.verify(authentication, mi, result);
return result;
}
/**

View File

@ -18,7 +18,8 @@ package org.springframework.security.authorization.method;
import java.util.function.Supplier;
import org.springframework.aop.MethodMatcher;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.AuthorizationManager;
@ -26,27 +27,26 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationMethodBeforeAdvice} which can determine if an
* {@link Authentication} has access to the {@link T} object using an
* {@link AuthorizationManager} if a {@link MethodMatcher} matches.
* An {@link AuthorizationMethodInterceptor} which uses a {@link AuthorizationManager} to
* determine if an {@link Authentication} may invoke the given {@link MethodInvocation}
*
* @param <T> the type of object that the authorization check is being done one.
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class AuthorizationManagerMethodBeforeAdvice<T> implements AuthorizationMethodBeforeAdvice<T> {
public final class AuthorizationManagerBeforeMethodInterceptor implements AuthorizationMethodInterceptor {
private final Pointcut pointcut;
private final AuthorizationManager<T> authorizationManager;
private final AuthorizationManager<MethodInvocation> authorizationManager;
/**
* Creates an instance.
* @param pointcut the {@link Pointcut} to use
* @param authorizationManager the {@link AuthorizationManager} to use
*/
public AuthorizationManagerMethodBeforeAdvice(Pointcut pointcut, AuthorizationManager<T> authorizationManager) {
public AuthorizationManagerBeforeMethodInterceptor(Pointcut pointcut,
AuthorizationManager<MethodInvocation> authorizationManager) {
Assert.notNull(pointcut, "pointcut cannot be null");
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
this.pointcut = pointcut;
@ -54,15 +54,16 @@ public final class AuthorizationManagerMethodBeforeAdvice<T> implements Authoriz
}
/**
* Determine if an {@link Authentication} has access to the {@link T} object using the
* configured {@link AuthorizationManager}.
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
* using the configured {@link AuthorizationManager}.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
* @param mi the {@link MethodInvocation} to check
* @throws AccessDeniedException if access is not granted
*/
@Override
public void before(Supplier<Authentication> authentication, T object) {
this.authorizationManager.verify(authentication, object);
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
this.authorizationManager.verify(authentication, mi);
return mi.proceed();
}
/**

View File

@ -1,70 +0,0 @@
/*
* Copyright 2002-2021 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.authorization.method;
import java.util.function.Supplier;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterAdvice;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.security.core.Authentication;
/**
* An {@link Advice} which can determine if an {@link Authentication} has access to the
* returned object from the {@link MethodInvocation}. {@link #getPointcut()} describes
* when the advice applies for the method.
*
* @param <T> the type of object that the authorization check is being done one.
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public interface AuthorizationMethodAfterAdvice<T> extends AfterAdvice, PointcutAdvisor, AopInfrastructureBean {
/**
* {@inheritDoc}
*/
@Override
default boolean isPerInstance() {
return true;
}
/**
* {@inheritDoc}
*/
@Override
default Advice getAdvice() {
return this;
}
/**
* Determine if an {@link Authentication} has access to a method invocation's return
* object.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
* @param returnedObject the returned object from the method invocation to check
* @return the {@code Object} that will ultimately be returned to the caller (if an
* implementation does not wish to modify the object to be returned to the caller, the
* implementation should simply return the same object it was passed by the
* {@code returnedObject} method argument)
*/
Object after(Supplier<Authentication> authentication, T object, Object returnedObject);
}

View File

@ -1,62 +0,0 @@
/*
* Copyright 2002-2021 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.authorization.method;
import java.util.function.Supplier;
import org.aopalliance.aop.Advice;
import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.security.core.Authentication;
/**
* An {@link Advice} which can determine if an {@link Authentication} has access to the
* {@link T} object. {@link #getPointcut()} describes when the advice applies.
*
* @param <T> the type of object that the authorization check is being done one.
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public interface AuthorizationMethodBeforeAdvice<T> extends BeforeAdvice, PointcutAdvisor, AopInfrastructureBean {
/**
* {@inheritDoc}
*/
@Override
default boolean isPerInstance() {
return true;
}
/**
* {@inheritDoc}
*/
@Override
default Advice getAdvice() {
return this;
}
/**
* Determine if an {@link Authentication} has access to the {@link T} object.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
*/
void before(Supplier<Authentication> authentication, T object);
}

View File

@ -16,65 +16,70 @@
package org.springframework.security.authorization.method;
import java.util.function.Supplier;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.lang.NonNull;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* Provides security interception of AOP Alliance based method invocations.
* A {@link MethodInterceptor} which can determine if an {@link Authentication} has access
* to the {@link MethodInvocation}. {@link #getPointcut()} describes when the interceptor
* applies.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class AuthorizationMethodInterceptor implements MethodInterceptor {
private final AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> beforeAdvice;
private final AuthorizationMethodAfterAdvice<MethodAuthorizationContext> afterAdvice;
public interface AuthorizationMethodInterceptor extends MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
/**
* Creates an instance.
* @param beforeAdvice the {@link AuthorizationMethodBeforeAdvice} to use
* @param afterAdvice the {@link AuthorizationMethodAfterAdvice} to use
*/
public AuthorizationMethodInterceptor(AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> beforeAdvice,
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> afterAdvice) {
this.beforeAdvice = beforeAdvice;
this.afterAdvice = afterAdvice;
}
/**
* Enforce security on this {@link MethodInvocation}.
* @param mi the method being invoked which requires a security decision
* @return the returned value from the {@link MethodInvocation}, possibly altered by
* the configured {@link AuthorizationMethodAfterAdvice}
* {@inheritDoc}
*/
@Override
public Object invoke(@NonNull MethodInvocation mi) throws Throwable {
MethodAuthorizationContext methodAuthorizationContext = getMethodAuthorizationContext(mi);
this.beforeAdvice.before(this::getAuthentication, methodAuthorizationContext);
Object returnedObject = mi.proceed();
return this.afterAdvice.after(this::getAuthentication, methodAuthorizationContext, returnedObject);
default Advice getAdvice() {
return this;
}
private MethodAuthorizationContext getMethodAuthorizationContext(MethodInvocation mi) {
Object target = mi.getThis();
Class<?> targetClass = (target != null) ? AopUtils.getTargetClass(target) : null;
return new MethodAuthorizationContext(mi, targetClass);
/**
* {@inheritDoc}
*/
@Override
default boolean isPerInstance() {
return true;
}
private Authentication getAuthentication() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new AuthenticationCredentialsNotFoundException(
"An Authentication object was not found in the SecurityContext");
}
return authentication;
/**
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
* @param mi the {@link MethodInvocation} to intercept and potentially invoke
* @return the result of the method invocation
* @throws Throwable if the interceptor or the target object throws an exception
*/
default Object invoke(MethodInvocation mi) throws Throwable {
Supplier<Authentication> supplier = () -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new AuthenticationCredentialsNotFoundException(
"An Authentication object was not found in the SecurityContext");
}
return authentication;
};
return invoke(supplier, new AuthorizationMethodInvocation(supplier, mi));
}
/**
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param mi the {@link MethodInvocation} to intercept and potentially invoke
* @return the result of the method invocation
* @throws Throwable if the interceptor or the target object throws an exception
*/
Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable;
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2002-2021 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.authorization.method;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
/**
* A static factory for constructing common {@link AuthorizationMethodInterceptor}s
*
* @author Josh Cummings
* @since 5.5
* @see PreAuthorizeAuthorizationManager
* @see PostAuthorizeAuthorizationManager
* @see SecuredAuthorizationManager
* @see Jsr250AuthorizationManager
*/
public final class AuthorizationMethodInterceptors {
public static AuthorizationMethodInterceptor preAuthorize() {
return preAuthorize(new PreAuthorizeAuthorizationManager());
}
public static AuthorizationMethodInterceptor preAuthorize(PreAuthorizeAuthorizationManager manager) {
return new AuthorizationManagerBeforeMethodInterceptor(
AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class), manager);
}
public static AuthorizationMethodInterceptor postAuthorize() {
return postAuthorize(new PostAuthorizeAuthorizationManager());
}
public static AuthorizationMethodInterceptor postAuthorize(PostAuthorizeAuthorizationManager manager) {
return new AuthorizationManagerAfterMethodInterceptor(
AuthorizationMethodPointcuts.forAnnotations(PostAuthorize.class), manager);
}
public static AuthorizationMethodInterceptor secured() {
return secured(new SecuredAuthorizationManager());
}
public static AuthorizationMethodInterceptor secured(SecuredAuthorizationManager manager) {
return new AuthorizationManagerBeforeMethodInterceptor(
AuthorizationMethodPointcuts.forAnnotations(Secured.class), manager);
}
public static AuthorizationMethodInterceptor jsr250() {
return jsr250(new Jsr250AuthorizationManager());
}
public static AuthorizationMethodInterceptor jsr250(Jsr250AuthorizationManager manager) {
return new AuthorizationManagerBeforeMethodInterceptor(
AuthorizationMethodPointcuts.forAnnotations(DenyAll.class, PermitAll.class, RolesAllowed.class),
manager);
}
private AuthorizationMethodInterceptors() {
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright 2002-2021 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.authorization.method;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.Authentication;
/**
* @author Josh Cummings
*/
class AuthorizationMethodInvocation implements MethodInvocation {
private final Log logger = LogFactory.getLog(getClass());
private final Supplier<Authentication> authentication;
private final MethodInvocation methodInvocation;
private final Class<?> targetClass;
private final List<AuthorizationMethodInterceptor> interceptors;
private final int size;
private int currentPosition = 0;
AuthorizationMethodInvocation(Supplier<Authentication> authentication, MethodInvocation methodInvocation) {
this(authentication, methodInvocation, Collections.emptyList());
}
AuthorizationMethodInvocation(Supplier<Authentication> authentication, MethodInvocation methodInvocation,
List<AuthorizationMethodInterceptor> interceptors) {
this.authentication = authentication;
this.methodInvocation = methodInvocation;
this.interceptors = interceptors;
Object target = methodInvocation.getThis();
this.targetClass = (target != null) ? AopUtils.getTargetClass(target) : null;
this.size = interceptors.size();
}
@Override
public Method getMethod() {
return this.methodInvocation.getMethod();
}
@Override
public Object[] getArguments() {
return this.methodInvocation.getArguments();
}
/**
* Return the target class.
* @return the target class
*/
Class<?> getTargetClass() {
return this.targetClass;
}
@Override
public Object proceed() throws Throwable {
if (this.currentPosition == this.size) {
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.of(() -> "Pre-Authorized " + this.methodInvocation.getMethod()));
}
return this.methodInvocation.proceed();
}
AuthorizationMethodInterceptor interceptor = this.interceptors.get(this.currentPosition);
this.currentPosition++;
Pointcut pointcut = interceptor.getPointcut();
if (!pointcut.getClassFilter().matches(getTargetClass())) {
return proceed();
}
if (!pointcut.getMethodMatcher().matches(getMethod(), getTargetClass())) {
return proceed();
}
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Applying %s (%d/%d)", interceptor.getClass().getSimpleName(),
this.currentPosition, this.size));
}
Object result = interceptor.invoke(this.authentication, this);
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.of(() -> "Post-Authorized " + this.methodInvocation.getMethod()));
}
return result;
}
@Override
public Object getThis() {
return this.methodInvocation.getThis();
}
@Override
public AccessibleObject getStaticPart() {
return this.methodInvocation.getStaticPart();
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2002-2021 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.authorization.method;
import java.lang.annotation.Annotation;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.Pointcuts;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
/**
* @author Josh Cummings
*/
final class AuthorizationMethodPointcuts {
@SafeVarargs
static Pointcut forAnnotations(Class<? extends Annotation>... annotations) {
ComposablePointcut pointcut = null;
for (Class<? extends Annotation> annotation : annotations) {
if (pointcut == null) {
pointcut = new ComposablePointcut(classOrMethod(annotation));
}
else {
pointcut.union(classOrMethod(annotation));
}
}
return pointcut;
}
private static Pointcut classOrMethod(Class<? extends Annotation> annotation) {
return Pointcuts.union(new AnnotationMatchingPointcut(null, annotation, true),
new AnnotationMatchingPointcut(annotation, true));
}
private AuthorizationMethodPointcuts() {
}
}

View File

@ -1,106 +0,0 @@
/*
* Copyright 2002-2021 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.authorization.method;
import java.util.List;
import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationMethodAfterAdvice} which delegates to specific
* {@link AuthorizationMethodAfterAdvice}s and returns the result (possibly modified) from
* the {@link MethodInvocation}.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class DelegatingAuthorizationMethodAfterAdvice<T> implements AuthorizationMethodAfterAdvice<T> {
private final Log logger = LogFactory.getLog(getClass());
private final Pointcut pointcut;
private final List<AuthorizationMethodAfterAdvice<T>> delegates;
/**
* Creates an instance.
* @param delegates the {@link AuthorizationMethodAfterAdvice}s to use
*/
public DelegatingAuthorizationMethodAfterAdvice(List<AuthorizationMethodAfterAdvice<T>> delegates) {
Assert.notEmpty(delegates, "delegates cannot be empty");
this.delegates = delegates;
ComposablePointcut pointcut = null;
for (AuthorizationMethodAfterAdvice<?> advice : delegates) {
if (pointcut == null) {
pointcut = new ComposablePointcut(advice.getPointcut());
}
else {
pointcut.union(advice.getPointcut());
}
}
this.pointcut = pointcut;
}
/**
* {@inheritDoc}
*/
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
/**
* Delegate to a series of {@link AuthorizationMethodAfterAdvice}s, each of which may
* replace the {@code returnedObject} with its own
*
* Advices may be of type {@link AuthorizationManagerMethodAfterAdvice} in which case,
* they will throw an
* {@link org.springframework.security.access.AccessDeniedException} in the event that
* they deny access to the {@code returnedObject}.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link MethodAuthorizationContext} to check
* @param returnedObject the returned object from the original method invocation
* @throws org.springframework.security.access.AccessDeniedException if any delegate
* advices deny access
*/
@Override
public Object after(Supplier<Authentication> authentication, T object, Object returnedObject) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Post Authorizing %s from %s", returnedObject, object));
}
Object result = returnedObject;
for (AuthorizationMethodAfterAdvice<T> delegate : this.delegates) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(
LogMessage.format("Checking authorization on %s from %s using %s", result, object, delegate));
}
result = delegate.after(authentication, object, result);
}
return result;
}
}

View File

@ -1,101 +0,0 @@
/*
* Copyright 2002-2021 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.authorization.method;
import java.util.List;
import java.util.function.Supplier;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationMethodBeforeAdvice} which delegates to a specific
* {@link AuthorizationMethodBeforeAdvice} and grants access if all
* {@link AuthorizationMethodBeforeAdvice}s granted or abstained. Denies access only if
* one of the {@link AuthorizationMethodBeforeAdvice}s denied.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class DelegatingAuthorizationMethodBeforeAdvice<T> implements AuthorizationMethodBeforeAdvice<T> {
private final Log logger = LogFactory.getLog(getClass());
private final Pointcut pointcut;
private final List<AuthorizationMethodBeforeAdvice<T>> delegates;
/**
* Creates an instance.
* @param delegates the {@link AuthorizationMethodBeforeAdvice}s to use
*/
public DelegatingAuthorizationMethodBeforeAdvice(List<AuthorizationMethodBeforeAdvice<T>> delegates) {
Assert.notEmpty(delegates, "delegates cannot be empty");
this.delegates = delegates;
ComposablePointcut pointcut = null;
for (AuthorizationMethodBeforeAdvice<?> advice : delegates) {
if (pointcut == null) {
pointcut = new ComposablePointcut(advice.getPointcut());
}
else {
pointcut.union(advice.getPointcut());
}
}
this.pointcut = pointcut;
}
/**
* {@inheritDoc}
*/
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
/**
* Delegate to a series of {@link AuthorizationMethodBeforeAdvice}s
*
* Advices may be of type {@link AuthorizationManagerMethodBeforeAdvice} in which
* case, they will throw an
* {@link org.springframework.security.access.AccessDeniedException} in the event that
* they deny access.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link MethodAuthorizationContext} to check
* @throws org.springframework.security.access.AccessDeniedException if any delegate
* advices deny access
*/
@Override
public void before(Supplier<Authentication> authentication, T object) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Pre Authorizing %s", object));
}
for (AuthorizationMethodBeforeAdvice<T> delegate : this.delegates) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Checking authorization on %s using %s", object, delegate));
}
delegate.before(authentication, object);
}
}
}

View File

@ -0,0 +1,89 @@
/*
* Copyright 2002-2021 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.authorization.method;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.security.core.Authentication;
/**
* Provides security interception of AOP Alliance based method invocations.
*
* Delegates to a collection of {@link AuthorizationMethodInterceptor}s
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class DelegatingAuthorizationMethodInterceptor implements AuthorizationMethodInterceptor {
private final List<AuthorizationMethodInterceptor> interceptors;
private final Pointcut pointcut;
/**
* Creates an instance using the provided parameters
* @param interceptors the delegate {@link AuthorizationMethodInterceptor}s to use
*/
public DelegatingAuthorizationMethodInterceptor(AuthorizationMethodInterceptor... interceptors) {
this(Arrays.asList(interceptors));
}
/**
* Creates an instance using the provided parameters
* @param interceptors the delegate {@link AuthorizationMethodInterceptor}s to use
*/
public DelegatingAuthorizationMethodInterceptor(List<AuthorizationMethodInterceptor> interceptors) {
ComposablePointcut pointcut = null;
for (AuthorizationMethodInterceptor interceptor : interceptors) {
if (pointcut == null) {
pointcut = new ComposablePointcut(interceptor.getPointcut());
}
else {
pointcut.union(interceptor.getPointcut());
}
}
this.pointcut = pointcut;
this.interceptors = interceptors;
}
/**
* Enforce security on this {@link MethodInvocation}.
* @param mi the method being invoked which requires a security decision
* @return the returned value from the {@link MethodInvocation}, possibly altered by
* the configured {@link AuthorizationMethodInterceptor}s
*/
@Override
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
return new AuthorizationMethodInvocation(authentication, mi, this.interceptors).proceed();
}
/**
* {@inheritDoc}
*/
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
}

View File

@ -39,14 +39,14 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has
* access to the {@link MethodInvocation} by evaluating if the {@link Authentication}
* An {@link AuthorizationManager} which can determine if an {@link Authentication} may
* invoke the {@link MethodInvocation} by evaluating if the {@link Authentication}
* contains a specified authority from the JSR-250 security annotations.
*
* @author Evgeniy Cheban
* @since 5.5
*/
public final class Jsr250AuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> {
public final class Jsr250AuthorizationManager implements AuthorizationManager<MethodInvocation> {
private static final Set<Class<? extends Annotation>> JSR250_ANNOTATIONS = new HashSet<>();
@ -72,25 +72,24 @@ public final class Jsr250AuthorizationManager implements AuthorizationManager<Me
/**
* Determine if an {@link Authentication} has access to a method by evaluating the
* {@link DenyAll}, {@link PermitAll}, and {@link RolesAllowed} annotations that
* {@link MethodAuthorizationContext} specifies.
* {@link AuthorizationMethodInvocation} specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @param methodInvocation the {@link AuthorizationMethodInvocation} to check
* @return an {@link AuthorizationDecision} or null if the JSR-250 security
* annotations is not present
*/
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext) {
AuthorizationManager<MethodAuthorizationContext> delegate = this.registry
.getManager(methodAuthorizationContext);
return delegate.check(authentication, methodAuthorizationContext);
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation methodInvocation) {
AuthorizationManager<MethodInvocation> delegate = this.registry
.getManager((AuthorizationMethodInvocation) methodInvocation);
return delegate.check(authentication, methodInvocation);
}
private final class Jsr250AuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry {
@NonNull
@Override
AuthorizationManager<MethodAuthorizationContext> resolveManager(Method method, Class<?> targetClass) {
AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass) {
for (Annotation annotation : findJsr250Annotations(method, targetClass)) {
if (annotation instanceof DenyAll) {
return (a, o) -> new AuthorizationDecision(false);

View File

@ -1,66 +0,0 @@
/*
* Copyright 2002-2021 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.authorization.method;
import org.aopalliance.intercept.MethodInvocation;
/**
* An authorization context which is holds the {@link MethodInvocation} and the target
* class
*
* @author Evgeniy Cheban
* @since 5.5
*/
public final class MethodAuthorizationContext {
private final MethodInvocation methodInvocation;
private final Class<?> targetClass;
/**
* Creates an instance.
* @param methodInvocation the {@link MethodInvocation} to use
* @param targetClass the target class to use
*/
public MethodAuthorizationContext(MethodInvocation methodInvocation, Class<?> targetClass) {
this.methodInvocation = methodInvocation;
this.targetClass = targetClass;
}
/**
* Return the {@link MethodInvocation}.
* @return the {@link MethodInvocation}
*/
public MethodInvocation getMethodInvocation() {
return this.methodInvocation;
}
/**
* Return the target class.
* @return the target class
*/
public Class<?> getTargetClass() {
return this.targetClass;
}
@Override
public String toString() {
return "MethodAuthorizationContext[methodInvocation=" + this.methodInvocation + ", targetClass="
+ this.targetClass + ']';
}
}

View File

@ -36,22 +36,21 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has
* access to the {@link MethodInvocation} by evaluating an expression from the
* {@link PostAuthorize} annotation.
* An {@link AuthorizationManager} which can determine if an {@link Authentication} may
* return the result from an invoked {@link MethodInvocation} by evaluating an expression
* from the {@link PostAuthorize} annotation.
*
* @author Evgeniy Cheban
* @since 5.5
*/
public final class PostAuthorizeAuthorizationManager
implements AfterMethodAuthorizationManager<MethodAuthorizationContext> {
public final class PostAuthorizeAuthorizationManager implements AfterMethodAuthorizationManager<MethodInvocation> {
private final PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
/**
* Sets the {@link MethodSecurityExpressionHandler}.
* Use this the {@link MethodSecurityExpressionHandler}.
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
*/
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
@ -62,22 +61,21 @@ public final class PostAuthorizeAuthorizationManager
/**
* Determine if an {@link Authentication} has access to the returned object by
* evaluating the {@link PostAuthorize} annotation that the
* {@link MethodAuthorizationContext} specifies.
* {@link AuthorizationMethodInvocation} specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @param mi the {@link AuthorizationMethodInvocation} to check
* @param returnedObject the returned object to check
* @return an {@link AuthorizationDecision} or {@code null} if the
* {@link PostAuthorize} annotation is not present
*/
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext, Object returnedObject) {
ExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext);
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi,
Object returnedObject) {
ExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi);
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
return null;
}
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(),
methodAuthorizationContext.getMethodInvocation());
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
this.expressionHandler.setReturnObject(returnedObject, ctx);
boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);
return new AuthorizationDecision(granted);

View File

@ -34,16 +34,15 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationMethodAfterAdvice} which filters a <code>returnedObject</code>
* from the {@link MethodInvocation} by evaluating an expression from the
* {@link PostFilter} annotation.
* An {@link AuthorizationMethodInterceptor} which filters a {@code returnedObject} from
* the {@link MethodInvocation} by evaluating an expression from the {@link PostFilter}
* annotation.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class PostFilterAuthorizationMethodAfterAdvice
implements AuthorizationMethodAfterAdvice<MethodAuthorizationContext> {
public final class PostFilterAuthorizationMethodInterceptor implements AuthorizationMethodInterceptor {
private final PostFilterExpressionAttributeRegistry registry = new PostFilterExpressionAttributeRegistry();
@ -52,16 +51,15 @@ public final class PostFilterAuthorizationMethodAfterAdvice
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
/**
* Create a {@link PostFilterAuthorizationMethodAfterAdvice} using the provided
* Creates a {@link PostFilterAuthorizationMethodInterceptor} using the provided
* parameters
* @param pointcut the {@link Pointcut} for when this advice applies
*/
public PostFilterAuthorizationMethodAfterAdvice(Pointcut pointcut) {
this.pointcut = pointcut;
public PostFilterAuthorizationMethodInterceptor() {
this.pointcut = AuthorizationMethodPointcuts.forAnnotations(PostFilter.class);
}
/**
* Sets the {@link MethodSecurityExpressionHandler}.
* Use this {@link MethodSecurityExpressionHandler}.
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
*/
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
@ -79,24 +77,19 @@ public final class PostFilterAuthorizationMethodAfterAdvice
/**
* Filter a {@code returnedObject} using the {@link PostFilter} annotation that the
* {@link MethodAuthorizationContext} specifies.
* {@link AuthorizationMethodInvocation} specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* check
* @param mi the {@link AuthorizationMethodInvocation} to check check
* @return filtered {@code returnedObject}
*/
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext methodAuthorizationContext,
Object returnedObject) {
if (returnedObject == null) {
return null;
}
ExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext);
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
Object returnedObject = mi.proceed();
ExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi);
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
return returnedObject;
}
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(),
methodAuthorizationContext.getMethodInvocation());
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
return this.expressionHandler.filter(returnedObject, attribute.getExpression(), ctx);
}
@ -111,7 +104,7 @@ public final class PostFilterAuthorizationMethodAfterAdvice
if (postFilter == null) {
return ExpressionAttribute.NULL_ATTRIBUTE;
}
Expression postFilterExpression = PostFilterAuthorizationMethodAfterAdvice.this.expressionHandler
Expression postFilterExpression = PostFilterAuthorizationMethodInterceptor.this.expressionHandler
.getExpressionParser().parseExpression(postFilter.value());
return new ExpressionAttribute(postFilterExpression);
}

View File

@ -36,14 +36,14 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has
* access to the {@link MethodInvocation} by evaluating an expression from the
* An {@link AuthorizationManager} which can determine if an {@link Authentication} may
* invoke the {@link MethodInvocation} by evaluating an expression from the
* {@link PreAuthorize} annotation.
*
* @author Evgeniy Cheban
* @since 5.5
*/
public final class PreAuthorizeAuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> {
public final class PreAuthorizeAuthorizationManager implements AuthorizationManager<MethodInvocation> {
private final PreAuthorizeExpressionAttributeRegistry registry = new PreAuthorizeExpressionAttributeRegistry();
@ -61,21 +61,19 @@ public final class PreAuthorizeAuthorizationManager implements AuthorizationMana
/**
* Determine if an {@link Authentication} has access to a method by evaluating an
* expression from the {@link PreAuthorize} annotation that the
* {@link MethodAuthorizationContext} specifies.
* {@link AuthorizationMethodInvocation} specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @param mi the {@link AuthorizationMethodInvocation} to check
* @return an {@link AuthorizationDecision} or {@code null} if the
* {@link PreAuthorize} annotation is not present
*/
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext) {
ExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext);
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) {
ExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi);
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
return null;
}
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(),
methodAuthorizationContext.getMethodInvocation());
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);
return new AuthorizationDecision(granted);
}

View File

@ -35,15 +35,14 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* An {@link AuthorizationMethodBeforeAdvice} which filters a method argument by
* evaluating an expression from the {@link PreFilter} annotation.
* An {@link AuthorizationMethodInterceptor} which filters a method argument by evaluating
* an expression from the {@link PreFilter} annotation.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.5
*/
public final class PreFilterAuthorizationMethodBeforeAdvice
implements AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> {
public final class PreFilterAuthorizationMethodInterceptor implements AuthorizationMethodInterceptor {
private final PreFilterExpressionAttributeRegistry registry = new PreFilterExpressionAttributeRegistry();
@ -52,12 +51,11 @@ public final class PreFilterAuthorizationMethodBeforeAdvice
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
/**
* Creates a {@link PreFilterAuthorizationMethodBeforeAdvice} using the provided
* Creates a {@link PreFilterAuthorizationMethodInterceptor} using the provided
* parameters
* @param pointcut the {@link Pointcut} for when this advice applies
*/
public PreFilterAuthorizationMethodBeforeAdvice(Pointcut pointcut) {
this.pointcut = pointcut;
public PreFilterAuthorizationMethodInterceptor() {
this.pointcut = AuthorizationMethodPointcuts.forAnnotations(PreFilter.class);
}
/**
@ -79,20 +77,20 @@ public final class PreFilterAuthorizationMethodBeforeAdvice
/**
* Filter the method argument specified in the {@link PreFilter} annotation that
* {@link MethodAuthorizationContext} specifies.
* {@link AuthorizationMethodInvocation} specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @param mi the {@link AuthorizationMethodInvocation} to check
*/
@Override
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext methodAuthorizationContext) {
PreFilterExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext);
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
PreFilterExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi);
if (attribute == PreFilterExpressionAttribute.NULL_ATTRIBUTE) {
return;
return mi.proceed();
}
MethodInvocation mi = methodAuthorizationContext.getMethodInvocation();
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
Object filterTarget = findFilterTarget(attribute.filterTarget, ctx, mi);
this.expressionHandler.filter(filterTarget, attribute.getExpression(), ctx);
return mi.proceed();
}
private Object findFilterTarget(String filterTargetName, EvaluationContext ctx, MethodInvocation methodInvocation) {
@ -126,7 +124,7 @@ public final class PreFilterAuthorizationMethodBeforeAdvice
if (preFilter == null) {
return PreFilterExpressionAttribute.NULL_ATTRIBUTE;
}
Expression preFilterExpression = PreFilterAuthorizationMethodBeforeAdvice.this.expressionHandler
Expression preFilterExpression = PreFilterAuthorizationMethodInterceptor.this.expressionHandler
.getExpressionParser().parseExpression(preFilter.value());
return new PreFilterExpressionAttribute(preFilterExpression, preFilter.filterTarget());
}

View File

@ -31,38 +31,36 @@ import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
/**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has
* access to the {@link MethodInvocation} by evaluating if the {@link Authentication}
* An {@link AuthorizationManager} which can determine if an {@link Authentication} may
* invoke the {@link MethodInvocation} by evaluating if the {@link Authentication}
* contains a specified authority from the Spring Security's {@link Secured} annotation.
*
* @author Evgeniy Cheban
* @since 5.5
*/
public final class SecuredAuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> {
public final class SecuredAuthorizationManager implements AuthorizationManager<MethodInvocation> {
private final SecuredAuthorizationManagerRegistry registry = new SecuredAuthorizationManagerRegistry();
/**
* Determine if an {@link Authentication} has access to a method by evaluating the
* {@link Secured} annotation that {@link MethodAuthorizationContext} specifies.
* {@link Secured} annotation that {@link AuthorizationMethodInvocation} specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @param mi the {@link AuthorizationMethodInvocation} to check
* @return an {@link AuthorizationDecision} or null if the {@link Secured} annotation
* is not present
*/
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext) {
AuthorizationManager<MethodAuthorizationContext> delegate = this.registry
.getManager(methodAuthorizationContext);
return delegate.check(authentication, methodAuthorizationContext);
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) {
AuthorizationManager<MethodInvocation> delegate = this.registry.getManager((AuthorizationMethodInvocation) mi);
return delegate.check(authentication, mi);
}
private static final class SecuredAuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry {
@NonNull
@Override
AuthorizationManager<MethodAuthorizationContext> resolveManager(Method method, Class<?> targetClass) {
AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass) {
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
Secured secured = findSecuredAnnotation(specificMethod);
return (secured != null) ? AuthorityAuthorizationManager.hasAnyAuthority(secured.value()) : NULL_MANAGER;

View File

@ -27,42 +27,44 @@ import org.springframework.security.core.Authentication;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link AuthorizationManagerMethodAfterAdvice}.
* Tests for {@link AuthorizationManagerAfterMethodInterceptor}.
*
* @author Evgeniy Cheban
*/
public class AuthorizationManagerMethodAfterAdviceTests {
public class AuthorizationManagerAfterMethodInterceptorTests {
@Test
public void instantiateWhenMethodMatcherNullThenException() {
AfterMethodAuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(
AfterMethodAuthorizationManager.class);
assertThatIllegalArgumentException()
.isThrownBy(() -> new AuthorizationManagerMethodAfterAdvice<>(null, mockAuthorizationManager))
.isThrownBy(() -> new AuthorizationManagerAfterMethodInterceptor(null, mockAuthorizationManager))
.withMessage("pointcut cannot be null");
}
@Test
public void instantiateWhenAuthorizationManagerNullThenException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new AuthorizationManagerMethodAfterAdvice<>(mock(Pointcut.class), null))
.isThrownBy(() -> new AuthorizationManagerAfterMethodInterceptor(mock(Pointcut.class), null))
.withMessage("authorizationManager cannot be null");
}
@Test
public void beforeWhenMockAuthorizationManagerThenVerifyAndReturnedObject() {
public void beforeWhenMockAuthorizationManagerThenVerifyAndReturnedObject() throws Throwable {
Supplier<Authentication> authentication = TestAuthentication::authenticatedUser;
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
Object returnedObject = new Object();
given(mockMethodInvocation.proceed()).willReturn(returnedObject);
AfterMethodAuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(
AfterMethodAuthorizationManager.class);
AuthorizationManagerMethodAfterAdvice<MethodInvocation> advice = new AuthorizationManagerMethodAfterAdvice<>(
mock(Pointcut.class), mockAuthorizationManager);
Object result = advice.after(authentication, mockMethodInvocation, returnedObject);
AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(
Pointcut.TRUE, mockAuthorizationManager);
Object result = advice.invoke(authentication, mockMethodInvocation);
assertThat(result).isEqualTo(returnedObject);
verify(mockAuthorizationManager).verify(authentication, mockMethodInvocation, returnedObject);
}

View File

@ -31,34 +31,35 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link AuthorizationManagerMethodBeforeAdvice}.
* Tests for {@link AuthorizationManagerBeforeMethodInterceptor}.
*
* @author Evgeniy Cheban
*/
public class AuthorizationManagerMethodBeforeAdviceTests {
public class AuthorizationManagerBeforeMethodInterceptorTests {
@Test
public void instantiateWhenMethodMatcherNullThenException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new AuthorizationManagerMethodBeforeAdvice<>(null, mock(AuthorizationManager.class)))
.isThrownBy(
() -> new AuthorizationManagerBeforeMethodInterceptor(null, mock(AuthorizationManager.class)))
.withMessage("pointcut cannot be null");
}
@Test
public void instantiateWhenAuthorizationManagerNullThenException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new AuthorizationManagerMethodBeforeAdvice<>(mock(Pointcut.class), null))
.isThrownBy(() -> new AuthorizationManagerBeforeMethodInterceptor(mock(Pointcut.class), null))
.withMessage("authorizationManager cannot be null");
}
@Test
public void beforeWhenMockAuthorizationManagerThenVerify() {
public void beforeWhenMockAuthorizationManagerThenVerify() throws Throwable {
Supplier<Authentication> authentication = TestAuthentication::authenticatedUser;
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
AuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(AuthorizationManager.class);
AuthorizationManagerMethodBeforeAdvice<MethodInvocation> advice = new AuthorizationManagerMethodBeforeAdvice<>(
mock(Pointcut.class), mockAuthorizationManager);
advice.before(authentication, mockMethodInvocation);
AuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(
Pointcut.TRUE, mockAuthorizationManager);
advice.invoke(authentication, mockMethodInvocation);
verify(mockAuthorizationManager).verify(authentication, mockMethodInvocation);
}

View File

@ -0,0 +1,167 @@
/*
* Copyright 2002-2021 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.authorization.method;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.Test;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AopUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link AuthorizationMethodPointcuts}
*/
public class AuthorizationMethodPointcutsTests {
@Test
public void forAnnotationsWhenAnnotationThenClassBasedAnnotationPointcut() {
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
assertThat(AopUtils.canApply(preAuthorize, ClassController.class)).isTrue();
assertThat(AopUtils.canApply(preAuthorize, NoController.class)).isFalse();
}
@Test
public void forAnnotationsWhenAnnotationThenMethodBasedAnnotationPointcut() {
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
assertThat(AopUtils.canApply(preAuthorize, MethodController.class)).isTrue();
}
@Test
public void forAnnotationsWhenAnnotationThenClassInheritancePointcut() {
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
assertThat(AopUtils.canApply(preAuthorize, InterfacedClassController.class)).isTrue();
}
@Test
public void forAnnotationsWhenAnnotationThenMethodInheritancePointcut() {
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
assertThat(AopUtils.canApply(preAuthorize, InterfacedMethodController.class)).isTrue();
}
@Test
public void forAnnotationsWhenAnnotationThenAnnotationClassInheritancePointcut() {
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
assertThat(AopUtils.canApply(preAuthorize, InterfacedAnnotationClassController.class)).isTrue();
}
@Test
public void forAnnotationsWhenAnnotationThenAnnotationMethodInheritancePointcut() {
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
assertThat(AopUtils.canApply(preAuthorize, InterfacedAnnotationMethodController.class)).isTrue();
}
@PreAuthorize("hasAuthority('APP')")
public static class ClassController {
String methodOne(String paramOne) {
return "value";
}
}
public static class MethodController {
@PreAuthorize("hasAuthority('APP')")
String methodOne(String paramOne) {
return "value";
}
}
public static class NoController {
String methodOne(String paramOne) {
return "value";
}
}
@PreAuthorize("hasAuthority('APP')")
public interface ClassControllerInterface {
String methodOne(String paramOne);
}
public static class InterfacedClassController implements ClassControllerInterface {
public String methodOne(String paramOne) {
return "value";
}
}
public interface MethodControllerInterface {
@PreAuthorize("hasAuthority('APP')")
String methodOne(String paramOne);
}
public static class InterfacedMethodController implements MethodControllerInterface {
public String methodOne(String paramOne) {
return "value";
}
}
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasAuthority('APP')")
@interface MyAnnotation {
}
@MyAnnotation
public interface ClassAnnotationControllerInterface {
String methodOne(String paramOne);
}
public static class InterfacedAnnotationClassController implements ClassAnnotationControllerInterface {
public String methodOne(String paramOne) {
return "value";
}
}
public interface MethodAnnotationControllerInterface {
@MyAnnotation
String methodOne(String paramOne);
}
public static class InterfacedAnnotationMethodController implements MethodAnnotationControllerInterface {
public String methodOne(String paramOne) {
return "value";
}
}
}

View File

@ -1,165 +0,0 @@
/*
* Copyright 2002-2021 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.authorization.method;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import org.junit.Test;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.core.Authentication;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DelegatingAuthorizationMethodAfterAdvice}.
*
* @author Evgeniy Cheban
*/
public class DelegatingAuthorizationMethodAfterAdviceTests {
@Test
public void methodMatcherWhenNoneMatchesThenNotMatches() throws Exception {
List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> delegates = new ArrayList<>();
delegates.add(new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext object,
Object returnedObject) {
return returnedObject;
}
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
};
}
});
delegates.add(new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext object,
Object returnedObject) {
return returnedObject;
}
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
};
}
});
DelegatingAuthorizationMethodAfterAdvice advice = new DelegatingAuthorizationMethodAfterAdvice(delegates);
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomething"), TestClass.class)).isFalse();
}
@Test
public void methodMatcherWhenAnyMatchesThenMatches() throws Exception {
List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> delegates = new ArrayList<>();
delegates.add(new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext object,
Object returnedObject) {
return returnedObject;
}
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
};
}
});
delegates.add(new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext object,
Object returnedObject) {
return returnedObject;
}
@Override
public Pointcut getPointcut() {
return Pointcut.TRUE;
}
});
DelegatingAuthorizationMethodAfterAdvice advice = new DelegatingAuthorizationMethodAfterAdvice(delegates);
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomething"), TestClass.class)).isTrue();
}
@Test
public void checkWhenDelegatingAdviceModifiesReturnedObjectThenModifiedReturnedObject() throws Exception {
List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> delegates = new ArrayList<>();
delegates.add(new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext object,
Object returnedObject) {
return returnedObject + "b";
}
@Override
public Pointcut getPointcut() {
return Pointcut.TRUE;
}
});
delegates.add(new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext object,
Object returnedObject) {
return returnedObject + "c";
}
@Override
public Pointcut getPointcut() {
return Pointcut.TRUE;
}
});
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
DelegatingAuthorizationMethodAfterAdvice advice = new DelegatingAuthorizationMethodAfterAdvice(delegates);
Object result = advice.after(TestAuthentication::authenticatedUser, methodAuthorizationContext, "a");
assertThat(result).isEqualTo("abc");
}
public static class TestClass {
public String doSomething() {
return null;
}
}
}

View File

@ -1,164 +0,0 @@
/*
* Copyright 2002-2021 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.authorization.method;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import org.junit.Test;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.core.Authentication;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for {@link DelegatingAuthorizationMethodBeforeAdvice}.
*
* @author Evgeniy Cheban
*/
public class DelegatingAuthorizationMethodBeforeAdviceTests {
@Test
public void methodMatcherWhenNoneMatchesThenNotMatches() throws Exception {
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> delegates = new ArrayList<>();
delegates.add(new AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>() {
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
};
}
@Override
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext object) {
}
});
delegates.add(new AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>() {
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
};
}
@Override
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext object) {
}
});
DelegatingAuthorizationMethodBeforeAdvice advice = new DelegatingAuthorizationMethodBeforeAdvice(delegates);
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomething"), TestClass.class)).isFalse();
}
@Test
public void methodMatcherWhenAnyMatchesThenMatches() throws Exception {
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> delegates = new ArrayList<>();
delegates.add(new AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>() {
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
};
}
@Override
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext object) {
}
});
delegates.add(new AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>() {
@Override
public Pointcut getPointcut() {
return Pointcut.TRUE;
}
@Override
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext object) {
}
});
DelegatingAuthorizationMethodBeforeAdvice advice = new DelegatingAuthorizationMethodBeforeAdvice(delegates);
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomething"), TestClass.class)).isTrue();
}
@Test
public void checkWhenAllGrantsOrAbstainsThenPasses() throws Exception {
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> delegates = new ArrayList<>();
delegates.add(new AuthorizationManagerMethodBeforeAdvice<>(Pointcut.TRUE, (a, o) -> null));
delegates.add(
new AuthorizationManagerMethodBeforeAdvice<>(Pointcut.TRUE, (a, o) -> new AuthorizationDecision(true)));
delegates.add(new AuthorizationManagerMethodBeforeAdvice<>(Pointcut.TRUE, (a, o) -> null));
DelegatingAuthorizationMethodBeforeAdvice advice = new DelegatingAuthorizationMethodBeforeAdvice(delegates);
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext);
}
@Test
public void checkWhenAnyDeniesThenAccessDeniedException() throws Exception {
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> delegates = new ArrayList<>();
delegates.add(new AuthorizationManagerMethodBeforeAdvice<>(Pointcut.TRUE, (a, o) -> null));
delegates.add(
new AuthorizationManagerMethodBeforeAdvice<>(Pointcut.TRUE, (a, o) -> new AuthorizationDecision(true)));
delegates.add(new AuthorizationManagerMethodBeforeAdvice<>(Pointcut.TRUE,
(a, o) -> new AuthorizationDecision(false)));
DelegatingAuthorizationMethodBeforeAdvice advice = new DelegatingAuthorizationMethodBeforeAdvice(delegates);
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext))
.withMessage("Access Denied");
}
@Test
public void checkWhenDelegatesEmptyThenFails() {
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> new DelegatingAuthorizationMethodBeforeAdvice(Collections.emptyList()));
}
public static class TestClass {
public void doSomething() {
}
}
}

View File

@ -16,8 +16,10 @@
package org.springframework.security.authorization.method;
import java.util.Arrays;
import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.After;
import org.junit.Test;
@ -32,18 +34,17 @@ import org.springframework.security.core.context.SecurityContextImpl;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
/**
* Tests for {@link AuthorizationMethodInterceptor}.
* Tests for {@link DelegatingAuthorizationMethodInterceptor}.
*
* @author Evgeniy Cheban
*/
public class AuthorizationMethodInterceptorTests {
public class DelegatingAuthorizationMethodInterceptorTests {
@After
public void tearDown() {
@ -56,41 +57,39 @@ public class AuthorizationMethodInterceptorTests {
SecurityContextHolder.setContext(new SecurityContextImpl(authentication));
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString");
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> mockBeforeAdvice = mock(
AuthorizationMethodBeforeAdvice.class);
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> mockAfterAdvice = mock(
AuthorizationMethodAfterAdvice.class);
given(mockAfterAdvice.after(any(), any(MethodAuthorizationContext.class), eq(null))).willReturn("abc");
AuthorizationMethodInterceptor interceptor = new AuthorizationMethodInterceptor(mockBeforeAdvice,
mockAfterAdvice);
Object result = interceptor.invoke(mockMethodInvocation);
AuthorizationMethodInterceptor interceptor = mock(AuthorizationMethodInterceptor.class);
given(interceptor.getPointcut()).willReturn(Pointcut.TRUE);
given(interceptor.invoke(any(), any(AuthorizationMethodInvocation.class))).willReturn("abc");
DelegatingAuthorizationMethodInterceptor chain = new DelegatingAuthorizationMethodInterceptor(
Arrays.asList(interceptor));
Object result = chain.invoke(mockMethodInvocation);
assertThat(result).isEqualTo("abc");
verify(mockAfterAdvice).after(any(), any(MethodAuthorizationContext.class), eq(null));
verify(interceptor).invoke(any(), any(AuthorizationMethodInvocation.class));
}
@Test
public void invokeWhenNotAuthenticatedThenAuthenticationCredentialsNotFoundException() throws Exception {
public void invokeWhenNotAuthenticatedThenAuthenticationCredentialsNotFoundException() throws Throwable {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString");
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> beforeAdvice = new AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>() {
AuthorizationMethodInterceptor first = new AuthorizationMethodInterceptor() {
@Override
public Pointcut getPointcut() {
return Pointcut.TRUE;
}
@Override
public void before(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext) {
authentication.get();
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) {
return authentication.get();
}
};
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> mockAfterAdvice = mock(
AuthorizationMethodAfterAdvice.class);
AuthorizationMethodInterceptor interceptor = new AuthorizationMethodInterceptor(beforeAdvice, mockAfterAdvice);
AuthorizationMethodInterceptor second = mock(AuthorizationMethodInterceptor.class);
given(second.getPointcut()).willReturn(Pointcut.TRUE);
DelegatingAuthorizationMethodInterceptor interceptor = new DelegatingAuthorizationMethodInterceptor(
Arrays.asList(first, second));
assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
.isThrownBy(() -> interceptor.invoke(mockMethodInvocation))
.withMessage("An Authentication object was not found in the SecurityContext");
verifyNoInteractions(mockAfterAdvice);
verify(second, times(0)).invoke(any(), any());
}
public static class TestClass {

View File

@ -16,6 +16,7 @@
package org.springframework.security.authorization.method;
import java.util.Collections;
import java.util.function.Supplier;
import javax.annotation.security.DenyAll;
@ -62,64 +63,59 @@ public class Jsr250AuthorizationManagerTests {
@Test
public void checkDoSomethingWhenNoJsr250AnnotationsThenNullDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNull();
}
@Test
public void checkPermitAllRolesAllowedAdminWhenRoleUserThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"permitAllRolesAllowedAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkDenyAllRolesAllowedAdminWhenRoleAdminThenDeniedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"denyAllRolesAllowedAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}
@Test
public void checkRolesAllowedUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"rolesAllowedUserOrAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkRolesAllowedUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"rolesAllowedUserOrAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@ -128,12 +124,12 @@ public class Jsr250AuthorizationManagerTests {
public void checkRolesAllowedUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
"ROLE_ANONYMOUS");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"rolesAllowedUserOrAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, methodAuthorizationContext);
AuthorizationDecision decision = manager.check(authentication, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}

View File

@ -58,11 +58,10 @@ public class PostAuthorizeAuthorizationManagerTests {
public void checkDoSomethingWhenNoPostAuthorizeAnnotationThenNullDecision() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething", new Class[] {}, new Object[] {});
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext, null);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation, null);
assertThat(decision).isNull();
}
@ -70,11 +69,10 @@ public class PostAuthorizeAuthorizationManagerTests {
public void checkDoSomethingStringWhenArgIsGrantThenGrantedDecision() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString", new Class[] { String.class }, new Object[] { "grant" });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext, null);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation, null);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@ -83,11 +81,10 @@ public class PostAuthorizeAuthorizationManagerTests {
public void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString", new Class[] { String.class }, new Object[] { "deny" });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext, null);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation, null);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}
@ -97,11 +94,10 @@ public class PostAuthorizeAuthorizationManagerTests {
List<String> list = Arrays.asList("grant", "deny");
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingList", new Class[] { List.class }, new Object[] { list });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext, list);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation, list);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@ -111,11 +107,10 @@ public class PostAuthorizeAuthorizationManagerTests {
List<String> list = Collections.singletonList("deny");
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingList", new Class[] { List.class }, new Object[] { list });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext, list);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation, list);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}

View File

@ -16,14 +16,12 @@
package org.springframework.security.authorization.method;
import java.lang.reflect.Method;
import java.util.Collections;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.Test;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
@ -34,43 +32,38 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link PostFilterAuthorizationMethodAfterAdvice}.
* Tests for {@link PostFilterAuthorizationMethodInterceptor}.
*
* @author Evgeniy Cheban
*/
public class PostFilterAuthorizationMethodAfterAdviceTests {
public class PostFilterAuthorizationMethodInterceptorTests {
@Test
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice(Pointcut.TRUE);
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
advice.setExpressionHandler(expressionHandler);
assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler);
}
@Test
public void setExpressionHandlerWhenNullThenException() {
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice(Pointcut.TRUE);
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null))
.withMessage("expressionHandler cannot be null");
}
@Test
public void methodMatcherWhenMethodHasNotPostFilterAnnotationThenNotMatches() throws Exception {
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice(
new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
});
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomething"), TestClass.class)).isFalse();
assertThat(methodMatcher.matches(NoPostFilterClass.class.getMethod("doSomething"), NoPostFilterClass.class))
.isFalse();
}
@Test
public void methodMatcherWhenMethodHasPostFilterAnnotationThenMatches() throws Exception {
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice(Pointcut.TRUE);
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
assertThat(
methodMatcher.matches(TestClass.class.getMethod("doSomethingArray", String[].class), TestClass.class))
@ -78,24 +71,25 @@ public class PostFilterAuthorizationMethodAfterAdviceTests {
}
@Test
public void afterWhenArrayNotNullThenFilteredArray() throws Exception {
public void afterWhenArrayNotNullThenFilteredArray() throws Throwable {
String[] array = { "john", "bob" };
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingArrayClassLevel", new Class[] { String[].class }, new Object[] { array });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice(Pointcut.TRUE);
Object result = advice.after(TestAuthentication::authenticatedUser, methodAuthorizationContext, array);
"doSomethingArrayClassLevel", new Class[] { String[].class }, new Object[] { array }) {
@Override
public Object proceed() {
return array;
}
};
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
Object result = advice.invoke(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(result).asInstanceOf(InstanceOfAssertFactories.array(String[].class)).containsOnly("john");
}
@PostFilter("filterObject == 'john'")
public static class TestClass {
public void doSomething() {
}
@PostFilter("filterObject == 'john'")
public String[] doSomethingArray(String[] array) {
return array;
@ -107,4 +101,12 @@ public class PostFilterAuthorizationMethodAfterAdviceTests {
}
public static class NoPostFilterClass {
public void doSomething() {
}
}
}

View File

@ -16,6 +16,8 @@
package org.springframework.security.authorization.method;
import java.util.Collections;
import org.junit.Test;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
@ -54,11 +56,10 @@ public class PreAuthorizeAuthorizationManagerTests {
public void checkDoSomethingWhenNoPostAuthorizeAnnotationThenNullDecision() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething", new Class[] {}, new Object[] {});
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNull();
}
@ -66,11 +67,10 @@ public class PreAuthorizeAuthorizationManagerTests {
public void checkDoSomethingStringWhenArgIsGrantThenGrantedDecision() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString", new Class[] { String.class }, new Object[] { "grant" });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@ -79,11 +79,10 @@ public class PreAuthorizeAuthorizationManagerTests {
public void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString", new Class[] { String.class }, new Object[] { "deny" });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}

View File

@ -16,15 +16,13 @@
package org.springframework.security.authorization.method;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
@ -36,43 +34,38 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Tests for {@link PreFilterAuthorizationMethodBeforeAdvice}.
* Tests for {@link PreFilterAuthorizationMethodInterceptor}.
*
* @author Evgeniy Cheban
*/
public class PreFilterAuthorizationMethodBeforeAdviceTests {
public class PreFilterAuthorizationMethodInterceptorTests {
@Test
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE);
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
advice.setExpressionHandler(expressionHandler);
assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler);
}
@Test
public void setExpressionHandlerWhenNullThenException() {
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE);
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null))
.withMessage("expressionHandler cannot be null");
}
@Test
public void methodMatcherWhenMethodHasNotPreFilterAnnotationThenNotMatches() throws Exception {
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(
new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
});
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomething"), TestClass.class)).isFalse();
assertThat(methodMatcher.matches(NoPreFilterClass.class.getMethod("doSomething"), NoPreFilterClass.class))
.isFalse();
}
@Test
public void methodMatcherWhenMethodHasPreFilterAnnotationThenMatches() throws Exception {
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE);
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomethingListFilterTargetMatch", List.class),
TestClass.class)).isTrue();
@ -82,12 +75,11 @@ public class PreFilterAuthorizationMethodBeforeAdviceTests {
public void findFilterTargetWhenNameProvidedAndNotMatchThenException() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetNotMatch", new Class[] { List.class }, new Object[] { new ArrayList<>() });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE);
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
assertThatIllegalArgumentException()
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext))
.withMessage(
.isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation)).withMessage(
"Filter target was null, or no argument with name 'filterTargetNotMatch' found in method.");
}
@ -95,25 +87,25 @@ public class PreFilterAuthorizationMethodBeforeAdviceTests {
public void findFilterTargetWhenNameProvidedAndMatchAndNullThenException() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetMatch", new Class[] { List.class }, new Object[] { null });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE);
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
assertThatIllegalArgumentException()
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext))
.isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation))
.withMessage("Filter target was null, or no argument with name 'list' found in method.");
}
@Test
public void findFilterTargetWhenNameProvidedAndMatchAndNotNullThenFiltersList() throws Exception {
public void findFilterTargetWhenNameProvidedAndMatchAndNotNullThenFiltersList() throws Throwable {
List<String> list = new ArrayList<>();
list.add("john");
list.add("bob");
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetMatch", new Class[] { List.class }, new Object[] { list });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE);
advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext);
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
advice.invoke(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo("john");
}
@ -122,25 +114,25 @@ public class PreFilterAuthorizationMethodBeforeAdviceTests {
public void findFilterTargetWhenNameNotProvidedAndSingleArgListNullThenException() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { null });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE);
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
assertThatIllegalArgumentException()
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext))
.isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation))
.withMessage("Filter target was null. Make sure you passing the correct value in the method argument.");
}
@Test
public void findFilterTargetWhenNameNotProvidedAndSingleArgListThenFiltersList() throws Exception {
public void findFilterTargetWhenNameNotProvidedAndSingleArgListThenFiltersList() throws Throwable {
List<String> list = new ArrayList<>();
list.add("john");
list.add("bob");
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { list });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE);
advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext);
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
advice.invoke(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo("john");
}
@ -150,12 +142,11 @@ public class PreFilterAuthorizationMethodBeforeAdviceTests {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingArrayFilterTargetNotProvided", new Class[] { String[].class },
new Object[] { new String[] {} });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE);
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
assertThatIllegalStateException()
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext))
.withMessage(
.isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation)).withMessage(
"Pre-filtering on array types is not supported. Using a Collection will solve this problem.");
}
@ -164,21 +155,17 @@ public class PreFilterAuthorizationMethodBeforeAdviceTests {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingTwoArgsFilterTargetNotProvided", new Class[] { String.class, List.class },
new Object[] { "", new ArrayList<>() });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE);
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
assertThatIllegalStateException()
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext))
.isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation))
.withMessage("Unable to determine the method argument for filtering. Specify the filter target.");
}
@PreFilter("filterObject == 'john'")
public static class TestClass {
public void doSomething() {
}
@PreFilter(value = "filterObject == 'john'", filterTarget = "filterTargetNotMatch")
public List<String> doSomethingListFilterTargetNotMatch(List<String> list) {
return list;
@ -205,4 +192,12 @@ public class PreFilterAuthorizationMethodBeforeAdviceTests {
}
public static class NoPreFilterClass {
public void doSomething() {
}
}
}

View File

@ -16,6 +16,7 @@
package org.springframework.security.authorization.method;
import java.util.Collections;
import java.util.function.Supplier;
import org.junit.Test;
@ -38,38 +39,35 @@ public class SecuredAuthorizationManagerTests {
@Test
public void checkDoSomethingWhenNoSecuredAnnotationThenNullDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNull();
}
@Test
public void checkSecuredUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"securedUserOrAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkSecuredUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"securedUserOrAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestAuthentication::authenticatedAdmin, mockMethodInvocation, Collections.emptyList());
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@ -78,12 +76,12 @@ public class SecuredAuthorizationManagerTests {
public void checkSecuredUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
"ROLE_ANONYMOUS");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"securedUserOrAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(authentication,
mockMethodInvocation, Collections.emptyList());
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, methodAuthorizationContext);
AuthorizationDecision decision = manager.check(authentication, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}

View File

@ -25,7 +25,7 @@ public class MethodSecurityConfig {
Adding an annotation to a method (on a class or interface) would then limit the access to that method accordingly.
Spring Security's native annotatino support defines a set of attributes for the method.
These will be passed to the `AuthorizationMethodInterceptor` for it to make the actual decision:
These will be passed to the `DefaultAuthorizationMethodInterceptorChain` for it to make the actual decision:
[source,java]
----
@ -100,41 +100,59 @@ If that authorization denies access, the method is not invoked and an `AccessDen
After-method authorization is performed after the method is invoked, but before the method returns to the caller.
If that authorization denies access, the value is not returned and an `AccessDeniedException` is thrown
You can customize before-method authorization by publishing your own `AuthorizationMethodBeforeAdvice` bean, which includes your custom authorization manager as well as the `Pointcut` that describes when your manager should be used.
For example, you may want to apply a default authorization rule to all methods in your service layer.
To do this, you'll supply the pointcut as well as the rule, like so:
To recreate what Spring Security does by default, you would publish the following bean:
[source,java]
----
@Bean
public AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> authorizationMethodBeforeAdvice() {
JdkRegexpMethodPointcut pattern = new JdkRegexpMethodPointcut();
pattern.setPattern("org.mycompany.myapp.service.*");
AuthorizationManager<MethodAuthorizationContext> rule = AuthorityAuthorizationManager.isAuthenticated();
return new AuthorizationManagerMethodBeforeAdvice(pattern, rule);
public List<AuthorizationMethodInterceptor> methodSecurity() {
return new DelegatingAuthorizationMethodInterceptor(
new PreFilterAuthorizationMethodInterceptor(), // before-method
AuthorizationMethodInterceptors.preAuthorize(), // before-method
new PostFilterAuthorizationMethodInterceptor(), // after-method
AuthorizationMethodInterceptors.postAuthorize() // after-method
);
}
----
This will replace any default before advice that Spring Security provides.
To use your custom rule as well as Spring Security's `@PreAuthorize` authorization support, you can do:
[NOTE]
Keep in mind that publishing a list of `AuthorizationMethodInterceptor`s will completely replace any Spring Security defaults.
Interceptors are invoked in the order that they are declared.
You may want to only support `@PreAuthorize` in your application, in which case you can do the following:
[source,java]
----
@Bean
public AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> authorizationMethodBeforeAdvice() {
JdkRegexpMethodPointcut pattern = new JdkRegexpMethodPointcut();
pattern.setPattern("org.mycompany.myapp.service.*");
AuthorizationManager rule = AuthorityAuthorizationManager.isAuthenticated();
AuthorizationMethodBeforeAdvice custom = new AuthorizationManagerMethodBeforeAdvice(pattern, rule);
AuthorizationMethodBeforeAdvice pre = new AuthorizationMethodBeforeAdvice(
AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class),
new PreAuthorizeAuthorizationManager());
return new DelegatingAuthorizationManagerBeforeAdvice(custom, pre);
public AuthorizationMethodInterceptor methodSecurity() {
return AuthorizationMethodInterceptors.preAuthorize();
}
----
The same can be done for after-method authorization.
Or, you may have a custom before-method `AuthorizationManager` that you want to add to the list.
In this case, you will need to tell Spring Security both the `AuthorizationManager` and to which methods and classes your authorization manager applies.
Spring Security integrates with Spring AOP to achieve this.
Thus, you can configure Spring Security to support `@PreAuthorize`, `@PostAuthorize`, and your own `AuthorizationManager` like so:
[source,java]
----
@Bean
public AuthorizationMethodInterceptor methodSecurity() {
JdkRegexpMethodPointcut pattern = new JdkRegexpMethodPointcut();
pattern.setPattern("org.mycompany.myapp.service.*");
AuthorizationManager<MethodInvocation> rule = AuthorityAuthorizationManager.isAuthenticated();
return new DelegatingAuthorizationMethodInterceptor(
AuthorizationMethodInterceptors.preAuthorize(),
new AuthorizationManagerBeforeMethodInterceptor(pattern, rule),
AuthorizationMethodInterceptors.postAuthorize()
);
}
----
The same can be done for after-method authorization and `AfterMethodAuthorizationManager`.
After-method authorization is generally concerned with analysing the return value to verify access.
For example, you might have a method that confirms that the account requested actually belongs to the logged-in user like so:
@ -149,22 +167,20 @@ public interface BankService {
}
----
You can supply your own `AuthorizationMethodAfterAdvice` to customize how access to the return value is evaluated.
You can supply your own `AuthorizationMethodInterceptor` to customize how access to the return value is evaluated.
For example, you can give special access to a given role in your system, like so:
For example, instead of embedding a great deal of logic into the `@PostAuthorize` SpEL expression, you may want to wire your own `@Bean`.
In that case, you can configure it like so:
[source,java]
----
@Bean
public AuthorizationMethodAfterAdvice<MethodAuthorizationContext> authorizationMethodAfterAdvice() {
JdkRegexpMethodPointcut pattern = new JdkRegexpMethodPointcut();
pattern.setPattern("org.mycompany.myapp.service.*");
AuthorizationManager<MethodAuthorizationContext> rule = AuthorityAuthorizationManager.hasRole("TELLER");
AuthorizationMethodBeforeAdvice custom = new AuthorizationManagerMethodBeforeAdvice(pattern, rule);
AuthorizationMethodBeforeAdvice post = new AuthorizationManagerMethodBeforeAdvice(
AuthorizationMethodPointcuts.forAnnotations(PostAuthorize.class),
new PostAuthorizeAuthorizationManager());
return new DelegatingAuthorizationManagerBeforeAdvice(custom, post);
public AuthorizationMethodInterceptor methodSecurity
(AfterMethodAuthorizationManager<MethodInvocation> rules) {
AnnotationMethodMatcher pattern = new AnnotationMethodMatcher(MySecurityAnnotation.class);
return new DelegatingAuthorizationMethodInterceptor(
AuthorizationMethodInterceptors.preAuthorize(),
new AuthorizationManagerAfterMethodInterceptor(pattern, rules));
}
----