Polish AuthorizationManager Method Security

- Removed consolidated pointcut advisor in favor of each interceptor
being an advisor. This allows Spring AOP to do more of the heavy
lifting of selecting the set of interceptors that applies
- Created new method context for after interceptors instead of
modifying existing one
- Added documentation
- Added XML support
- Added AuthorizationInterceptorsOrder to simplify interceptor
ordering
- Adjusted annotation lookup to comply with JSR-250 spec
- Adjusted annotation lookup to exhaustively search for duplicate
annotations
- Separated into three @Configuration classes, one for each set of
authorization annotations

Issue gh-9289
This commit is contained in:
Josh Cummings
2021-04-07 15:33:47 -06:00
parent 84e2e80915
commit 67e5c05a47
72 changed files with 4510 additions and 2342 deletions

View File

@@ -16,12 +16,16 @@
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;
@@ -69,4 +73,15 @@ 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 == 2")
List<String> manyAnnotations(List<String> array);
@RequireUserRole
@RequireAdminRole
void repeatedAnnotations();
}

View File

@@ -16,6 +16,8 @@
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;
@@ -86,4 +88,13 @@ public class MethodSecurityServiceImpl implements MethodSecurityService {
return null;
}
@Override
public List<String> manyAnnotations(List<String> object) {
return object;
}
@Override
public void repeatedAnnotations() {
}
}

View File

@@ -18,31 +18,39 @@ 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.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Advisor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut;
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.Role;
import org.springframework.core.annotation.AnnotationConfigurationException;
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.BusinessServiceImpl;
import org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl;
import org.springframework.security.access.annotation.Jsr250BusinessServiceImpl;
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.authorization.method.AuthorizationInterceptorsOrder;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
import org.springframework.security.test.context.support.WithAnonymousUser;
import org.springframework.security.test.context.support.WithMockUser;
@@ -52,13 +60,14 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for {@link MethodSecurityConfiguration}.
* Tests for {@link PrePostMethodSecurityConfiguration}.
*
* @author Evgeniy Cheban
* @author Josh Cummings
*/
@RunWith(SpringRunner.class)
@SecurityTestExecutionListeners
public class MethodSecurityConfigurationTests {
public class PrePostMethodSecurityConfigurationTests {
@Rule
public final SpringTestRule spring = new SpringTestRule();
@@ -103,7 +112,7 @@ public class MethodSecurityConfigurationTests {
@WithMockUser
@Test
public void securedWhenRoleUserThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::secured)
.withMessage("Access Denied");
}
@@ -119,7 +128,7 @@ public class MethodSecurityConfigurationTests {
@WithMockUser(roles = "ADMIN")
@Test
public void securedUserWhenRoleAdminThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)
.withMessage("Access Denied");
}
@@ -147,6 +156,13 @@ public class MethodSecurityConfigurationTests {
this.methodSecurityService.preAuthorizeAdmin();
}
@WithMockUser(authorities = "PREFIX_ADMIN")
@Test
public void preAuthorizeAdminWhenRoleAdminAndCustomPrefixThenPasses() {
this.spring.register(CustomGrantedAuthorityDefaultsConfig.class, MethodSecurityServiceConfig.class).autowire();
this.methodSecurityService.preAuthorizeAdmin();
}
@WithMockUser
@Test
public void postHasPermissionWhenParameterIsNotGrantThenAccessDeniedException() {
@@ -244,7 +260,7 @@ public class MethodSecurityConfigurationTests {
@WithMockUser(roles = "ADMIN")
@Test
public void jsr250WhenRoleAdminThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::jsr250)
.withMessage("Access Denied");
}
@@ -252,7 +268,7 @@ public class MethodSecurityConfigurationTests {
@WithAnonymousUser
@Test
public void jsr250PermitAllWhenRoleAnonymousThenPasses() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
String result = this.methodSecurityService.jsr250PermitAll();
assertThat(result).isNull();
}
@@ -272,7 +288,70 @@ public class MethodSecurityConfigurationTests {
this.businessService.rolesAllowedUser();
}
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
@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");
}
// gh-4003
// gh-4103
@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)));
}
// gh-3183
@Test
public void repeatedAnnotationsWhenPresentThenFails() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> this.methodSecurityService.repeatedAnnotations());
}
// gh-3183
@Test
public void repeatedJsr250AnnotationsWhenPresentThenFails() {
this.spring.register(Jsr250Config.class).autowire();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> this.businessService.repeatedAnnotations());
}
// gh-3183
@Test
public void repeatedSecuredAnnotationsWhenPresentThenFails() {
this.spring.register(SecuredConfig.class).autowire();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> this.businessService.repeatedAnnotations());
}
@EnableMethodSecurity
static class MethodSecurityServiceConfig {
@Bean
@@ -292,6 +371,36 @@ public class MethodSecurityConfigurationTests {
}
@EnableMethodSecurity(prePostEnabled = false, securedEnabled = true)
static class SecuredConfig {
@Bean
BusinessService businessService() {
return new BusinessServiceImpl<>();
}
}
@EnableMethodSecurity(prePostEnabled = false, jsr250Enabled = true)
static class Jsr250Config {
@Bean
BusinessService businessService() {
return new Jsr250BusinessServiceImpl();
}
}
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
static class MethodSecurityServiceEnabledConfig {
@Bean
MethodSecurityService methodSecurityService() {
return new MethodSecurityServiceImpl();
}
}
@EnableMethodSecurity
static class CustomPermissionEvaluatorConfig {
@@ -316,16 +425,27 @@ public class MethodSecurityConfigurationTests {
}
@EnableMethodSecurity
static class CustomGrantedAuthorityDefaultsConfig {
@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("PREFIX_");
}
}
@EnableMethodSecurity
static class CustomAuthorizationManagerBeforeAdviceConfig {
@Bean
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> customBeforeAdvice() {
JdkRegexpMethodPointcut methodMatcher = new JdkRegexpMethodPointcut();
methodMatcher.setPattern(".*MethodSecurityServiceImpl.*securedUser");
AuthorizationManager<MethodAuthorizationContext> authorizationManager = (a,
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor customBeforeAdvice() {
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPattern(".*MethodSecurityServiceImpl.*securedUser");
AuthorizationManager<MethodInvocation> authorizationManager = (a,
o) -> new AuthorizationDecision("bob".equals(a.get().getName()));
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager);
return new AuthorizationManagerBeforeMethodInterceptor(pointcut, authorizationManager);
}
}
@@ -334,25 +454,20 @@ public class MethodSecurityConfigurationTests {
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() + "'");
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor customAfterAdvice() {
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPattern(".*MethodSecurityServiceImpl.*securedUser");
MethodInterceptor interceptor = (mi) -> {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if ("bob".equals(auth.getName())) {
return "granted";
}
throw new AccessDeniedException("Access Denied for User '" + auth.getName() + "'");
};
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, interceptor);
advisor.setOrder(AuthorizationInterceptorsOrder.POST_FILTER.getOrder() + 1);
return advisor;
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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.Retention;
import java.lang.annotation.RetentionPolicy;
import org.springframework.security.access.prepost.PreAuthorize;
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole('ADMIN')")
public @interface RequireAdminRole {
}

View File

@@ -0,0 +1,28 @@
/*
* 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.Retention;
import java.lang.annotation.RetentionPolicy;
import org.springframework.security.access.prepost.PreAuthorize;
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole('USER')")
public @interface RequireUserRole {
}

View File

@@ -52,7 +52,6 @@ public class XsdDocumentedTests {
"nsa-authentication",
"nsa-websocket-security",
"nsa-ldap",
"nsa-method-security",
"nsa-web",
// deprecated and for removal
"nsa-frame-options-strategy",

View File

@@ -0,0 +1,385 @@
/*
* Copyright 2002-2017 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.method;
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.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.jetbrains.annotations.NotNull;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.lang.Nullable;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.annotation.BusinessService;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.annotation.method.configuration.MethodSecurityService;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
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;
/**
* @author Josh Cummings
*/
@RunWith(SpringRunner.class)
@SecurityTestExecutionListeners
public class MethodSecurityBeanDefinitionParserTests {
private static final String CONFIG_LOCATION_PREFIX = "classpath:org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests";
private final UsernamePasswordAuthenticationToken bob = new UsernamePasswordAuthenticationToken("bob",
"bobspassword");
@Autowired(required = false)
MethodSecurityService methodSecurityService;
@Autowired(required = false)
BusinessService businessService;
@Rule
public final SpringTestRule spring = new SpringTestRule();
@WithMockUser(roles = "ADMIN")
@Test
public void preAuthorizeWhenRoleAdminThenAccessDeniedException() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorize)
.withMessage("Access Denied");
}
@WithAnonymousUser
@Test
public void preAuthorizePermitAllWhenRoleAnonymousThenPasses() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
String result = this.methodSecurityService.preAuthorizePermitAll();
assertThat(result).isNull();
}
@WithAnonymousUser
@Test
public void preAuthorizeNotAnonymousWhenRoleAnonymousThenAccessDeniedException() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(this.methodSecurityService::preAuthorizeNotAnonymous).withMessage("Access Denied");
}
@WithMockUser
@Test
public void preAuthorizeNotAnonymousWhenRoleUserThenPasses() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
this.methodSecurityService.preAuthorizeNotAnonymous();
}
@WithMockUser
@Test
public void securedWhenRoleUserThenAccessDeniedException() {
this.spring.configLocations(xml("MethodSecurityServiceEnabled")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::secured)
.withMessage("Access Denied");
}
@WithMockUser(roles = "ADMIN")
@Test
public void securedWhenRoleAdminThenPasses() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
String result = this.methodSecurityService.secured();
assertThat(result).isNull();
}
@WithMockUser(roles = "ADMIN")
@Test
public void securedUserWhenRoleAdminThenAccessDeniedException() {
this.spring.configLocations(xml("MethodSecurityServiceEnabled")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)
.withMessage("Access Denied");
}
@WithMockUser
@Test
public void securedUserWhenRoleUserThenPasses() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
String result = this.methodSecurityService.securedUser();
assertThat(result).isNull();
}
@WithMockUser
@Test
public void preAuthorizeAdminWhenRoleUserThenAccessDeniedException() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorizeAdmin)
.withMessage("Access Denied");
}
@WithMockUser(roles = "ADMIN")
@Test
public void preAuthorizeAdminWhenRoleAdminThenPasses() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
this.methodSecurityService.preAuthorizeAdmin();
}
@WithMockUser(authorities = "PREFIX_ADMIN")
@Test
public void preAuthorizeAdminWhenRoleAdminAndCustomPrefixThenPasses() {
this.spring.configLocations(xml("CustomGrantedAuthorityDefaults")).autowire();
this.methodSecurityService.preAuthorizeAdmin();
}
@WithMockUser
@Test
public void postHasPermissionWhenParameterIsNotGrantThenAccessDeniedException() {
this.spring.configLocations(xml("CustomPermissionEvaluator")).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.postHasPermission("deny")).withMessage("Access Denied");
}
@WithMockUser
@Test
public void postHasPermissionWhenParameterIsGrantThenPasses() {
this.spring.configLocations(xml("CustomPermissionEvaluator")).autowire();
String result = this.methodSecurityService.postHasPermission("grant");
assertThat(result).isNull();
}
@WithMockUser
@Test
public void postAnnotationWhenParameterIsNotGrantThenAccessDeniedException() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.postAnnotation("deny")).withMessage("Access Denied");
}
@WithMockUser
@Test
public void postAnnotationWhenParameterIsGrantThenPasses() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
String result = this.methodSecurityService.postAnnotation("grant");
assertThat(result).isNull();
}
@WithMockUser("bob")
@Test
public void methodReturningAListWhenPrePostFiltersConfiguredThenFiltersList() {
this.spring.configLocations(xml("BusinessService")).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.configLocations(xml("BusinessService")).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.configLocations(xml("CustomAuthorizationManagerBeforeAdvice")).autowire();
String result = this.methodSecurityService.securedUser();
assertThat(result).isNull();
}
@WithMockUser("joe")
@Test
public void securedUserWhenCustomBeforeAdviceConfiguredAndNameNotBobThenAccessDeniedException() {
this.spring.configLocations(xml("CustomAuthorizationManagerBeforeAdvice")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)
.withMessage("Access Denied");
}
@WithMockUser("bob")
@Test
public void securedUserWhenCustomAfterAdviceConfiguredAndNameBobThenGranted() {
this.spring.configLocations(xml("CustomAuthorizationManagerAfterAdvice")).autowire();
String result = this.methodSecurityService.securedUser();
assertThat(result).isEqualTo("granted");
}
@WithMockUser("joe")
@Test
public void securedUserWhenCustomAfterAdviceConfiguredAndNameNotBobThenAccessDeniedException() {
this.spring.configLocations(xml("CustomAuthorizationManagerAfterAdvice")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)
.withMessage("Access Denied for User 'joe'");
}
@WithMockUser(roles = "ADMIN")
@Test
public void jsr250WhenRoleAdminThenAccessDeniedException() {
this.spring.configLocations(xml("MethodSecurityServiceEnabled")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::jsr250)
.withMessage("Access Denied");
}
@WithAnonymousUser
@Test
public void jsr250PermitAllWhenRoleAnonymousThenPasses() {
this.spring.configLocations(xml("MethodSecurityServiceEnabled")).autowire();
String result = this.methodSecurityService.jsr250PermitAll();
assertThat(result).isNull();
}
@WithMockUser(roles = "ADMIN")
@Test
public void rolesAllowedUserWhenRoleAdminThenAccessDeniedException() {
this.spring.configLocations(xml("BusinessService")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.businessService::rolesAllowedUser)
.withMessage("Access Denied");
}
@WithMockUser
@Test
public void rolesAllowedUserWhenRoleUserThenPasses() {
this.spring.configLocations(xml("BusinessService")).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.configLocations(xml("MethodSecurityServiceEnabled")).autowire();
List<String> filtered = this.methodSecurityService.manyAnnotations(new ArrayList<>(names));
assertThat(filtered).hasSize(2);
assertThat(filtered).containsExactly("harold", "jonathan");
}
// gh-4003
// gh-4103
@WithMockUser
@Test
public void manyAnnotationsWhenUserThenFails() {
List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo");
this.spring.configLocations(xml("MethodSecurityServiceEnabled")).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.configLocations(xml("MethodSecurityServiceEnabled")).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.configLocations(xml("MethodSecurityServiceEnabled")).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));
}
// gh-3183
@Test
public void repeatedAnnotationsWhenPresentThenFails() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> this.methodSecurityService.repeatedAnnotations());
}
// gh-3183
@Test
public void repeatedJsr250AnnotationsWhenPresentThenFails() {
this.spring.configLocations(xml("Jsr250")).autowire();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> this.businessService.repeatedAnnotations());
}
// gh-3183
@Test
public void repeatedSecuredAnnotationsWhenPresentThenFails() {
this.spring.configLocations(xml("Secured")).autowire();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> this.businessService.repeatedAnnotations());
}
private static String xml(String configName) {
return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml";
}
static class MyPermissionEvaluator implements 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();
}
}
static class MyAuthorizationManager implements AuthorizationManager<MethodInvocation> {
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation object) {
return new AuthorizationDecision("bob".equals(authentication.get().getName()));
}
}
static class MyAdvice implements MethodInterceptor {
@Nullable
@Override
public Object invoke(@NotNull MethodInvocation invocation) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if ("bob".equals(auth.getName())) {
return "granted";
}
throw new AccessDeniedException("Access Denied for User '" + auth.getName() + "'");
}
}
}