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:
parent
122346bd27
commit
df8abcfae7
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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 + ']';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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 {
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue