35 changed files with 3682 additions and 5 deletions
@ -0,0 +1,87 @@
@@ -0,0 +1,87 @@
|
||||
/* |
||||
* 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.config.annotation.method.configuration; |
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
import org.springframework.context.annotation.AdviceMode; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.Import; |
||||
import org.springframework.core.Ordered; |
||||
import org.springframework.security.access.annotation.Secured; |
||||
|
||||
/** |
||||
* Enables Spring Security Method Security. |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target(ElementType.TYPE) |
||||
@Documented |
||||
@Import(MethodSecuritySelector.class) |
||||
@Configuration |
||||
public @interface EnableMethodSecurity { |
||||
|
||||
/** |
||||
* Determines if Spring Security's {@link Secured} annotation should be enabled. |
||||
* Default is false. |
||||
* @return true if {@link Secured} annotation should be enabled false otherwise |
||||
*/ |
||||
boolean securedEnabled() default false; |
||||
|
||||
/** |
||||
* Determines if JSR-250 annotations should be enabled. Default is false. |
||||
* @return true if JSR-250 should be enabled false otherwise |
||||
*/ |
||||
boolean jsr250Enabled() default false; |
||||
|
||||
/** |
||||
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to |
||||
* standard Java interface-based proxies. The default is {@code false}. <strong> |
||||
* Applicable only if {@link #mode()} is set to {@link AdviceMode#PROXY}</strong>. |
||||
* <p> |
||||
* Note that setting this attribute to {@code true} will affect <em>all</em> |
||||
* Spring-managed beans requiring proxying, not just those marked with |
||||
* {@code @Cacheable}. For example, other beans marked with Spring's |
||||
* {@code @Transactional} annotation will be upgraded to subclass proxying at the same |
||||
* time. This approach has no negative impact in practice unless one is explicitly |
||||
* expecting one type of proxy vs another, e.g. in tests. |
||||
* @return true if subclass-based (CGLIB) proxies are to be created |
||||
*/ |
||||
boolean proxyTargetClass() default false; |
||||
|
||||
/** |
||||
* Indicate how security advice should be applied. The default is |
||||
* {@link AdviceMode#PROXY}. |
||||
* @see AdviceMode |
||||
* @return the {@link AdviceMode} to use |
||||
*/ |
||||
AdviceMode mode() default AdviceMode.PROXY; |
||||
|
||||
/** |
||||
* Indicate the ordering of the execution of the security advisor when multiple |
||||
* advices are applied at a specific joinpoint. The default is |
||||
* {@link Ordered#LOWEST_PRECEDENCE}. |
||||
* @return the order the security advisor should be applied |
||||
*/ |
||||
int order() default Ordered.LOWEST_PRECEDENCE; |
||||
|
||||
} |
||||
@ -0,0 +1,252 @@
@@ -0,0 +1,252 @@
|
||||
/* |
||||
* 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.config.annotation.method.configuration; |
||||
|
||||
import java.lang.annotation.Annotation; |
||||
import java.lang.reflect.AnnotatedElement; |
||||
import java.lang.reflect.Method; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.HashSet; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
|
||||
import javax.annotation.security.DenyAll; |
||||
import javax.annotation.security.PermitAll; |
||||
import javax.annotation.security.RolesAllowed; |
||||
|
||||
import org.springframework.aop.MethodMatcher; |
||||
import org.springframework.aop.Pointcut; |
||||
import org.springframework.aop.support.AopUtils; |
||||
import org.springframework.aop.support.DefaultPointcutAdvisor; |
||||
import org.springframework.aop.support.Pointcuts; |
||||
import org.springframework.aop.support.StaticMethodMatcher; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.beans.factory.config.BeanDefinition; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.ImportAware; |
||||
import org.springframework.context.annotation.Role; |
||||
import org.springframework.core.annotation.AnnotatedElementUtils; |
||||
import org.springframework.core.annotation.AnnotationAttributes; |
||||
import org.springframework.core.type.AnnotationMetadata; |
||||
import org.springframework.security.access.annotation.Jsr250AuthorizationManager; |
||||
import org.springframework.security.access.annotation.Secured; |
||||
import org.springframework.security.access.annotation.SecuredAuthorizationManager; |
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.intercept.aopalliance.AuthorizationMethodInterceptor; |
||||
import org.springframework.security.access.method.AuthorizationManagerMethodAfterAdvice; |
||||
import org.springframework.security.access.method.AuthorizationManagerMethodBeforeAdvice; |
||||
import org.springframework.security.access.method.AuthorizationMethodAfterAdvice; |
||||
import org.springframework.security.access.method.AuthorizationMethodBeforeAdvice; |
||||
import org.springframework.security.access.method.DelegatingAuthorizationMethodAfterAdvice; |
||||
import org.springframework.security.access.method.DelegatingAuthorizationMethodBeforeAdvice; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.access.prepost.PostAuthorize; |
||||
import org.springframework.security.access.prepost.PreAuthorize; |
||||
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager; |
||||
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodAfterAdvice; |
||||
import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager; |
||||
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodBeforeAdvice; |
||||
import org.springframework.security.config.core.GrantedAuthorityDefaults; |
||||
|
||||
/** |
||||
* Base {@link Configuration} for enabling Spring Security Method Security. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @see EnableMethodSecurity |
||||
* @since 5.5 |
||||
*/ |
||||
@Configuration(proxyBeanMethods = false) |
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) |
||||
final class MethodSecurityConfiguration implements ImportAware { |
||||
|
||||
private MethodSecurityExpressionHandler methodSecurityExpressionHandler; |
||||
|
||||
private GrantedAuthorityDefaults grantedAuthorityDefaults; |
||||
|
||||
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> authorizationMethodBeforeAdvice; |
||||
|
||||
private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> authorizationMethodAfterAdvice; |
||||
|
||||
private AnnotationAttributes enableMethodSecurity; |
||||
|
||||
@Bean |
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) |
||||
DefaultPointcutAdvisor methodSecurityAdvisor(AuthorizationMethodInterceptor interceptor) { |
||||
Pointcut pointcut = Pointcuts.union(getAuthorizationMethodBeforeAdvice(), getAuthorizationMethodAfterAdvice()); |
||||
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, interceptor); |
||||
advisor.setOrder(order()); |
||||
return advisor; |
||||
} |
||||
|
||||
@Bean |
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) |
||||
AuthorizationMethodInterceptor authorizationMethodInterceptor() { |
||||
return new AuthorizationMethodInterceptor(getAuthorizationMethodBeforeAdvice(), |
||||
getAuthorizationMethodAfterAdvice()); |
||||
} |
||||
|
||||
private MethodSecurityExpressionHandler getMethodSecurityExpressionHandler() { |
||||
if (this.methodSecurityExpressionHandler == null) { |
||||
this.methodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler(); |
||||
} |
||||
return this.methodSecurityExpressionHandler; |
||||
} |
||||
|
||||
@Autowired(required = false) |
||||
void setMethodSecurityExpressionHandler(MethodSecurityExpressionHandler methodSecurityExpressionHandler) { |
||||
this.methodSecurityExpressionHandler = methodSecurityExpressionHandler; |
||||
} |
||||
|
||||
@Autowired(required = false) |
||||
void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) { |
||||
this.grantedAuthorityDefaults = grantedAuthorityDefaults; |
||||
} |
||||
|
||||
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> getAuthorizationMethodBeforeAdvice() { |
||||
if (this.authorizationMethodBeforeAdvice == null) { |
||||
this.authorizationMethodBeforeAdvice = createDefaultAuthorizationMethodBeforeAdvice(); |
||||
} |
||||
return this.authorizationMethodBeforeAdvice; |
||||
} |
||||
|
||||
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> createDefaultAuthorizationMethodBeforeAdvice() { |
||||
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> beforeAdvices = new ArrayList<>(); |
||||
beforeAdvices.add(getPreFilterAuthorizationMethodBeforeAdvice()); |
||||
beforeAdvices.add(getPreAuthorizeAuthorizationMethodBeforeAdvice()); |
||||
if (securedEnabled()) { |
||||
beforeAdvices.add(getSecuredAuthorizationMethodBeforeAdvice()); |
||||
} |
||||
if (jsr250Enabled()) { |
||||
beforeAdvices.add(getJsr250AuthorizationMethodBeforeAdvice()); |
||||
} |
||||
return new DelegatingAuthorizationMethodBeforeAdvice(beforeAdvices); |
||||
} |
||||
|
||||
private PreFilterAuthorizationMethodBeforeAdvice getPreFilterAuthorizationMethodBeforeAdvice() { |
||||
PreFilterAuthorizationMethodBeforeAdvice preFilterBeforeAdvice = new PreFilterAuthorizationMethodBeforeAdvice(); |
||||
preFilterBeforeAdvice.setExpressionHandler(getMethodSecurityExpressionHandler()); |
||||
return preFilterBeforeAdvice; |
||||
} |
||||
|
||||
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> getPreAuthorizeAuthorizationMethodBeforeAdvice() { |
||||
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(PreAuthorize.class); |
||||
PreAuthorizeAuthorizationManager authorizationManager = new PreAuthorizeAuthorizationManager(); |
||||
authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler()); |
||||
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager); |
||||
} |
||||
|
||||
private AuthorizationManagerMethodBeforeAdvice<MethodAuthorizationContext> getSecuredAuthorizationMethodBeforeAdvice() { |
||||
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(Secured.class); |
||||
SecuredAuthorizationManager authorizationManager = new SecuredAuthorizationManager(); |
||||
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager); |
||||
} |
||||
|
||||
private AuthorizationManagerMethodBeforeAdvice<MethodAuthorizationContext> getJsr250AuthorizationMethodBeforeAdvice() { |
||||
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(DenyAll.class, PermitAll.class, |
||||
RolesAllowed.class); |
||||
Jsr250AuthorizationManager authorizationManager = new Jsr250AuthorizationManager(); |
||||
if (this.grantedAuthorityDefaults != null) { |
||||
authorizationManager.setRolePrefix(this.grantedAuthorityDefaults.getRolePrefix()); |
||||
} |
||||
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager); |
||||
} |
||||
|
||||
@Autowired(required = false) |
||||
void setAuthorizationMethodBeforeAdvice( |
||||
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> authorizationMethodBeforeAdvice) { |
||||
this.authorizationMethodBeforeAdvice = authorizationMethodBeforeAdvice; |
||||
} |
||||
|
||||
private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> getAuthorizationMethodAfterAdvice() { |
||||
if (this.authorizationMethodAfterAdvice == null) { |
||||
this.authorizationMethodAfterAdvice = createDefaultAuthorizationMethodAfterAdvice(); |
||||
} |
||||
return this.authorizationMethodAfterAdvice; |
||||
} |
||||
|
||||
private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> createDefaultAuthorizationMethodAfterAdvice() { |
||||
List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> afterAdvices = new ArrayList<>(); |
||||
afterAdvices.add(getPostFilterAuthorizationMethodAfterAdvice()); |
||||
afterAdvices.add(getPostAuthorizeAuthorizationMethodAfterAdvice()); |
||||
return new DelegatingAuthorizationMethodAfterAdvice(afterAdvices); |
||||
} |
||||
|
||||
private PostFilterAuthorizationMethodAfterAdvice getPostFilterAuthorizationMethodAfterAdvice() { |
||||
PostFilterAuthorizationMethodAfterAdvice postFilterAfterAdvice = new PostFilterAuthorizationMethodAfterAdvice(); |
||||
postFilterAfterAdvice.setExpressionHandler(getMethodSecurityExpressionHandler()); |
||||
return postFilterAfterAdvice; |
||||
} |
||||
|
||||
private AuthorizationManagerMethodAfterAdvice<MethodAuthorizationContext> getPostAuthorizeAuthorizationMethodAfterAdvice() { |
||||
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(PostAuthorize.class); |
||||
PostAuthorizeAuthorizationManager authorizationManager = new PostAuthorizeAuthorizationManager(); |
||||
authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler()); |
||||
return new AuthorizationManagerMethodAfterAdvice<>(methodMatcher, authorizationManager); |
||||
} |
||||
|
||||
@Autowired(required = false) |
||||
void setAuthorizationMethodAfterAdvice( |
||||
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> authorizationMethodAfterAdvice) { |
||||
this.authorizationMethodAfterAdvice = authorizationMethodAfterAdvice; |
||||
} |
||||
|
||||
@Override |
||||
public void setImportMetadata(AnnotationMetadata importMetadata) { |
||||
Map<String, Object> attributes = importMetadata.getAnnotationAttributes(EnableMethodSecurity.class.getName()); |
||||
this.enableMethodSecurity = AnnotationAttributes.fromMap(attributes); |
||||
} |
||||
|
||||
private boolean securedEnabled() { |
||||
return this.enableMethodSecurity.getBoolean("securedEnabled"); |
||||
} |
||||
|
||||
private boolean jsr250Enabled() { |
||||
return this.enableMethodSecurity.getBoolean("jsr250Enabled"); |
||||
} |
||||
|
||||
private int order() { |
||||
return this.enableMethodSecurity.getNumber("order"); |
||||
} |
||||
|
||||
private static final class SecurityAnnotationsStaticMethodMatcher extends StaticMethodMatcher { |
||||
|
||||
private final Set<Class<? extends Annotation>> annotationClasses; |
||||
|
||||
@SafeVarargs |
||||
private SecurityAnnotationsStaticMethodMatcher(Class<? extends Annotation>... annotationClasses) { |
||||
this.annotationClasses = new HashSet<>(Arrays.asList(annotationClasses)); |
||||
} |
||||
|
||||
@Override |
||||
public boolean matches(Method method, Class<?> targetClass) { |
||||
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); |
||||
return hasAnnotations(specificMethod) || hasAnnotations(specificMethod.getDeclaringClass()); |
||||
} |
||||
|
||||
private boolean hasAnnotations(AnnotatedElement annotatedElement) { |
||||
Set<Annotation> annotations = AnnotatedElementUtils.findAllMergedAnnotations(annotatedElement, |
||||
this.annotationClasses); |
||||
return !annotations.isEmpty(); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
/* |
||||
* 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.config.annotation.method.configuration; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.context.annotation.AdviceMode; |
||||
import org.springframework.context.annotation.AdviceModeImportSelector; |
||||
import org.springframework.context.annotation.AutoProxyRegistrar; |
||||
|
||||
/** |
||||
* Dynamically determines which imports to include using the {@link EnableMethodSecurity} |
||||
* annotation. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
final class MethodSecuritySelector extends AdviceModeImportSelector<EnableMethodSecurity> { |
||||
|
||||
@Override |
||||
protected String[] selectImports(AdviceMode adviceMode) { |
||||
if (adviceMode == AdviceMode.PROXY) { |
||||
return getProxyImports(); |
||||
} |
||||
throw new IllegalStateException("AdviceMode '" + adviceMode + "' is not supported"); |
||||
} |
||||
|
||||
private String[] getProxyImports() { |
||||
List<String> result = new ArrayList<>(); |
||||
result.add(AutoProxyRegistrar.class.getName()); |
||||
result.add(MethodSecurityConfiguration.class.getName()); |
||||
return result.toArray(new String[0]); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,360 @@
@@ -0,0 +1,360 @@
|
||||
/* |
||||
* 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.config.annotation.method.configuration; |
||||
|
||||
import java.io.Serializable; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.function.Supplier; |
||||
|
||||
import org.junit.Rule; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
|
||||
import org.springframework.aop.MethodMatcher; |
||||
import org.springframework.aop.support.JdkRegexpMethodPointcut; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.security.access.AccessDeniedException; |
||||
import org.springframework.security.access.PermissionEvaluator; |
||||
import org.springframework.security.access.annotation.BusinessService; |
||||
import org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl; |
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.method.AuthorizationManagerMethodBeforeAdvice; |
||||
import org.springframework.security.access.method.AuthorizationMethodAfterAdvice; |
||||
import org.springframework.security.access.method.AuthorizationMethodBeforeAdvice; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.authorization.AuthorizationDecision; |
||||
import org.springframework.security.authorization.AuthorizationManager; |
||||
import org.springframework.security.config.test.SpringTestRule; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners; |
||||
import org.springframework.security.test.context.support.WithAnonymousUser; |
||||
import org.springframework.security.test.context.support.WithMockUser; |
||||
import org.springframework.test.context.junit4.SpringRunner; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
|
||||
/** |
||||
* Tests for {@link MethodSecurityConfiguration}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
*/ |
||||
@RunWith(SpringRunner.class) |
||||
@SecurityTestExecutionListeners |
||||
public class MethodSecurityConfigurationTests { |
||||
|
||||
@Rule |
||||
public final SpringTestRule spring = new SpringTestRule(); |
||||
|
||||
@Autowired(required = false) |
||||
MethodSecurityService methodSecurityService; |
||||
|
||||
@Autowired(required = false) |
||||
BusinessService businessService; |
||||
|
||||
@WithMockUser(roles = "ADMIN") |
||||
@Test |
||||
public void preAuthorizeWhenRoleAdminThenAccessDeniedException() { |
||||
this.spring.register(MethodSecurityServiceConfig.class).autowire(); |
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorize) |
||||
.withMessage("Access Denied"); |
||||
} |
||||
|
||||
@WithAnonymousUser |
||||
@Test |
||||
public void preAuthorizePermitAllWhenRoleAnonymousThenPasses() { |
||||
this.spring.register(MethodSecurityServiceConfig.class).autowire(); |
||||
String result = this.methodSecurityService.preAuthorizePermitAll(); |
||||
assertThat(result).isNull(); |
||||
} |
||||
|
||||
@WithAnonymousUser |
||||
@Test |
||||
public void preAuthorizeNotAnonymousWhenRoleAnonymousThenAccessDeniedException() { |
||||
this.spring.register(MethodSecurityServiceConfig.class).autowire(); |
||||
assertThatExceptionOfType(AccessDeniedException.class) |
||||
.isThrownBy(this.methodSecurityService::preAuthorizeNotAnonymous).withMessage("Access Denied"); |
||||
} |
||||
|
||||
@WithMockUser |
||||
@Test |
||||
public void preAuthorizeNotAnonymousWhenRoleUserThenPasses() { |
||||
this.spring.register(MethodSecurityServiceConfig.class).autowire(); |
||||
this.methodSecurityService.preAuthorizeNotAnonymous(); |
||||
} |
||||
|
||||
@WithMockUser |
||||
@Test |
||||
public void securedWhenRoleUserThenAccessDeniedException() { |
||||
this.spring.register(MethodSecurityServiceConfig.class).autowire(); |
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::secured) |
||||
.withMessage("Access Denied"); |
||||
} |
||||
|
||||
@WithMockUser(roles = "ADMIN") |
||||
@Test |
||||
public void securedWhenRoleAdminThenPasses() { |
||||
this.spring.register(MethodSecurityServiceConfig.class).autowire(); |
||||
String result = this.methodSecurityService.secured(); |
||||
assertThat(result).isNull(); |
||||
} |
||||
|
||||
@WithMockUser(roles = "ADMIN") |
||||
@Test |
||||
public void securedUserWhenRoleAdminThenAccessDeniedException() { |
||||
this.spring.register(MethodSecurityServiceConfig.class).autowire(); |
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser) |
||||
.withMessage("Access Denied"); |
||||
} |
||||
|
||||
@WithMockUser |
||||
@Test |
||||
public void securedUserWhenRoleUserThenPasses() { |
||||
this.spring.register(MethodSecurityServiceConfig.class).autowire(); |
||||
String result = this.methodSecurityService.securedUser(); |
||||
assertThat(result).isNull(); |
||||
} |
||||
|
||||
@WithMockUser |
||||
@Test |
||||
public void preAuthorizeAdminWhenRoleUserThenAccessDeniedException() { |
||||
this.spring.register(MethodSecurityServiceConfig.class).autowire(); |
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorizeAdmin) |
||||
.withMessage("Access Denied"); |
||||
} |
||||
|
||||
@WithMockUser(roles = "ADMIN") |
||||
@Test |
||||
public void preAuthorizeAdminWhenRoleAdminThenPasses() { |
||||
this.spring.register(MethodSecurityServiceConfig.class).autowire(); |
||||
this.methodSecurityService.preAuthorizeAdmin(); |
||||
} |
||||
|
||||
@WithMockUser |
||||
@Test |
||||
public void postHasPermissionWhenParameterIsNotGrantThenAccessDeniedException() { |
||||
this.spring.register(CustomPermissionEvaluatorConfig.class, MethodSecurityServiceConfig.class).autowire(); |
||||
assertThatExceptionOfType(AccessDeniedException.class) |
||||
.isThrownBy(() -> this.methodSecurityService.postHasPermission("deny")).withMessage("Access Denied"); |
||||
} |
||||
|
||||
@WithMockUser |
||||
@Test |
||||
public void postHasPermissionWhenParameterIsGrantThenPasses() { |
||||
this.spring.register(CustomPermissionEvaluatorConfig.class, MethodSecurityServiceConfig.class).autowire(); |
||||
String result = this.methodSecurityService.postHasPermission("grant"); |
||||
assertThat(result).isNull(); |
||||
} |
||||
|
||||
@WithMockUser |
||||
@Test |
||||
public void postAnnotationWhenParameterIsNotGrantThenAccessDeniedException() { |
||||
this.spring.register(MethodSecurityServiceConfig.class).autowire(); |
||||
assertThatExceptionOfType(AccessDeniedException.class) |
||||
.isThrownBy(() -> this.methodSecurityService.postAnnotation("deny")).withMessage("Access Denied"); |
||||
} |
||||
|
||||
@WithMockUser |
||||
@Test |
||||
public void postAnnotationWhenParameterIsGrantThenPasses() { |
||||
this.spring.register(MethodSecurityServiceConfig.class).autowire(); |
||||
String result = this.methodSecurityService.postAnnotation("grant"); |
||||
assertThat(result).isNull(); |
||||
} |
||||
|
||||
@WithMockUser("bob") |
||||
@Test |
||||
public void methodReturningAListWhenPrePostFiltersConfiguredThenFiltersList() { |
||||
this.spring.register(BusinessServiceConfig.class).autowire(); |
||||
List<String> names = new ArrayList<>(); |
||||
names.add("bob"); |
||||
names.add("joe"); |
||||
names.add("sam"); |
||||
List<?> result = this.businessService.methodReturningAList(names); |
||||
assertThat(result).hasSize(1); |
||||
assertThat(result.get(0)).isEqualTo("bob"); |
||||
} |
||||
|
||||
@WithMockUser("bob") |
||||
@Test |
||||
public void methodReturningAnArrayWhenPostFilterConfiguredThenFiltersArray() { |
||||
this.spring.register(BusinessServiceConfig.class).autowire(); |
||||
List<String> names = new ArrayList<>(); |
||||
names.add("bob"); |
||||
names.add("joe"); |
||||
names.add("sam"); |
||||
Object[] result = this.businessService.methodReturningAnArray(names.toArray()); |
||||
assertThat(result).hasSize(1); |
||||
assertThat(result[0]).isEqualTo("bob"); |
||||
} |
||||
|
||||
@WithMockUser("bob") |
||||
@Test |
||||
public void securedUserWhenCustomBeforeAdviceConfiguredAndNameBobThenPasses() { |
||||
this.spring.register(CustomAuthorizationManagerBeforeAdviceConfig.class, MethodSecurityServiceConfig.class) |
||||
.autowire(); |
||||
String result = this.methodSecurityService.securedUser(); |
||||
assertThat(result).isNull(); |
||||
} |
||||
|
||||
@WithMockUser("joe") |
||||
@Test |
||||
public void securedUserWhenCustomBeforeAdviceConfiguredAndNameNotBobThenAccessDeniedException() { |
||||
this.spring.register(CustomAuthorizationManagerBeforeAdviceConfig.class, MethodSecurityServiceConfig.class) |
||||
.autowire(); |
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser) |
||||
.withMessage("Access Denied"); |
||||
} |
||||
|
||||
@WithMockUser("bob") |
||||
@Test |
||||
public void securedUserWhenCustomAfterAdviceConfiguredAndNameBobThenGranted() { |
||||
this.spring.register(CustomAuthorizationManagerAfterAdviceConfig.class, MethodSecurityServiceConfig.class) |
||||
.autowire(); |
||||
String result = this.methodSecurityService.securedUser(); |
||||
assertThat(result).isEqualTo("granted"); |
||||
} |
||||
|
||||
@WithMockUser("joe") |
||||
@Test |
||||
public void securedUserWhenCustomAfterAdviceConfiguredAndNameNotBobThenAccessDeniedException() { |
||||
this.spring.register(CustomAuthorizationManagerAfterAdviceConfig.class, MethodSecurityServiceConfig.class) |
||||
.autowire(); |
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser) |
||||
.withMessage("Access Denied for User 'joe'"); |
||||
} |
||||
|
||||
@WithMockUser(roles = "ADMIN") |
||||
@Test |
||||
public void jsr250WhenRoleAdminThenAccessDeniedException() { |
||||
this.spring.register(MethodSecurityServiceConfig.class).autowire(); |
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::jsr250) |
||||
.withMessage("Access Denied"); |
||||
} |
||||
|
||||
@WithAnonymousUser |
||||
@Test |
||||
public void jsr250PermitAllWhenRoleAnonymousThenPasses() { |
||||
this.spring.register(MethodSecurityServiceConfig.class).autowire(); |
||||
String result = this.methodSecurityService.jsr250PermitAll(); |
||||
assertThat(result).isNull(); |
||||
} |
||||
|
||||
@WithMockUser(roles = "ADMIN") |
||||
@Test |
||||
public void rolesAllowedUserWhenRoleAdminThenAccessDeniedException() { |
||||
this.spring.register(BusinessServiceConfig.class).autowire(); |
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.businessService::rolesAllowedUser) |
||||
.withMessage("Access Denied"); |
||||
} |
||||
|
||||
@WithMockUser |
||||
@Test |
||||
public void rolesAllowedUserWhenRoleUserThenPasses() { |
||||
this.spring.register(BusinessServiceConfig.class).autowire(); |
||||
this.businessService.rolesAllowedUser(); |
||||
} |
||||
|
||||
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true) |
||||
static class MethodSecurityServiceConfig { |
||||
|
||||
@Bean |
||||
MethodSecurityService methodSecurityService() { |
||||
return new MethodSecurityServiceImpl(); |
||||
} |
||||
|
||||
} |
||||
|
||||
@EnableMethodSecurity(jsr250Enabled = true) |
||||
static class BusinessServiceConfig { |
||||
|
||||
@Bean |
||||
BusinessService businessService() { |
||||
return new ExpressionProtectedBusinessServiceImpl(); |
||||
} |
||||
|
||||
} |
||||
|
||||
@EnableMethodSecurity |
||||
static class CustomPermissionEvaluatorConfig { |
||||
|
||||
@Bean |
||||
MethodSecurityExpressionHandler methodSecurityExpressionHandler() { |
||||
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); |
||||
expressionHandler.setPermissionEvaluator(new PermissionEvaluator() { |
||||
@Override |
||||
public boolean hasPermission(Authentication authentication, Object targetDomainObject, |
||||
Object permission) { |
||||
return "grant".equals(targetDomainObject); |
||||
} |
||||
|
||||
@Override |
||||
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, |
||||
Object permission) { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
}); |
||||
return expressionHandler; |
||||
} |
||||
|
||||
} |
||||
|
||||
@EnableMethodSecurity |
||||
static class CustomAuthorizationManagerBeforeAdviceConfig { |
||||
|
||||
@Bean |
||||
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> customBeforeAdvice() { |
||||
JdkRegexpMethodPointcut methodMatcher = new JdkRegexpMethodPointcut(); |
||||
methodMatcher.setPattern(".*MethodSecurityServiceImpl.*securedUser"); |
||||
AuthorizationManager<MethodAuthorizationContext> authorizationManager = (a, |
||||
o) -> new AuthorizationDecision("bob".equals(a.get().getName())); |
||||
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager); |
||||
} |
||||
|
||||
} |
||||
|
||||
@EnableMethodSecurity |
||||
static class CustomAuthorizationManagerAfterAdviceConfig { |
||||
|
||||
@Bean |
||||
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> customAfterAdvice() { |
||||
JdkRegexpMethodPointcut methodMatcher = new JdkRegexpMethodPointcut(); |
||||
methodMatcher.setPattern(".*MethodSecurityServiceImpl.*securedUser"); |
||||
return new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() { |
||||
@Override |
||||
public MethodMatcher getMethodMatcher() { |
||||
return methodMatcher; |
||||
} |
||||
|
||||
@Override |
||||
public Object after(Supplier<Authentication> authentication, |
||||
MethodAuthorizationContext methodAuthorizationContext, Object returnedObject) { |
||||
Authentication auth = authentication.get(); |
||||
if ("bob".equals(auth.getName())) { |
||||
return "granted"; |
||||
} |
||||
throw new AccessDeniedException("Access Denied for User '" + auth.getName() + "'"); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
/* |
||||
* 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.access.annotation; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.util.Map; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
|
||||
import org.springframework.core.MethodClassKey; |
||||
import org.springframework.lang.NonNull; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.authorization.AuthorizationManager; |
||||
|
||||
/** |
||||
* An abstract registry which provides an {@link AuthorizationManager} for the |
||||
* {@link MethodInvocation}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
abstract class AbstractAuthorizationManagerRegistry { |
||||
|
||||
static final AuthorizationManager<MethodAuthorizationContext> NULL_MANAGER = (a, o) -> null; |
||||
|
||||
private final Map<MethodClassKey, AuthorizationManager<MethodAuthorizationContext>> cachedManagers = new ConcurrentHashMap<>(); |
||||
|
||||
/** |
||||
* Returns an {@link AuthorizationManager} for the {@link MethodAuthorizationContext}. |
||||
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to use |
||||
* @return an {@link AuthorizationManager} to use |
||||
*/ |
||||
final AuthorizationManager<MethodAuthorizationContext> getManager( |
||||
MethodAuthorizationContext methodAuthorizationContext) { |
||||
MethodInvocation methodInvocation = methodAuthorizationContext.getMethodInvocation(); |
||||
Method method = methodInvocation.getMethod(); |
||||
Class<?> targetClass = methodAuthorizationContext.getTargetClass(); |
||||
MethodClassKey cacheKey = new MethodClassKey(method, targetClass); |
||||
return this.cachedManagers.computeIfAbsent(cacheKey, (k) -> resolveManager(method, targetClass)); |
||||
} |
||||
|
||||
/** |
||||
* Subclasses should implement this method to provide the non-null |
||||
* {@link AuthorizationManager} for the method and the target class. |
||||
* @param method the method |
||||
* @param targetClass the target class
|
||||
* @return the non-null {@link AuthorizationManager} |
||||
*/ |
||||
@NonNull |
||||
abstract AuthorizationManager<MethodAuthorizationContext> resolveManager(Method method, Class<?> targetClass); |
||||
|
||||
} |
||||
@ -0,0 +1,123 @@
@@ -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.access.annotation; |
||||
|
||||
import java.lang.annotation.Annotation; |
||||
import java.lang.reflect.AnnotatedElement; |
||||
import java.lang.reflect.Method; |
||||
import java.util.HashSet; |
||||
import java.util.Set; |
||||
import java.util.function.Supplier; |
||||
|
||||
import javax.annotation.security.DenyAll; |
||||
import javax.annotation.security.PermitAll; |
||||
import javax.annotation.security.RolesAllowed; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
|
||||
import org.springframework.aop.support.AopUtils; |
||||
import org.springframework.core.annotation.AnnotatedElementUtils; |
||||
import org.springframework.lang.NonNull; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.authorization.AuthorityAuthorizationManager; |
||||
import org.springframework.security.authorization.AuthorizationDecision; |
||||
import org.springframework.security.authorization.AuthorizationManager; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has |
||||
* access to the {@link MethodInvocation} by evaluating if the {@link Authentication} |
||||
* contains a specified authority from the JSR-250 security annotations. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
public final class Jsr250AuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> { |
||||
|
||||
private static final Set<Class<? extends Annotation>> JSR250_ANNOTATIONS = new HashSet<>(); |
||||
|
||||
static { |
||||
JSR250_ANNOTATIONS.add(DenyAll.class); |
||||
JSR250_ANNOTATIONS.add(PermitAll.class); |
||||
JSR250_ANNOTATIONS.add(RolesAllowed.class); |
||||
} |
||||
|
||||
private final Jsr250AuthorizationManagerRegistry registry = new Jsr250AuthorizationManagerRegistry(); |
||||
|
||||
private String rolePrefix = "ROLE_"; |
||||
|
||||
/** |
||||
* Sets the role prefix. Defaults to "ROLE_". |
||||
* @param rolePrefix the role prefix to use |
||||
*/ |
||||
public void setRolePrefix(String rolePrefix) { |
||||
Assert.notNull(rolePrefix, "rolePrefix cannot be null"); |
||||
this.rolePrefix = rolePrefix; |
||||
} |
||||
|
||||
/** |
||||
* Determines if an {@link Authentication} has access to the {@link MethodInvocation} |
||||
* by evaluating if the {@link Authentication} contains a specified authority from the |
||||
* JSR-250 security annotations. |
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check |
||||
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check |
||||
* @return an {@link AuthorizationDecision} or null if the JSR-250 security |
||||
* annotations is not present |
||||
*/ |
||||
@Override |
||||
public AuthorizationDecision check(Supplier<Authentication> authentication, |
||||
MethodAuthorizationContext methodAuthorizationContext) { |
||||
AuthorizationManager<MethodAuthorizationContext> delegate = this.registry |
||||
.getManager(methodAuthorizationContext); |
||||
return delegate.check(authentication, methodAuthorizationContext); |
||||
} |
||||
|
||||
private final class Jsr250AuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry { |
||||
|
||||
@NonNull |
||||
@Override |
||||
AuthorizationManager<MethodAuthorizationContext> resolveManager(Method method, Class<?> targetClass) { |
||||
for (Annotation annotation : findJsr250Annotations(method, targetClass)) { |
||||
if (annotation instanceof DenyAll) { |
||||
return (a, o) -> new AuthorizationDecision(false); |
||||
} |
||||
if (annotation instanceof PermitAll) { |
||||
return (a, o) -> new AuthorizationDecision(true); |
||||
} |
||||
if (annotation instanceof RolesAllowed) { |
||||
RolesAllowed rolesAllowed = (RolesAllowed) annotation; |
||||
return AuthorityAuthorizationManager.hasAnyRole(Jsr250AuthorizationManager.this.rolePrefix, |
||||
rolesAllowed.value()); |
||||
} |
||||
} |
||||
return NULL_MANAGER; |
||||
} |
||||
|
||||
private Set<Annotation> findJsr250Annotations(Method method, Class<?> targetClass) { |
||||
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); |
||||
Set<Annotation> annotations = findAnnotations(specificMethod); |
||||
return (annotations.isEmpty()) ? findAnnotations(specificMethod.getDeclaringClass()) : annotations; |
||||
} |
||||
|
||||
private Set<Annotation> findAnnotations(AnnotatedElement annotatedElement) { |
||||
return AnnotatedElementUtils.findAllMergedAnnotations(annotatedElement, JSR250_ANNOTATIONS); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,80 @@
@@ -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.access.annotation; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.util.function.Supplier; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
|
||||
import org.springframework.aop.support.AopUtils; |
||||
import org.springframework.core.annotation.AnnotationUtils; |
||||
import org.springframework.lang.NonNull; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.authorization.AuthorityAuthorizationManager; |
||||
import org.springframework.security.authorization.AuthorizationDecision; |
||||
import org.springframework.security.authorization.AuthorizationManager; |
||||
import org.springframework.security.core.Authentication; |
||||
|
||||
/** |
||||
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has |
||||
* access to the {@link MethodInvocation} by evaluating if the {@link Authentication} |
||||
* contains a specified authority from the Spring Security's {@link Secured} annotation. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
public final class SecuredAuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> { |
||||
|
||||
private final SecuredAuthorizationManagerRegistry registry = new SecuredAuthorizationManagerRegistry(); |
||||
|
||||
/** |
||||
* Determines if an {@link Authentication} has access to the {@link MethodInvocation} |
||||
* by evaluating if the {@link Authentication} contains a specified authority from the |
||||
* Spring Security's {@link Secured} annotation. |
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check |
||||
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check |
||||
* @return an {@link AuthorizationDecision} or null if the {@link Secured} annotation |
||||
* is not present |
||||
*/ |
||||
@Override |
||||
public AuthorizationDecision check(Supplier<Authentication> authentication, |
||||
MethodAuthorizationContext methodAuthorizationContext) { |
||||
AuthorizationManager<MethodAuthorizationContext> delegate = this.registry |
||||
.getManager(methodAuthorizationContext); |
||||
return delegate.check(authentication, methodAuthorizationContext); |
||||
} |
||||
|
||||
private static final class SecuredAuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry { |
||||
|
||||
@NonNull |
||||
@Override |
||||
AuthorizationManager<MethodAuthorizationContext> resolveManager(Method method, Class<?> targetClass) { |
||||
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); |
||||
Secured secured = findSecuredAnnotation(specificMethod); |
||||
return (secured != null) ? AuthorityAuthorizationManager.hasAnyAuthority(secured.value()) : NULL_MANAGER; |
||||
} |
||||
|
||||
private Secured findSecuredAnnotation(Method method) { |
||||
Secured secured = AnnotationUtils.findAnnotation(method, Secured.class); |
||||
return (secured != null) ? secured |
||||
: AnnotationUtils.findAnnotation(method.getDeclaringClass(), Secured.class); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,82 @@
@@ -0,0 +1,82 @@
|
||||
/* |
||||
* 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.access.intercept.aopalliance; |
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor; |
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
|
||||
import org.springframework.aop.support.AopUtils; |
||||
import org.springframework.lang.NonNull; |
||||
import org.springframework.security.access.method.AuthorizationMethodAfterAdvice; |
||||
import org.springframework.security.access.method.AuthorizationMethodBeforeAdvice; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.core.context.SecurityContextHolder; |
||||
|
||||
/** |
||||
* Provides security interception of AOP Alliance based method invocations. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
public final class AuthorizationMethodInterceptor implements MethodInterceptor { |
||||
|
||||
private final AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> beforeAdvice; |
||||
|
||||
private final AuthorizationMethodAfterAdvice<MethodAuthorizationContext> afterAdvice; |
||||
|
||||
/** |
||||
* Creates an instance. |
||||
* @param beforeAdvice the {@link AuthorizationMethodBeforeAdvice} to use |
||||
* @param afterAdvice the {@link AuthorizationMethodAfterAdvice} to use |
||||
*/ |
||||
public AuthorizationMethodInterceptor(AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> beforeAdvice, |
||||
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> afterAdvice) { |
||||
this.beforeAdvice = beforeAdvice; |
||||
this.afterAdvice = afterAdvice; |
||||
} |
||||
|
||||
/** |
||||
* This method should be used to enforce security on a {@link MethodInvocation}. |
||||
* @param mi the method being invoked which requires a security decision |
||||
* @return the returned value from the {@link MethodInvocation} |
||||
*/ |
||||
@Override |
||||
public Object invoke(@NonNull MethodInvocation mi) throws Throwable { |
||||
MethodAuthorizationContext methodAuthorizationContext = getMethodAuthorizationContext(mi); |
||||
this.beforeAdvice.before(this::getAuthentication, methodAuthorizationContext); |
||||
Object returnedObject = mi.proceed(); |
||||
return this.afterAdvice.after(this::getAuthentication, methodAuthorizationContext, returnedObject); |
||||
} |
||||
|
||||
private MethodAuthorizationContext getMethodAuthorizationContext(MethodInvocation mi) { |
||||
Object target = mi.getThis(); |
||||
Class<?> targetClass = (target != null) ? AopUtils.getTargetClass(target) : null; |
||||
return new MethodAuthorizationContext(mi, targetClass); |
||||
} |
||||
|
||||
private Authentication getAuthentication() { |
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); |
||||
if (authentication == null) { |
||||
throw new AuthenticationCredentialsNotFoundException( |
||||
"An Authentication object was not found in the SecurityContext"); |
||||
} |
||||
return authentication; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,73 @@
@@ -0,0 +1,73 @@
|
||||
/* |
||||
* 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.access.method; |
||||
|
||||
import java.util.function.Supplier; |
||||
|
||||
import org.springframework.aop.MethodMatcher; |
||||
import org.springframework.security.access.AccessDeniedException; |
||||
import org.springframework.security.authorization.AuthorizationManager; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* An {@link AuthorizationMethodAfterAdvice} which can determine if an |
||||
* {@link Authentication} has access to the {@link T} object using an |
||||
* {@link AuthorizationManager} if a {@link MethodMatcher} matches. |
||||
* |
||||
* @param <T> the type of object that the authorization check is being done one. |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
public final class AuthorizationManagerMethodAfterAdvice<T> implements AuthorizationMethodAfterAdvice<T> { |
||||
|
||||
private final MethodMatcher methodMatcher; |
||||
|
||||
private final AuthorizationManager<T> authorizationManager; |
||||
|
||||
/** |
||||
* Creates an instance. |
||||
* @param methodMatcher the {@link MethodMatcher} to use |
||||
* @param authorizationManager the {@link AuthorizationManager} to use |
||||
*/ |
||||
public AuthorizationManagerMethodAfterAdvice(MethodMatcher methodMatcher, |
||||
AuthorizationManager<T> authorizationManager) { |
||||
Assert.notNull(methodMatcher, "methodMatcher cannot be null"); |
||||
Assert.notNull(authorizationManager, "authorizationManager cannot be null"); |
||||
this.methodMatcher = methodMatcher; |
||||
this.authorizationManager = authorizationManager; |
||||
} |
||||
|
||||
/** |
||||
* Determines if an {@link Authentication} has access to the {@link T} object using |
||||
* the {@link AuthorizationManager}. |
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check |
||||
* @param object the {@link T} object to check |
||||
* @throws AccessDeniedException if access is not granted |
||||
*/ |
||||
@Override |
||||
public Object after(Supplier<Authentication> authentication, T object, Object returnedObject) { |
||||
this.authorizationManager.verify(authentication, object); |
||||
return returnedObject; |
||||
} |
||||
|
||||
@Override |
||||
public MethodMatcher getMethodMatcher() { |
||||
return this.methodMatcher; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,72 @@
@@ -0,0 +1,72 @@
|
||||
/* |
||||
* 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.access.method; |
||||
|
||||
import java.util.function.Supplier; |
||||
|
||||
import org.springframework.aop.MethodMatcher; |
||||
import org.springframework.security.access.AccessDeniedException; |
||||
import org.springframework.security.authorization.AuthorizationManager; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* An {@link AuthorizationMethodBeforeAdvice} which can determine if an |
||||
* {@link Authentication} has access to the {@link T} object using an |
||||
* {@link AuthorizationManager} if a {@link MethodMatcher} matches. |
||||
* |
||||
* @param <T> the type of object that the authorization check is being done one. |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
public final class AuthorizationManagerMethodBeforeAdvice<T> implements AuthorizationMethodBeforeAdvice<T> { |
||||
|
||||
private final MethodMatcher methodMatcher; |
||||
|
||||
private final AuthorizationManager<T> authorizationManager; |
||||
|
||||
/** |
||||
* Creates an instance. |
||||
* @param methodMatcher the {@link MethodMatcher} to use |
||||
* @param authorizationManager the {@link AuthorizationManager} to use |
||||
*/ |
||||
public AuthorizationManagerMethodBeforeAdvice(MethodMatcher methodMatcher, |
||||
AuthorizationManager<T> authorizationManager) { |
||||
Assert.notNull(methodMatcher, "methodMatcher cannot be null"); |
||||
Assert.notNull(authorizationManager, "authorizationManager cannot be null"); |
||||
this.methodMatcher = methodMatcher; |
||||
this.authorizationManager = authorizationManager; |
||||
} |
||||
|
||||
/** |
||||
* Determines if an {@link Authentication} has access to the {@link T} object using |
||||
* the {@link AuthorizationManager}. |
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check |
||||
* @param object the {@link T} object to check |
||||
* @throws AccessDeniedException if access is not granted |
||||
*/ |
||||
@Override |
||||
public void before(Supplier<Authentication> authentication, T object) { |
||||
this.authorizationManager.verify(authentication, object); |
||||
} |
||||
|
||||
@Override |
||||
public MethodMatcher getMethodMatcher() { |
||||
return this.methodMatcher; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
/* |
||||
* 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.access.method; |
||||
|
||||
import java.util.function.Supplier; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
|
||||
import org.springframework.aop.ClassFilter; |
||||
import org.springframework.aop.Pointcut; |
||||
import org.springframework.security.core.Authentication; |
||||
|
||||
/** |
||||
* An Authorization advice that can determine if an {@link Authentication} has access to |
||||
* the returned object from the {@link MethodInvocation}. The {@link #getMethodMatcher()} |
||||
* 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 |
||||
* @since 5.5 |
||||
*/ |
||||
public interface AuthorizationMethodAfterAdvice<T> extends Pointcut { |
||||
|
||||
/** |
||||
* Returns the default {@link ClassFilter}. |
||||
* @return the {@link ClassFilter#TRUE} to use |
||||
*/ |
||||
@Override |
||||
default ClassFilter getClassFilter() { |
||||
return ClassFilter.TRUE; |
||||
} |
||||
|
||||
/** |
||||
* Determines if an {@link Authentication} has access to the returned object from the |
||||
* {@link MethodInvocation}. |
||||
* @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 {@link MethodInvocation} to |
||||
* check |
||||
* @return the <code>Object</code> 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</code> method argument) |
||||
*/ |
||||
Object after(Supplier<Authentication> authentication, T object, Object returnedObject); |
||||
|
||||
} |
||||
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
/* |
||||
* 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.access.method; |
||||
|
||||
import java.util.function.Supplier; |
||||
|
||||
import org.springframework.aop.ClassFilter; |
||||
import org.springframework.aop.Pointcut; |
||||
import org.springframework.security.core.Authentication; |
||||
|
||||
/** |
||||
* An advice which can determine if an {@link Authentication} has access to the {@link T} |
||||
* object. The {@link #getMethodMatcher()} 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 |
||||
* @since 5.5 |
||||
*/ |
||||
public interface AuthorizationMethodBeforeAdvice<T> extends Pointcut { |
||||
|
||||
/** |
||||
* Returns the default {@link ClassFilter}. |
||||
* @return the {@link ClassFilter#TRUE} to use |
||||
*/ |
||||
@Override |
||||
default ClassFilter getClassFilter() { |
||||
return ClassFilter.TRUE; |
||||
} |
||||
|
||||
/** |
||||
* Determines 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); |
||||
|
||||
} |
||||
@ -0,0 +1,102 @@
@@ -0,0 +1,102 @@
|
||||
/* |
||||
* 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.access.method; |
||||
|
||||
import java.lang.reflect.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.MethodMatcher; |
||||
import org.springframework.aop.support.StaticMethodMatcher; |
||||
import org.springframework.core.log.LogMessage; |
||||
import org.springframework.security.core.Authentication; |
||||
|
||||
/** |
||||
* An {@link AuthorizationMethodAfterAdvice} which delegates to specific |
||||
* {@link AuthorizationMethodAfterAdvice}s and returns the result (possibly modified) from |
||||
* the {@link MethodInvocation}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
public final class DelegatingAuthorizationMethodAfterAdvice |
||||
implements AuthorizationMethodAfterAdvice<MethodAuthorizationContext> { |
||||
|
||||
private final Log logger = LogFactory.getLog(getClass()); |
||||
|
||||
private final MethodMatcher methodMatcher = new StaticMethodMatcher() { |
||||
@Override |
||||
public boolean matches(Method method, Class<?> targetClass) { |
||||
for (AuthorizationMethodAfterAdvice<MethodAuthorizationContext> delegate : DelegatingAuthorizationMethodAfterAdvice.this.delegates) { |
||||
MethodMatcher methodMatcher = delegate.getMethodMatcher(); |
||||
if (methodMatcher.matches(method, targetClass)) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
}; |
||||
|
||||
private final List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> delegates; |
||||
|
||||
/** |
||||
* Creates an instance. |
||||
* @param delegates the {@link AuthorizationMethodAfterAdvice}s to use |
||||
*/ |
||||
public DelegatingAuthorizationMethodAfterAdvice( |
||||
List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> delegates) { |
||||
this.delegates = delegates; |
||||
} |
||||
|
||||
@Override |
||||
public MethodMatcher getMethodMatcher() { |
||||
return this.methodMatcher; |
||||
} |
||||
|
||||
/** |
||||
* Delegates to specific {@link AuthorizationMethodAfterAdvice}s and returns the |
||||
* <code>returnedObject</code> (possibly modified) from the method argument. |
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check |
||||
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check |
||||
* @param returnedObject the returned object from the {@link MethodInvocation} to |
||||
* check |
||||
* @return the <code>returnedObject</code> (possibly modified) from the method |
||||
* argument |
||||
*/ |
||||
@Override |
||||
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext methodAuthorizationContext, |
||||
Object returnedObject) { |
||||
if (this.logger.isTraceEnabled()) { |
||||
this.logger.trace( |
||||
LogMessage.format("Post Authorizing %s from %s", returnedObject, methodAuthorizationContext)); |
||||
} |
||||
Object result = returnedObject; |
||||
for (AuthorizationMethodAfterAdvice<MethodAuthorizationContext> delegate : this.delegates) { |
||||
if (this.logger.isTraceEnabled()) { |
||||
this.logger.trace(LogMessage.format("Checking authorization on %s from %s using %s", result, |
||||
methodAuthorizationContext, delegate)); |
||||
} |
||||
result = delegate.after(authentication, methodAuthorizationContext, result); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,95 @@
@@ -0,0 +1,95 @@
|
||||
/* |
||||
* 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.access.method; |
||||
|
||||
import java.lang.reflect.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.MethodMatcher; |
||||
import org.springframework.aop.support.StaticMethodMatcher; |
||||
import org.springframework.core.log.LogMessage; |
||||
import org.springframework.security.core.Authentication; |
||||
|
||||
/** |
||||
* 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 |
||||
* @since 5.5 |
||||
*/ |
||||
public final class DelegatingAuthorizationMethodBeforeAdvice |
||||
implements AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> { |
||||
|
||||
private final Log logger = LogFactory.getLog(getClass()); |
||||
|
||||
private final MethodMatcher methodMatcher = new StaticMethodMatcher() { |
||||
@Override |
||||
public boolean matches(Method method, Class<?> targetClass) { |
||||
for (AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> delegate : DelegatingAuthorizationMethodBeforeAdvice.this.delegates) { |
||||
MethodMatcher methodMatcher = delegate.getMethodMatcher(); |
||||
if (methodMatcher.matches(method, targetClass)) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
}; |
||||
|
||||
private final List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> delegates; |
||||
|
||||
/** |
||||
* Creates an instance. |
||||
* @param delegates the {@link AuthorizationMethodBeforeAdvice}s to use |
||||
*/ |
||||
public DelegatingAuthorizationMethodBeforeAdvice( |
||||
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> delegates) { |
||||
this.delegates = delegates; |
||||
} |
||||
|
||||
@Override |
||||
public MethodMatcher getMethodMatcher() { |
||||
return this.methodMatcher; |
||||
} |
||||
|
||||
/** |
||||
* Delegates to a specific {@link AuthorizationMethodBeforeAdvice} and grants access |
||||
* if all {@link AuthorizationMethodBeforeAdvice}s granted or abstained. Denies only |
||||
* if one of the {@link AuthorizationMethodBeforeAdvice}s denied. |
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check |
||||
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check |
||||
*/ |
||||
@Override |
||||
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext methodAuthorizationContext) { |
||||
if (this.logger.isTraceEnabled()) { |
||||
this.logger.trace(LogMessage.format("Pre Authorizing %s", methodAuthorizationContext)); |
||||
} |
||||
for (AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> delegate : this.delegates) { |
||||
if (this.logger.isTraceEnabled()) { |
||||
this.logger.trace(LogMessage.format("Checking authorization on %s using %s", methodAuthorizationContext, |
||||
delegate)); |
||||
} |
||||
delegate.before(authentication, methodAuthorizationContext); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
|
||||
/* |
||||
* 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.access.method; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
|
||||
/** |
||||
* An authorization context which is holds the {@link MethodInvocation}, the target class
|
||||
* and the returned object. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
public final class MethodAuthorizationContext { |
||||
|
||||
private final MethodInvocation methodInvocation; |
||||
|
||||
private final Class<?> targetClass; |
||||
|
||||
private Object returnObject; |
||||
|
||||
/** |
||||
* 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; |
||||
} |
||||
|
||||
/** |
||||
* Returns the {@link MethodInvocation}. |
||||
* @return the {@link MethodInvocation} to use |
||||
*/ |
||||
public MethodInvocation getMethodInvocation() { |
||||
return this.methodInvocation; |
||||
} |
||||
|
||||
/** |
||||
* Returns the target class. |
||||
* @return the target class to use |
||||
*/ |
||||
public Class<?> getTargetClass() { |
||||
return this.targetClass; |
||||
} |
||||
|
||||
/** |
||||
* Returns the returned object from the {@link MethodInvocation}. |
||||
* @return the returned object from the {@link MethodInvocation} to use |
||||
*/ |
||||
public Object getReturnObject() { |
||||
return this.returnObject; |
||||
} |
||||
|
||||
/** |
||||
* Sets the returned object from the {@link MethodInvocation}. |
||||
* @param returnObject the returned object from the {@link MethodInvocation} to use |
||||
*/ |
||||
public void setReturnObject(Object returnObject) { |
||||
this.returnObject = returnObject; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "MethodAuthorizationContext[methodInvocation=" + this.methodInvocation + ", targetClass=" |
||||
+ this.targetClass + ", returnObject=" + this.returnObject + ']'; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,73 @@
@@ -0,0 +1,73 @@
|
||||
/* |
||||
* 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.Map; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
|
||||
import org.springframework.core.MethodClassKey; |
||||
import org.springframework.lang.NonNull; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
|
||||
/** |
||||
* An abstract registry which provides an {@link ExpressionAttribute} for the |
||||
* {@link MethodInvocation}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
abstract class AbstractExpressionAttributeRegistry<T extends ExpressionAttribute> { |
||||
|
||||
private final Map<MethodClassKey, T> cachedAttributes = new ConcurrentHashMap<>(); |
||||
|
||||
/** |
||||
* Returns an {@link ExpressionAttribute} for the {@link MethodAuthorizationContext}. |
||||
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to use |
||||
* @return the {@link ExpressionAttribute} to use |
||||
*/ |
||||
final T getAttribute(MethodAuthorizationContext methodAuthorizationContext) { |
||||
MethodInvocation methodInvocation = methodAuthorizationContext.getMethodInvocation(); |
||||
Method method = methodInvocation.getMethod(); |
||||
Class<?> targetClass = methodAuthorizationContext.getTargetClass(); |
||||
return getAttribute(method, targetClass); |
||||
} |
||||
|
||||
/** |
||||
* Returns an {@link ExpressionAttribute} for the method and the target class. |
||||
* @param method the method |
||||
* @param targetClass the target class
|
||||
* @return the {@link ExpressionAttribute} to use |
||||
*/ |
||||
final T getAttribute(Method method, Class<?> targetClass) { |
||||
MethodClassKey cacheKey = new MethodClassKey(method, targetClass); |
||||
return this.cachedAttributes.computeIfAbsent(cacheKey, (k) -> resolveAttribute(method, targetClass)); |
||||
} |
||||
|
||||
/** |
||||
* Subclasses should implement this method to provide the non-null |
||||
* {@link ExpressionAttribute} for the method and the target class. |
||||
* @param method the method |
||||
* @param targetClass the target class
|
||||
* @return the non-null {@link ExpressionAttribute} |
||||
*/ |
||||
@NonNull |
||||
abstract T resolveAttribute(Method method, Class<?> targetClass); |
||||
|
||||
} |
||||
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
/* |
||||
* 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.springframework.expression.Expression; |
||||
|
||||
/** |
||||
* An {@link Expression} attribute. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
class ExpressionAttribute { |
||||
|
||||
/** |
||||
* Represents an empty attribute with null {@link Expression}. |
||||
*/ |
||||
static final ExpressionAttribute NULL_ATTRIBUTE = new ExpressionAttribute(null); |
||||
|
||||
private final Expression expression; |
||||
|
||||
/** |
||||
* Creates an instance. |
||||
* @param expression the {@link Expression} to use |
||||
*/ |
||||
ExpressionAttribute(Expression expression) { |
||||
this.expression = expression; |
||||
} |
||||
|
||||
/** |
||||
* Returns the {@link Expression}. |
||||
* @return the {@link Expression} to use |
||||
*/ |
||||
Expression getExpression() { |
||||
return this.expression; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,108 @@
@@ -0,0 +1,108 @@
|
||||
/* |
||||
* 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.function.Supplier; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
import reactor.util.annotation.NonNull; |
||||
|
||||
import org.springframework.aop.support.AopUtils; |
||||
import org.springframework.core.annotation.AnnotationUtils; |
||||
import org.springframework.expression.EvaluationContext; |
||||
import org.springframework.expression.Expression; |
||||
import org.springframework.security.access.expression.ExpressionUtils; |
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.access.prepost.PostAuthorize; |
||||
import org.springframework.security.authorization.AuthorizationDecision; |
||||
import org.springframework.security.authorization.AuthorizationManager; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has |
||||
* access to the {@link MethodInvocation} by evaluating an expression from the |
||||
* {@link PostAuthorize} annotation. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
public final class PostAuthorizeAuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> { |
||||
|
||||
private final PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry(); |
||||
|
||||
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); |
||||
|
||||
/** |
||||
* Sets the {@link MethodSecurityExpressionHandler}. |
||||
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use |
||||
*/ |
||||
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) { |
||||
Assert.notNull(expressionHandler, "expressionHandler cannot be null"); |
||||
this.expressionHandler = expressionHandler; |
||||
} |
||||
|
||||
/** |
||||
* Determines if an {@link Authentication} has access to the {@link MethodInvocation} |
||||
* by evaluating an expression from the {@link PostAuthorize} annotation. |
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check |
||||
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check |
||||
* @return an {@link AuthorizationDecision} or null if the {@link PostAuthorize} |
||||
* annotation is not present |
||||
*/ |
||||
@Override |
||||
public AuthorizationDecision check(Supplier<Authentication> authentication, |
||||
MethodAuthorizationContext methodAuthorizationContext) { |
||||
ExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext); |
||||
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) { |
||||
return null; |
||||
} |
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), |
||||
methodAuthorizationContext.getMethodInvocation()); |
||||
this.expressionHandler.setReturnObject(methodAuthorizationContext.getReturnObject(), ctx); |
||||
boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx); |
||||
return new AuthorizationDecision(granted); |
||||
} |
||||
|
||||
private final class PostAuthorizeExpressionAttributeRegistry |
||||
extends AbstractExpressionAttributeRegistry<ExpressionAttribute> { |
||||
|
||||
@NonNull |
||||
@Override |
||||
ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) { |
||||
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); |
||||
PostAuthorize postAuthorize = findPostAuthorizeAnnotation(specificMethod); |
||||
if (postAuthorize == null) { |
||||
return ExpressionAttribute.NULL_ATTRIBUTE; |
||||
} |
||||
Expression postAuthorizeExpression = PostAuthorizeAuthorizationManager.this.expressionHandler |
||||
.getExpressionParser().parseExpression(postAuthorize.value()); |
||||
return new ExpressionAttribute(postAuthorizeExpression); |
||||
} |
||||
|
||||
private PostAuthorize findPostAuthorizeAnnotation(Method method) { |
||||
PostAuthorize postAuthorize = AnnotationUtils.findAnnotation(method, PostAuthorize.class); |
||||
return (postAuthorize != null) ? postAuthorize |
||||
: AnnotationUtils.findAnnotation(method.getDeclaringClass(), PostAuthorize.class); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,126 @@
@@ -0,0 +1,126 @@
|
||||
/* |
||||
* 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.function.Supplier; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
|
||||
import org.springframework.aop.MethodMatcher; |
||||
import org.springframework.aop.support.AopUtils; |
||||
import org.springframework.aop.support.StaticMethodMatcher; |
||||
import org.springframework.core.annotation.AnnotationUtils; |
||||
import org.springframework.expression.EvaluationContext; |
||||
import org.springframework.expression.Expression; |
||||
import org.springframework.lang.NonNull; |
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.method.AuthorizationMethodAfterAdvice; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.access.prepost.PostFilter; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* An {@link AuthorizationMethodAfterAdvice} which filters a <code>returnedObject</code> |
||||
* from the {@link MethodInvocation} by evaluating an expression from the |
||||
* {@link PostFilter} annotation. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
public final class PostFilterAuthorizationMethodAfterAdvice |
||||
implements AuthorizationMethodAfterAdvice<MethodAuthorizationContext> { |
||||
|
||||
private final PostFilterExpressionAttributeRegistry registry = new PostFilterExpressionAttributeRegistry(); |
||||
|
||||
private final MethodMatcher methodMatcher = new StaticMethodMatcher() { |
||||
@Override |
||||
public boolean matches(Method method, Class<?> targetClass) { |
||||
return PostFilterAuthorizationMethodAfterAdvice.this.registry.getAttribute(method, |
||||
targetClass) != ExpressionAttribute.NULL_ATTRIBUTE; |
||||
} |
||||
}; |
||||
|
||||
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); |
||||
|
||||
/** |
||||
* Sets the {@link MethodSecurityExpressionHandler}. |
||||
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use |
||||
*/ |
||||
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) { |
||||
Assert.notNull(expressionHandler, "expressionHandler cannot be null"); |
||||
this.expressionHandler = expressionHandler; |
||||
} |
||||
|
||||
@Override |
||||
public MethodMatcher getMethodMatcher() { |
||||
return this.methodMatcher; |
||||
} |
||||
|
||||
/** |
||||
* Filters a <code>returnedObject</code> from the {@link MethodInvocation} by |
||||
* evaluating an expression from the {@link PostFilter} annotation. |
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check |
||||
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check |
||||
* @param returnedObject the returned object from the {@link MethodInvocation} to |
||||
* check |
||||
* @return filtered <code>returnedObject</code> from the {@link MethodInvocation} |
||||
*/ |
||||
@Override |
||||
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext methodAuthorizationContext, |
||||
Object returnedObject) { |
||||
if (returnedObject == null) { |
||||
return null; |
||||
} |
||||
ExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext); |
||||
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) { |
||||
return returnedObject; |
||||
} |
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), |
||||
methodAuthorizationContext.getMethodInvocation()); |
||||
Object result = this.expressionHandler.filter(returnedObject, attribute.getExpression(), ctx); |
||||
methodAuthorizationContext.setReturnObject(result); |
||||
return result; |
||||
} |
||||
|
||||
private final class PostFilterExpressionAttributeRegistry |
||||
extends AbstractExpressionAttributeRegistry<ExpressionAttribute> { |
||||
|
||||
@NonNull |
||||
@Override |
||||
ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) { |
||||
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); |
||||
PostFilter postFilter = findPostFilterAnnotation(specificMethod); |
||||
if (postFilter == null) { |
||||
return ExpressionAttribute.NULL_ATTRIBUTE; |
||||
} |
||||
Expression postFilterExpression = PostFilterAuthorizationMethodAfterAdvice.this.expressionHandler |
||||
.getExpressionParser().parseExpression(postFilter.value()); |
||||
return new ExpressionAttribute(postFilterExpression); |
||||
} |
||||
|
||||
private PostFilter findPostFilterAnnotation(Method method) { |
||||
PostFilter postFilter = AnnotationUtils.findAnnotation(method, PostFilter.class); |
||||
return (postFilter != null) ? postFilter |
||||
: AnnotationUtils.findAnnotation(method.getDeclaringClass(), PostFilter.class); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,107 @@
@@ -0,0 +1,107 @@
|
||||
/* |
||||
* 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.function.Supplier; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
import reactor.util.annotation.NonNull; |
||||
|
||||
import org.springframework.aop.support.AopUtils; |
||||
import org.springframework.core.annotation.AnnotationUtils; |
||||
import org.springframework.expression.EvaluationContext; |
||||
import org.springframework.expression.Expression; |
||||
import org.springframework.security.access.expression.ExpressionUtils; |
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.access.prepost.PreAuthorize; |
||||
import org.springframework.security.authorization.AuthorizationDecision; |
||||
import org.springframework.security.authorization.AuthorizationManager; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has |
||||
* access to the {@link MethodInvocation} by evaluating an expression from the |
||||
* {@link PreAuthorize} annotation. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
public final class PreAuthorizeAuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> { |
||||
|
||||
private final PreAuthorizeExpressionAttributeRegistry registry = new PreAuthorizeExpressionAttributeRegistry(); |
||||
|
||||
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); |
||||
|
||||
/** |
||||
* Sets the {@link MethodSecurityExpressionHandler}. |
||||
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use |
||||
*/ |
||||
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) { |
||||
Assert.notNull(expressionHandler, "expressionHandler cannot be null"); |
||||
this.expressionHandler = expressionHandler; |
||||
} |
||||
|
||||
/** |
||||
* Determines if an {@link Authentication} has access to the {@link MethodInvocation} |
||||
* by evaluating an expression from the {@link PreAuthorize} annotation. |
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check |
||||
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check |
||||
* @return an {@link AuthorizationDecision} or null if the {@link PreAuthorize} |
||||
* annotation is not present |
||||
*/ |
||||
@Override |
||||
public AuthorizationDecision check(Supplier<Authentication> authentication, |
||||
MethodAuthorizationContext methodAuthorizationContext) { |
||||
ExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext); |
||||
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) { |
||||
return null; |
||||
} |
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), |
||||
methodAuthorizationContext.getMethodInvocation()); |
||||
boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx); |
||||
return new AuthorizationDecision(granted); |
||||
} |
||||
|
||||
private final class PreAuthorizeExpressionAttributeRegistry |
||||
extends AbstractExpressionAttributeRegistry<ExpressionAttribute> { |
||||
|
||||
@NonNull |
||||
@Override |
||||
ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) { |
||||
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); |
||||
PreAuthorize preAuthorize = findPreAuthorizeAnnotation(specificMethod); |
||||
if (preAuthorize == null) { |
||||
return ExpressionAttribute.NULL_ATTRIBUTE; |
||||
} |
||||
Expression preAuthorizeExpression = PreAuthorizeAuthorizationManager.this.expressionHandler |
||||
.getExpressionParser().parseExpression(preAuthorize.value()); |
||||
return new ExpressionAttribute(preAuthorizeExpression); |
||||
} |
||||
|
||||
private PreAuthorize findPreAuthorizeAnnotation(Method method) { |
||||
PreAuthorize preAuthorize = AnnotationUtils.findAnnotation(method, PreAuthorize.class); |
||||
return (preAuthorize != null) ? preAuthorize |
||||
: AnnotationUtils.findAnnotation(method.getDeclaringClass(), PreAuthorize.class); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,151 @@
@@ -0,0 +1,151 @@
|
||||
/* |
||||
* 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.function.Supplier; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
|
||||
import org.springframework.aop.MethodMatcher; |
||||
import org.springframework.aop.support.AopUtils; |
||||
import org.springframework.aop.support.StaticMethodMatcher; |
||||
import org.springframework.core.annotation.AnnotationUtils; |
||||
import org.springframework.expression.EvaluationContext; |
||||
import org.springframework.expression.Expression; |
||||
import org.springframework.lang.NonNull; |
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.method.AuthorizationMethodBeforeAdvice; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.access.prepost.PreFilter; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* An {@link AuthorizationMethodBeforeAdvice} which filters a method argument by |
||||
* evaluating an expression from the {@link PreFilter} annotation. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.5 |
||||
*/ |
||||
public final class PreFilterAuthorizationMethodBeforeAdvice |
||||
implements AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> { |
||||
|
||||
private final PreFilterExpressionAttributeRegistry registry = new PreFilterExpressionAttributeRegistry(); |
||||
|
||||
private final MethodMatcher methodMatcher = new StaticMethodMatcher() { |
||||
@Override |
||||
public boolean matches(Method method, Class<?> targetClass) { |
||||
return PreFilterAuthorizationMethodBeforeAdvice.this.registry.getAttribute(method, |
||||
targetClass) != PreFilterExpressionAttribute.NULL_ATTRIBUTE; |
||||
} |
||||
}; |
||||
|
||||
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); |
||||
|
||||
/** |
||||
* Sets the {@link MethodSecurityExpressionHandler}. |
||||
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use |
||||
*/ |
||||
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) { |
||||
Assert.notNull(expressionHandler, "expressionHandler cannot be null"); |
||||
this.expressionHandler = expressionHandler; |
||||
} |
||||
|
||||
@Override |
||||
public MethodMatcher getMethodMatcher() { |
||||
return this.methodMatcher; |
||||
} |
||||
|
||||
/** |
||||
* Filters a method argument by evaluating an expression from the {@link PreFilter} |
||||
* annotation. |
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check |
||||
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check |
||||
*/ |
||||
@Override |
||||
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext methodAuthorizationContext) { |
||||
PreFilterExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext); |
||||
if (attribute == PreFilterExpressionAttribute.NULL_ATTRIBUTE) { |
||||
return; |
||||
} |
||||
MethodInvocation mi = methodAuthorizationContext.getMethodInvocation(); |
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi); |
||||
Object filterTarget = findFilterTarget(attribute.filterTarget, ctx, mi); |
||||
this.expressionHandler.filter(filterTarget, attribute.getExpression(), ctx); |
||||
} |
||||
|
||||
private Object findFilterTarget(String filterTargetName, EvaluationContext ctx, MethodInvocation methodInvocation) { |
||||
Object filterTarget; |
||||
if (StringUtils.hasText(filterTargetName)) { |
||||
filterTarget = ctx.lookupVariable(filterTargetName); |
||||
Assert.notNull(filterTarget, () -> "Filter target was null, or no argument with name '" + filterTargetName |
||||
+ "' found in method."); |
||||
} |
||||
else { |
||||
Object[] arguments = methodInvocation.getArguments(); |
||||
Assert.state(arguments.length == 1, |
||||
"Unable to determine the method argument for filtering. Specify the filter target."); |
||||
filterTarget = arguments[0]; |
||||
Assert.notNull(filterTarget, |
||||
"Filter target was null. Make sure you passing the correct value in the method argument."); |
||||
} |
||||
Assert.state(!filterTarget.getClass().isArray(), |
||||
"Pre-filtering on array types is not supported. Using a Collection will solve this problem."); |
||||
return filterTarget; |
||||
} |
||||
|
||||
private final class PreFilterExpressionAttributeRegistry |
||||
extends AbstractExpressionAttributeRegistry<PreFilterExpressionAttribute> { |
||||
|
||||
@NonNull |
||||
@Override |
||||
PreFilterExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) { |
||||
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); |
||||
PreFilter preFilter = findPreFilterAnnotation(specificMethod); |
||||
if (preFilter == null) { |
||||
return PreFilterExpressionAttribute.NULL_ATTRIBUTE; |
||||
} |
||||
Expression preFilterExpression = PreFilterAuthorizationMethodBeforeAdvice.this.expressionHandler |
||||
.getExpressionParser().parseExpression(preFilter.value()); |
||||
return new PreFilterExpressionAttribute(preFilterExpression, preFilter.filterTarget()); |
||||
} |
||||
|
||||
private PreFilter findPreFilterAnnotation(Method method) { |
||||
PreFilter preFilter = AnnotationUtils.findAnnotation(method, PreFilter.class); |
||||
return (preFilter != null) ? preFilter |
||||
: AnnotationUtils.findAnnotation(method.getDeclaringClass(), PreFilter.class); |
||||
} |
||||
|
||||
} |
||||
|
||||
private static final class PreFilterExpressionAttribute extends ExpressionAttribute { |
||||
|
||||
private static final PreFilterExpressionAttribute NULL_ATTRIBUTE = new PreFilterExpressionAttribute(null, null); |
||||
|
||||
private final String filterTarget; |
||||
|
||||
private PreFilterExpressionAttribute(Expression expression, String filterTarget) { |
||||
super(expression); |
||||
this.filterTarget = filterTarget; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,167 @@
@@ -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.access.annotation; |
||||
|
||||
import java.util.function.Supplier; |
||||
|
||||
import javax.annotation.security.DenyAll; |
||||
import javax.annotation.security.PermitAll; |
||||
import javax.annotation.security.RolesAllowed; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.security.access.intercept.method.MockMethodInvocation; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.authentication.TestAuthentication; |
||||
import org.springframework.security.authentication.TestingAuthenticationToken; |
||||
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.assertThatIllegalArgumentException; |
||||
|
||||
/** |
||||
* Tests for {@link Jsr250AuthorizationManager}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
*/ |
||||
public class Jsr250AuthorizationManagerTests { |
||||
|
||||
@Test |
||||
public void rolePrefixWhenNotSetThenDefaultsToRole() { |
||||
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); |
||||
assertThat(manager).extracting("rolePrefix").isEqualTo("ROLE_"); |
||||
} |
||||
|
||||
@Test |
||||
public void setRolePrefixWhenNullThenException() { |
||||
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); |
||||
assertThatIllegalArgumentException().isThrownBy(() -> manager.setRolePrefix(null)) |
||||
.withMessage("rolePrefix cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void setRolePrefixWhenNotNullThenSets() { |
||||
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); |
||||
manager.setRolePrefix("CUSTOM_"); |
||||
assertThat(manager).extracting("rolePrefix").isEqualTo("CUSTOM_"); |
||||
} |
||||
|
||||
@Test |
||||
public void checkDoSomethingWhenNoJsr250AnnotationsThenNullDecision() throws Exception { |
||||
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomething"); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation, |
||||
TestClass.class); |
||||
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
public void checkPermitAllRolesAllowedAdminWhenRoleUserThenGrantedDecision() throws Exception { |
||||
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"permitAllRolesAllowedAdmin"); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation, |
||||
TestClass.class); |
||||
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void checkDenyAllRolesAllowedAdminWhenRoleAdminThenDeniedDecision() throws Exception { |
||||
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"denyAllRolesAllowedAdmin"); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation, |
||||
TestClass.class); |
||||
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void checkRolesAllowedUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception { |
||||
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"rolesAllowedUserOrAdmin"); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation, |
||||
TestClass.class); |
||||
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void checkRolesAllowedUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception { |
||||
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"rolesAllowedUserOrAdmin"); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation, |
||||
TestClass.class); |
||||
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void checkRolesAllowedUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception { |
||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", |
||||
"ROLE_ANONYMOUS"); |
||||
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"rolesAllowedUserOrAdmin"); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation, |
||||
TestClass.class); |
||||
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(authentication, methodAuthorizationContext); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isFalse(); |
||||
} |
||||
|
||||
public static class TestClass { |
||||
|
||||
public void doSomething() { |
||||
|
||||
} |
||||
|
||||
@DenyAll |
||||
@RolesAllowed("ADMIN") |
||||
public void denyAllRolesAllowedAdmin() { |
||||
|
||||
} |
||||
|
||||
@PermitAll |
||||
@RolesAllowed("ADMIN") |
||||
public void permitAllRolesAllowedAdmin() { |
||||
|
||||
} |
||||
|
||||
@RolesAllowed({ "USER", "ADMIN" }) |
||||
public void rolesAllowedUserOrAdmin() { |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,104 @@
@@ -0,0 +1,104 @@
|
||||
/* |
||||
* 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.access.annotation; |
||||
|
||||
import java.util.function.Supplier; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.security.access.intercept.method.MockMethodInvocation; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.authentication.TestAuthentication; |
||||
import org.springframework.security.authentication.TestingAuthenticationToken; |
||||
import org.springframework.security.authorization.AuthorizationDecision; |
||||
import org.springframework.security.core.Authentication; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link SecuredAuthorizationManager}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
*/ |
||||
public class SecuredAuthorizationManagerTests { |
||||
|
||||
@Test |
||||
public void checkDoSomethingWhenNoSecuredAnnotationThenNullDecision() throws Exception { |
||||
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomething"); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation, |
||||
TestClass.class); |
||||
SecuredAuthorizationManager manager = new SecuredAuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
public void checkSecuredUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception { |
||||
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"securedUserOrAdmin"); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation, |
||||
TestClass.class); |
||||
SecuredAuthorizationManager manager = new SecuredAuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void checkSecuredUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception { |
||||
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"securedUserOrAdmin"); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation, |
||||
TestClass.class); |
||||
SecuredAuthorizationManager manager = new SecuredAuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void checkSecuredUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception { |
||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", |
||||
"ROLE_ANONYMOUS"); |
||||
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"securedUserOrAdmin"); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation, |
||||
TestClass.class); |
||||
SecuredAuthorizationManager manager = new SecuredAuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(authentication, methodAuthorizationContext); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isFalse(); |
||||
} |
||||
|
||||
public static class TestClass { |
||||
|
||||
public void doSomething() { |
||||
|
||||
} |
||||
|
||||
@Secured({ "ROLE_USER", "ROLE_ADMIN" }) |
||||
public void securedUserOrAdmin() { |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,107 @@
@@ -0,0 +1,107 @@
|
||||
/* |
||||
* 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.access.intercept.aopalliance; |
||||
|
||||
import java.util.function.Supplier; |
||||
|
||||
import org.junit.After; |
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.aop.MethodMatcher; |
||||
import org.springframework.security.access.intercept.method.MockMethodInvocation; |
||||
import org.springframework.security.access.method.AuthorizationMethodAfterAdvice; |
||||
import org.springframework.security.access.method.AuthorizationMethodBeforeAdvice; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; |
||||
import org.springframework.security.authentication.TestAuthentication; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.core.context.SecurityContextHolder; |
||||
import org.springframework.security.core.context.SecurityContextImpl; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.ArgumentMatchers.eq; |
||||
import static org.mockito.BDDMockito.given; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.verifyNoInteractions; |
||||
|
||||
/** |
||||
* Tests for {@link AuthorizationMethodInterceptor}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
*/ |
||||
public class AuthorizationMethodInterceptorTests { |
||||
|
||||
@After |
||||
public void tearDown() { |
||||
SecurityContextHolder.clearContext(); |
||||
} |
||||
|
||||
@Test |
||||
public void invokeWhenAuthenticatedThenVerifyAdvicesUsage() throws Throwable { |
||||
Authentication authentication = TestAuthentication.authenticatedUser(); |
||||
SecurityContextHolder.setContext(new SecurityContextImpl(authentication)); |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingString"); |
||||
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> mockBeforeAdvice = mock( |
||||
AuthorizationMethodBeforeAdvice.class); |
||||
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> mockAfterAdvice = mock( |
||||
AuthorizationMethodAfterAdvice.class); |
||||
given(mockAfterAdvice.after(any(), any(MethodAuthorizationContext.class), eq(null))).willReturn("abc"); |
||||
AuthorizationMethodInterceptor interceptor = new AuthorizationMethodInterceptor(mockBeforeAdvice, |
||||
mockAfterAdvice); |
||||
Object result = interceptor.invoke(mockMethodInvocation); |
||||
assertThat(result).isEqualTo("abc"); |
||||
verify(mockAfterAdvice).after(any(), any(MethodAuthorizationContext.class), eq(null)); |
||||
} |
||||
|
||||
@Test |
||||
public void invokeWhenNotAuthenticatedThenAuthenticationCredentialsNotFoundException() throws Exception { |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingString"); |
||||
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> beforeAdvice = new AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>() { |
||||
@Override |
||||
public MethodMatcher getMethodMatcher() { |
||||
return MethodMatcher.TRUE; |
||||
} |
||||
|
||||
@Override |
||||
public void before(Supplier<Authentication> authentication, |
||||
MethodAuthorizationContext methodAuthorizationContext) { |
||||
authentication.get(); |
||||
} |
||||
}; |
||||
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> mockAfterAdvice = mock( |
||||
AuthorizationMethodAfterAdvice.class); |
||||
AuthorizationMethodInterceptor interceptor = new AuthorizationMethodInterceptor(beforeAdvice, mockAfterAdvice); |
||||
assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class) |
||||
.isThrownBy(() -> interceptor.invoke(mockMethodInvocation)) |
||||
.withMessage("An Authentication object was not found in the SecurityContext"); |
||||
verifyNoInteractions(mockAfterAdvice); |
||||
} |
||||
|
||||
public static class TestClass { |
||||
|
||||
public String doSomethingString() { |
||||
return null; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
/* |
||||
* 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.access.method; |
||||
|
||||
import java.util.function.Supplier; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.aop.MethodMatcher; |
||||
import org.springframework.security.authentication.TestAuthentication; |
||||
import org.springframework.security.authorization.AuthorizationManager; |
||||
import org.springframework.security.core.Authentication; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.verify; |
||||
|
||||
/** |
||||
* Tests for {@link AuthorizationManagerMethodAfterAdvice}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
*/ |
||||
public class AuthorizationManagerMethodAfterAdviceTests { |
||||
|
||||
@Test |
||||
public void instantiateWhenMethodMatcherNullThenException() { |
||||
assertThatIllegalArgumentException() |
||||
.isThrownBy(() -> new AuthorizationManagerMethodAfterAdvice<>(null, mock(AuthorizationManager.class))) |
||||
.withMessage("methodMatcher cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void instantiateWhenAuthorizationManagerNullThenException() { |
||||
assertThatIllegalArgumentException() |
||||
.isThrownBy(() -> new AuthorizationManagerMethodAfterAdvice<>(mock(MethodMatcher.class), null)) |
||||
.withMessage("authorizationManager cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void beforeWhenMockAuthorizationManagerThenVerifyAndReturnedObject() { |
||||
Supplier<Authentication> authentication = TestAuthentication::authenticatedUser; |
||||
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class); |
||||
Object returnedObject = new Object(); |
||||
AuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(AuthorizationManager.class); |
||||
AuthorizationManagerMethodAfterAdvice<MethodInvocation> advice = new AuthorizationManagerMethodAfterAdvice<>( |
||||
mock(MethodMatcher.class), mockAuthorizationManager); |
||||
Object result = advice.after(authentication, mockMethodInvocation, returnedObject); |
||||
assertThat(result).isEqualTo(returnedObject); |
||||
verify(mockAuthorizationManager).verify(authentication, mockMethodInvocation); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,65 @@
@@ -0,0 +1,65 @@
|
||||
/* |
||||
* 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.access.method; |
||||
|
||||
import java.util.function.Supplier; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.aop.MethodMatcher; |
||||
import org.springframework.security.authentication.TestAuthentication; |
||||
import org.springframework.security.authorization.AuthorizationManager; |
||||
import org.springframework.security.core.Authentication; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.verify; |
||||
|
||||
/** |
||||
* Tests for {@link AuthorizationManagerMethodBeforeAdvice}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
*/ |
||||
public class AuthorizationManagerMethodBeforeAdviceTests { |
||||
|
||||
@Test |
||||
public void instantiateWhenMethodMatcherNullThenException() { |
||||
assertThatIllegalArgumentException() |
||||
.isThrownBy(() -> new AuthorizationManagerMethodBeforeAdvice<>(null, mock(AuthorizationManager.class))) |
||||
.withMessage("methodMatcher cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void instantiateWhenAuthorizationManagerNullThenException() { |
||||
assertThatIllegalArgumentException() |
||||
.isThrownBy(() -> new AuthorizationManagerMethodBeforeAdvice<>(mock(MethodMatcher.class), null)) |
||||
.withMessage("authorizationManager cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void beforeWhenMockAuthorizationManagerThenVerify() { |
||||
Supplier<Authentication> authentication = TestAuthentication::authenticatedUser; |
||||
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class); |
||||
AuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(AuthorizationManager.class); |
||||
AuthorizationManagerMethodBeforeAdvice<MethodInvocation> advice = new AuthorizationManagerMethodBeforeAdvice<>( |
||||
mock(MethodMatcher.class), mockAuthorizationManager); |
||||
advice.before(authentication, mockMethodInvocation); |
||||
verify(mockAuthorizationManager).verify(authentication, mockMethodInvocation); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,164 @@
@@ -0,0 +1,164 @@
|
||||
/* |
||||
* 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.access.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.support.StaticMethodMatcher; |
||||
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 MethodMatcher getMethodMatcher() { |
||||
return new StaticMethodMatcher() { |
||||
@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 MethodMatcher getMethodMatcher() { |
||||
return new StaticMethodMatcher() { |
||||
@Override |
||||
public boolean matches(Method method, Class<?> targetClass) { |
||||
return false; |
||||
} |
||||
}; |
||||
} |
||||
}); |
||||
DelegatingAuthorizationMethodAfterAdvice advice = new DelegatingAuthorizationMethodAfterAdvice(delegates); |
||||
MethodMatcher methodMatcher = advice.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 MethodMatcher getMethodMatcher() { |
||||
return new StaticMethodMatcher() { |
||||
@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 MethodMatcher getMethodMatcher() { |
||||
return MethodMatcher.TRUE; |
||||
} |
||||
}); |
||||
DelegatingAuthorizationMethodAfterAdvice advice = new DelegatingAuthorizationMethodAfterAdvice(delegates); |
||||
MethodMatcher methodMatcher = advice.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 MethodMatcher getMethodMatcher() { |
||||
return MethodMatcher.TRUE; |
||||
} |
||||
}); |
||||
delegates.add(new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() { |
||||
@Override |
||||
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext object, |
||||
Object returnedObject) { |
||||
return returnedObject + "c"; |
||||
} |
||||
|
||||
@Override |
||||
public MethodMatcher getMethodMatcher() { |
||||
return MethodMatcher.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; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,168 @@
@@ -0,0 +1,168 @@
|
||||
/* |
||||
* 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.access.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.support.StaticMethodMatcher; |
||||
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 MethodMatcher getMethodMatcher() { |
||||
return new StaticMethodMatcher() { |
||||
@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 MethodMatcher getMethodMatcher() { |
||||
return new StaticMethodMatcher() { |
||||
@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.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 MethodMatcher getMethodMatcher() { |
||||
return new StaticMethodMatcher() { |
||||
@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 MethodMatcher getMethodMatcher() { |
||||
return MethodMatcher.TRUE; |
||||
} |
||||
|
||||
@Override |
||||
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext object) { |
||||
} |
||||
}); |
||||
DelegatingAuthorizationMethodBeforeAdvice advice = new DelegatingAuthorizationMethodBeforeAdvice(delegates); |
||||
MethodMatcher methodMatcher = advice.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<>(MethodMatcher.TRUE, (a, o) -> null)); |
||||
delegates.add(new AuthorizationManagerMethodBeforeAdvice<>(MethodMatcher.TRUE, |
||||
(a, o) -> new AuthorizationDecision(true))); |
||||
delegates.add(new AuthorizationManagerMethodBeforeAdvice<>(MethodMatcher.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<>(MethodMatcher.TRUE, (a, o) -> null)); |
||||
delegates.add(new AuthorizationManagerMethodBeforeAdvice<>(MethodMatcher.TRUE, |
||||
(a, o) -> new AuthorizationDecision(true))); |
||||
delegates.add(new AuthorizationManagerMethodBeforeAdvice<>(MethodMatcher.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 checkWhenDelegatesEmptyThenPasses() throws Exception { |
||||
DelegatingAuthorizationMethodBeforeAdvice advice = new DelegatingAuthorizationMethodBeforeAdvice( |
||||
Collections.emptyList()); |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomething"); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext); |
||||
} |
||||
|
||||
public static class TestClass { |
||||
|
||||
public void doSomething() { |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,144 @@
@@ -0,0 +1,144 @@
|
||||
/* |
||||
* 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.Collections; |
||||
import java.util.List; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.intercept.method.MockMethodInvocation; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.access.prepost.PostAuthorize; |
||||
import org.springframework.security.authentication.TestAuthentication; |
||||
import org.springframework.security.authorization.AuthorizationDecision; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||
|
||||
/** |
||||
* Tests for {@link PostAuthorizeAuthorizationManager}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
*/ |
||||
public class PostAuthorizeAuthorizationManagerTests { |
||||
|
||||
@Test |
||||
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() { |
||||
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); |
||||
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); |
||||
manager.setExpressionHandler(expressionHandler); |
||||
assertThat(manager).extracting("expressionHandler").isEqualTo(expressionHandler); |
||||
} |
||||
|
||||
@Test |
||||
public void setExpressionHandlerWhenNullThenException() { |
||||
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); |
||||
assertThatIllegalArgumentException().isThrownBy(() -> manager.setExpressionHandler(null)) |
||||
.withMessage("expressionHandler cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void checkDoSomethingWhenNoPostAuthorizeAnnotationThenNullDecision() throws Exception { |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomething", new Class[] {}, new Object[] {}); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
public void checkDoSomethingStringWhenArgIsGrantThenGrantedDecision() throws Exception { |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingString", new Class[] { String.class }, new Object[] { "grant" }); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws Exception { |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingString", new Class[] { String.class }, new Object[] { "deny" }); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void checkDoSomethingListWhenReturnObjectContainsGrantThenGrantedDecision() throws Exception { |
||||
List<String> list = Arrays.asList("grant", "deny"); |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingList", new Class[] { List.class }, new Object[] { list }); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
methodAuthorizationContext.setReturnObject(list); |
||||
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void checkDoSomethingListWhenReturnObjectNotContainsGrantThenDeniedDecision() throws Exception { |
||||
List<String> list = Collections.singletonList("deny"); |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingList", new Class[] { List.class }, new Object[] { list }); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
methodAuthorizationContext.setReturnObject(list); |
||||
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isFalse(); |
||||
} |
||||
|
||||
public static class TestClass { |
||||
|
||||
public void doSomething() { |
||||
|
||||
} |
||||
|
||||
@PostAuthorize("#s == 'grant'") |
||||
public String doSomethingString(String s) { |
||||
return s; |
||||
} |
||||
|
||||
@PostAuthorize("returnObject.contains('grant')") |
||||
public List<String> doSomethingList(List<String> list) { |
||||
return list; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,96 @@
@@ -0,0 +1,96 @@
|
||||
/* |
||||
* 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.assertj.core.api.InstanceOfAssertFactories; |
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.aop.MethodMatcher; |
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.intercept.method.MockMethodInvocation; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.access.prepost.PostFilter; |
||||
import org.springframework.security.authentication.TestAuthentication; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||
|
||||
/** |
||||
* Tests for {@link PostFilterAuthorizationMethodAfterAdvice}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
*/ |
||||
public class PostFilterAuthorizationMethodAfterAdviceTests { |
||||
|
||||
@Test |
||||
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() { |
||||
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); |
||||
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice(); |
||||
advice.setExpressionHandler(expressionHandler); |
||||
assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler); |
||||
} |
||||
|
||||
@Test |
||||
public void setExpressionHandlerWhenNullThenException() { |
||||
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice(); |
||||
assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null)) |
||||
.withMessage("expressionHandler cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void methodMatcherWhenMethodHasNotPostFilterAnnotationThenNotMatches() throws Exception { |
||||
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice(); |
||||
MethodMatcher methodMatcher = advice.getMethodMatcher(); |
||||
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomething"), TestClass.class)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void methodMatcherWhenMethodHasPostFilterAnnotationThenMatches() throws Exception { |
||||
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice(); |
||||
MethodMatcher methodMatcher = advice.getMethodMatcher(); |
||||
assertThat( |
||||
methodMatcher.matches(TestClass.class.getMethod("doSomethingArray", String[].class), TestClass.class)) |
||||
.isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void afterWhenArrayNotNullThenFilteredArray() throws Exception { |
||||
String[] array = { "john", "bob" }; |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingArray", new Class[] { String[].class }, new Object[] { array }); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice(); |
||||
Object result = advice.after(TestAuthentication::authenticatedUser, methodAuthorizationContext, array); |
||||
assertThat(result).asInstanceOf(InstanceOfAssertFactories.array(String[].class)).containsOnly("john"); |
||||
} |
||||
|
||||
public static class TestClass { |
||||
|
||||
public void doSomething() { |
||||
|
||||
} |
||||
|
||||
@PostFilter("filterObject == 'john'") |
||||
public String[] doSomethingArray(String[] array) { |
||||
return array; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,105 @@
@@ -0,0 +1,105 @@
|
||||
/* |
||||
* 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.junit.Test; |
||||
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.intercept.method.MockMethodInvocation; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.access.prepost.PreAuthorize; |
||||
import org.springframework.security.authentication.TestAuthentication; |
||||
import org.springframework.security.authorization.AuthorizationDecision; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||
|
||||
/** |
||||
* Tests for {@link PreAuthorizeAuthorizationManager}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
*/ |
||||
public class PreAuthorizeAuthorizationManagerTests { |
||||
|
||||
@Test |
||||
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() { |
||||
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); |
||||
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); |
||||
manager.setExpressionHandler(expressionHandler); |
||||
assertThat(manager).extracting("expressionHandler").isEqualTo(expressionHandler); |
||||
} |
||||
|
||||
@Test |
||||
public void setExpressionHandlerWhenNullThenException() { |
||||
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); |
||||
assertThatIllegalArgumentException().isThrownBy(() -> manager.setExpressionHandler(null)) |
||||
.withMessage("expressionHandler cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void checkDoSomethingWhenNoPostAuthorizeAnnotationThenNullDecision() throws Exception { |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomething", new Class[] {}, new Object[] {}); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
public void checkDoSomethingStringWhenArgIsGrantThenGrantedDecision() throws Exception { |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingString", new Class[] { String.class }, new Object[] { "grant" }); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws Exception { |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingString", new Class[] { String.class }, new Object[] { "deny" }); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, |
||||
methodAuthorizationContext); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isFalse(); |
||||
} |
||||
|
||||
public static class TestClass { |
||||
|
||||
public void doSomething() { |
||||
|
||||
} |
||||
|
||||
@PreAuthorize("#s == 'grant'") |
||||
public String doSomethingString(String s) { |
||||
return s; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,200 @@
@@ -0,0 +1,200 @@
|
||||
/* |
||||
* 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.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.aop.MethodMatcher; |
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.intercept.method.MockMethodInvocation; |
||||
import org.springframework.security.access.method.MethodAuthorizationContext; |
||||
import org.springframework.security.access.prepost.PreFilter; |
||||
import org.springframework.security.authentication.TestAuthentication; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException; |
||||
|
||||
/** |
||||
* Tests for {@link PreFilterAuthorizationMethodBeforeAdvice}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
*/ |
||||
public class PreFilterAuthorizationMethodBeforeAdviceTests { |
||||
|
||||
@Test |
||||
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() { |
||||
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); |
||||
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(); |
||||
advice.setExpressionHandler(expressionHandler); |
||||
assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler); |
||||
} |
||||
|
||||
@Test |
||||
public void setExpressionHandlerWhenNullThenException() { |
||||
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(); |
||||
assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null)) |
||||
.withMessage("expressionHandler cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void methodMatcherWhenMethodHasNotPreFilterAnnotationThenNotMatches() throws Exception { |
||||
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(); |
||||
MethodMatcher methodMatcher = advice.getMethodMatcher(); |
||||
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomething"), TestClass.class)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void methodMatcherWhenMethodHasPreFilterAnnotationThenMatches() throws Exception { |
||||
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(); |
||||
MethodMatcher methodMatcher = advice.getMethodMatcher(); |
||||
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomethingListFilterTargetMatch", List.class), |
||||
TestClass.class)).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void findFilterTargetWhenNameProvidedAndNotMatchThenException() throws Exception { |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingListFilterTargetNotMatch", new Class[] { List.class }, new Object[] { new ArrayList<>() }); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(); |
||||
assertThatIllegalArgumentException() |
||||
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext)) |
||||
.withMessage( |
||||
"Filter target was null, or no argument with name 'filterTargetNotMatch' found in method."); |
||||
} |
||||
|
||||
@Test |
||||
public void findFilterTargetWhenNameProvidedAndMatchAndNullThenException() throws Exception { |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingListFilterTargetMatch", new Class[] { List.class }, new Object[] { null }); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(); |
||||
assertThatIllegalArgumentException() |
||||
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext)) |
||||
.withMessage("Filter target was null, or no argument with name 'list' found in method."); |
||||
} |
||||
|
||||
@Test |
||||
public void findFilterTargetWhenNameProvidedAndMatchAndNotNullThenFiltersList() throws Exception { |
||||
List<String> list = new ArrayList<>(); |
||||
list.add("john"); |
||||
list.add("bob"); |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingListFilterTargetMatch", new Class[] { List.class }, new Object[] { list }); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(); |
||||
advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext); |
||||
assertThat(list).hasSize(1); |
||||
assertThat(list.get(0)).isEqualTo("john"); |
||||
} |
||||
|
||||
@Test |
||||
public void findFilterTargetWhenNameNotProvidedAndSingleArgListNullThenException() throws Exception { |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { null }); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(); |
||||
assertThatIllegalArgumentException() |
||||
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext)) |
||||
.withMessage("Filter target was null. Make sure you passing the correct value in the method argument."); |
||||
} |
||||
|
||||
@Test |
||||
public void findFilterTargetWhenNameNotProvidedAndSingleArgListThenFiltersList() throws Exception { |
||||
List<String> list = new ArrayList<>(); |
||||
list.add("john"); |
||||
list.add("bob"); |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { list }); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(); |
||||
advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext); |
||||
assertThat(list).hasSize(1); |
||||
assertThat(list.get(0)).isEqualTo("john"); |
||||
} |
||||
|
||||
@Test |
||||
public void findFilterTargetWhenNameNotProvidedAndSingleArgArrayThenException() throws Exception { |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingArrayFilterTargetNotProvided", new Class[] { String[].class }, |
||||
new Object[] { new String[] {} }); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(); |
||||
assertThatIllegalStateException() |
||||
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext)) |
||||
.withMessage( |
||||
"Pre-filtering on array types is not supported. Using a Collection will solve this problem."); |
||||
} |
||||
|
||||
@Test |
||||
public void findFilterTargetWhenNameNotProvidedAndNotSingleArgThenException() throws Exception { |
||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, |
||||
"doSomethingTwoArgsFilterTargetNotProvided", new Class[] { String.class, List.class }, |
||||
new Object[] { "", new ArrayList<>() }); |
||||
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation, |
||||
TestClass.class); |
||||
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice(); |
||||
assertThatIllegalStateException() |
||||
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext)) |
||||
.withMessage("Unable to determine the method argument for filtering. Specify the filter target."); |
||||
} |
||||
|
||||
public static class TestClass { |
||||
|
||||
public void doSomething() { |
||||
|
||||
} |
||||
|
||||
@PreFilter(value = "filterObject == 'john'", filterTarget = "filterTargetNotMatch") |
||||
public List<String> doSomethingListFilterTargetNotMatch(List<String> list) { |
||||
return list; |
||||
} |
||||
|
||||
@PreFilter(value = "filterObject == 'john'", filterTarget = "list") |
||||
public List<String> doSomethingListFilterTargetMatch(List<String> list) { |
||||
return list; |
||||
} |
||||
|
||||
@PreFilter("filterObject == 'john'") |
||||
public List<String> doSomethingListFilterTargetNotProvided(List<String> list) { |
||||
return list; |
||||
} |
||||
|
||||
@PreFilter("filterObject == 'john'") |
||||
public String[] doSomethingArrayFilterTargetNotProvided(String[] array) { |
||||
return array; |
||||
} |
||||
|
||||
@PreFilter("filterObject == 'john'") |
||||
public List<String> doSomethingTwoArgsFilterTargetNotProvided(String s, List<String> list) { |
||||
return list; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue