35 changed files with 3682 additions and 5 deletions
@ -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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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