mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-30 22:28:46 +00:00 
			
		
		
		
	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.jetbrains.kotlin:kotlin-reflect' | ||||
| 	optional 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' | ||||
| 	optional 'javax.annotation:jsr250-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; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import javax.annotation.security.DenyAll; | ||||
| import javax.annotation.security.PermitAll; | ||||
| 
 | ||||
| import org.springframework.security.access.annotation.Secured; | ||||
| import org.springframework.security.access.prepost.PostAuthorize; | ||||
| import org.springframework.security.access.prepost.PostFilter; | ||||
| import org.springframework.security.access.prepost.PreAuthorize; | ||||
| import org.springframework.security.access.prepost.PreFilter; | ||||
| import org.springframework.security.core.Authentication; | ||||
| import org.springframework.security.core.parameters.P; | ||||
| 
 | ||||
| @ -73,11 +69,4 @@ public interface MethodSecurityService { | ||||
| 	@PostAuthorize("#o?.contains('grant')") | ||||
| 	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; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import org.springframework.security.core.Authentication; | ||||
| import org.springframework.security.core.context.SecurityContextHolder; | ||||
| 
 | ||||
| @ -88,9 +86,4 @@ public class MethodSecurityServiceImpl implements MethodSecurityService { | ||||
| 		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"); | ||||
|  * 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 | ||||
| 	 */ | ||||
| 	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.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); | ||||
| 	} | ||||
| 
 | ||||
| 	private static String[] toNamedRolesArray(String rolePrefix, String[] roles) { | ||||
| 	private static String[] toNamedRolesArray(String... roles) { | ||||
| 		String[] result = new String[roles.length]; | ||||
| 		for (int i = 0; i < roles.length; i++) { | ||||
| 			result[i] = rolePrefix + roles[i]; | ||||
| 			result[i] = ROLE_PREFIX + roles[i]; | ||||
| 		} | ||||
| 		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"); | ||||
|  * 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"); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void hasAnyRoleWhenCustomRolePrefixNullThenException() { | ||||
| 		assertThatIllegalArgumentException() | ||||
| 				.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole(null, new String[] { "ADMIN", "USER" })) | ||||
| 				.withMessage("rolePrefix cannot be null"); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void hasAnyAuthorityWhenNullThenException() { | ||||
| 		assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority(null)) | ||||
| @ -154,17 +147,6 @@ public class AuthorityAuthorizationManagerTests { | ||||
| 		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 | ||||
| 	public void hasAnyAuthorityWhenUserHasAnyAuthorityThenGrantedDecision() { | ||||
| 		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>>. | ||||
| 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 | ||||
| 
 | ||||
| We can enable annotation-based security using the `@EnableGlobalMethodSecurity` annotation on any `@Configuration` instance. | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user