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; package org.springframework.security.config.annotation.method.configuration;
import java.lang.annotation.Annotation;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; 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.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.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition; 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.context.annotation.Role;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata; 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.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; 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.AuthorizationMethodInterceptor;
import org.springframework.security.authorization.method.DelegatingAuthorizationMethodAfterAdvice; import org.springframework.security.authorization.method.AuthorizationMethodInterceptors;
import org.springframework.security.authorization.method.DelegatingAuthorizationMethodBeforeAdvice; import org.springframework.security.authorization.method.DelegatingAuthorizationMethodInterceptor;
import org.springframework.security.authorization.method.Jsr250AuthorizationManager; 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.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.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.authorization.method.SecuredAuthorizationManager;
import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -79,30 +60,19 @@ final class MethodSecurityConfiguration implements ImportAware, InitializingBean
private GrantedAuthorityDefaults grantedAuthorityDefaults; private GrantedAuthorityDefaults grantedAuthorityDefaults;
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> authorizationMethodBeforeAdvice; private AuthorizationMethodInterceptor interceptor;
private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> authorizationMethodAfterAdvice;
private AnnotationAttributes enableMethodSecurity; private AnnotationAttributes enableMethodSecurity;
@Bean @Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
DefaultPointcutAdvisor methodSecurityAdvisor(AuthorizationMethodInterceptor interceptor) { DefaultPointcutAdvisor methodSecurityAdvisor() {
AuthorizationMethodBeforeAdvice<?> beforeAdvice = getAuthorizationMethodBeforeAdvice(); AuthorizationMethodInterceptor interceptor = getInterceptor();
AuthorizationMethodAfterAdvice<?> afterAdvice = getAuthorizationMethodAfterAdvice(); DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(interceptor.getPointcut(), interceptor);
Pointcut pointcut = Pointcuts.union(beforeAdvice.getPointcut(), afterAdvice.getPointcut());
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, interceptor);
advisor.setOrder(order()); advisor.setOrder(order());
return advisor; return advisor;
} }
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
AuthorizationMethodInterceptor authorizationMethodInterceptor() {
return new AuthorizationMethodInterceptor(getAuthorizationMethodBeforeAdvice(),
getAuthorizationMethodAfterAdvice());
}
private MethodSecurityExpressionHandler getMethodSecurityExpressionHandler() { private MethodSecurityExpressionHandler getMethodSecurityExpressionHandler() {
if (this.methodSecurityExpressionHandler == null) { if (this.methodSecurityExpressionHandler == null) {
DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler(); DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
@ -124,15 +94,18 @@ final class MethodSecurityConfiguration implements ImportAware, InitializingBean
this.grantedAuthorityDefaults = grantedAuthorityDefaults; this.grantedAuthorityDefaults = grantedAuthorityDefaults;
} }
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> getAuthorizationMethodBeforeAdvice() { private AuthorizationMethodInterceptor getInterceptor() {
if (this.authorizationMethodBeforeAdvice == null) { if (this.interceptor != null) {
this.authorizationMethodBeforeAdvice = createDefaultAuthorizationMethodBeforeAdvice(); 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() { private List<AuthorizationMethodInterceptor> createDefaultAuthorizationMethodBeforeAdvice() {
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> beforeAdvices = new ArrayList<>(); List<AuthorizationMethodInterceptor> beforeAdvices = new ArrayList<>();
beforeAdvices.add(getPreFilterAuthorizationMethodBeforeAdvice()); beforeAdvices.add(getPreFilterAuthorizationMethodBeforeAdvice());
beforeAdvices.add(getPreAuthorizeAuthorizationMethodBeforeAdvice()); beforeAdvices.add(getPreAuthorizeAuthorizationMethodBeforeAdvice());
if (securedEnabled()) { if (securedEnabled()) {
@ -141,79 +114,55 @@ final class MethodSecurityConfiguration implements ImportAware, InitializingBean
if (jsr250Enabled()) { if (jsr250Enabled()) {
beforeAdvices.add(getJsr250AuthorizationMethodBeforeAdvice()); beforeAdvices.add(getJsr250AuthorizationMethodBeforeAdvice());
} }
return new DelegatingAuthorizationMethodBeforeAdvice<>(beforeAdvices); return beforeAdvices;
} }
private PreFilterAuthorizationMethodBeforeAdvice getPreFilterAuthorizationMethodBeforeAdvice() { private PreFilterAuthorizationMethodInterceptor getPreFilterAuthorizationMethodBeforeAdvice() {
Pointcut pointcut = forAnnotation(PreFilter.class); PreFilterAuthorizationMethodInterceptor interceptor = new PreFilterAuthorizationMethodInterceptor();
PreFilterAuthorizationMethodBeforeAdvice preFilterBeforeAdvice = new PreFilterAuthorizationMethodBeforeAdvice( interceptor.setExpressionHandler(getMethodSecurityExpressionHandler());
pointcut); return interceptor;
preFilterBeforeAdvice.setExpressionHandler(getMethodSecurityExpressionHandler());
return preFilterBeforeAdvice;
} }
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> getPreAuthorizeAuthorizationMethodBeforeAdvice() { private AuthorizationMethodInterceptor getPreAuthorizeAuthorizationMethodBeforeAdvice() {
Pointcut pointcut = forAnnotation(PreAuthorize.class);
PreAuthorizeAuthorizationManager authorizationManager = new PreAuthorizeAuthorizationManager(); PreAuthorizeAuthorizationManager authorizationManager = new PreAuthorizeAuthorizationManager();
authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler()); authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler());
return new AuthorizationManagerMethodBeforeAdvice<>(pointcut, authorizationManager); return AuthorizationMethodInterceptors.preAuthorize(authorizationManager);
} }
private AuthorizationManagerMethodBeforeAdvice<MethodAuthorizationContext> getSecuredAuthorizationMethodBeforeAdvice() { private AuthorizationMethodInterceptor getSecuredAuthorizationMethodBeforeAdvice() {
Pointcut pointcut = forAnnotation(Secured.class); return AuthorizationMethodInterceptors.secured(new SecuredAuthorizationManager());
SecuredAuthorizationManager authorizationManager = new SecuredAuthorizationManager();
return new AuthorizationManagerMethodBeforeAdvice<>(pointcut, authorizationManager);
} }
private AuthorizationManagerMethodBeforeAdvice<MethodAuthorizationContext> getJsr250AuthorizationMethodBeforeAdvice() { private AuthorizationMethodInterceptor getJsr250AuthorizationMethodBeforeAdvice() {
Pointcut pointcut = new ComposablePointcut(forAnnotation(DenyAll.class)).union(forAnnotation(PermitAll.class))
.union(forAnnotation(RolesAllowed.class));
Jsr250AuthorizationManager authorizationManager = new Jsr250AuthorizationManager(); Jsr250AuthorizationManager authorizationManager = new Jsr250AuthorizationManager();
if (this.grantedAuthorityDefaults != null) { if (this.grantedAuthorityDefaults != null) {
authorizationManager.setRolePrefix(this.grantedAuthorityDefaults.getRolePrefix()); authorizationManager.setRolePrefix(this.grantedAuthorityDefaults.getRolePrefix());
} }
return new AuthorizationManagerMethodBeforeAdvice<>(pointcut, authorizationManager); return AuthorizationMethodInterceptors.jsr250(authorizationManager);
} }
@Autowired(required = false) @Autowired(required = false)
void setAuthorizationMethodBeforeAdvice( void setAuthorizationMethodInterceptor(AuthorizationMethodInterceptor interceptor) {
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> authorizationMethodBeforeAdvice) { this.interceptor = interceptor;
this.authorizationMethodBeforeAdvice = authorizationMethodBeforeAdvice;
} }
private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> getAuthorizationMethodAfterAdvice() { private List<AuthorizationMethodInterceptor> createDefaultAuthorizationMethodAfterAdvice() {
if (this.authorizationMethodAfterAdvice == null) { List<AuthorizationMethodInterceptor> afterAdvices = new ArrayList<>();
this.authorizationMethodAfterAdvice = createDefaultAuthorizationMethodAfterAdvice();
}
return this.authorizationMethodAfterAdvice;
}
private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> createDefaultAuthorizationMethodAfterAdvice() {
List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> afterAdvices = new ArrayList<>();
afterAdvices.add(getPostFilterAuthorizationMethodAfterAdvice()); afterAdvices.add(getPostFilterAuthorizationMethodAfterAdvice());
afterAdvices.add(getPostAuthorizeAuthorizationMethodAfterAdvice()); afterAdvices.add(getPostAuthorizeAuthorizationMethodAfterAdvice());
return new DelegatingAuthorizationMethodAfterAdvice<>(afterAdvices); return afterAdvices;
} }
private PostFilterAuthorizationMethodAfterAdvice getPostFilterAuthorizationMethodAfterAdvice() { private AuthorizationMethodInterceptor getPostFilterAuthorizationMethodAfterAdvice() {
Pointcut pointcut = forAnnotation(PostFilter.class); PostFilterAuthorizationMethodInterceptor interceptor = new PostFilterAuthorizationMethodInterceptor();
PostFilterAuthorizationMethodAfterAdvice postFilterAfterAdvice = new PostFilterAuthorizationMethodAfterAdvice( interceptor.setExpressionHandler(getMethodSecurityExpressionHandler());
pointcut); return interceptor;
postFilterAfterAdvice.setExpressionHandler(getMethodSecurityExpressionHandler());
return postFilterAfterAdvice;
} }
private AuthorizationManagerMethodAfterAdvice<MethodAuthorizationContext> getPostAuthorizeAuthorizationMethodAfterAdvice() { private AuthorizationMethodInterceptor getPostAuthorizeAuthorizationMethodAfterAdvice() {
Pointcut pointcut = forAnnotation(PostAuthorize.class);
PostAuthorizeAuthorizationManager authorizationManager = new PostAuthorizeAuthorizationManager(); PostAuthorizeAuthorizationManager authorizationManager = new PostAuthorizeAuthorizationManager();
authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler()); authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler());
return new AuthorizationManagerMethodAfterAdvice<>(pointcut, authorizationManager); return AuthorizationMethodInterceptors.postAuthorize(authorizationManager);
}
@Autowired(required = false)
void setAuthorizationMethodAfterAdvice(
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> authorizationMethodAfterAdvice) {
this.authorizationMethodAfterAdvice = authorizationMethodAfterAdvice;
} }
@Override @Override
@ -227,7 +176,7 @@ final class MethodSecurityConfiguration implements ImportAware, InitializingBean
if (!securedEnabled() && !jsr250Enabled()) { if (!securedEnabled() && !jsr250Enabled()) {
return; 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."); "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"); 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.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; 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.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.method.AuthorizationManagerMethodBeforeAdvice; import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.authorization.method.AuthorizationMethodAfterAdvice; import org.springframework.security.authorization.method.AuthorizationMethodInterceptor;
import org.springframework.security.authorization.method.AuthorizationMethodBeforeAdvice;
import org.springframework.security.authorization.method.MethodAuthorizationContext;
import org.springframework.security.config.test.SpringTestRule; import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners; import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
@ -273,6 +273,43 @@ public class MethodSecurityConfigurationTests {
this.businessService.rolesAllowedUser(); 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 @Test
public void configureWhenCustomAdviceAndSecureEnabledThenException() { public void configureWhenCustomAdviceAndSecureEnabledThenException() {
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> this.spring assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> this.spring
@ -338,12 +375,12 @@ public class MethodSecurityConfigurationTests {
static class CustomAuthorizationManagerBeforeAdviceConfig { static class CustomAuthorizationManagerBeforeAdviceConfig {
@Bean @Bean
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> customBeforeAdvice() { AuthorizationMethodInterceptor customBeforeAdvice() {
JdkRegexpMethodPointcut methodMatcher = new JdkRegexpMethodPointcut(); JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
methodMatcher.setPattern(".*MethodSecurityServiceImpl.*securedUser"); pointcut.setPattern(".*MethodSecurityServiceImpl.*securedUser");
AuthorizationManager<MethodAuthorizationContext> authorizationManager = (a, AuthorizationManager<MethodInvocation> authorizationManager = (a,
o) -> new AuthorizationDecision("bob".equals(a.get().getName())); 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 { static class CustomAuthorizationManagerAfterAdviceConfig {
@Bean @Bean
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> customAfterAdvice() {
AuthorizationMethodInterceptor customAfterAdvice() {
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut(); JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPattern(".*MethodSecurityServiceImpl.*securedUser"); pointcut.setPattern(".*MethodSecurityServiceImpl.*securedUser");
return new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() { AuthorizationMethodInterceptor interceptor = new AuthorizationMethodInterceptor() {
@Override @Override
public Pointcut getPointcut() { public Pointcut getPointcut() {
return pointcut; return pointcut;
} }
@Override @Override
public Object after(Supplier<Authentication> authentication, public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) {
MethodAuthorizationContext methodAuthorizationContext, Object returnedObject) {
Authentication auth = authentication.get(); Authentication auth = authentication.get();
if ("bob".equals(auth.getName())) { if ("bob".equals(auth.getName())) {
return "granted"; return "granted";
@ -371,6 +408,7 @@ public class MethodSecurityConfigurationTests {
throw new AccessDeniedException("Access Denied for User '" + auth.getName() + "'"); 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; package org.springframework.security.config.annotation.method.configuration;
import java.util.List;
import javax.annotation.security.DenyAll; import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll; import javax.annotation.security.PermitAll;
import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostAuthorize; 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.PreAuthorize;
import org.springframework.security.access.prepost.PreFilter;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.parameters.P; import org.springframework.security.core.parameters.P;
@ -69,4 +73,11 @@ public interface MethodSecurityService {
@PostAuthorize("#o?.contains('grant')") @PostAuthorize("#o?.contains('grant')")
String postAnnotation(@P("o") String object); 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; package org.springframework.security.config.annotation.method.configuration;
import java.util.List;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
@ -86,4 +88,9 @@ public class MethodSecurityServiceImpl implements MethodSecurityService {
return null; 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; import org.springframework.security.authorization.AuthorizationManager;
/** /**
* An abstract registry which provides an {@link AuthorizationManager} for the * For internal use only, as this contract is likely to change
* {@link MethodInvocation}.
* *
* @author Evgeniy Cheban * @author Evgeniy Cheban
* @since 5.5
*/ */
abstract class AbstractAuthorizationManagerRegistry { 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}. * Returns an {@link AuthorizationManager} for the
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to use * {@link AuthorizationMethodInvocation}.
* @param methodInvocation the {@link AuthorizationMethodInvocation} to use
* @return an {@link AuthorizationManager} to use * @return an {@link AuthorizationManager} to use
*/ */
final AuthorizationManager<MethodAuthorizationContext> getManager( final AuthorizationManager<MethodInvocation> getManager(AuthorizationMethodInvocation methodInvocation) {
MethodAuthorizationContext methodAuthorizationContext) {
MethodInvocation methodInvocation = methodAuthorizationContext.getMethodInvocation();
Method method = methodInvocation.getMethod(); Method method = methodInvocation.getMethod();
Class<?> targetClass = methodAuthorizationContext.getTargetClass(); Class<?> targetClass = methodInvocation.getTargetClass();
MethodClassKey cacheKey = new MethodClassKey(method, targetClass); MethodClassKey cacheKey = new MethodClassKey(method, targetClass);
return this.cachedManagers.computeIfAbsent(cacheKey, (k) -> resolveManager(method, targetClass)); return this.cachedManagers.computeIfAbsent(cacheKey, (k) -> resolveManager(method, targetClass));
} }
@ -61,6 +58,6 @@ abstract class AbstractAuthorizationManagerRegistry {
* @return the non-null {@link AuthorizationManager} * @return the non-null {@link AuthorizationManager}
*/ */
@NonNull @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.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.MethodClassKey; import org.springframework.core.MethodClassKey;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
/** /**
* An abstract registry which provides an {@link ExpressionAttribute} for the * For internal use only, as this contract is likely to change
* {@link MethodInvocation}.
* *
* @author Evgeniy Cheban * @author Evgeniy Cheban
* @since 5.5
*/ */
abstract class AbstractExpressionAttributeRegistry<T extends ExpressionAttribute> { abstract class AbstractExpressionAttributeRegistry<T extends ExpressionAttribute> {
private final Map<MethodClassKey, T> cachedAttributes = new ConcurrentHashMap<>(); private final Map<MethodClassKey, T> cachedAttributes = new ConcurrentHashMap<>();
/** /**
* Returns an {@link ExpressionAttribute} for the {@link MethodAuthorizationContext}. * Returns an {@link ExpressionAttribute} for the
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to use * {@link AuthorizationMethodInvocation}.
* @param mi the {@link AuthorizationMethodInvocation} to use
* @return the {@link ExpressionAttribute} to use * @return the {@link ExpressionAttribute} to use
*/ */
final T getAttribute(MethodAuthorizationContext methodAuthorizationContext) { final T getAttribute(AuthorizationMethodInvocation mi) {
MethodInvocation methodInvocation = methodAuthorizationContext.getMethodInvocation(); Method method = mi.getMethod();
Method method = methodInvocation.getMethod(); Class<?> targetClass = mi.getTargetClass();
Class<?> targetClass = methodAuthorizationContext.getTargetClass();
return getAttribute(method, targetClass); return getAttribute(method, targetClass);
} }

View File

@ -18,7 +18,8 @@ package org.springframework.security.authorization.method;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.springframework.aop.MethodMatcher; import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut; import org.springframework.aop.Pointcut;
import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.AuthorizationManager;
@ -26,28 +27,27 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* An {@link AuthorizationMethodAfterAdvice} which can determine if an * An {@link AuthorizationMethodInterceptor} which can determine if an
* {@link Authentication} has access to the {@link T} object using an * {@link Authentication} has access to the result of an {@link MethodInvocation} using an
* {@link AuthorizationManager} if a {@link MethodMatcher} matches. * {@link AuthorizationManager}
* *
* @param <T> the type of object that the authorization check is being done one.
* @author Evgeniy Cheban * @author Evgeniy Cheban
* @author Josh Cummings * @author Josh Cummings
* @since 5.5 * @since 5.5
*/ */
public final class AuthorizationManagerMethodAfterAdvice<T> implements AuthorizationMethodAfterAdvice<T> { public final class AuthorizationManagerAfterMethodInterceptor implements AuthorizationMethodInterceptor {
private final Pointcut pointcut; private final Pointcut pointcut;
private final AfterMethodAuthorizationManager<T> authorizationManager; private final AfterMethodAuthorizationManager<MethodInvocation> authorizationManager;
/** /**
* Creates an instance. * Creates an instance.
* @param pointcut the {@link Pointcut} to use * @param pointcut the {@link Pointcut} to use
* @param authorizationManager the {@link AuthorizationManager} to use * @param authorizationManager the {@link AuthorizationManager} to use
*/ */
public AuthorizationManagerMethodAfterAdvice(Pointcut pointcut, public AuthorizationManagerAfterMethodInterceptor(Pointcut pointcut,
AfterMethodAuthorizationManager<T> authorizationManager) { AfterMethodAuthorizationManager<MethodInvocation> authorizationManager) {
Assert.notNull(pointcut, "pointcut cannot be null"); Assert.notNull(pointcut, "pointcut cannot be null");
Assert.notNull(authorizationManager, "authorizationManager cannot be null"); Assert.notNull(authorizationManager, "authorizationManager cannot be null");
this.pointcut = pointcut; 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 * Determine if an {@link Authentication} has access to the {@link MethodInvocation}
* {@link AuthorizationManager}. * using the {@link AuthorizationManager}.
* @param authentication the {@link Supplier} of the {@link Authentication} to check * @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 * @throws AccessDeniedException if access is not granted
*/ */
@Override @Override
public Object after(Supplier<Authentication> authentication, T context, Object object) { public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
this.authorizationManager.verify(authentication, context, object); Object result = mi.proceed();
return object; 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 java.util.function.Supplier;
import org.springframework.aop.MethodMatcher; import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut; import org.springframework.aop.Pointcut;
import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.AuthorizationManager;
@ -26,27 +27,26 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* An {@link AuthorizationMethodBeforeAdvice} which can determine if an * An {@link AuthorizationMethodInterceptor} which uses a {@link AuthorizationManager} to
* {@link Authentication} has access to the {@link T} object using an * determine if an {@link Authentication} may invoke the given {@link MethodInvocation}
* {@link AuthorizationManager} if a {@link MethodMatcher} matches.
* *
* @param <T> the type of object that the authorization check is being done one.
* @author Evgeniy Cheban * @author Evgeniy Cheban
* @author Josh Cummings * @author Josh Cummings
* @since 5.5 * @since 5.5
*/ */
public final class AuthorizationManagerMethodBeforeAdvice<T> implements AuthorizationMethodBeforeAdvice<T> { public final class AuthorizationManagerBeforeMethodInterceptor implements AuthorizationMethodInterceptor {
private final Pointcut pointcut; private final Pointcut pointcut;
private final AuthorizationManager<T> authorizationManager; private final AuthorizationManager<MethodInvocation> authorizationManager;
/** /**
* Creates an instance. * Creates an instance.
* @param pointcut the {@link Pointcut} to use * @param pointcut the {@link Pointcut} to use
* @param authorizationManager the {@link AuthorizationManager} 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(pointcut, "pointcut cannot be null");
Assert.notNull(authorizationManager, "authorizationManager cannot be null"); Assert.notNull(authorizationManager, "authorizationManager cannot be null");
this.pointcut = pointcut; 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 * Determine if an {@link Authentication} has access to the {@link MethodInvocation}
* configured {@link AuthorizationManager}. * using the configured {@link AuthorizationManager}.
* @param authentication the {@link Supplier} of the {@link Authentication} to check * @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 * @throws AccessDeniedException if access is not granted
*/ */
@Override @Override
public void before(Supplier<Authentication> authentication, T object) { public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
this.authorizationManager.verify(authentication, object); 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; 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.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation; import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils; import org.springframework.aop.PointcutAdvisor;
import org.springframework.lang.NonNull; import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder; 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 Evgeniy Cheban
* @author Josh Cummings
* @since 5.5 * @since 5.5
*/ */
public final class AuthorizationMethodInterceptor implements MethodInterceptor { public interface AuthorizationMethodInterceptor extends MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
private final AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> beforeAdvice;
private final AuthorizationMethodAfterAdvice<MethodAuthorizationContext> afterAdvice;
/** /**
* Creates an instance. * {@inheritDoc}
* @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}
*/ */
@Override @Override
public Object invoke(@NonNull MethodInvocation mi) throws Throwable { default Advice getAdvice() {
MethodAuthorizationContext methodAuthorizationContext = getMethodAuthorizationContext(mi); return this;
this.beforeAdvice.before(this::getAuthentication, methodAuthorizationContext);
Object returnedObject = mi.proceed();
return this.afterAdvice.after(this::getAuthentication, methodAuthorizationContext, returnedObject);
} }
private MethodAuthorizationContext getMethodAuthorizationContext(MethodInvocation mi) { /**
Object target = mi.getThis(); * {@inheritDoc}
Class<?> targetClass = (target != null) ? AopUtils.getTargetClass(target) : null; */
return new MethodAuthorizationContext(mi, targetClass); @Override
default boolean isPerInstance() {
return true;
} }
private Authentication getAuthentication() { /**
* 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(); Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) { if (authentication == null) {
throw new AuthenticationCredentialsNotFoundException( throw new AuthenticationCredentialsNotFoundException(
"An Authentication object was not found in the SecurityContext"); "An Authentication object was not found in the SecurityContext");
} }
return authentication; 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; import org.springframework.util.Assert;
/** /**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has * An {@link AuthorizationManager} which can determine if an {@link Authentication} may
* access to the {@link MethodInvocation} by evaluating if the {@link Authentication} * invoke the {@link MethodInvocation} by evaluating if the {@link Authentication}
* contains a specified authority from the JSR-250 security annotations. * contains a specified authority from the JSR-250 security annotations.
* *
* @author Evgeniy Cheban * @author Evgeniy Cheban
* @since 5.5 * @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<>(); 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 * Determine if an {@link Authentication} has access to a method by evaluating the
* {@link DenyAll}, {@link PermitAll}, and {@link RolesAllowed} annotations that * {@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 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 * @return an {@link AuthorizationDecision} or null if the JSR-250 security
* annotations is not present * annotations is not present
*/ */
@Override @Override
public AuthorizationDecision check(Supplier<Authentication> authentication, public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation methodInvocation) {
MethodAuthorizationContext methodAuthorizationContext) { AuthorizationManager<MethodInvocation> delegate = this.registry
AuthorizationManager<MethodAuthorizationContext> delegate = this.registry .getManager((AuthorizationMethodInvocation) methodInvocation);
.getManager(methodAuthorizationContext); return delegate.check(authentication, methodInvocation);
return delegate.check(authentication, methodAuthorizationContext);
} }
private final class Jsr250AuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry { private final class Jsr250AuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry {
@NonNull @NonNull
@Override @Override
AuthorizationManager<MethodAuthorizationContext> resolveManager(Method method, Class<?> targetClass) { AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass) {
for (Annotation annotation : findJsr250Annotations(method, targetClass)) { for (Annotation annotation : findJsr250Annotations(method, targetClass)) {
if (annotation instanceof DenyAll) { if (annotation instanceof DenyAll) {
return (a, o) -> new AuthorizationDecision(false); 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; import org.springframework.util.Assert;
/** /**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has * An {@link AuthorizationManager} which can determine if an {@link Authentication} may
* access to the {@link MethodInvocation} by evaluating an expression from the * return the result from an invoked {@link MethodInvocation} by evaluating an expression
* {@link PostAuthorize} annotation. * from the {@link PostAuthorize} annotation.
* *
* @author Evgeniy Cheban * @author Evgeniy Cheban
* @since 5.5 * @since 5.5
*/ */
public final class PostAuthorizeAuthorizationManager public final class PostAuthorizeAuthorizationManager implements AfterMethodAuthorizationManager<MethodInvocation> {
implements AfterMethodAuthorizationManager<MethodAuthorizationContext> {
private final PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry(); private final PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
/** /**
* Sets the {@link MethodSecurityExpressionHandler}. * Use this the {@link MethodSecurityExpressionHandler}.
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use * @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
*/ */
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) { 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 * Determine if an {@link Authentication} has access to the returned object by
* evaluating the {@link PostAuthorize} annotation that the * 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 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 * @param returnedObject the returned object to check
* @return an {@link AuthorizationDecision} or {@code null} if the * @return an {@link AuthorizationDecision} or {@code null} if the
* {@link PostAuthorize} annotation is not present * {@link PostAuthorize} annotation is not present
*/ */
@Override @Override
public AuthorizationDecision check(Supplier<Authentication> authentication, public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi,
MethodAuthorizationContext methodAuthorizationContext, Object returnedObject) { Object returnedObject) {
ExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext); ExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi);
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) { if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
return null; return null;
} }
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
methodAuthorizationContext.getMethodInvocation());
this.expressionHandler.setReturnObject(returnedObject, ctx); this.expressionHandler.setReturnObject(returnedObject, ctx);
boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx); boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);
return new AuthorizationDecision(granted); return new AuthorizationDecision(granted);

View File

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

View File

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

View File

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

View File

@ -31,38 +31,36 @@ import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
/** /**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has * An {@link AuthorizationManager} which can determine if an {@link Authentication} may
* access to the {@link MethodInvocation} by evaluating if the {@link Authentication} * invoke the {@link MethodInvocation} by evaluating if the {@link Authentication}
* contains a specified authority from the Spring Security's {@link Secured} annotation. * contains a specified authority from the Spring Security's {@link Secured} annotation.
* *
* @author Evgeniy Cheban * @author Evgeniy Cheban
* @since 5.5 * @since 5.5
*/ */
public final class SecuredAuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> { public final class SecuredAuthorizationManager implements AuthorizationManager<MethodInvocation> {
private final SecuredAuthorizationManagerRegistry registry = new SecuredAuthorizationManagerRegistry(); private final SecuredAuthorizationManagerRegistry registry = new SecuredAuthorizationManagerRegistry();
/** /**
* Determine if an {@link Authentication} has access to a method by evaluating the * 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 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 * @return an {@link AuthorizationDecision} or null if the {@link Secured} annotation
* is not present * is not present
*/ */
@Override @Override
public AuthorizationDecision check(Supplier<Authentication> authentication, public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) {
MethodAuthorizationContext methodAuthorizationContext) { AuthorizationManager<MethodInvocation> delegate = this.registry.getManager((AuthorizationMethodInvocation) mi);
AuthorizationManager<MethodAuthorizationContext> delegate = this.registry return delegate.check(authentication, mi);
.getManager(methodAuthorizationContext);
return delegate.check(authentication, methodAuthorizationContext);
} }
private static final class SecuredAuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry { private static final class SecuredAuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry {
@NonNull @NonNull
@Override @Override
AuthorizationManager<MethodAuthorizationContext> resolveManager(Method method, Class<?> targetClass) { AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass) {
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
Secured secured = findSecuredAnnotation(specificMethod); Secured secured = findSecuredAnnotation(specificMethod);
return (secured != null) ? AuthorityAuthorizationManager.hasAnyAuthority(secured.value()) : NULL_MANAGER; 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.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; 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.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
/** /**
* Tests for {@link AuthorizationManagerMethodAfterAdvice}. * Tests for {@link AuthorizationManagerAfterMethodInterceptor}.
* *
* @author Evgeniy Cheban * @author Evgeniy Cheban
*/ */
public class AuthorizationManagerMethodAfterAdviceTests { public class AuthorizationManagerAfterMethodInterceptorTests {
@Test @Test
public void instantiateWhenMethodMatcherNullThenException() { public void instantiateWhenMethodMatcherNullThenException() {
AfterMethodAuthorizationManager<MethodInvocation> mockAuthorizationManager = mock( AfterMethodAuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(
AfterMethodAuthorizationManager.class); AfterMethodAuthorizationManager.class);
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy(() -> new AuthorizationManagerMethodAfterAdvice<>(null, mockAuthorizationManager)) .isThrownBy(() -> new AuthorizationManagerAfterMethodInterceptor(null, mockAuthorizationManager))
.withMessage("pointcut cannot be null"); .withMessage("pointcut cannot be null");
} }
@Test @Test
public void instantiateWhenAuthorizationManagerNullThenException() { public void instantiateWhenAuthorizationManagerNullThenException() {
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy(() -> new AuthorizationManagerMethodAfterAdvice<>(mock(Pointcut.class), null)) .isThrownBy(() -> new AuthorizationManagerAfterMethodInterceptor(mock(Pointcut.class), null))
.withMessage("authorizationManager cannot be null"); .withMessage("authorizationManager cannot be null");
} }
@Test @Test
public void beforeWhenMockAuthorizationManagerThenVerifyAndReturnedObject() { public void beforeWhenMockAuthorizationManagerThenVerifyAndReturnedObject() throws Throwable {
Supplier<Authentication> authentication = TestAuthentication::authenticatedUser; Supplier<Authentication> authentication = TestAuthentication::authenticatedUser;
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class); MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
Object returnedObject = new Object(); Object returnedObject = new Object();
given(mockMethodInvocation.proceed()).willReturn(returnedObject);
AfterMethodAuthorizationManager<MethodInvocation> mockAuthorizationManager = mock( AfterMethodAuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(
AfterMethodAuthorizationManager.class); AfterMethodAuthorizationManager.class);
AuthorizationManagerMethodAfterAdvice<MethodInvocation> advice = new AuthorizationManagerMethodAfterAdvice<>( AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(
mock(Pointcut.class), mockAuthorizationManager); Pointcut.TRUE, mockAuthorizationManager);
Object result = advice.after(authentication, mockMethodInvocation, returnedObject); Object result = advice.invoke(authentication, mockMethodInvocation);
assertThat(result).isEqualTo(returnedObject); assertThat(result).isEqualTo(returnedObject);
verify(mockAuthorizationManager).verify(authentication, mockMethodInvocation, 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; import static org.mockito.Mockito.verify;
/** /**
* Tests for {@link AuthorizationManagerMethodBeforeAdvice}. * Tests for {@link AuthorizationManagerBeforeMethodInterceptor}.
* *
* @author Evgeniy Cheban * @author Evgeniy Cheban
*/ */
public class AuthorizationManagerMethodBeforeAdviceTests { public class AuthorizationManagerBeforeMethodInterceptorTests {
@Test @Test
public void instantiateWhenMethodMatcherNullThenException() { public void instantiateWhenMethodMatcherNullThenException() {
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy(() -> new AuthorizationManagerMethodBeforeAdvice<>(null, mock(AuthorizationManager.class))) .isThrownBy(
() -> new AuthorizationManagerBeforeMethodInterceptor(null, mock(AuthorizationManager.class)))
.withMessage("pointcut cannot be null"); .withMessage("pointcut cannot be null");
} }
@Test @Test
public void instantiateWhenAuthorizationManagerNullThenException() { public void instantiateWhenAuthorizationManagerNullThenException() {
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy(() -> new AuthorizationManagerMethodBeforeAdvice<>(mock(Pointcut.class), null)) .isThrownBy(() -> new AuthorizationManagerBeforeMethodInterceptor(mock(Pointcut.class), null))
.withMessage("authorizationManager cannot be null"); .withMessage("authorizationManager cannot be null");
} }
@Test @Test
public void beforeWhenMockAuthorizationManagerThenVerify() { public void beforeWhenMockAuthorizationManagerThenVerify() throws Throwable {
Supplier<Authentication> authentication = TestAuthentication::authenticatedUser; Supplier<Authentication> authentication = TestAuthentication::authenticatedUser;
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class); MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
AuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(AuthorizationManager.class); AuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(AuthorizationManager.class);
AuthorizationManagerMethodBeforeAdvice<MethodInvocation> advice = new AuthorizationManagerMethodBeforeAdvice<>( AuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(
mock(Pointcut.class), mockAuthorizationManager); Pointcut.TRUE, mockAuthorizationManager);
advice.before(authentication, mockMethodInvocation); advice.invoke(authentication, mockMethodInvocation);
verify(mockAuthorizationManager).verify(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; package org.springframework.security.authorization.method;
import java.util.Arrays;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.After; import org.junit.After;
import org.junit.Test; 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.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
/** /**
* Tests for {@link AuthorizationMethodInterceptor}. * Tests for {@link DelegatingAuthorizationMethodInterceptor}.
* *
* @author Evgeniy Cheban * @author Evgeniy Cheban
*/ */
public class AuthorizationMethodInterceptorTests { public class DelegatingAuthorizationMethodInterceptorTests {
@After @After
public void tearDown() { public void tearDown() {
@ -56,41 +57,39 @@ public class AuthorizationMethodInterceptorTests {
SecurityContextHolder.setContext(new SecurityContextImpl(authentication)); SecurityContextHolder.setContext(new SecurityContextImpl(authentication));
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString"); "doSomethingString");
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> mockBeforeAdvice = mock( AuthorizationMethodInterceptor interceptor = mock(AuthorizationMethodInterceptor.class);
AuthorizationMethodBeforeAdvice.class); given(interceptor.getPointcut()).willReturn(Pointcut.TRUE);
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> mockAfterAdvice = mock( given(interceptor.invoke(any(), any(AuthorizationMethodInvocation.class))).willReturn("abc");
AuthorizationMethodAfterAdvice.class); DelegatingAuthorizationMethodInterceptor chain = new DelegatingAuthorizationMethodInterceptor(
given(mockAfterAdvice.after(any(), any(MethodAuthorizationContext.class), eq(null))).willReturn("abc"); Arrays.asList(interceptor));
AuthorizationMethodInterceptor interceptor = new AuthorizationMethodInterceptor(mockBeforeAdvice, Object result = chain.invoke(mockMethodInvocation);
mockAfterAdvice);
Object result = interceptor.invoke(mockMethodInvocation);
assertThat(result).isEqualTo("abc"); assertThat(result).isEqualTo("abc");
verify(mockAfterAdvice).after(any(), any(MethodAuthorizationContext.class), eq(null)); verify(interceptor).invoke(any(), any(AuthorizationMethodInvocation.class));
} }
@Test @Test
public void invokeWhenNotAuthenticatedThenAuthenticationCredentialsNotFoundException() throws Exception { public void invokeWhenNotAuthenticatedThenAuthenticationCredentialsNotFoundException() throws Throwable {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString"); "doSomethingString");
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> beforeAdvice = new AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>() { AuthorizationMethodInterceptor first = new AuthorizationMethodInterceptor() {
@Override @Override
public Pointcut getPointcut() { public Pointcut getPointcut() {
return Pointcut.TRUE; return Pointcut.TRUE;
} }
@Override @Override
public void before(Supplier<Authentication> authentication, public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) {
MethodAuthorizationContext methodAuthorizationContext) { return authentication.get();
authentication.get();
} }
}; };
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> mockAfterAdvice = mock( AuthorizationMethodInterceptor second = mock(AuthorizationMethodInterceptor.class);
AuthorizationMethodAfterAdvice.class); given(second.getPointcut()).willReturn(Pointcut.TRUE);
AuthorizationMethodInterceptor interceptor = new AuthorizationMethodInterceptor(beforeAdvice, mockAfterAdvice); DelegatingAuthorizationMethodInterceptor interceptor = new DelegatingAuthorizationMethodInterceptor(
Arrays.asList(first, second));
assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class) assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
.isThrownBy(() -> interceptor.invoke(mockMethodInvocation)) .isThrownBy(() -> interceptor.invoke(mockMethodInvocation))
.withMessage("An Authentication object was not found in the SecurityContext"); .withMessage("An Authentication object was not found in the SecurityContext");
verifyNoInteractions(mockAfterAdvice); verify(second, times(0)).invoke(any(), any());
} }
public static class TestClass { public static class TestClass {

View File

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

View File

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

View File

@ -16,14 +16,12 @@
package org.springframework.security.authorization.method; package org.springframework.security.authorization.method;
import java.lang.reflect.Method; import java.util.Collections;
import org.assertj.core.api.InstanceOfAssertFactories; import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.Test; import org.junit.Test;
import org.springframework.aop.MethodMatcher; 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.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.method.MockMethodInvocation; 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; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/** /**
* Tests for {@link PostFilterAuthorizationMethodAfterAdvice}. * Tests for {@link PostFilterAuthorizationMethodInterceptor}.
* *
* @author Evgeniy Cheban * @author Evgeniy Cheban
*/ */
public class PostFilterAuthorizationMethodAfterAdviceTests { public class PostFilterAuthorizationMethodInterceptorTests {
@Test @Test
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() { public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice(Pointcut.TRUE); PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
advice.setExpressionHandler(expressionHandler); advice.setExpressionHandler(expressionHandler);
assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler); assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler);
} }
@Test @Test
public void setExpressionHandlerWhenNullThenException() { public void setExpressionHandlerWhenNullThenException() {
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice(Pointcut.TRUE); PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null)) assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null))
.withMessage("expressionHandler cannot be null"); .withMessage("expressionHandler cannot be null");
} }
@Test @Test
public void methodMatcherWhenMethodHasNotPostFilterAnnotationThenNotMatches() throws Exception { public void methodMatcherWhenMethodHasNotPostFilterAnnotationThenNotMatches() throws Exception {
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice( PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
});
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher(); 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 @Test
public void methodMatcherWhenMethodHasPostFilterAnnotationThenMatches() throws Exception { public void methodMatcherWhenMethodHasPostFilterAnnotationThenMatches() throws Exception {
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice(Pointcut.TRUE); PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher(); MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
assertThat( assertThat(
methodMatcher.matches(TestClass.class.getMethod("doSomethingArray", String[].class), TestClass.class)) methodMatcher.matches(TestClass.class.getMethod("doSomethingArray", String[].class), TestClass.class))
@ -78,24 +71,25 @@ public class PostFilterAuthorizationMethodAfterAdviceTests {
} }
@Test @Test
public void afterWhenArrayNotNullThenFilteredArray() throws Exception { public void afterWhenArrayNotNullThenFilteredArray() throws Throwable {
String[] array = { "john", "bob" }; String[] array = { "john", "bob" };
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingArrayClassLevel", new Class[] { String[].class }, new Object[] { array }); "doSomethingArrayClassLevel", new Class[] { String[].class }, new Object[] { array }) {
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, @Override
TestClass.class); public Object proceed() {
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice(Pointcut.TRUE); return array;
Object result = advice.after(TestAuthentication::authenticatedUser, methodAuthorizationContext, 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"); assertThat(result).asInstanceOf(InstanceOfAssertFactories.array(String[].class)).containsOnly("john");
} }
@PostFilter("filterObject == 'john'") @PostFilter("filterObject == 'john'")
public static class TestClass { public static class TestClass {
public void doSomething() {
}
@PostFilter("filterObject == 'john'") @PostFilter("filterObject == 'john'")
public String[] doSomethingArray(String[] array) { public String[] doSomethingArray(String[] array) {
return 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; package org.springframework.security.authorization.method;
import java.util.Collections;
import org.junit.Test; import org.junit.Test;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
@ -54,11 +56,10 @@ public class PreAuthorizeAuthorizationManagerTests {
public void checkDoSomethingWhenNoPostAuthorizeAnnotationThenNullDecision() throws Exception { public void checkDoSomethingWhenNoPostAuthorizeAnnotationThenNullDecision() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething", new Class[] {}, new Object[] {}); "doSomething", new Class[] {}, new Object[] {});
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestClass.class); TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
methodAuthorizationContext);
assertThat(decision).isNull(); assertThat(decision).isNull();
} }
@ -66,11 +67,10 @@ public class PreAuthorizeAuthorizationManagerTests {
public void checkDoSomethingStringWhenArgIsGrantThenGrantedDecision() throws Exception { public void checkDoSomethingStringWhenArgIsGrantThenGrantedDecision() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString", new Class[] { String.class }, new Object[] { "grant" }); "doSomethingString", new Class[] { String.class }, new Object[] { "grant" });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestClass.class); TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
methodAuthorizationContext);
assertThat(decision).isNotNull(); assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue(); assertThat(decision.isGranted()).isTrue();
} }
@ -79,11 +79,10 @@ public class PreAuthorizeAuthorizationManagerTests {
public void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws Exception { public void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString", new Class[] { String.class }, new Object[] { "deny" }); "doSomethingString", new Class[] { String.class }, new Object[] { "deny" });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestClass.class); TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
methodAuthorizationContext);
assertThat(decision).isNotNull(); assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse(); assertThat(decision.isGranted()).isFalse();
} }

View File

@ -16,15 +16,13 @@
package org.springframework.security.authorization.method; package org.springframework.security.authorization.method;
import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.junit.Test; import org.junit.Test;
import org.springframework.aop.MethodMatcher; 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.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.method.MockMethodInvocation; 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; import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/** /**
* Tests for {@link PreFilterAuthorizationMethodBeforeAdvice}. * Tests for {@link PreFilterAuthorizationMethodInterceptor}.
* *
* @author Evgeniy Cheban * @author Evgeniy Cheban
*/ */
public class PreFilterAuthorizationMethodBeforeAdviceTests { public class PreFilterAuthorizationMethodInterceptorTests {
@Test @Test
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() { public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE); PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
advice.setExpressionHandler(expressionHandler); advice.setExpressionHandler(expressionHandler);
assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler); assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler);
} }
@Test @Test
public void setExpressionHandlerWhenNullThenException() { public void setExpressionHandlerWhenNullThenException() {
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE); PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null)) assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null))
.withMessage("expressionHandler cannot be null"); .withMessage("expressionHandler cannot be null");
} }
@Test @Test
public void methodMatcherWhenMethodHasNotPreFilterAnnotationThenNotMatches() throws Exception { public void methodMatcherWhenMethodHasNotPreFilterAnnotationThenNotMatches() throws Exception {
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice( PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
});
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher(); 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 @Test
public void methodMatcherWhenMethodHasPreFilterAnnotationThenMatches() throws Exception { public void methodMatcherWhenMethodHasPreFilterAnnotationThenMatches() throws Exception {
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE); PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher(); MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomethingListFilterTargetMatch", List.class), assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomethingListFilterTargetMatch", List.class),
TestClass.class)).isTrue(); TestClass.class)).isTrue();
@ -82,12 +75,11 @@ public class PreFilterAuthorizationMethodBeforeAdviceTests {
public void findFilterTargetWhenNameProvidedAndNotMatchThenException() throws Exception { public void findFilterTargetWhenNameProvidedAndNotMatchThenException() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetNotMatch", new Class[] { List.class }, new Object[] { new ArrayList<>() }); "doSomethingListFilterTargetNotMatch", new Class[] { List.class }, new Object[] { new ArrayList<>() });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
TestClass.class); AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE); TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext)) .isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation)).withMessage(
.withMessage(
"Filter target was null, or no argument with name 'filterTargetNotMatch' found in method."); "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 { public void findFilterTargetWhenNameProvidedAndMatchAndNullThenException() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetMatch", new Class[] { List.class }, new Object[] { null }); "doSomethingListFilterTargetMatch", new Class[] { List.class }, new Object[] { null });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
TestClass.class); AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE); TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
assertThatIllegalArgumentException() 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."); .withMessage("Filter target was null, or no argument with name 'list' found in method.");
} }
@Test @Test
public void findFilterTargetWhenNameProvidedAndMatchAndNotNullThenFiltersList() throws Exception { public void findFilterTargetWhenNameProvidedAndMatchAndNotNullThenFiltersList() throws Throwable {
List<String> list = new ArrayList<>(); List<String> list = new ArrayList<>();
list.add("john"); list.add("john");
list.add("bob"); list.add("bob");
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetMatch", new Class[] { List.class }, new Object[] { list }); "doSomethingListFilterTargetMatch", new Class[] { List.class }, new Object[] { list });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
TestClass.class); AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE); TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext); advice.invoke(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(list).hasSize(1); assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo("john"); assertThat(list.get(0)).isEqualTo("john");
} }
@ -122,25 +114,25 @@ public class PreFilterAuthorizationMethodBeforeAdviceTests {
public void findFilterTargetWhenNameNotProvidedAndSingleArgListNullThenException() throws Exception { public void findFilterTargetWhenNameNotProvidedAndSingleArgListNullThenException() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { null }); "doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { null });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
TestClass.class); AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE); TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
assertThatIllegalArgumentException() 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."); .withMessage("Filter target was null. Make sure you passing the correct value in the method argument.");
} }
@Test @Test
public void findFilterTargetWhenNameNotProvidedAndSingleArgListThenFiltersList() throws Exception { public void findFilterTargetWhenNameNotProvidedAndSingleArgListThenFiltersList() throws Throwable {
List<String> list = new ArrayList<>(); List<String> list = new ArrayList<>();
list.add("john"); list.add("john");
list.add("bob"); list.add("bob");
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { list }); "doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { list });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
TestClass.class); AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE); TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext); advice.invoke(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(list).hasSize(1); assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo("john"); assertThat(list.get(0)).isEqualTo("john");
} }
@ -150,12 +142,11 @@ public class PreFilterAuthorizationMethodBeforeAdviceTests {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingArrayFilterTargetNotProvided", new Class[] { String[].class }, "doSomethingArrayFilterTargetNotProvided", new Class[] { String[].class },
new Object[] { new String[] {} }); new Object[] { new String[] {} });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
TestClass.class); AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE); TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext)) .isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation)).withMessage(
.withMessage(
"Pre-filtering on array types is not supported. Using a Collection will solve this problem."); "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, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingTwoArgsFilterTargetNotProvided", new Class[] { String.class, List.class }, "doSomethingTwoArgsFilterTargetNotProvided", new Class[] { String.class, List.class },
new Object[] { "", new ArrayList<>() }); new Object[] { "", new ArrayList<>() });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
TestClass.class); AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(Pointcut.TRUE); TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
assertThatIllegalStateException() 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."); .withMessage("Unable to determine the method argument for filtering. Specify the filter target.");
} }
@PreFilter("filterObject == 'john'") @PreFilter("filterObject == 'john'")
public static class TestClass { public static class TestClass {
public void doSomething() {
}
@PreFilter(value = "filterObject == 'john'", filterTarget = "filterTargetNotMatch") @PreFilter(value = "filterObject == 'john'", filterTarget = "filterTargetNotMatch")
public List<String> doSomethingListFilterTargetNotMatch(List<String> list) { public List<String> doSomethingListFilterTargetNotMatch(List<String> list) {
return 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; package org.springframework.security.authorization.method;
import java.util.Collections;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.junit.Test; import org.junit.Test;
@ -38,38 +39,35 @@ public class SecuredAuthorizationManagerTests {
@Test @Test
public void checkDoSomethingWhenNoSecuredAnnotationThenNullDecision() throws Exception { public void checkDoSomethingWhenNoSecuredAnnotationThenNullDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething"); "doSomething");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation, AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestClass.class); TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
SecuredAuthorizationManager manager = new SecuredAuthorizationManager(); SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
methodAuthorizationContext);
assertThat(decision).isNull(); assertThat(decision).isNull();
} }
@Test @Test
public void checkSecuredUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception { public void checkSecuredUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"securedUserOrAdmin"); "securedUserOrAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation, AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestClass.class); TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
SecuredAuthorizationManager manager = new SecuredAuthorizationManager(); SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
methodAuthorizationContext);
assertThat(decision).isNotNull(); assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue(); assertThat(decision.isGranted()).isTrue();
} }
@Test @Test
public void checkSecuredUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception { public void checkSecuredUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"securedUserOrAdmin"); "securedUserOrAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation, AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
TestClass.class); TestAuthentication::authenticatedAdmin, mockMethodInvocation, Collections.emptyList());
SecuredAuthorizationManager manager = new SecuredAuthorizationManager(); SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, methodInvocation);
methodAuthorizationContext);
assertThat(decision).isNotNull(); assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue(); assertThat(decision.isGranted()).isTrue();
} }
@ -78,12 +76,12 @@ public class SecuredAuthorizationManagerTests {
public void checkSecuredUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception { public void checkSecuredUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
"ROLE_ANONYMOUS"); "ROLE_ANONYMOUS");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"securedUserOrAdmin"); "securedUserOrAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation, AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(authentication,
TestClass.class); mockMethodInvocation, Collections.emptyList());
SecuredAuthorizationManager manager = new SecuredAuthorizationManager(); SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, methodAuthorizationContext); AuthorizationDecision decision = manager.check(authentication, methodInvocation);
assertThat(decision).isNotNull(); assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse(); 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. 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. 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] [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. 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 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. To recreate what Spring Security does by default, you would publish the following bean:
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:
[source,java] [source,java]
---- ----
@Bean @Bean
public AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> authorizationMethodBeforeAdvice() { public List<AuthorizationMethodInterceptor> methodSecurity() {
JdkRegexpMethodPointcut pattern = new JdkRegexpMethodPointcut(); return new DelegatingAuthorizationMethodInterceptor(
pattern.setPattern("org.mycompany.myapp.service.*"); new PreFilterAuthorizationMethodInterceptor(), // before-method
AuthorizationManager<MethodAuthorizationContext> rule = AuthorityAuthorizationManager.isAuthenticated(); AuthorizationMethodInterceptors.preAuthorize(), // before-method
return new AuthorizationManagerMethodBeforeAdvice(pattern, rule); new PostFilterAuthorizationMethodInterceptor(), // after-method
AuthorizationMethodInterceptors.postAuthorize() // after-method
);
} }
---- ----
This will replace any default before advice that Spring Security provides. [NOTE]
To use your custom rule as well as Spring Security's `@PreAuthorize` authorization support, you can do: 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] [source,java]
---- ----
@Bean @Bean
public AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> authorizationMethodBeforeAdvice() { public AuthorizationMethodInterceptor methodSecurity() {
JdkRegexpMethodPointcut pattern = new JdkRegexpMethodPointcut(); return AuthorizationMethodInterceptors.preAuthorize();
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);
} }
---- ----
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. 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: 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] [source,java]
---- ----
@Bean @Bean
public AuthorizationMethodAfterAdvice<MethodAuthorizationContext> authorizationMethodAfterAdvice() { public AuthorizationMethodInterceptor methodSecurity
JdkRegexpMethodPointcut pattern = new JdkRegexpMethodPointcut(); (AfterMethodAuthorizationManager<MethodInvocation> rules) {
pattern.setPattern("org.mycompany.myapp.service.*"); AnnotationMethodMatcher pattern = new AnnotationMethodMatcher(MySecurityAnnotation.class);
AuthorizationManager<MethodAuthorizationContext> rule = AuthorityAuthorizationManager.hasRole("TELLER"); return new DelegatingAuthorizationMethodInterceptor(
AuthorizationMethodBeforeAdvice custom = new AuthorizationManagerMethodBeforeAdvice(pattern, rule); AuthorizationMethodInterceptors.preAuthorize(),
AuthorizationMethodBeforeAdvice post = new AuthorizationManagerMethodBeforeAdvice( new AuthorizationManagerAfterMethodInterceptor(pattern, rules));
AuthorizationMethodPointcuts.forAnnotations(PostAuthorize.class),
new PostAuthorizeAuthorizationManager());
return new DelegatingAuthorizationManagerBeforeAdvice(custom, post);
} }
---- ----