Revert AuthorizationManager Method Security
This commit is contained in:
parent
b352c8f1da
commit
163b5943ca
|
@ -38,7 +38,6 @@ dependencies {
|
||||||
optional'org.springframework:spring-websocket'
|
optional'org.springframework:spring-websocket'
|
||||||
optional 'org.jetbrains.kotlin:kotlin-reflect'
|
optional 'org.jetbrains.kotlin:kotlin-reflect'
|
||||||
optional 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
optional 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
||||||
optional 'javax.annotation:jsr250-api'
|
|
||||||
|
|
||||||
provided 'javax.servlet:javax.servlet-api'
|
provided 'javax.servlet:javax.servlet-api'
|
||||||
|
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.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;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,195 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.method.configuration;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
|
||||||
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.AnnotationAttributes;
|
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
|
||||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
|
||||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
|
||||||
import org.springframework.security.authorization.method.AuthorizationMethodInterceptor;
|
|
||||||
import org.springframework.security.authorization.method.AuthorizationMethodInterceptors;
|
|
||||||
import org.springframework.security.authorization.method.DelegatingAuthorizationMethodInterceptor;
|
|
||||||
import org.springframework.security.authorization.method.Jsr250AuthorizationManager;
|
|
||||||
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
|
|
||||||
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor;
|
|
||||||
import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;
|
|
||||||
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;
|
|
||||||
import org.springframework.security.authorization.method.SecuredAuthorizationManager;
|
|
||||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base {@link Configuration} for enabling Spring Security Method Security.
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
* @author Josh Cummings
|
|
||||||
* @see EnableMethodSecurity
|
|
||||||
* @since 5.5
|
|
||||||
*/
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
||||||
final class MethodSecurityConfiguration implements ImportAware, InitializingBean {
|
|
||||||
|
|
||||||
private MethodSecurityExpressionHandler methodSecurityExpressionHandler;
|
|
||||||
|
|
||||||
private GrantedAuthorityDefaults grantedAuthorityDefaults;
|
|
||||||
|
|
||||||
private AuthorizationMethodInterceptor interceptor;
|
|
||||||
|
|
||||||
private AnnotationAttributes enableMethodSecurity;
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
||||||
DefaultPointcutAdvisor methodSecurityAdvisor() {
|
|
||||||
AuthorizationMethodInterceptor interceptor = getInterceptor();
|
|
||||||
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(interceptor.getPointcut(), interceptor);
|
|
||||||
advisor.setOrder(order());
|
|
||||||
return advisor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MethodSecurityExpressionHandler getMethodSecurityExpressionHandler() {
|
|
||||||
if (this.methodSecurityExpressionHandler == null) {
|
|
||||||
DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
|
|
||||||
if (this.grantedAuthorityDefaults != null) {
|
|
||||||
methodSecurityExpressionHandler.setDefaultRolePrefix(this.grantedAuthorityDefaults.getRolePrefix());
|
|
||||||
}
|
|
||||||
this.methodSecurityExpressionHandler = methodSecurityExpressionHandler;
|
|
||||||
}
|
|
||||||
return this.methodSecurityExpressionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
|
||||||
void setMethodSecurityExpressionHandler(MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
|
|
||||||
this.methodSecurityExpressionHandler = methodSecurityExpressionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
|
||||||
void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) {
|
|
||||||
this.grantedAuthorityDefaults = grantedAuthorityDefaults;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AuthorizationMethodInterceptor getInterceptor() {
|
|
||||||
if (this.interceptor != null) {
|
|
||||||
return this.interceptor;
|
|
||||||
}
|
|
||||||
List<AuthorizationMethodInterceptor> interceptors = new ArrayList<>();
|
|
||||||
interceptors.addAll(createDefaultAuthorizationMethodBeforeAdvice());
|
|
||||||
interceptors.addAll(createDefaultAuthorizationMethodAfterAdvice());
|
|
||||||
return new DelegatingAuthorizationMethodInterceptor(interceptors);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<AuthorizationMethodInterceptor> createDefaultAuthorizationMethodBeforeAdvice() {
|
|
||||||
List<AuthorizationMethodInterceptor> beforeAdvices = new ArrayList<>();
|
|
||||||
beforeAdvices.add(getPreFilterAuthorizationMethodBeforeAdvice());
|
|
||||||
beforeAdvices.add(getPreAuthorizeAuthorizationMethodBeforeAdvice());
|
|
||||||
if (securedEnabled()) {
|
|
||||||
beforeAdvices.add(getSecuredAuthorizationMethodBeforeAdvice());
|
|
||||||
}
|
|
||||||
if (jsr250Enabled()) {
|
|
||||||
beforeAdvices.add(getJsr250AuthorizationMethodBeforeAdvice());
|
|
||||||
}
|
|
||||||
return beforeAdvices;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PreFilterAuthorizationMethodInterceptor getPreFilterAuthorizationMethodBeforeAdvice() {
|
|
||||||
PreFilterAuthorizationMethodInterceptor interceptor = new PreFilterAuthorizationMethodInterceptor();
|
|
||||||
interceptor.setExpressionHandler(getMethodSecurityExpressionHandler());
|
|
||||||
return interceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AuthorizationMethodInterceptor getPreAuthorizeAuthorizationMethodBeforeAdvice() {
|
|
||||||
PreAuthorizeAuthorizationManager authorizationManager = new PreAuthorizeAuthorizationManager();
|
|
||||||
authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler());
|
|
||||||
return AuthorizationMethodInterceptors.preAuthorize(authorizationManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AuthorizationMethodInterceptor getSecuredAuthorizationMethodBeforeAdvice() {
|
|
||||||
return AuthorizationMethodInterceptors.secured(new SecuredAuthorizationManager());
|
|
||||||
}
|
|
||||||
|
|
||||||
private AuthorizationMethodInterceptor getJsr250AuthorizationMethodBeforeAdvice() {
|
|
||||||
Jsr250AuthorizationManager authorizationManager = new Jsr250AuthorizationManager();
|
|
||||||
if (this.grantedAuthorityDefaults != null) {
|
|
||||||
authorizationManager.setRolePrefix(this.grantedAuthorityDefaults.getRolePrefix());
|
|
||||||
}
|
|
||||||
return AuthorizationMethodInterceptors.jsr250(authorizationManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
|
||||||
void setAuthorizationMethodInterceptor(AuthorizationMethodInterceptor interceptor) {
|
|
||||||
this.interceptor = interceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<AuthorizationMethodInterceptor> createDefaultAuthorizationMethodAfterAdvice() {
|
|
||||||
List<AuthorizationMethodInterceptor> afterAdvices = new ArrayList<>();
|
|
||||||
afterAdvices.add(getPostFilterAuthorizationMethodAfterAdvice());
|
|
||||||
afterAdvices.add(getPostAuthorizeAuthorizationMethodAfterAdvice());
|
|
||||||
return afterAdvices;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AuthorizationMethodInterceptor getPostFilterAuthorizationMethodAfterAdvice() {
|
|
||||||
PostFilterAuthorizationMethodInterceptor interceptor = new PostFilterAuthorizationMethodInterceptor();
|
|
||||||
interceptor.setExpressionHandler(getMethodSecurityExpressionHandler());
|
|
||||||
return interceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AuthorizationMethodInterceptor getPostAuthorizeAuthorizationMethodAfterAdvice() {
|
|
||||||
PostAuthorizeAuthorizationManager authorizationManager = new PostAuthorizeAuthorizationManager();
|
|
||||||
authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler());
|
|
||||||
return AuthorizationMethodInterceptors.postAuthorize(authorizationManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
|
||||||
Map<String, Object> attributes = importMetadata.getAnnotationAttributes(EnableMethodSecurity.class.getName());
|
|
||||||
this.enableMethodSecurity = AnnotationAttributes.fromMap(attributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
|
||||||
if (!securedEnabled() && !jsr250Enabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Assert.isNull(this.interceptor,
|
|
||||||
"You have specified your own advice, meaning that the annotation attributes securedEnabled and jsr250Enabled will be ignored. Please choose one or the other.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean securedEnabled() {
|
|
||||||
return this.enableMethodSecurity.getBoolean("securedEnabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean jsr250Enabled() {
|
|
||||||
return this.enableMethodSecurity.getBoolean("jsr250Enabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
private int order() {
|
|
||||||
return this.enableMethodSecurity.getNumber("order");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.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]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,416 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.method.configuration;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
|
||||||
import org.springframework.aop.support.JdkRegexpMethodPointcut;
|
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
|
||||||
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.authorization.AuthorizationDecision;
|
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
|
||||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
|
||||||
import org.springframework.security.authorization.method.AuthorizationMethodInterceptor;
|
|
||||||
import org.springframework.security.config.test.SpringTestRule;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
|
||||||
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(MethodSecurityServiceEnabledConfig.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(MethodSecurityServiceEnabledConfig.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(MethodSecurityServiceEnabledConfig.class).autowire();
|
|
||||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::jsr250)
|
|
||||||
.withMessage("Access Denied");
|
|
||||||
}
|
|
||||||
|
|
||||||
@WithAnonymousUser
|
|
||||||
@Test
|
|
||||||
public void jsr250PermitAllWhenRoleAnonymousThenPasses() {
|
|
||||||
this.spring.register(MethodSecurityServiceEnabledConfig.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();
|
|
||||||
}
|
|
||||||
|
|
||||||
@WithMockUser(roles = { "ADMIN", "USER" })
|
|
||||||
@Test
|
|
||||||
public void manyAnnotationsWhenMeetsConditionsThenReturnsFilteredList() throws Exception {
|
|
||||||
List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo");
|
|
||||||
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
|
|
||||||
List<String> filtered = this.methodSecurityService.manyAnnotations(new ArrayList<>(names));
|
|
||||||
assertThat(filtered).hasSize(2);
|
|
||||||
assertThat(filtered).containsExactly("harold", "jonathan");
|
|
||||||
}
|
|
||||||
|
|
||||||
@WithMockUser
|
|
||||||
@Test
|
|
||||||
public void manyAnnotationsWhenUserThenFails() {
|
|
||||||
List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo");
|
|
||||||
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
|
|
||||||
assertThatExceptionOfType(AccessDeniedException.class)
|
|
||||||
.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@WithMockUser
|
|
||||||
@Test
|
|
||||||
public void manyAnnotationsWhenShortListThenFails() {
|
|
||||||
List<String> names = Arrays.asList("harold", "jonathan", "pete");
|
|
||||||
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
|
|
||||||
assertThatExceptionOfType(AccessDeniedException.class)
|
|
||||||
.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@WithMockUser(roles = "ADMIN")
|
|
||||||
@Test
|
|
||||||
public void manyAnnotationsWhenAdminThenFails() {
|
|
||||||
List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo");
|
|
||||||
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
|
|
||||||
assertThatExceptionOfType(AccessDeniedException.class)
|
|
||||||
.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void configureWhenCustomAdviceAndSecureEnabledThenException() {
|
|
||||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> this.spring
|
|
||||||
.register(CustomAuthorizationManagerBeforeAdviceConfig.class, MethodSecurityServiceEnabledConfig.class)
|
|
||||||
.autowire());
|
|
||||||
}
|
|
||||||
|
|
||||||
@EnableMethodSecurity
|
|
||||||
static class MethodSecurityServiceConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
MethodSecurityService methodSecurityService() {
|
|
||||||
return new MethodSecurityServiceImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@EnableMethodSecurity(jsr250Enabled = true)
|
|
||||||
static class BusinessServiceConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
BusinessService businessService() {
|
|
||||||
return new ExpressionProtectedBusinessServiceImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
|
|
||||||
static class MethodSecurityServiceEnabledConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
MethodSecurityService methodSecurityService() {
|
|
||||||
return new MethodSecurityServiceImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
|
||||||
AuthorizationMethodInterceptor customBeforeAdvice() {
|
|
||||||
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
|
|
||||||
pointcut.setPattern(".*MethodSecurityServiceImpl.*securedUser");
|
|
||||||
AuthorizationManager<MethodInvocation> authorizationManager = (a,
|
|
||||||
o) -> new AuthorizationDecision("bob".equals(a.get().getName()));
|
|
||||||
return new AuthorizationManagerBeforeMethodInterceptor(pointcut, authorizationManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@EnableMethodSecurity
|
|
||||||
static class CustomAuthorizationManagerAfterAdviceConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
|
|
||||||
AuthorizationMethodInterceptor customAfterAdvice() {
|
|
||||||
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
|
|
||||||
pointcut.setPattern(".*MethodSecurityServiceImpl.*securedUser");
|
|
||||||
AuthorizationMethodInterceptor interceptor = new AuthorizationMethodInterceptor() {
|
|
||||||
@Override
|
|
||||||
public Pointcut getPointcut() {
|
|
||||||
return pointcut;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) {
|
|
||||||
Authentication auth = authentication.get();
|
|
||||||
if ("bob".equals(auth.getName())) {
|
|
||||||
return "granted";
|
|
||||||
}
|
|
||||||
throw new AccessDeniedException("Access Denied for User '" + auth.getName() + "'");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return interceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -16,16 +16,12 @@
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.method.configuration;
|
package org.springframework.security.config.annotation.method.configuration;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.annotation.security.DenyAll;
|
import javax.annotation.security.DenyAll;
|
||||||
import javax.annotation.security.PermitAll;
|
import javax.annotation.security.PermitAll;
|
||||||
|
|
||||||
import org.springframework.security.access.annotation.Secured;
|
import org.springframework.security.access.annotation.Secured;
|
||||||
import org.springframework.security.access.prepost.PostAuthorize;
|
import org.springframework.security.access.prepost.PostAuthorize;
|
||||||
import org.springframework.security.access.prepost.PostFilter;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.access.prepost.PreFilter;
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.parameters.P;
|
import org.springframework.security.core.parameters.P;
|
||||||
|
|
||||||
|
@ -73,11 +69,4 @@ public interface MethodSecurityService {
|
||||||
@PostAuthorize("#o?.contains('grant')")
|
@PostAuthorize("#o?.contains('grant')")
|
||||||
String postAnnotation(@P("o") String object);
|
String postAnnotation(@P("o") String object);
|
||||||
|
|
||||||
@PreFilter("filterObject.length > 3")
|
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
|
||||||
@Secured("ROLE_USER")
|
|
||||||
@PostFilter("filterObject.length > 5")
|
|
||||||
@PostAuthorize("returnObject.size > 1")
|
|
||||||
List<String> manyAnnotations(List<String> array);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.method.configuration;
|
package org.springframework.security.config.annotation.method.configuration;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
||||||
|
@ -88,9 +86,4 @@ public class MethodSecurityServiceImpl implements MethodSecurityService {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> manyAnnotations(List<String> object) {
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -75,22 +75,9 @@ public final class AuthorityAuthorizationManager<T> implements AuthorizationMana
|
||||||
* @return the new instance
|
* @return the new instance
|
||||||
*/
|
*/
|
||||||
public static <T> AuthorityAuthorizationManager<T> hasAnyRole(String... roles) {
|
public static <T> AuthorityAuthorizationManager<T> hasAnyRole(String... roles) {
|
||||||
return hasAnyRole(ROLE_PREFIX, roles);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance of {@link AuthorityAuthorizationManager} with the provided
|
|
||||||
* authorities.
|
|
||||||
* @param rolePrefix the role prefix for <code>roles</code>
|
|
||||||
* @param roles the authorities to check for prefixed with <code>rolePrefix</code>
|
|
||||||
* @param <T> the type of object being authorized
|
|
||||||
* @return the new instance
|
|
||||||
*/
|
|
||||||
public static <T> AuthorityAuthorizationManager<T> hasAnyRole(String rolePrefix, String[] roles) {
|
|
||||||
Assert.notNull(rolePrefix, "rolePrefix cannot be null");
|
|
||||||
Assert.notEmpty(roles, "roles cannot be empty");
|
Assert.notEmpty(roles, "roles cannot be empty");
|
||||||
Assert.noNullElements(roles, "roles cannot contain null values");
|
Assert.noNullElements(roles, "roles cannot contain null values");
|
||||||
return hasAnyAuthority(toNamedRolesArray(rolePrefix, roles));
|
return hasAnyAuthority(toNamedRolesArray(roles));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -106,10 +93,10 @@ public final class AuthorityAuthorizationManager<T> implements AuthorizationMana
|
||||||
return new AuthorityAuthorizationManager<>(authorities);
|
return new AuthorityAuthorizationManager<>(authorities);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String[] toNamedRolesArray(String rolePrefix, String[] roles) {
|
private static String[] toNamedRolesArray(String... roles) {
|
||||||
String[] result = new String[roles.length];
|
String[] result = new String[roles.length];
|
||||||
for (int i = 0; i < roles.length; i++) {
|
for (int i = 0; i < roles.length; i++) {
|
||||||
result[i] = rolePrefix + roles[i];
|
result[i] = ROLE_PREFIX + roles[i];
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.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.authorization.AuthorizationManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For internal use only, as this contract is likely to change
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
*/
|
|
||||||
abstract class AbstractAuthorizationManagerRegistry {
|
|
||||||
|
|
||||||
static final AuthorizationManager<MethodInvocation> NULL_MANAGER = (a, o) -> null;
|
|
||||||
|
|
||||||
private final Map<MethodClassKey, AuthorizationManager<MethodInvocation>> cachedManagers = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an {@link AuthorizationManager} for the
|
|
||||||
* {@link AuthorizationMethodInvocation}.
|
|
||||||
* @param methodInvocation the {@link AuthorizationMethodInvocation} to use
|
|
||||||
* @return an {@link AuthorizationManager} to use
|
|
||||||
*/
|
|
||||||
final AuthorizationManager<MethodInvocation> getManager(AuthorizationMethodInvocation methodInvocation) {
|
|
||||||
Method method = methodInvocation.getMethod();
|
|
||||||
Class<?> targetClass = methodInvocation.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<MethodInvocation> resolveManager(Method method, Class<?> targetClass);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
import org.springframework.core.MethodClassKey;
|
|
||||||
import org.springframework.lang.NonNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For internal use only, as this contract is likely to change
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
*/
|
|
||||||
abstract class AbstractExpressionAttributeRegistry<T extends ExpressionAttribute> {
|
|
||||||
|
|
||||||
private final Map<MethodClassKey, T> cachedAttributes = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an {@link ExpressionAttribute} for the
|
|
||||||
* {@link AuthorizationMethodInvocation}.
|
|
||||||
* @param mi the {@link AuthorizationMethodInvocation} to use
|
|
||||||
* @return the {@link ExpressionAttribute} to use
|
|
||||||
*/
|
|
||||||
final T getAttribute(AuthorizationMethodInvocation mi) {
|
|
||||||
Method method = mi.getMethod();
|
|
||||||
Class<?> targetClass = mi.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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
|
|
||||||
import org.springframework.lang.Nullable;
|
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
|
||||||
import org.springframework.security.authorization.AuthorizationDecision;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An Authorization manager which can determine if an {@link Authentication} has access to
|
|
||||||
* a specific object and associated return object. Intended for use specifically to
|
|
||||||
* evaluate the returning state of a method invocation.
|
|
||||||
*
|
|
||||||
* @param <T> the type of object that the authorization check is being done one.
|
|
||||||
* @author Josh Cummings
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
* @since 5.5
|
|
||||||
*/
|
|
||||||
public interface AfterMethodAuthorizationManager<T> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if access should be granted for a specific authentication, object and
|
|
||||||
* returnedObject.
|
|
||||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
|
||||||
* @param object the {@code T} object to check, typically a {@link MethodInvocation}
|
|
||||||
* @param returnedObject the returnedObject from the method invocation to check
|
|
||||||
* @throws AccessDeniedException if access is not granted
|
|
||||||
*/
|
|
||||||
default void verify(Supplier<Authentication> authentication, T object, Object returnedObject) {
|
|
||||||
AuthorizationDecision decision = check(authentication, object, returnedObject);
|
|
||||||
if (decision != null && !decision.isGranted()) {
|
|
||||||
throw new AccessDeniedException("Access Denied");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if access is granted for a specific authentication, object, and
|
|
||||||
* returnedObject.
|
|
||||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
|
||||||
* @param object the {@code T} object to check, typically a {@link MethodInvocation}
|
|
||||||
* @param returnedObject the returned object from the method invocation to check
|
|
||||||
* @return an {@link AuthorizationDecision} or null if no decision could be made
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
AuthorizationDecision check(Supplier<Authentication> authentication, T object, Object returnedObject);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
|
|
||||||
import org.springframework.security.authorization.AuthorizationDecision;
|
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapts an {@link AuthorizationManager} into an {@link AfterMethodAuthorizationManager}
|
|
||||||
*
|
|
||||||
* @param <T> the {@code T} object to authorize, typically a {@link MethodInvocation}
|
|
||||||
* @author Josh Cummings
|
|
||||||
* @since 5.5
|
|
||||||
*/
|
|
||||||
public final class AfterMethodAuthorizationManagerAdapter<T> implements AfterMethodAuthorizationManager<T> {
|
|
||||||
|
|
||||||
private final AuthorizationManager<T> authorizationManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a {@link AfterMethodAuthorizationManagerAdapter} with the provided
|
|
||||||
* parameters
|
|
||||||
* @param authorizationManager the {@link AuthorizationManager} to adapt
|
|
||||||
*/
|
|
||||||
public AfterMethodAuthorizationManagerAdapter(AuthorizationManager<T> authorizationManager) {
|
|
||||||
this.authorizationManager = authorizationManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if access is granted for a specific authentication and {@code T} object.
|
|
||||||
*
|
|
||||||
* Note that the {@code returnedObject} parameter is ignored
|
|
||||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
|
||||||
* @param object the {@code T} object to check, typically a {@link MethodInvocation}
|
|
||||||
* @param returnedObject the returned object from the method invocation, ignored in
|
|
||||||
* this implementation
|
|
||||||
* @return an {@link AuthorizationDecision} or null if no decision could be made
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public AuthorizationDecision check(Supplier<Authentication> authentication, T object, Object returnedObject) {
|
|
||||||
return this.authorizationManager.check(authentication, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
|
||||||
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 AuthorizationMethodInterceptor} which can determine if an
|
|
||||||
* {@link Authentication} has access to the result of an {@link MethodInvocation} using an
|
|
||||||
* {@link AuthorizationManager}
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
* @author Josh Cummings
|
|
||||||
* @since 5.5
|
|
||||||
*/
|
|
||||||
public final class AuthorizationManagerAfterMethodInterceptor implements AuthorizationMethodInterceptor {
|
|
||||||
|
|
||||||
private final Pointcut pointcut;
|
|
||||||
|
|
||||||
private final AfterMethodAuthorizationManager<MethodInvocation> authorizationManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance.
|
|
||||||
* @param pointcut the {@link Pointcut} to use
|
|
||||||
* @param authorizationManager the {@link AuthorizationManager} to use
|
|
||||||
*/
|
|
||||||
public AuthorizationManagerAfterMethodInterceptor(Pointcut pointcut,
|
|
||||||
AfterMethodAuthorizationManager<MethodInvocation> authorizationManager) {
|
|
||||||
Assert.notNull(pointcut, "pointcut cannot be null");
|
|
||||||
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
|
|
||||||
this.pointcut = pointcut;
|
|
||||||
this.authorizationManager = authorizationManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
|
|
||||||
* using the {@link AuthorizationManager}.
|
|
||||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
|
||||||
* @param mi the {@link MethodInvocation} to check
|
|
||||||
* @throws AccessDeniedException if access is not granted
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
|
|
||||||
Object result = mi.proceed();
|
|
||||||
this.authorizationManager.verify(authentication, mi, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Pointcut getPointcut() {
|
|
||||||
return this.pointcut;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
|
||||||
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 AuthorizationMethodInterceptor} which uses a {@link AuthorizationManager} to
|
|
||||||
* determine if an {@link Authentication} may invoke the given {@link MethodInvocation}
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
* @author Josh Cummings
|
|
||||||
* @since 5.5
|
|
||||||
*/
|
|
||||||
public final class AuthorizationManagerBeforeMethodInterceptor implements AuthorizationMethodInterceptor {
|
|
||||||
|
|
||||||
private final Pointcut pointcut;
|
|
||||||
|
|
||||||
private final AuthorizationManager<MethodInvocation> authorizationManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance.
|
|
||||||
* @param pointcut the {@link Pointcut} to use
|
|
||||||
* @param authorizationManager the {@link AuthorizationManager} to use
|
|
||||||
*/
|
|
||||||
public AuthorizationManagerBeforeMethodInterceptor(Pointcut pointcut,
|
|
||||||
AuthorizationManager<MethodInvocation> authorizationManager) {
|
|
||||||
Assert.notNull(pointcut, "pointcut cannot be null");
|
|
||||||
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
|
|
||||||
this.pointcut = pointcut;
|
|
||||||
this.authorizationManager = authorizationManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
|
|
||||||
* using the configured {@link AuthorizationManager}.
|
|
||||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
|
||||||
* @param mi the {@link MethodInvocation} to check
|
|
||||||
* @throws AccessDeniedException if access is not granted
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
|
|
||||||
this.authorizationManager.verify(authentication, mi);
|
|
||||||
return mi.proceed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Pointcut getPointcut() {
|
|
||||||
return this.pointcut;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.aopalliance.aop.Advice;
|
|
||||||
import org.aopalliance.intercept.MethodInterceptor;
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
|
|
||||||
import org.springframework.aop.PointcutAdvisor;
|
|
||||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
|
||||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link MethodInterceptor} which can determine if an {@link Authentication} has access
|
|
||||||
* to the {@link MethodInvocation}. {@link #getPointcut()} describes when the interceptor
|
|
||||||
* applies.
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
* @author Josh Cummings
|
|
||||||
* @since 5.5
|
|
||||||
*/
|
|
||||||
public interface AuthorizationMethodInterceptor extends MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
default Advice getAdvice() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
default boolean isPerInstance() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
|
|
||||||
* @param mi the {@link MethodInvocation} to intercept and potentially invoke
|
|
||||||
* @return the result of the method invocation
|
|
||||||
* @throws Throwable if the interceptor or the target object throws an exception
|
|
||||||
*/
|
|
||||||
default Object invoke(MethodInvocation mi) throws Throwable {
|
|
||||||
Supplier<Authentication> supplier = () -> {
|
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
|
||||||
if (authentication == null) {
|
|
||||||
throw new AuthenticationCredentialsNotFoundException(
|
|
||||||
"An Authentication object was not found in the SecurityContext");
|
|
||||||
}
|
|
||||||
return authentication;
|
|
||||||
};
|
|
||||||
return invoke(supplier, new AuthorizationMethodInvocation(supplier, mi));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
|
|
||||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
|
||||||
* @param mi the {@link MethodInvocation} to intercept and potentially invoke
|
|
||||||
* @return the result of the method invocation
|
|
||||||
* @throws Throwable if the interceptor or the target object throws an exception
|
|
||||||
*/
|
|
||||||
Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import javax.annotation.security.DenyAll;
|
|
||||||
import javax.annotation.security.PermitAll;
|
|
||||||
import javax.annotation.security.RolesAllowed;
|
|
||||||
|
|
||||||
import org.springframework.security.access.annotation.Secured;
|
|
||||||
import org.springframework.security.access.prepost.PostAuthorize;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A static factory for constructing common {@link AuthorizationMethodInterceptor}s
|
|
||||||
*
|
|
||||||
* @author Josh Cummings
|
|
||||||
* @since 5.5
|
|
||||||
* @see PreAuthorizeAuthorizationManager
|
|
||||||
* @see PostAuthorizeAuthorizationManager
|
|
||||||
* @see SecuredAuthorizationManager
|
|
||||||
* @see Jsr250AuthorizationManager
|
|
||||||
*/
|
|
||||||
public final class AuthorizationMethodInterceptors {
|
|
||||||
|
|
||||||
public static AuthorizationMethodInterceptor preAuthorize() {
|
|
||||||
return preAuthorize(new PreAuthorizeAuthorizationManager());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AuthorizationMethodInterceptor preAuthorize(PreAuthorizeAuthorizationManager manager) {
|
|
||||||
return new AuthorizationManagerBeforeMethodInterceptor(
|
|
||||||
AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class), manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AuthorizationMethodInterceptor postAuthorize() {
|
|
||||||
return postAuthorize(new PostAuthorizeAuthorizationManager());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AuthorizationMethodInterceptor postAuthorize(PostAuthorizeAuthorizationManager manager) {
|
|
||||||
return new AuthorizationManagerAfterMethodInterceptor(
|
|
||||||
AuthorizationMethodPointcuts.forAnnotations(PostAuthorize.class), manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AuthorizationMethodInterceptor secured() {
|
|
||||||
return secured(new SecuredAuthorizationManager());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AuthorizationMethodInterceptor secured(SecuredAuthorizationManager manager) {
|
|
||||||
return new AuthorizationManagerBeforeMethodInterceptor(
|
|
||||||
AuthorizationMethodPointcuts.forAnnotations(Secured.class), manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AuthorizationMethodInterceptor jsr250() {
|
|
||||||
return jsr250(new Jsr250AuthorizationManager());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AuthorizationMethodInterceptor jsr250(Jsr250AuthorizationManager manager) {
|
|
||||||
return new AuthorizationManagerBeforeMethodInterceptor(
|
|
||||||
AuthorizationMethodPointcuts.forAnnotations(DenyAll.class, PermitAll.class, RolesAllowed.class),
|
|
||||||
manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AuthorizationMethodInterceptors() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,123 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.lang.reflect.AccessibleObject;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
|
||||||
import org.springframework.aop.support.AopUtils;
|
|
||||||
import org.springframework.core.log.LogMessage;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Josh Cummings
|
|
||||||
*/
|
|
||||||
class AuthorizationMethodInvocation implements MethodInvocation {
|
|
||||||
|
|
||||||
private final Log logger = LogFactory.getLog(getClass());
|
|
||||||
|
|
||||||
private final Supplier<Authentication> authentication;
|
|
||||||
|
|
||||||
private final MethodInvocation methodInvocation;
|
|
||||||
|
|
||||||
private final Class<?> targetClass;
|
|
||||||
|
|
||||||
private final List<AuthorizationMethodInterceptor> interceptors;
|
|
||||||
|
|
||||||
private final int size;
|
|
||||||
|
|
||||||
private int currentPosition = 0;
|
|
||||||
|
|
||||||
AuthorizationMethodInvocation(Supplier<Authentication> authentication, MethodInvocation methodInvocation) {
|
|
||||||
this(authentication, methodInvocation, Collections.emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
AuthorizationMethodInvocation(Supplier<Authentication> authentication, MethodInvocation methodInvocation,
|
|
||||||
List<AuthorizationMethodInterceptor> interceptors) {
|
|
||||||
this.authentication = authentication;
|
|
||||||
this.methodInvocation = methodInvocation;
|
|
||||||
this.interceptors = interceptors;
|
|
||||||
Object target = methodInvocation.getThis();
|
|
||||||
this.targetClass = (target != null) ? AopUtils.getTargetClass(target) : null;
|
|
||||||
this.size = interceptors.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Method getMethod() {
|
|
||||||
return this.methodInvocation.getMethod();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object[] getArguments() {
|
|
||||||
return this.methodInvocation.getArguments();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the target class.
|
|
||||||
* @return the target class
|
|
||||||
*/
|
|
||||||
Class<?> getTargetClass() {
|
|
||||||
return this.targetClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object proceed() throws Throwable {
|
|
||||||
if (this.currentPosition == this.size) {
|
|
||||||
if (this.logger.isDebugEnabled()) {
|
|
||||||
this.logger.debug(LogMessage.of(() -> "Pre-Authorized " + this.methodInvocation.getMethod()));
|
|
||||||
}
|
|
||||||
return this.methodInvocation.proceed();
|
|
||||||
}
|
|
||||||
AuthorizationMethodInterceptor interceptor = this.interceptors.get(this.currentPosition);
|
|
||||||
this.currentPosition++;
|
|
||||||
Pointcut pointcut = interceptor.getPointcut();
|
|
||||||
if (!pointcut.getClassFilter().matches(getTargetClass())) {
|
|
||||||
return proceed();
|
|
||||||
}
|
|
||||||
if (!pointcut.getMethodMatcher().matches(getMethod(), getTargetClass())) {
|
|
||||||
return proceed();
|
|
||||||
}
|
|
||||||
if (this.logger.isTraceEnabled()) {
|
|
||||||
this.logger.trace(LogMessage.format("Applying %s (%d/%d)", interceptor.getClass().getSimpleName(),
|
|
||||||
this.currentPosition, this.size));
|
|
||||||
}
|
|
||||||
Object result = interceptor.invoke(this.authentication, this);
|
|
||||||
if (this.logger.isDebugEnabled()) {
|
|
||||||
this.logger.debug(LogMessage.of(() -> "Post-Authorized " + this.methodInvocation.getMethod()));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getThis() {
|
|
||||||
return this.methodInvocation.getThis();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AccessibleObject getStaticPart() {
|
|
||||||
return this.methodInvocation.getStaticPart();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
|
||||||
import org.springframework.aop.support.ComposablePointcut;
|
|
||||||
import org.springframework.aop.support.Pointcuts;
|
|
||||||
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Josh Cummings
|
|
||||||
*/
|
|
||||||
final class AuthorizationMethodPointcuts {
|
|
||||||
|
|
||||||
@SafeVarargs
|
|
||||||
static Pointcut forAnnotations(Class<? extends Annotation>... annotations) {
|
|
||||||
ComposablePointcut pointcut = null;
|
|
||||||
for (Class<? extends Annotation> annotation : annotations) {
|
|
||||||
if (pointcut == null) {
|
|
||||||
pointcut = new ComposablePointcut(classOrMethod(annotation));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pointcut.union(classOrMethod(annotation));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pointcut;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Pointcut classOrMethod(Class<? extends Annotation> annotation) {
|
|
||||||
return Pointcuts.union(new AnnotationMatchingPointcut(null, annotation, true),
|
|
||||||
new AnnotationMatchingPointcut(annotation, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
private AuthorizationMethodPointcuts() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
|
||||||
import org.springframework.aop.support.ComposablePointcut;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides security interception of AOP Alliance based method invocations.
|
|
||||||
*
|
|
||||||
* Delegates to a collection of {@link AuthorizationMethodInterceptor}s
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
* @author Josh Cummings
|
|
||||||
* @since 5.5
|
|
||||||
*/
|
|
||||||
public final class DelegatingAuthorizationMethodInterceptor implements AuthorizationMethodInterceptor {
|
|
||||||
|
|
||||||
private final List<AuthorizationMethodInterceptor> interceptors;
|
|
||||||
|
|
||||||
private final Pointcut pointcut;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance using the provided parameters
|
|
||||||
* @param interceptors the delegate {@link AuthorizationMethodInterceptor}s to use
|
|
||||||
*/
|
|
||||||
public DelegatingAuthorizationMethodInterceptor(AuthorizationMethodInterceptor... interceptors) {
|
|
||||||
this(Arrays.asList(interceptors));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance using the provided parameters
|
|
||||||
* @param interceptors the delegate {@link AuthorizationMethodInterceptor}s to use
|
|
||||||
*/
|
|
||||||
public DelegatingAuthorizationMethodInterceptor(List<AuthorizationMethodInterceptor> interceptors) {
|
|
||||||
ComposablePointcut pointcut = null;
|
|
||||||
for (AuthorizationMethodInterceptor interceptor : interceptors) {
|
|
||||||
if (pointcut == null) {
|
|
||||||
pointcut = new ComposablePointcut(interceptor.getPointcut());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pointcut.union(interceptor.getPointcut());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.pointcut = pointcut;
|
|
||||||
this.interceptors = interceptors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enforce security on this {@link MethodInvocation}.
|
|
||||||
* @param mi the method being invoked which requires a security decision
|
|
||||||
* @return the returned value from the {@link MethodInvocation}, possibly altered by
|
|
||||||
* the configured {@link AuthorizationMethodInterceptor}s
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
|
|
||||||
return new AuthorizationMethodInvocation(authentication, mi, this.interceptors).proceed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Pointcut getPointcut() {
|
|
||||||
return this.pointcut;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import org.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,121 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.lang.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.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} may
|
|
||||||
* invoke the {@link MethodInvocation} by evaluating if the {@link Authentication}
|
|
||||||
* contains a specified authority from the JSR-250 security annotations.
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
* @since 5.5
|
|
||||||
*/
|
|
||||||
public final class Jsr250AuthorizationManager implements AuthorizationManager<MethodInvocation> {
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if an {@link Authentication} has access to a method by evaluating the
|
|
||||||
* {@link DenyAll}, {@link PermitAll}, and {@link RolesAllowed} annotations that
|
|
||||||
* {@link AuthorizationMethodInvocation} specifies.
|
|
||||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
|
||||||
* @param methodInvocation the {@link AuthorizationMethodInvocation} to check
|
|
||||||
* @return an {@link AuthorizationDecision} or null if the JSR-250 security
|
|
||||||
* annotations is not present
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation methodInvocation) {
|
|
||||||
AuthorizationManager<MethodInvocation> delegate = this.registry
|
|
||||||
.getManager((AuthorizationMethodInvocation) methodInvocation);
|
|
||||||
return delegate.check(authentication, methodInvocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class Jsr250AuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry {
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
AuthorizationManager<MethodInvocation> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.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.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} may
|
|
||||||
* return the result from an invoked {@link MethodInvocation} by evaluating an expression
|
|
||||||
* from the {@link PostAuthorize} annotation.
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
* @since 5.5
|
|
||||||
*/
|
|
||||||
public final class PostAuthorizeAuthorizationManager implements AfterMethodAuthorizationManager<MethodInvocation> {
|
|
||||||
|
|
||||||
private final PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();
|
|
||||||
|
|
||||||
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use this 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if an {@link Authentication} has access to the returned object by
|
|
||||||
* evaluating the {@link PostAuthorize} annotation that the
|
|
||||||
* {@link AuthorizationMethodInvocation} specifies.
|
|
||||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
|
||||||
* @param mi the {@link AuthorizationMethodInvocation} to check
|
|
||||||
* @param returnedObject the returned object to check
|
|
||||||
* @return an {@link AuthorizationDecision} or {@code null} if the
|
|
||||||
* {@link PostAuthorize} annotation is not present
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi,
|
|
||||||
Object returnedObject) {
|
|
||||||
ExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi);
|
|
||||||
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
|
|
||||||
this.expressionHandler.setReturnObject(returnedObject, 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
|
||||||
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.lang.NonNull;
|
|
||||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
|
||||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
|
||||||
import org.springframework.security.access.prepost.PostFilter;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An {@link AuthorizationMethodInterceptor} which filters a {@code returnedObject} from
|
|
||||||
* the {@link MethodInvocation} by evaluating an expression from the {@link PostFilter}
|
|
||||||
* annotation.
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
* @author Josh Cummings
|
|
||||||
* @since 5.5
|
|
||||||
*/
|
|
||||||
public final class PostFilterAuthorizationMethodInterceptor implements AuthorizationMethodInterceptor {
|
|
||||||
|
|
||||||
private final PostFilterExpressionAttributeRegistry registry = new PostFilterExpressionAttributeRegistry();
|
|
||||||
|
|
||||||
private final Pointcut pointcut;
|
|
||||||
|
|
||||||
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@link PostFilterAuthorizationMethodInterceptor} using the provided
|
|
||||||
* parameters
|
|
||||||
*/
|
|
||||||
public PostFilterAuthorizationMethodInterceptor() {
|
|
||||||
this.pointcut = AuthorizationMethodPointcuts.forAnnotations(PostFilter.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use this {@link MethodSecurityExpressionHandler}.
|
|
||||||
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
|
|
||||||
*/
|
|
||||||
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
|
|
||||||
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
|
|
||||||
this.expressionHandler = expressionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Pointcut getPointcut() {
|
|
||||||
return this.pointcut;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter a {@code returnedObject} using the {@link PostFilter} annotation that the
|
|
||||||
* {@link AuthorizationMethodInvocation} specifies.
|
|
||||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
|
||||||
* @param mi the {@link AuthorizationMethodInvocation} to check check
|
|
||||||
* @return filtered {@code returnedObject}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
|
|
||||||
Object returnedObject = mi.proceed();
|
|
||||||
ExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi);
|
|
||||||
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
|
|
||||||
return returnedObject;
|
|
||||||
}
|
|
||||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
|
|
||||||
return this.expressionHandler.filter(returnedObject, attribute.getExpression(), ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = PostFilterAuthorizationMethodInterceptor.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,105 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.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.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} may
|
|
||||||
* invoke the {@link MethodInvocation} by evaluating an expression from the
|
|
||||||
* {@link PreAuthorize} annotation.
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
* @since 5.5
|
|
||||||
*/
|
|
||||||
public final class PreAuthorizeAuthorizationManager implements AuthorizationManager<MethodInvocation> {
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if an {@link Authentication} has access to a method by evaluating an
|
|
||||||
* expression from the {@link PreAuthorize} annotation that the
|
|
||||||
* {@link AuthorizationMethodInvocation} specifies.
|
|
||||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
|
||||||
* @param mi the {@link AuthorizationMethodInvocation} to check
|
|
||||||
* @return an {@link AuthorizationDecision} or {@code null} if the
|
|
||||||
* {@link PreAuthorize} annotation is not present
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) {
|
|
||||||
ExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi);
|
|
||||||
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,153 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
|
||||||
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.lang.NonNull;
|
|
||||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
|
||||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
|
||||||
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 AuthorizationMethodInterceptor} which filters a method argument by evaluating
|
|
||||||
* an expression from the {@link PreFilter} annotation.
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
* @author Josh Cummings
|
|
||||||
* @since 5.5
|
|
||||||
*/
|
|
||||||
public final class PreFilterAuthorizationMethodInterceptor implements AuthorizationMethodInterceptor {
|
|
||||||
|
|
||||||
private final PreFilterExpressionAttributeRegistry registry = new PreFilterExpressionAttributeRegistry();
|
|
||||||
|
|
||||||
private final Pointcut pointcut;
|
|
||||||
|
|
||||||
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@link PreFilterAuthorizationMethodInterceptor} using the provided
|
|
||||||
* parameters
|
|
||||||
*/
|
|
||||||
public PreFilterAuthorizationMethodInterceptor() {
|
|
||||||
this.pointcut = AuthorizationMethodPointcuts.forAnnotations(PreFilter.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use this {@link MethodSecurityExpressionHandler}
|
|
||||||
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
|
|
||||||
*/
|
|
||||||
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
|
|
||||||
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
|
|
||||||
this.expressionHandler = expressionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Pointcut getPointcut() {
|
|
||||||
return this.pointcut;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter the method argument specified in the {@link PreFilter} annotation that
|
|
||||||
* {@link AuthorizationMethodInvocation} specifies.
|
|
||||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
|
||||||
* @param mi the {@link AuthorizationMethodInvocation} to check
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable {
|
|
||||||
PreFilterExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi);
|
|
||||||
if (attribute == PreFilterExpressionAttribute.NULL_ATTRIBUTE) {
|
|
||||||
return mi.proceed();
|
|
||||||
}
|
|
||||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
|
|
||||||
Object filterTarget = findFilterTarget(attribute.filterTarget, ctx, mi);
|
|
||||||
this.expressionHandler.filter(filterTarget, attribute.getExpression(), ctx);
|
|
||||||
return mi.proceed();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object findFilterTarget(String filterTargetName, EvaluationContext ctx, MethodInvocation methodInvocation) {
|
|
||||||
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 = PreFilterAuthorizationMethodInterceptor.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.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.annotation.Secured;
|
|
||||||
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} may
|
|
||||||
* invoke the {@link MethodInvocation} by evaluating if the {@link Authentication}
|
|
||||||
* contains a specified authority from the Spring Security's {@link Secured} annotation.
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
* @since 5.5
|
|
||||||
*/
|
|
||||||
public final class SecuredAuthorizationManager implements AuthorizationManager<MethodInvocation> {
|
|
||||||
|
|
||||||
private final SecuredAuthorizationManagerRegistry registry = new SecuredAuthorizationManagerRegistry();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if an {@link Authentication} has access to a method by evaluating the
|
|
||||||
* {@link Secured} annotation that {@link AuthorizationMethodInvocation} specifies.
|
|
||||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
|
||||||
* @param mi the {@link AuthorizationMethodInvocation} to check
|
|
||||||
* @return an {@link AuthorizationDecision} or null if the {@link Secured} annotation
|
|
||||||
* is not present
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) {
|
|
||||||
AuthorizationManager<MethodInvocation> delegate = this.registry.getManager((AuthorizationMethodInvocation) mi);
|
|
||||||
return delegate.check(authentication, mi);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class SecuredAuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry {
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass) {
|
|
||||||
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
|
|
||||||
Secured secured = findSecuredAnnotation(specificMethod);
|
|
||||||
return (secured != null) ? AuthorityAuthorizationManager.hasAnyAuthority(secured.value()) : NULL_MANAGER;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Secured findSecuredAnnotation(Method method) {
|
|
||||||
Secured secured = AnnotationUtils.findAnnotation(method, Secured.class);
|
|
||||||
return (secured != null) ? secured
|
|
||||||
: AnnotationUtils.findAnnotation(method.getDeclaringClass(), Secured.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -64,13 +64,6 @@ public class AuthorityAuthorizationManagerTests {
|
||||||
.withMessage("roles cannot contain null values");
|
.withMessage("roles cannot contain null values");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hasAnyRoleWhenCustomRolePrefixNullThenException() {
|
|
||||||
assertThatIllegalArgumentException()
|
|
||||||
.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole(null, new String[] { "ADMIN", "USER" }))
|
|
||||||
.withMessage("rolePrefix cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void hasAnyAuthorityWhenNullThenException() {
|
public void hasAnyAuthorityWhenNullThenException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority(null))
|
assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority(null))
|
||||||
|
@ -154,17 +147,6 @@ public class AuthorityAuthorizationManagerTests {
|
||||||
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hasAnyRoleWhenCustomRolePrefixProvidedThenUseCustomRolePrefix() {
|
|
||||||
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyRole("CUSTOM_",
|
|
||||||
new String[] { "USER" });
|
|
||||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
|
|
||||||
"CUSTOM_USER");
|
|
||||||
Object object = new Object();
|
|
||||||
|
|
||||||
assertThat(manager.check(authentication, object).isGranted()).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void hasAnyAuthorityWhenUserHasAnyAuthorityThenGrantedDecision() {
|
public void hasAnyAuthorityWhenUserHasAnyAuthorityThenGrantedDecision() {
|
||||||
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyAuthority("ADMIN", "USER");
|
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyAuthority("ADMIN", "USER");
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
|
||||||
import org.springframework.security.authentication.TestAuthentication;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
|
||||||
import static org.mockito.BDDMockito.given;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link AuthorizationManagerAfterMethodInterceptor}.
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
*/
|
|
||||||
public class AuthorizationManagerAfterMethodInterceptorTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void instantiateWhenMethodMatcherNullThenException() {
|
|
||||||
AfterMethodAuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(
|
|
||||||
AfterMethodAuthorizationManager.class);
|
|
||||||
assertThatIllegalArgumentException()
|
|
||||||
.isThrownBy(() -> new AuthorizationManagerAfterMethodInterceptor(null, mockAuthorizationManager))
|
|
||||||
.withMessage("pointcut cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void instantiateWhenAuthorizationManagerNullThenException() {
|
|
||||||
assertThatIllegalArgumentException()
|
|
||||||
.isThrownBy(() -> new AuthorizationManagerAfterMethodInterceptor(mock(Pointcut.class), null))
|
|
||||||
.withMessage("authorizationManager cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void beforeWhenMockAuthorizationManagerThenVerifyAndReturnedObject() throws Throwable {
|
|
||||||
Supplier<Authentication> authentication = TestAuthentication::authenticatedUser;
|
|
||||||
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
|
|
||||||
Object returnedObject = new Object();
|
|
||||||
given(mockMethodInvocation.proceed()).willReturn(returnedObject);
|
|
||||||
AfterMethodAuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(
|
|
||||||
AfterMethodAuthorizationManager.class);
|
|
||||||
AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(
|
|
||||||
Pointcut.TRUE, mockAuthorizationManager);
|
|
||||||
Object result = advice.invoke(authentication, mockMethodInvocation);
|
|
||||||
assertThat(result).isEqualTo(returnedObject);
|
|
||||||
verify(mockAuthorizationManager).verify(authentication, mockMethodInvocation, returnedObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
|
||||||
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 AuthorizationManagerBeforeMethodInterceptor}.
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
*/
|
|
||||||
public class AuthorizationManagerBeforeMethodInterceptorTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void instantiateWhenMethodMatcherNullThenException() {
|
|
||||||
assertThatIllegalArgumentException()
|
|
||||||
.isThrownBy(
|
|
||||||
() -> new AuthorizationManagerBeforeMethodInterceptor(null, mock(AuthorizationManager.class)))
|
|
||||||
.withMessage("pointcut cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void instantiateWhenAuthorizationManagerNullThenException() {
|
|
||||||
assertThatIllegalArgumentException()
|
|
||||||
.isThrownBy(() -> new AuthorizationManagerBeforeMethodInterceptor(mock(Pointcut.class), null))
|
|
||||||
.withMessage("authorizationManager cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void beforeWhenMockAuthorizationManagerThenVerify() throws Throwable {
|
|
||||||
Supplier<Authentication> authentication = TestAuthentication::authenticatedUser;
|
|
||||||
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
|
|
||||||
AuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(AuthorizationManager.class);
|
|
||||||
AuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(
|
|
||||||
Pointcut.TRUE, mockAuthorizationManager);
|
|
||||||
advice.invoke(authentication, mockMethodInvocation);
|
|
||||||
verify(mockAuthorizationManager).verify(authentication, mockMethodInvocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,167 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
|
||||||
import org.springframework.aop.support.AopUtils;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link AuthorizationMethodPointcuts}
|
|
||||||
*/
|
|
||||||
public class AuthorizationMethodPointcutsTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void forAnnotationsWhenAnnotationThenClassBasedAnnotationPointcut() {
|
|
||||||
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
|
|
||||||
assertThat(AopUtils.canApply(preAuthorize, ClassController.class)).isTrue();
|
|
||||||
assertThat(AopUtils.canApply(preAuthorize, NoController.class)).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void forAnnotationsWhenAnnotationThenMethodBasedAnnotationPointcut() {
|
|
||||||
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
|
|
||||||
assertThat(AopUtils.canApply(preAuthorize, MethodController.class)).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void forAnnotationsWhenAnnotationThenClassInheritancePointcut() {
|
|
||||||
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
|
|
||||||
assertThat(AopUtils.canApply(preAuthorize, InterfacedClassController.class)).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void forAnnotationsWhenAnnotationThenMethodInheritancePointcut() {
|
|
||||||
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
|
|
||||||
assertThat(AopUtils.canApply(preAuthorize, InterfacedMethodController.class)).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void forAnnotationsWhenAnnotationThenAnnotationClassInheritancePointcut() {
|
|
||||||
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
|
|
||||||
assertThat(AopUtils.canApply(preAuthorize, InterfacedAnnotationClassController.class)).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void forAnnotationsWhenAnnotationThenAnnotationMethodInheritancePointcut() {
|
|
||||||
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
|
|
||||||
assertThat(AopUtils.canApply(preAuthorize, InterfacedAnnotationMethodController.class)).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('APP')")
|
|
||||||
public static class ClassController {
|
|
||||||
|
|
||||||
String methodOne(String paramOne) {
|
|
||||||
return "value";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class MethodController {
|
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('APP')")
|
|
||||||
String methodOne(String paramOne) {
|
|
||||||
return "value";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class NoController {
|
|
||||||
|
|
||||||
String methodOne(String paramOne) {
|
|
||||||
return "value";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('APP')")
|
|
||||||
public interface ClassControllerInterface {
|
|
||||||
|
|
||||||
String methodOne(String paramOne);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InterfacedClassController implements ClassControllerInterface {
|
|
||||||
|
|
||||||
public String methodOne(String paramOne) {
|
|
||||||
return "value";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface MethodControllerInterface {
|
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('APP')")
|
|
||||||
String methodOne(String paramOne);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InterfacedMethodController implements MethodControllerInterface {
|
|
||||||
|
|
||||||
public String methodOne(String paramOne) {
|
|
||||||
return "value";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@PreAuthorize("hasAuthority('APP')")
|
|
||||||
@interface MyAnnotation {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@MyAnnotation
|
|
||||||
public interface ClassAnnotationControllerInterface {
|
|
||||||
|
|
||||||
String methodOne(String paramOne);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InterfacedAnnotationClassController implements ClassAnnotationControllerInterface {
|
|
||||||
|
|
||||||
public String methodOne(String paramOne) {
|
|
||||||
return "value";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface MethodAnnotationControllerInterface {
|
|
||||||
|
|
||||||
@MyAnnotation
|
|
||||||
String methodOne(String paramOne);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InterfacedAnnotationMethodController implements MethodAnnotationControllerInterface {
|
|
||||||
|
|
||||||
public String methodOne(String paramOne) {
|
|
||||||
return "value";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
|
||||||
import org.springframework.security.access.intercept.method.MockMethodInvocation;
|
|
||||||
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.BDDMockito.given;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link DelegatingAuthorizationMethodInterceptor}.
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
*/
|
|
||||||
public class DelegatingAuthorizationMethodInterceptorTests {
|
|
||||||
|
|
||||||
@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");
|
|
||||||
AuthorizationMethodInterceptor interceptor = mock(AuthorizationMethodInterceptor.class);
|
|
||||||
given(interceptor.getPointcut()).willReturn(Pointcut.TRUE);
|
|
||||||
given(interceptor.invoke(any(), any(AuthorizationMethodInvocation.class))).willReturn("abc");
|
|
||||||
DelegatingAuthorizationMethodInterceptor chain = new DelegatingAuthorizationMethodInterceptor(
|
|
||||||
Arrays.asList(interceptor));
|
|
||||||
Object result = chain.invoke(mockMethodInvocation);
|
|
||||||
assertThat(result).isEqualTo("abc");
|
|
||||||
verify(interceptor).invoke(any(), any(AuthorizationMethodInvocation.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void invokeWhenNotAuthenticatedThenAuthenticationCredentialsNotFoundException() throws Throwable {
|
|
||||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"doSomethingString");
|
|
||||||
AuthorizationMethodInterceptor first = new AuthorizationMethodInterceptor() {
|
|
||||||
@Override
|
|
||||||
public Pointcut getPointcut() {
|
|
||||||
return Pointcut.TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) {
|
|
||||||
return authentication.get();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
AuthorizationMethodInterceptor second = mock(AuthorizationMethodInterceptor.class);
|
|
||||||
given(second.getPointcut()).willReturn(Pointcut.TRUE);
|
|
||||||
DelegatingAuthorizationMethodInterceptor interceptor = new DelegatingAuthorizationMethodInterceptor(
|
|
||||||
Arrays.asList(first, second));
|
|
||||||
assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
|
|
||||||
.isThrownBy(() -> interceptor.invoke(mockMethodInvocation))
|
|
||||||
.withMessage("An Authentication object was not found in the SecurityContext");
|
|
||||||
verify(second, times(0)).invoke(any(), any());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TestClass {
|
|
||||||
|
|
||||||
public String doSomethingString() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,162 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
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.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 mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"doSomething");
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
|
|
||||||
assertThat(decision).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkPermitAllRolesAllowedAdminWhenRoleUserThenGrantedDecision() throws Exception {
|
|
||||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"permitAllRolesAllowedAdmin");
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
|
|
||||||
assertThat(decision).isNotNull();
|
|
||||||
assertThat(decision.isGranted()).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkDenyAllRolesAllowedAdminWhenRoleAdminThenDeniedDecision() throws Exception {
|
|
||||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"denyAllRolesAllowedAdmin");
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, methodInvocation);
|
|
||||||
assertThat(decision).isNotNull();
|
|
||||||
assertThat(decision.isGranted()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkRolesAllowedUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception {
|
|
||||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"rolesAllowedUserOrAdmin");
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
|
|
||||||
assertThat(decision).isNotNull();
|
|
||||||
assertThat(decision.isGranted()).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkRolesAllowedUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception {
|
|
||||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"rolesAllowedUserOrAdmin");
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, methodInvocation);
|
|
||||||
assertThat(decision).isNotNull();
|
|
||||||
assertThat(decision.isGranted()).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkRolesAllowedUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception {
|
|
||||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
|
|
||||||
"ROLE_ANONYMOUS");
|
|
||||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"rolesAllowedUserOrAdmin");
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(authentication, methodInvocation);
|
|
||||||
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() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,136 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.util.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.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[] {});
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation, null);
|
|
||||||
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" });
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation, null);
|
|
||||||
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" });
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation, null);
|
|
||||||
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 });
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation, list);
|
|
||||||
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 });
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation, list);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,112 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
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.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 PostFilterAuthorizationMethodInterceptor}.
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
*/
|
|
||||||
public class PostFilterAuthorizationMethodInterceptorTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {
|
|
||||||
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
|
||||||
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
|
|
||||||
advice.setExpressionHandler(expressionHandler);
|
|
||||||
assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setExpressionHandlerWhenNullThenException() {
|
|
||||||
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
|
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null))
|
|
||||||
.withMessage("expressionHandler cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void methodMatcherWhenMethodHasNotPostFilterAnnotationThenNotMatches() throws Exception {
|
|
||||||
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
|
|
||||||
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
|
|
||||||
assertThat(methodMatcher.matches(NoPostFilterClass.class.getMethod("doSomething"), NoPostFilterClass.class))
|
|
||||||
.isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void methodMatcherWhenMethodHasPostFilterAnnotationThenMatches() throws Exception {
|
|
||||||
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
|
|
||||||
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
|
|
||||||
assertThat(
|
|
||||||
methodMatcher.matches(TestClass.class.getMethod("doSomethingArray", String[].class), TestClass.class))
|
|
||||||
.isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void afterWhenArrayNotNullThenFilteredArray() throws Throwable {
|
|
||||||
String[] array = { "john", "bob" };
|
|
||||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"doSomethingArrayClassLevel", new Class[] { String[].class }, new Object[] { array }) {
|
|
||||||
@Override
|
|
||||||
public Object proceed() {
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
Object result = advice.invoke(TestAuthentication::authenticatedUser, methodInvocation);
|
|
||||||
assertThat(result).asInstanceOf(InstanceOfAssertFactories.array(String[].class)).containsOnly("john");
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostFilter("filterObject == 'john'")
|
|
||||||
public static class TestClass {
|
|
||||||
|
|
||||||
@PostFilter("filterObject == 'john'")
|
|
||||||
public String[] doSomethingArray(String[] array) {
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] doSomethingArrayClassLevel(String[] array) {
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class NoPostFilterClass {
|
|
||||||
|
|
||||||
public void doSomething() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
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.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[] {});
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
|
|
||||||
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" });
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
|
|
||||||
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" });
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
|
|
||||||
assertThat(decision).isNotNull();
|
|
||||||
assertThat(decision.isGranted()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TestClass {
|
|
||||||
|
|
||||||
public void doSomething() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreAuthorize("#s == 'grant'")
|
|
||||||
public String doSomethingString(String s) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,203 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
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.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 PreFilterAuthorizationMethodInterceptor}.
|
|
||||||
*
|
|
||||||
* @author Evgeniy Cheban
|
|
||||||
*/
|
|
||||||
public class PreFilterAuthorizationMethodInterceptorTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {
|
|
||||||
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
|
||||||
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
|
|
||||||
advice.setExpressionHandler(expressionHandler);
|
|
||||||
assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setExpressionHandlerWhenNullThenException() {
|
|
||||||
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
|
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null))
|
|
||||||
.withMessage("expressionHandler cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void methodMatcherWhenMethodHasNotPreFilterAnnotationThenNotMatches() throws Exception {
|
|
||||||
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
|
|
||||||
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
|
|
||||||
assertThat(methodMatcher.matches(NoPreFilterClass.class.getMethod("doSomething"), NoPreFilterClass.class))
|
|
||||||
.isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void methodMatcherWhenMethodHasPreFilterAnnotationThenMatches() throws Exception {
|
|
||||||
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
|
|
||||||
MethodMatcher methodMatcher = advice.getPointcut().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<>() });
|
|
||||||
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
assertThatIllegalArgumentException()
|
|
||||||
.isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation)).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 });
|
|
||||||
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
assertThatIllegalArgumentException()
|
|
||||||
.isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation))
|
|
||||||
.withMessage("Filter target was null, or no argument with name 'list' found in method.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void findFilterTargetWhenNameProvidedAndMatchAndNotNullThenFiltersList() throws Throwable {
|
|
||||||
List<String> list = new ArrayList<>();
|
|
||||||
list.add("john");
|
|
||||||
list.add("bob");
|
|
||||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"doSomethingListFilterTargetMatch", new Class[] { List.class }, new Object[] { list });
|
|
||||||
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
advice.invoke(TestAuthentication::authenticatedUser, methodInvocation);
|
|
||||||
assertThat(list).hasSize(1);
|
|
||||||
assertThat(list.get(0)).isEqualTo("john");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void findFilterTargetWhenNameNotProvidedAndSingleArgListNullThenException() throws Exception {
|
|
||||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { null });
|
|
||||||
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
assertThatIllegalArgumentException()
|
|
||||||
.isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation))
|
|
||||||
.withMessage("Filter target was null. Make sure you passing the correct value in the method argument.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void findFilterTargetWhenNameNotProvidedAndSingleArgListThenFiltersList() throws Throwable {
|
|
||||||
List<String> list = new ArrayList<>();
|
|
||||||
list.add("john");
|
|
||||||
list.add("bob");
|
|
||||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { list });
|
|
||||||
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
advice.invoke(TestAuthentication::authenticatedUser, methodInvocation);
|
|
||||||
assertThat(list).hasSize(1);
|
|
||||||
assertThat(list.get(0)).isEqualTo("john");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void findFilterTargetWhenNameNotProvidedAndSingleArgArrayThenException() throws Exception {
|
|
||||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"doSomethingArrayFilterTargetNotProvided", new Class[] { String[].class },
|
|
||||||
new Object[] { new String[] {} });
|
|
||||||
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
assertThatIllegalStateException()
|
|
||||||
.isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation)).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<>() });
|
|
||||||
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
assertThatIllegalStateException()
|
|
||||||
.isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation))
|
|
||||||
.withMessage("Unable to determine the method argument for filtering. Specify the filter target.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreFilter("filterObject == 'john'")
|
|
||||||
public static class TestClass {
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> doSomethingTwoArgsFilterTargetNotProvided(String s, List<String> list) {
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class NoPreFilterClass {
|
|
||||||
|
|
||||||
public void doSomething() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import org.springframework.security.access.annotation.Secured;
|
|
||||||
import org.springframework.security.access.intercept.method.MockMethodInvocation;
|
|
||||||
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 mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"doSomething");
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
|
|
||||||
assertThat(decision).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkSecuredUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception {
|
|
||||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"securedUserOrAdmin");
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList());
|
|
||||||
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
|
|
||||||
assertThat(decision).isNotNull();
|
|
||||||
assertThat(decision.isGranted()).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkSecuredUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception {
|
|
||||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"securedUserOrAdmin");
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(
|
|
||||||
TestAuthentication::authenticatedAdmin, mockMethodInvocation, Collections.emptyList());
|
|
||||||
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, methodInvocation);
|
|
||||||
assertThat(decision).isNotNull();
|
|
||||||
assertThat(decision.isGranted()).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkSecuredUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception {
|
|
||||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
|
|
||||||
"ROLE_ANONYMOUS");
|
|
||||||
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
|
|
||||||
"securedUserOrAdmin");
|
|
||||||
AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(authentication,
|
|
||||||
mockMethodInvocation, Collections.emptyList());
|
|
||||||
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
|
|
||||||
AuthorizationDecision decision = manager.check(authentication, methodInvocation);
|
|
||||||
assertThat(decision).isNotNull();
|
|
||||||
assertThat(decision.isGranted()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TestClass {
|
|
||||||
|
|
||||||
public void doSomething() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Secured({ "ROLE_USER", "ROLE_ADMIN" })
|
|
||||||
public void securedUserOrAdmin() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -6,185 +6,6 @@ It provides support for JSR-250 annotation security as well as the framework's o
|
||||||
From 3.0 you can also make use of new <<el-access,expression-based annotations>>.
|
From 3.0 you can also make use of new <<el-access,expression-based annotations>>.
|
||||||
You can apply security to a single bean, using the `intercept-methods` element to decorate the bean declaration, or you can secure multiple beans across the entire service layer using the AspectJ style pointcuts.
|
You can apply security to a single bean, using the `intercept-methods` element to decorate the bean declaration, or you can secure multiple beans across the entire service layer using the AspectJ style pointcuts.
|
||||||
|
|
||||||
=== EnableMethodSecurity
|
|
||||||
|
|
||||||
In 5.5, we can enable annotation-based security using the `@EnableMethodSecurity` annotation on any `@Configuration` instance.
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
For earlier versions, please read about similar support with <<jc-enable-global-method-security, @EnableGlobalMethodSecurity>>.
|
|
||||||
|
|
||||||
For example, the following would enable Spring Security's `@PreAuthorize` annotation:
|
|
||||||
|
|
||||||
[source,java]
|
|
||||||
----
|
|
||||||
@EnableMethodSecurity
|
|
||||||
public class MethodSecurityConfig {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
Adding an annotation to a method (on a class or interface) would then limit the access to that method accordingly.
|
|
||||||
Spring Security's native annotatino support defines a set of attributes for the method.
|
|
||||||
These will be passed to the `DefaultAuthorizationMethodInterceptorChain` for it to make the actual decision:
|
|
||||||
|
|
||||||
[source,java]
|
|
||||||
----
|
|
||||||
public interface BankService {
|
|
||||||
|
|
||||||
@PreAuthorize("hasRole('USER')")
|
|
||||||
Account readAccount(Long id);
|
|
||||||
|
|
||||||
@PreAuthorize("hasRole('USER')")
|
|
||||||
Account[] findAccounts();
|
|
||||||
|
|
||||||
@PreAuthorize("hasRole('TELLER')")
|
|
||||||
Account post(Account account, double amount);
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
You can enable support for Spring Security's `@Secured` annotation using:
|
|
||||||
|
|
||||||
[source,java]
|
|
||||||
----
|
|
||||||
@EnableMethodSecurity(secureEnabled = true)
|
|
||||||
public class MethodSecurityConfig {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
or JSR-250 using:
|
|
||||||
|
|
||||||
[source,java]
|
|
||||||
----
|
|
||||||
@EnableMethodSecurity(jsr250Enabled = true)
|
|
||||||
public class MethodSecurityConfig {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
==== Customizing Authorization
|
|
||||||
|
|
||||||
Spring Security's `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFilter` ship with rich expression-based support.
|
|
||||||
|
|
||||||
If you need to customize the way that expressions are handled, you can expose a custom `MethodSecurityExpressionHandler`, like so:
|
|
||||||
|
|
||||||
[source,java]
|
|
||||||
----
|
|
||||||
@Bean
|
|
||||||
MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
|
|
||||||
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
|
|
||||||
handler.setTrustResolver(myCustomTrustResolver);
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
Also, for role-based authorization, Spring Security adds a default `ROLE_` prefix, which is uses when evaluating expressions like `hasRole`.
|
|
||||||
|
|
||||||
You can configure the authorization rules to use a different prefix by exposing a `GrantedAuthorityDefaults` bean, like so:
|
|
||||||
|
|
||||||
[source,java]
|
|
||||||
----
|
|
||||||
@Bean
|
|
||||||
GrantedAuthorityDefaults grantedAuthorityDefaults() {
|
|
||||||
return new GrantedAuthorityDefaults("MYPREFIX_");
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
==== Custom Authorization Managers
|
|
||||||
|
|
||||||
Method authorization is a combination of before- and after-method authorization.
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
Before-method authorization is performed before the method is invoked.
|
|
||||||
If that authorization denies access, the method is not invoked and an `AccessDeniedException` is thrown
|
|
||||||
After-method authorization is performed after the method is invoked, but before the method returns to the caller.
|
|
||||||
If that authorization denies access, the value is not returned and an `AccessDeniedException` is thrown
|
|
||||||
|
|
||||||
To recreate what Spring Security does by default, you would publish the following bean:
|
|
||||||
|
|
||||||
[source,java]
|
|
||||||
----
|
|
||||||
@Bean
|
|
||||||
public List<AuthorizationMethodInterceptor> methodSecurity() {
|
|
||||||
return new DelegatingAuthorizationMethodInterceptor(
|
|
||||||
new PreFilterAuthorizationMethodInterceptor(), // before-method
|
|
||||||
AuthorizationMethodInterceptors.preAuthorize(), // before-method
|
|
||||||
new PostFilterAuthorizationMethodInterceptor(), // after-method
|
|
||||||
AuthorizationMethodInterceptors.postAuthorize() // after-method
|
|
||||||
);
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
Keep in mind that publishing a list of `AuthorizationMethodInterceptor`s will completely replace any Spring Security defaults.
|
|
||||||
|
|
||||||
Interceptors are invoked in the order that they are declared.
|
|
||||||
|
|
||||||
You may want to only support `@PreAuthorize` in your application, in which case you can do the following:
|
|
||||||
|
|
||||||
[source,java]
|
|
||||||
----
|
|
||||||
@Bean
|
|
||||||
public AuthorizationMethodInterceptor methodSecurity() {
|
|
||||||
return AuthorizationMethodInterceptors.preAuthorize();
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
Or, you may have a custom before-method `AuthorizationManager` that you want to add to the list.
|
|
||||||
|
|
||||||
In this case, you will need to tell Spring Security both the `AuthorizationManager` and to which methods and classes your authorization manager applies.
|
|
||||||
|
|
||||||
Spring Security integrates with Spring AOP to achieve this.
|
|
||||||
Thus, you can configure Spring Security to support `@PreAuthorize`, `@PostAuthorize`, and your own `AuthorizationManager` like so:
|
|
||||||
|
|
||||||
[source,java]
|
|
||||||
----
|
|
||||||
@Bean
|
|
||||||
public AuthorizationMethodInterceptor methodSecurity() {
|
|
||||||
JdkRegexpMethodPointcut pattern = new JdkRegexpMethodPointcut();
|
|
||||||
pattern.setPattern("org.mycompany.myapp.service.*");
|
|
||||||
AuthorizationManager<MethodInvocation> rule = AuthorityAuthorizationManager.isAuthenticated();
|
|
||||||
return new DelegatingAuthorizationMethodInterceptor(
|
|
||||||
AuthorizationMethodInterceptors.preAuthorize(),
|
|
||||||
new AuthorizationManagerBeforeMethodInterceptor(pattern, rule),
|
|
||||||
AuthorizationMethodInterceptors.postAuthorize()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
The same can be done for after-method authorization and `AfterMethodAuthorizationManager`.
|
|
||||||
After-method authorization is generally concerned with analysing the return value to verify access.
|
|
||||||
|
|
||||||
For example, you might have a method that confirms that the account requested actually belongs to the logged-in user like so:
|
|
||||||
|
|
||||||
[source,java]
|
|
||||||
----
|
|
||||||
public interface BankService {
|
|
||||||
|
|
||||||
@PreAuthorize("hasRole('USER')")
|
|
||||||
@PostAuthorize("returnObject.owner == authentication.name")
|
|
||||||
Account readAccount(Long id);
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
You can supply your own `AuthorizationMethodInterceptor` to customize how access to the return value is evaluated.
|
|
||||||
|
|
||||||
For example, instead of embedding a great deal of logic into the `@PostAuthorize` SpEL expression, you may want to wire your own `@Bean`.
|
|
||||||
In that case, you can configure it like so:
|
|
||||||
|
|
||||||
[source,java]
|
|
||||||
----
|
|
||||||
@Bean
|
|
||||||
public AuthorizationMethodInterceptor methodSecurity
|
|
||||||
(AfterMethodAuthorizationManager<MethodInvocation> rules) {
|
|
||||||
AnnotationMethodMatcher pattern = new AnnotationMethodMatcher(MySecurityAnnotation.class);
|
|
||||||
return new DelegatingAuthorizationMethodInterceptor(
|
|
||||||
AuthorizationMethodInterceptors.preAuthorize(),
|
|
||||||
new AuthorizationManagerAfterMethodInterceptor(pattern, rules));
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
[[jc-enable-global-method-security]]
|
|
||||||
=== EnableGlobalMethodSecurity
|
=== EnableGlobalMethodSecurity
|
||||||
|
|
||||||
We can enable annotation-based security using the `@EnableGlobalMethodSecurity` annotation on any `@Configuration` instance.
|
We can enable annotation-based security using the `@EnableGlobalMethodSecurity` annotation on any `@Configuration` instance.
|
||||||
|
|
Loading…
Reference in New Issue