1
0
mirror of synced 2026-08-05 01:36:56 +00:00

Consider AuthorizationManager for Method Security

Closes gh-9289
This commit is contained in:
Evgeniy Cheban
2021-01-18 12:26:52 +03:00
committed by Josh Cummings
parent a8a7ab4ffa
commit 20778f727b
35 changed files with 3682 additions and 5 deletions
@@ -0,0 +1,87 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.security.access.annotation.Secured;
/**
* Enables Spring Security Method Security.
* @author Evgeniy Cheban
* @since 5.5
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MethodSecuritySelector.class)
@Configuration
public @interface EnableMethodSecurity {
/**
* Determines if Spring Security's {@link Secured} annotation should be enabled.
* Default is false.
* @return true if {@link Secured} annotation should be enabled false otherwise
*/
boolean securedEnabled() default false;
/**
* Determines if JSR-250 annotations should be enabled. Default is false.
* @return true if JSR-250 should be enabled false otherwise
*/
boolean jsr250Enabled() default false;
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to
* standard Java interface-based proxies. The default is {@code false}. <strong>
* Applicable only if {@link #mode()} is set to {@link AdviceMode#PROXY}</strong>.
* <p>
* Note that setting this attribute to {@code true} will affect <em>all</em>
* Spring-managed beans requiring proxying, not just those marked with
* {@code @Cacheable}. For example, other beans marked with Spring's
* {@code @Transactional} annotation will be upgraded to subclass proxying at the same
* time. This approach has no negative impact in practice unless one is explicitly
* expecting one type of proxy vs another, e.g. in tests.
* @return true if subclass-based (CGLIB) proxies are to be created
*/
boolean proxyTargetClass() default false;
/**
* Indicate how security advice should be applied. The default is
* {@link AdviceMode#PROXY}.
* @see AdviceMode
* @return the {@link AdviceMode} to use
*/
AdviceMode mode() default AdviceMode.PROXY;
/**
* Indicate the ordering of the execution of the security advisor when multiple
* advices are applied at a specific joinpoint. The default is
* {@link Ordered#LOWEST_PRECEDENCE}.
* @return the order the security advisor should be applied
*/
int order() default Ordered.LOWEST_PRECEDENCE;
}
@@ -0,0 +1,252 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.Pointcuts;
import org.springframework.aop.support.StaticMethodMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.context.annotation.Role;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.access.annotation.Jsr250AuthorizationManager;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.annotation.SecuredAuthorizationManager;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.aopalliance.AuthorizationMethodInterceptor;
import org.springframework.security.access.method.AuthorizationManagerMethodAfterAdvice;
import org.springframework.security.access.method.AuthorizationManagerMethodBeforeAdvice;
import org.springframework.security.access.method.AuthorizationMethodAfterAdvice;
import org.springframework.security.access.method.AuthorizationMethodBeforeAdvice;
import org.springframework.security.access.method.DelegatingAuthorizationMethodAfterAdvice;
import org.springframework.security.access.method.DelegatingAuthorizationMethodBeforeAdvice;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodAfterAdvice;
import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodBeforeAdvice;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
/**
* Base {@link Configuration} for enabling Spring Security Method Security.
*
* @author Evgeniy Cheban
* @see EnableMethodSecurity
* @since 5.5
*/
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
final class MethodSecurityConfiguration implements ImportAware {
private MethodSecurityExpressionHandler methodSecurityExpressionHandler;
private GrantedAuthorityDefaults grantedAuthorityDefaults;
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> authorizationMethodBeforeAdvice;
private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> authorizationMethodAfterAdvice;
private AnnotationAttributes enableMethodSecurity;
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
DefaultPointcutAdvisor methodSecurityAdvisor(AuthorizationMethodInterceptor interceptor) {
Pointcut pointcut = Pointcuts.union(getAuthorizationMethodBeforeAdvice(), getAuthorizationMethodAfterAdvice());
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, interceptor);
advisor.setOrder(order());
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
AuthorizationMethodInterceptor authorizationMethodInterceptor() {
return new AuthorizationMethodInterceptor(getAuthorizationMethodBeforeAdvice(),
getAuthorizationMethodAfterAdvice());
}
private MethodSecurityExpressionHandler getMethodSecurityExpressionHandler() {
if (this.methodSecurityExpressionHandler == null) {
this.methodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
}
return this.methodSecurityExpressionHandler;
}
@Autowired(required = false)
void setMethodSecurityExpressionHandler(MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
this.methodSecurityExpressionHandler = methodSecurityExpressionHandler;
}
@Autowired(required = false)
void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) {
this.grantedAuthorityDefaults = grantedAuthorityDefaults;
}
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> getAuthorizationMethodBeforeAdvice() {
if (this.authorizationMethodBeforeAdvice == null) {
this.authorizationMethodBeforeAdvice = createDefaultAuthorizationMethodBeforeAdvice();
}
return this.authorizationMethodBeforeAdvice;
}
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> createDefaultAuthorizationMethodBeforeAdvice() {
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> beforeAdvices = new ArrayList<>();
beforeAdvices.add(getPreFilterAuthorizationMethodBeforeAdvice());
beforeAdvices.add(getPreAuthorizeAuthorizationMethodBeforeAdvice());
if (securedEnabled()) {
beforeAdvices.add(getSecuredAuthorizationMethodBeforeAdvice());
}
if (jsr250Enabled()) {
beforeAdvices.add(getJsr250AuthorizationMethodBeforeAdvice());
}
return new DelegatingAuthorizationMethodBeforeAdvice(beforeAdvices);
}
private PreFilterAuthorizationMethodBeforeAdvice getPreFilterAuthorizationMethodBeforeAdvice() {
PreFilterAuthorizationMethodBeforeAdvice preFilterBeforeAdvice = new PreFilterAuthorizationMethodBeforeAdvice();
preFilterBeforeAdvice.setExpressionHandler(getMethodSecurityExpressionHandler());
return preFilterBeforeAdvice;
}
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> getPreAuthorizeAuthorizationMethodBeforeAdvice() {
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(PreAuthorize.class);
PreAuthorizeAuthorizationManager authorizationManager = new PreAuthorizeAuthorizationManager();
authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler());
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager);
}
private AuthorizationManagerMethodBeforeAdvice<MethodAuthorizationContext> getSecuredAuthorizationMethodBeforeAdvice() {
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(Secured.class);
SecuredAuthorizationManager authorizationManager = new SecuredAuthorizationManager();
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager);
}
private AuthorizationManagerMethodBeforeAdvice<MethodAuthorizationContext> getJsr250AuthorizationMethodBeforeAdvice() {
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(DenyAll.class, PermitAll.class,
RolesAllowed.class);
Jsr250AuthorizationManager authorizationManager = new Jsr250AuthorizationManager();
if (this.grantedAuthorityDefaults != null) {
authorizationManager.setRolePrefix(this.grantedAuthorityDefaults.getRolePrefix());
}
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager);
}
@Autowired(required = false)
void setAuthorizationMethodBeforeAdvice(
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> authorizationMethodBeforeAdvice) {
this.authorizationMethodBeforeAdvice = authorizationMethodBeforeAdvice;
}
private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> getAuthorizationMethodAfterAdvice() {
if (this.authorizationMethodAfterAdvice == null) {
this.authorizationMethodAfterAdvice = createDefaultAuthorizationMethodAfterAdvice();
}
return this.authorizationMethodAfterAdvice;
}
private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> createDefaultAuthorizationMethodAfterAdvice() {
List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> afterAdvices = new ArrayList<>();
afterAdvices.add(getPostFilterAuthorizationMethodAfterAdvice());
afterAdvices.add(getPostAuthorizeAuthorizationMethodAfterAdvice());
return new DelegatingAuthorizationMethodAfterAdvice(afterAdvices);
}
private PostFilterAuthorizationMethodAfterAdvice getPostFilterAuthorizationMethodAfterAdvice() {
PostFilterAuthorizationMethodAfterAdvice postFilterAfterAdvice = new PostFilterAuthorizationMethodAfterAdvice();
postFilterAfterAdvice.setExpressionHandler(getMethodSecurityExpressionHandler());
return postFilterAfterAdvice;
}
private AuthorizationManagerMethodAfterAdvice<MethodAuthorizationContext> getPostAuthorizeAuthorizationMethodAfterAdvice() {
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(PostAuthorize.class);
PostAuthorizeAuthorizationManager authorizationManager = new PostAuthorizeAuthorizationManager();
authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler());
return new AuthorizationManagerMethodAfterAdvice<>(methodMatcher, authorizationManager);
}
@Autowired(required = false)
void setAuthorizationMethodAfterAdvice(
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> authorizationMethodAfterAdvice) {
this.authorizationMethodAfterAdvice = authorizationMethodAfterAdvice;
}
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> attributes = importMetadata.getAnnotationAttributes(EnableMethodSecurity.class.getName());
this.enableMethodSecurity = AnnotationAttributes.fromMap(attributes);
}
private boolean securedEnabled() {
return this.enableMethodSecurity.getBoolean("securedEnabled");
}
private boolean jsr250Enabled() {
return this.enableMethodSecurity.getBoolean("jsr250Enabled");
}
private int order() {
return this.enableMethodSecurity.getNumber("order");
}
private static final class SecurityAnnotationsStaticMethodMatcher extends StaticMethodMatcher {
private final Set<Class<? extends Annotation>> annotationClasses;
@SafeVarargs
private SecurityAnnotationsStaticMethodMatcher(Class<? extends Annotation>... annotationClasses) {
this.annotationClasses = new HashSet<>(Arrays.asList(annotationClasses));
}
@Override
public boolean matches(Method method, Class<?> targetClass) {
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
return hasAnnotations(specificMethod) || hasAnnotations(specificMethod.getDeclaringClass());
}
private boolean hasAnnotations(AnnotatedElement annotatedElement) {
Set<Annotation> annotations = AnnotatedElementUtils.findAllMergedAnnotations(annotatedElement,
this.annotationClasses);
return !annotations.isEmpty();
}
}
}
@@ -0,0 +1,50 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.context.annotation.AutoProxyRegistrar;
/**
* Dynamically determines which imports to include using the {@link EnableMethodSecurity}
* annotation.
*
* @author Evgeniy Cheban
* @since 5.5
*/
final class MethodSecuritySelector extends AdviceModeImportSelector<EnableMethodSecurity> {
@Override
protected String[] selectImports(AdviceMode adviceMode) {
if (adviceMode == AdviceMode.PROXY) {
return getProxyImports();
}
throw new IllegalStateException("AdviceMode '" + adviceMode + "' is not supported");
}
private String[] getProxyImports() {
List<String> result = new ArrayList<>();
result.add(AutoProxyRegistrar.class.getName());
result.add(MethodSecurityConfiguration.class.getName());
return result.toArray(new String[0]);
}
}
@@ -0,0 +1,360 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.support.JdkRegexpMethodPointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.annotation.BusinessService;
import org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.method.AuthorizationManagerMethodBeforeAdvice;
import org.springframework.security.access.method.AuthorizationMethodAfterAdvice;
import org.springframework.security.access.method.AuthorizationMethodBeforeAdvice;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.Authentication;
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
import org.springframework.security.test.context.support.WithAnonymousUser;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for {@link MethodSecurityConfiguration}.
*
* @author Evgeniy Cheban
*/
@RunWith(SpringRunner.class)
@SecurityTestExecutionListeners
public class MethodSecurityConfigurationTests {
@Rule
public final SpringTestRule spring = new SpringTestRule();
@Autowired(required = false)
MethodSecurityService methodSecurityService;
@Autowired(required = false)
BusinessService businessService;
@WithMockUser(roles = "ADMIN")
@Test
public void preAuthorizeWhenRoleAdminThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorize)
.withMessage("Access Denied");
}
@WithAnonymousUser
@Test
public void preAuthorizePermitAllWhenRoleAnonymousThenPasses() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
String result = this.methodSecurityService.preAuthorizePermitAll();
assertThat(result).isNull();
}
@WithAnonymousUser
@Test
public void preAuthorizeNotAnonymousWhenRoleAnonymousThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(this.methodSecurityService::preAuthorizeNotAnonymous).withMessage("Access Denied");
}
@WithMockUser
@Test
public void preAuthorizeNotAnonymousWhenRoleUserThenPasses() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
this.methodSecurityService.preAuthorizeNotAnonymous();
}
@WithMockUser
@Test
public void securedWhenRoleUserThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::secured)
.withMessage("Access Denied");
}
@WithMockUser(roles = "ADMIN")
@Test
public void securedWhenRoleAdminThenPasses() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
String result = this.methodSecurityService.secured();
assertThat(result).isNull();
}
@WithMockUser(roles = "ADMIN")
@Test
public void securedUserWhenRoleAdminThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)
.withMessage("Access Denied");
}
@WithMockUser
@Test
public void securedUserWhenRoleUserThenPasses() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
String result = this.methodSecurityService.securedUser();
assertThat(result).isNull();
}
@WithMockUser
@Test
public void preAuthorizeAdminWhenRoleUserThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorizeAdmin)
.withMessage("Access Denied");
}
@WithMockUser(roles = "ADMIN")
@Test
public void preAuthorizeAdminWhenRoleAdminThenPasses() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
this.methodSecurityService.preAuthorizeAdmin();
}
@WithMockUser
@Test
public void postHasPermissionWhenParameterIsNotGrantThenAccessDeniedException() {
this.spring.register(CustomPermissionEvaluatorConfig.class, MethodSecurityServiceConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.postHasPermission("deny")).withMessage("Access Denied");
}
@WithMockUser
@Test
public void postHasPermissionWhenParameterIsGrantThenPasses() {
this.spring.register(CustomPermissionEvaluatorConfig.class, MethodSecurityServiceConfig.class).autowire();
String result = this.methodSecurityService.postHasPermission("grant");
assertThat(result).isNull();
}
@WithMockUser
@Test
public void postAnnotationWhenParameterIsNotGrantThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.postAnnotation("deny")).withMessage("Access Denied");
}
@WithMockUser
@Test
public void postAnnotationWhenParameterIsGrantThenPasses() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
String result = this.methodSecurityService.postAnnotation("grant");
assertThat(result).isNull();
}
@WithMockUser("bob")
@Test
public void methodReturningAListWhenPrePostFiltersConfiguredThenFiltersList() {
this.spring.register(BusinessServiceConfig.class).autowire();
List<String> names = new ArrayList<>();
names.add("bob");
names.add("joe");
names.add("sam");
List<?> result = this.businessService.methodReturningAList(names);
assertThat(result).hasSize(1);
assertThat(result.get(0)).isEqualTo("bob");
}
@WithMockUser("bob")
@Test
public void methodReturningAnArrayWhenPostFilterConfiguredThenFiltersArray() {
this.spring.register(BusinessServiceConfig.class).autowire();
List<String> names = new ArrayList<>();
names.add("bob");
names.add("joe");
names.add("sam");
Object[] result = this.businessService.methodReturningAnArray(names.toArray());
assertThat(result).hasSize(1);
assertThat(result[0]).isEqualTo("bob");
}
@WithMockUser("bob")
@Test
public void securedUserWhenCustomBeforeAdviceConfiguredAndNameBobThenPasses() {
this.spring.register(CustomAuthorizationManagerBeforeAdviceConfig.class, MethodSecurityServiceConfig.class)
.autowire();
String result = this.methodSecurityService.securedUser();
assertThat(result).isNull();
}
@WithMockUser("joe")
@Test
public void securedUserWhenCustomBeforeAdviceConfiguredAndNameNotBobThenAccessDeniedException() {
this.spring.register(CustomAuthorizationManagerBeforeAdviceConfig.class, MethodSecurityServiceConfig.class)
.autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)
.withMessage("Access Denied");
}
@WithMockUser("bob")
@Test
public void securedUserWhenCustomAfterAdviceConfiguredAndNameBobThenGranted() {
this.spring.register(CustomAuthorizationManagerAfterAdviceConfig.class, MethodSecurityServiceConfig.class)
.autowire();
String result = this.methodSecurityService.securedUser();
assertThat(result).isEqualTo("granted");
}
@WithMockUser("joe")
@Test
public void securedUserWhenCustomAfterAdviceConfiguredAndNameNotBobThenAccessDeniedException() {
this.spring.register(CustomAuthorizationManagerAfterAdviceConfig.class, MethodSecurityServiceConfig.class)
.autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)
.withMessage("Access Denied for User 'joe'");
}
@WithMockUser(roles = "ADMIN")
@Test
public void jsr250WhenRoleAdminThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::jsr250)
.withMessage("Access Denied");
}
@WithAnonymousUser
@Test
public void jsr250PermitAllWhenRoleAnonymousThenPasses() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
String result = this.methodSecurityService.jsr250PermitAll();
assertThat(result).isNull();
}
@WithMockUser(roles = "ADMIN")
@Test
public void rolesAllowedUserWhenRoleAdminThenAccessDeniedException() {
this.spring.register(BusinessServiceConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.businessService::rolesAllowedUser)
.withMessage("Access Denied");
}
@WithMockUser
@Test
public void rolesAllowedUserWhenRoleUserThenPasses() {
this.spring.register(BusinessServiceConfig.class).autowire();
this.businessService.rolesAllowedUser();
}
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
static class MethodSecurityServiceConfig {
@Bean
MethodSecurityService methodSecurityService() {
return new MethodSecurityServiceImpl();
}
}
@EnableMethodSecurity(jsr250Enabled = true)
static class BusinessServiceConfig {
@Bean
BusinessService businessService() {
return new ExpressionProtectedBusinessServiceImpl();
}
}
@EnableMethodSecurity
static class CustomPermissionEvaluatorConfig {
@Bean
MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new PermissionEvaluator() {
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject,
Object permission) {
return "grant".equals(targetDomainObject);
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,
Object permission) {
throw new UnsupportedOperationException();
}
});
return expressionHandler;
}
}
@EnableMethodSecurity
static class CustomAuthorizationManagerBeforeAdviceConfig {
@Bean
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> customBeforeAdvice() {
JdkRegexpMethodPointcut methodMatcher = new JdkRegexpMethodPointcut();
methodMatcher.setPattern(".*MethodSecurityServiceImpl.*securedUser");
AuthorizationManager<MethodAuthorizationContext> authorizationManager = (a,
o) -> new AuthorizationDecision("bob".equals(a.get().getName()));
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager);
}
}
@EnableMethodSecurity
static class CustomAuthorizationManagerAfterAdviceConfig {
@Bean
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> customAfterAdvice() {
JdkRegexpMethodPointcut methodMatcher = new JdkRegexpMethodPointcut();
methodMatcher.setPattern(".*MethodSecurityServiceImpl.*securedUser");
return new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
@Override
public MethodMatcher getMethodMatcher() {
return methodMatcher;
}
@Override
public Object after(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext, Object returnedObject) {
Authentication auth = authentication.get();
if ("bob".equals(auth.getName())) {
return "granted";
}
throw new AccessDeniedException("Access Denied for User '" + auth.getName() + "'");
}
};
}
}
}