mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-30 16:52:13 +00:00
Add AuthorizationManager
Closes gh-8900
This commit is contained in:
parent
5306d4c4d5
commit
34b4b1054f
@ -25,6 +25,7 @@ import javax.servlet.Filter;
|
||||
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
||||
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
@ -111,6 +112,7 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
|
||||
put(SessionManagementFilter.class, order.next());
|
||||
put(ExceptionTranslationFilter.class, order.next());
|
||||
put(FilterSecurityInterceptor.class, order.next());
|
||||
put(AuthorizationFilter.class, order.next());
|
||||
put(SwitchUserFilter.class, order.next());
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.
|
||||
@ -40,6 +40,8 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry;
|
||||
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.CorsConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
|
||||
@ -1254,6 +1256,90 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
return HttpSecurity.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows restricting access based upon the {@link HttpServletRequest} using
|
||||
* {@link RequestMatcher} implementations (i.e. via URL patterns).
|
||||
*
|
||||
* <h2>Example Configurations</h2>
|
||||
*
|
||||
* The most basic example is to configure all URLs to require the role "ROLE_USER".
|
||||
* The configuration below requires authentication to every URL and will grant access
|
||||
* to both the user "admin" and "user".
|
||||
*
|
||||
* <pre>
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
*
|
||||
* @Override
|
||||
* protected void configure(HttpSecurity http) throws Exception {
|
||||
* http
|
||||
* .authorizeHttpRequests((authorizeHttpRequests) ->
|
||||
* authorizeHttpRequests
|
||||
* .antMatchers("/**").hasRole("USER")
|
||||
* )
|
||||
* .formLogin(withDefaults());
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* We can also configure multiple URLs. The configuration below requires
|
||||
* authentication to every URL and will grant access to URLs starting with /admin/ to
|
||||
* only the "admin" user. All other URLs either user can access.
|
||||
*
|
||||
* <pre>
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
*
|
||||
* @Override
|
||||
* protected void configure(HttpSecurity http) throws Exception {
|
||||
* http
|
||||
* .authorizeHttpRequests((authorizeHttpRequests) ->
|
||||
* authorizeHttpRequests
|
||||
* .antMatchers("/admin/**").hasRole("ADMIN")
|
||||
* .antMatchers("/**").hasRole("USER")
|
||||
* )
|
||||
* .formLogin(withDefaults());
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Note that the matchers are considered in order. Therefore, the following is invalid
|
||||
* because the first matcher matches every request and will never get to the second
|
||||
* mapping:
|
||||
*
|
||||
* <pre>
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
*
|
||||
* @Override
|
||||
* protected void configure(HttpSecurity http) throws Exception {
|
||||
* http
|
||||
* .authorizeHttpRequests((authorizeHttpRequests) ->
|
||||
* authorizeHttpRequests
|
||||
* .antMatchers("/**").hasRole("USER")
|
||||
* .antMatchers("/admin/**").hasRole("ADMIN")
|
||||
* );
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* @param authorizeHttpRequestsCustomizer the {@link Customizer} to provide more
|
||||
* options for the {@link AuthorizationManagerRequestMatcherRegistry}
|
||||
* @return the {@link HttpSecurity} for further customizations
|
||||
* @throws Exception
|
||||
* @see #requestMatcher(RequestMatcher)
|
||||
*/
|
||||
public HttpSecurity authorizeHttpRequests(
|
||||
Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer)
|
||||
throws Exception {
|
||||
ApplicationContext context = getContext();
|
||||
authorizeHttpRequestsCustomizer
|
||||
.customize(getOrApply(new AuthorizeHttpRequestsConfigurer<>(context)).getRegistry());
|
||||
return HttpSecurity.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuring the Request Cache. For example, a protected page (/protected)
|
||||
* may be requested prior to authentication. The application will redirect the user to
|
||||
|
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.web.configurers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorityAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||
import org.springframework.security.web.access.intercept.DelegatingAuthorizationManager;
|
||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Adds a URL based authorization using {@link AuthorizationManager}.
|
||||
*
|
||||
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured.
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
extends AbstractHttpConfigurer<AuthorizeHttpRequestsConfigurer<H>, H> {
|
||||
|
||||
private final AuthorizationManagerRequestMatcherRegistry registry;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
* @param context the {@link ApplicationContext} to use
|
||||
*/
|
||||
public AuthorizeHttpRequestsConfigurer(ApplicationContext context) {
|
||||
this.registry = new AuthorizationManagerRequestMatcherRegistry(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link AuthorizationManagerRequestMatcherRegistry} is what users will interact
|
||||
* with after applying the {@link AuthorizeHttpRequestsConfigurer}.
|
||||
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||
* customizations
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry getRegistry() {
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) {
|
||||
AuthorizationManager<HttpServletRequest> authorizationManager = this.registry.createAuthorizationManager();
|
||||
AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);
|
||||
http.addFilter(postProcess(authorizationFilter));
|
||||
}
|
||||
|
||||
private AuthorizationManagerRequestMatcherRegistry addMapping(List<? extends RequestMatcher> matchers,
|
||||
AuthorizationManager<RequestAuthorizationContext> manager) {
|
||||
for (RequestMatcher matcher : matchers) {
|
||||
this.registry.addMapping(matcher, manager);
|
||||
}
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registry for mapping a {@link RequestMatcher} to an {@link AuthorizationManager}.
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
public final class AuthorizationManagerRequestMatcherRegistry
|
||||
extends AbstractRequestMatcherRegistry<AuthorizedUrl> {
|
||||
|
||||
private final DelegatingAuthorizationManager.Builder managerBuilder = DelegatingAuthorizationManager.builder();
|
||||
|
||||
private List<RequestMatcher> unmappedMatchers;
|
||||
|
||||
private int mappingCount;
|
||||
|
||||
private AuthorizationManagerRequestMatcherRegistry(ApplicationContext context) {
|
||||
setApplicationContext(context);
|
||||
}
|
||||
|
||||
private void addMapping(RequestMatcher matcher, AuthorizationManager<RequestAuthorizationContext> manager) {
|
||||
this.unmappedMatchers = null;
|
||||
this.managerBuilder.add(matcher, manager);
|
||||
this.mappingCount++;
|
||||
}
|
||||
|
||||
private AuthorizationManager<HttpServletRequest> createAuthorizationManager() {
|
||||
Assert.state(this.unmappedMatchers == null,
|
||||
() -> "An incomplete mapping was found for " + this.unmappedMatchers
|
||||
+ ". Try completing it with something like requestUrls().<something>.hasRole('USER')");
|
||||
Assert.state(this.mappingCount > 0,
|
||||
"At least one mapping is required (for example, authorizeHttpRequests().anyRequest().authenticated())");
|
||||
return postProcess(this.managerBuilder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MvcMatchersAuthorizedUrl mvcMatchers(String... mvcPatterns) {
|
||||
return mvcMatchers(null, mvcPatterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MvcMatchersAuthorizedUrl mvcMatchers(HttpMethod method, String... mvcPatterns) {
|
||||
return new MvcMatchersAuthorizedUrl(createMvcMatchers(method, mvcPatterns));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthorizedUrl chainRequestMatchers(List<RequestMatcher> requestMatchers) {
|
||||
this.unmappedMatchers = requestMatchers;
|
||||
return new AuthorizedUrl(requestMatchers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link ObjectPostProcessor} for this class.
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
|
||||
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||
* customizations
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry withObjectPostProcessor(
|
||||
ObjectPostProcessor<?> objectPostProcessor) {
|
||||
addObjectPostProcessor(objectPostProcessor);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link HttpSecurityBuilder} when done using the
|
||||
* {@link AuthorizeHttpRequestsConfigurer}. This is useful for method chaining.
|
||||
* @return the {@link HttpSecurityBuilder} for further customizations
|
||||
*/
|
||||
public H and() {
|
||||
return AuthorizeHttpRequestsConfigurer.this.and();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link AuthorizeHttpRequestsConfigurer.AuthorizedUrl} that allows optionally
|
||||
* configuring the {@link MvcRequestMatcher#setServletPath(String)}.
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
public final class MvcMatchersAuthorizedUrl extends AuthorizedUrl {
|
||||
|
||||
private MvcMatchersAuthorizedUrl(List<MvcRequestMatcher> matchers) {
|
||||
super(matchers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures <code>servletPath</code> to {@link MvcRequestMatcher}s.
|
||||
* @param servletPath the servlet path
|
||||
* @return the {@link MvcMatchersAuthorizedUrl} for further customizations
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public MvcMatchersAuthorizedUrl servletPath(String servletPath) {
|
||||
for (MvcRequestMatcher matcher : (List<MvcRequestMatcher>) getMatchers()) {
|
||||
matcher.setServletPath(servletPath);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that allows configuring the {@link AuthorizationManager} for
|
||||
* {@link RequestMatcher}s.
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
public class AuthorizedUrl {
|
||||
|
||||
private final List<? extends RequestMatcher> matchers;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
* @param matchers the {@link RequestMatcher} instances to map
|
||||
*/
|
||||
AuthorizedUrl(List<? extends RequestMatcher> matchers) {
|
||||
this.matchers = matchers;
|
||||
}
|
||||
|
||||
protected List<? extends RequestMatcher> getMatchers() {
|
||||
return this.matchers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that URLs are allowed by anyone.
|
||||
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||
* customizations
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry permitAll() {
|
||||
return access((a, o) -> new AuthorizationDecision(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that URLs are not allowed by anyone.
|
||||
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||
* customizations
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry denyAll() {
|
||||
return access((a, o) -> new AuthorizationDecision(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a user requires a role.
|
||||
* @param role the role that should be required which is prepended with ROLE_
|
||||
* automatically (i.e. USER, ADMIN, etc). It should not start with ROLE_
|
||||
* @return {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||
* customizations
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry hasRole(String role) {
|
||||
return access(AuthorityAuthorizationManager.hasRole(role));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that a user requires one of many roles.
|
||||
* @param roles the roles that the user should have at least one of (i.e. ADMIN,
|
||||
* USER, etc). Each role should not start with ROLE_ since it is automatically
|
||||
* prepended already
|
||||
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||
* customizations
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry hasAnyRole(String... roles) {
|
||||
return access(AuthorityAuthorizationManager.hasAnyRole(roles));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a user requires an authority.
|
||||
* @param authority the authority that should be required
|
||||
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||
* customizations
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry hasAuthority(String authority) {
|
||||
return access(AuthorityAuthorizationManager.hasAuthority(authority));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that a user requires one of many authorities.
|
||||
* @param authorities the authorities that the user should have at least one of
|
||||
* (i.e. ROLE_USER, ROLE_ADMIN, etc)
|
||||
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||
* customizations
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry hasAnyAuthority(String... authorities) {
|
||||
return access(AuthorityAuthorizationManager.hasAnyAuthority(authorities));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that URLs are allowed by any authenticated user.
|
||||
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||
* customizations
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry authenticated() {
|
||||
return access(AuthenticatedAuthorizationManager.authenticated());
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows specifying a custom {@link AuthorizationManager}.
|
||||
* @param manager the {@link AuthorizationManager} to use
|
||||
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||
* customizations
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry access(
|
||||
AuthorizationManager<RequestAuthorizationContext> manager) {
|
||||
Assert.notNull(manager, "manager cannot be null");
|
||||
return AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,628 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.web.configurers;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||
import org.springframework.security.web.access.intercept.DelegatingAuthorizationManager;
|
||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.security.config.Customizer.withDefaults;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for {@link AuthorizeHttpRequestsConfigurer}.
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
public class AuthorizeHttpRequestsConfigurerTests {
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
MockMvc mvc;
|
||||
|
||||
@Test
|
||||
public void configureWhenAuthorizedHttpRequestsAndNoRequestsThenException() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.spring.register(NoRequestsConfig.class).autowire()).withMessageContaining(
|
||||
"At least one mapping is required (for example, authorizeHttpRequests().anyRequest().authenticated())");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenAnyRequestIncompleteMappingThenException() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.spring.register(IncompleteMappingConfig.class).autowire())
|
||||
.withMessageContaining("An incomplete mapping was found for ");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenMvcMatcherAfterAnyRequestThenException() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.spring.register(AfterAnyRequestConfig.class).autowire())
|
||||
.withMessageContaining("Can't configure mvcMatchers after anyRequest");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureMvcMatcherAccessAuthorizationManagerWhenNotNullThenVerifyUse() throws Exception {
|
||||
CustomAuthorizationManagerConfig.authorizationManager = mock(AuthorizationManager.class);
|
||||
this.spring.register(CustomAuthorizationManagerConfig.class, BasicController.class).autowire();
|
||||
this.mvc.perform(get("/")).andExpect(status().isOk());
|
||||
verify(CustomAuthorizationManagerConfig.authorizationManager).check(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureMvcMatcherAccessAuthorizationManagerWhenNullThenException() {
|
||||
CustomAuthorizationManagerConfig.authorizationManager = null;
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.spring.register(CustomAuthorizationManagerConfig.class).autowire())
|
||||
.withMessageContaining("manager cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenObjectPostProcessorRegisteredThenInvokedOnAuthorizationManagerAndAuthorizationFilter() {
|
||||
this.spring.register(ObjectPostProcessorConfig.class).autowire();
|
||||
verify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(DelegatingAuthorizationManager.class));
|
||||
verify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(AuthorizationFilter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenHasAnyAuthorityRoleUserConfiguredAndAuthorityIsRoleUserThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(RoleUserAnyAuthorityConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||
.with(user("user")
|
||||
.authorities(new SimpleGrantedAuthority("ROLE_USER")));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenHasAnyAuthorityRoleUserConfiguredAndAuthorityIsRoleAdminThenRespondsWithForbidden()
|
||||
throws Exception {
|
||||
this.spring.register(RoleUserAnyAuthorityConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithAdmin = get("/")
|
||||
.with(user("user")
|
||||
.authorities(new SimpleGrantedAuthority("ROLE_ADMIN")));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenHasAnyAuthorityRoleUserConfiguredAndNoAuthorityThenRespondsWithUnauthorized() throws Exception {
|
||||
this.spring.register(RoleUserAnyAuthorityConfig.class, BasicController.class).autowire();
|
||||
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenHasAuthorityRoleUserConfiguredAndAuthorityIsRoleUserThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(RoleUserAuthorityConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||
.with(user("user")
|
||||
.authorities(new SimpleGrantedAuthority("ROLE_USER")));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenHasAuthorityRoleUserConfiguredAndAuthorityIsRoleAdminThenRespondsWithForbidden()
|
||||
throws Exception {
|
||||
this.spring.register(RoleUserAuthorityConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithAdmin = get("/")
|
||||
.with(user("user")
|
||||
.authorities(new SimpleGrantedAuthority("ROLE_ADMIN")));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenHasAuthorityRoleUserConfiguredAndNoAuthorityThenRespondsWithUnauthorized() throws Exception {
|
||||
this.spring.register(RoleUserAuthorityConfig.class, BasicController.class).autowire();
|
||||
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenAuthorityRoleUserOrAdminRequiredAndAuthorityIsRoleUserThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||
.with(user("user")
|
||||
.authorities(new SimpleGrantedAuthority("ROLE_USER")));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenAuthorityRoleUserOrAdminRequiredAndAuthorityIsRoleAdminThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithAdmin = get("/")
|
||||
.with(user("user")
|
||||
.authorities(new SimpleGrantedAuthority("ROLE_ADMIN")));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithAdmin).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenAuthorityRoleUserOrAdminRequiredAndAuthorityIsRoleOtherThenRespondsWithForbidden()
|
||||
throws Exception {
|
||||
this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithOther = get("/")
|
||||
.with(user("user")
|
||||
.authorities(new SimpleGrantedAuthority("ROLE_OTHER")));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithOther).andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenAuthorityRoleUserOrAdminAuthRequiredAndNoUserThenRespondsWithUnauthorized() throws Exception {
|
||||
this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();
|
||||
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenHasRoleUserConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(RoleUserConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||
.with(user("user")
|
||||
.roles("USER"));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenHasRoleUserConfiguredAndRoleIsAdminThenRespondsWithForbidden() throws Exception {
|
||||
this.spring.register(RoleUserConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithAdmin = get("/")
|
||||
.with(user("user")
|
||||
.roles("ADMIN"));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenRoleUserOrAdminConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||
.with(user("user")
|
||||
.roles("USER"));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenRoleUserOrAdminConfiguredAndRoleIsAdminThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithAdmin = get("/")
|
||||
.with(user("user")
|
||||
.roles("ADMIN"));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithAdmin).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenRoleUserOrAdminConfiguredAndRoleIsOtherThenRespondsWithForbidden() throws Exception {
|
||||
this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithRoleOther = get("/")
|
||||
.with(user("user")
|
||||
.roles("OTHER"));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithRoleOther).andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenDenyAllConfiguredAndNoUserThenRespondsWithUnauthorized() throws Exception {
|
||||
this.spring.register(DenyAllConfig.class, BasicController.class).autowire();
|
||||
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenDenyAllConfiguredAndUserLoggedInThenRespondsWithForbidden() throws Exception {
|
||||
this.spring.register(DenyAllConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||
.with(user("user")
|
||||
.roles("USER"));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithUser).andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenPermitAllConfiguredAndNoUserThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(PermitAllConfig.class, BasicController.class).autowire();
|
||||
this.mvc.perform(get("/")).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenPermitAllConfiguredAndUserLoggedInThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(PermitAllConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||
.with(user("user")
|
||||
.roles("USER"));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authorizeHttpRequestsWhenInvokedTwiceThenUsesOriginalConfiguration() throws Exception {
|
||||
this.spring.register(InvokeTwiceDoesNotResetConfig.class, BasicController.class).autowire();
|
||||
this.mvc.perform(post("/").with(csrf())).andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenServletPathRoleAdminConfiguredAndRoleIsUserThenRespondsWithForbidden() throws Exception {
|
||||
this.spring.register(ServletPathConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithUser = get("/spring/")
|
||||
.servletPath("/spring")
|
||||
.with(user("user")
|
||||
.roles("USER"));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithUser).andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenServletPathRoleAdminConfiguredAndRoleIsAdminThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(ServletPathConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithAdmin = get("/spring/")
|
||||
.servletPath("/spring")
|
||||
.with(user("user")
|
||||
.roles("ADMIN"));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithAdmin).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenAnyRequestAuthenticatedConfiguredAndNoUserThenRespondsWithUnauthorized() throws Exception {
|
||||
this.spring.register(AuthenticatedConfig.class, BasicController.class).autowire();
|
||||
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenAnyRequestAuthenticatedConfiguredAndUserLoggedInThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(AuthenticatedConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||
.with(user("user")
|
||||
.roles("USER"));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class NoRequestsConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.authorizeHttpRequests(withDefaults())
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class IncompleteMappingConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.authorizeHttpRequests(AbstractRequestMatcherRegistry::anyRequest)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class AfterAnyRequestConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().authenticated()
|
||||
.mvcMatchers("/path").hasRole("USER")
|
||||
)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomAuthorizationManagerConfig {
|
||||
|
||||
static AuthorizationManager<RequestAuthorizationContext> authorizationManager;
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().access(authorizationManager)
|
||||
)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class ObjectPostProcessorConfig {
|
||||
|
||||
static ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
static ObjectPostProcessor<Object> objectPostProcessor() {
|
||||
return objectPostProcessor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {
|
||||
|
||||
@Override
|
||||
public <O> O postProcess(O object) {
|
||||
return object;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RoleUserAnyAuthorityConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().hasAnyAuthority("ROLE_USER")
|
||||
)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RoleUserAuthorityConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().hasAuthority("ROLE_USER")
|
||||
)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RoleUserOrRoleAdminAuthorityConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().hasAnyAuthority("ROLE_USER", "ROLE_ADMIN")
|
||||
)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RoleUserConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().hasRole("USER")
|
||||
)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RoleUserOrAdminConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().hasAnyRole("USER", "ADMIN")
|
||||
)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class DenyAllConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().denyAll()
|
||||
)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class PermitAllConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().permitAll()
|
||||
)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class InvokeTwiceDoesNotResetConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.authorizeHttpRequests(withDefaults())
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebMvc
|
||||
@EnableWebSecurity
|
||||
static class ServletPathConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.mvcMatchers("/").servletPath("/spring").hasRole("ADMIN")
|
||||
)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class AuthenticatedConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class BasicController {
|
||||
|
||||
@GetMapping("/")
|
||||
void rootGet() {
|
||||
}
|
||||
|
||||
@PostMapping("/")
|
||||
void rootPost() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* An {@link AuthorizationManager} that determines if the current user is authenticated.
|
||||
*
|
||||
* @param <T> the type of object authorization is being performed against. This does not.
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
public final class AuthenticatedAuthorizationManager<T> implements AuthorizationManager<T> {
|
||||
|
||||
private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link AuthenticatedAuthorizationManager}.
|
||||
* @param <T> the type of object being authorized
|
||||
* @return the new instance
|
||||
*/
|
||||
public static <T> AuthenticatedAuthorizationManager<T> authenticated() {
|
||||
return new AuthenticatedAuthorizationManager<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current user is authorized by evaluating if the
|
||||
* {@link Authentication} is not anonymous and authenticated.
|
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
||||
* @param object the {@link T} object to check
|
||||
* @return an {@link AuthorizationDecision}
|
||||
*/
|
||||
@Override
|
||||
public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
|
||||
boolean granted = isGranted(authentication.get());
|
||||
return new AuthorizationDecision(granted);
|
||||
}
|
||||
|
||||
private boolean isGranted(Authentication authentication) {
|
||||
return authentication != null && isNotAnonymous(authentication) && authentication.isAuthenticated();
|
||||
}
|
||||
|
||||
private boolean isNotAnonymous(Authentication authentication) {
|
||||
return !this.trustResolver.isAnonymous(authentication);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link AuthorizationManager} that determines if the current user is authorized by
|
||||
* evaluating if the {@link Authentication} contains a specified authority.
|
||||
*
|
||||
* @param <T> the type of object being authorized.
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
public final class AuthorityAuthorizationManager<T> implements AuthorizationManager<T> {
|
||||
|
||||
private static final String ROLE_PREFIX = "ROLE_";
|
||||
|
||||
private final Set<String> authorities;
|
||||
|
||||
private AuthorityAuthorizationManager(String... authorities) {
|
||||
this.authorities = new HashSet<>(Arrays.asList(authorities));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link AuthorityAuthorizationManager} with the provided
|
||||
* authority.
|
||||
* @param role the authority to check for prefixed with "ROLE_"
|
||||
* @param <T> the type of object being authorized
|
||||
* @return the new instance
|
||||
*/
|
||||
public static <T> AuthorityAuthorizationManager<T> hasRole(String role) {
|
||||
Assert.notNull(role, "role cannot be null");
|
||||
return hasAuthority(ROLE_PREFIX + role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link AuthorityAuthorizationManager} with the provided
|
||||
* authority.
|
||||
* @param authority the authority to check for
|
||||
* @param <T> the type of object being authorized
|
||||
* @return the new instance
|
||||
*/
|
||||
public static <T> AuthorityAuthorizationManager<T> hasAuthority(String authority) {
|
||||
Assert.notNull(authority, "authority cannot be null");
|
||||
return new AuthorityAuthorizationManager<>(authority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link AuthorityAuthorizationManager} with the provided
|
||||
* authorities.
|
||||
* @param roles the authorities to check for prefixed with "ROLE_"
|
||||
* @param <T> the type of object being authorized
|
||||
* @return the new instance
|
||||
*/
|
||||
public static <T> AuthorityAuthorizationManager<T> hasAnyRole(String... roles) {
|
||||
Assert.notEmpty(roles, "roles cannot be empty");
|
||||
Assert.noNullElements(roles, "roles cannot contain null values");
|
||||
return hasAnyAuthority(toNamedRolesArray(roles));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link AuthorityAuthorizationManager} with the provided
|
||||
* authorities.
|
||||
* @param authorities the authorities to check for
|
||||
* @param <T> the type of object being authorized
|
||||
* @return the new instance
|
||||
*/
|
||||
public static <T> AuthorityAuthorizationManager<T> hasAnyAuthority(String... authorities) {
|
||||
Assert.notEmpty(authorities, "authorities cannot be empty");
|
||||
Assert.noNullElements(authorities, "authorities cannot contain null values");
|
||||
return new AuthorityAuthorizationManager<>(authorities);
|
||||
}
|
||||
|
||||
private static String[] toNamedRolesArray(String... roles) {
|
||||
String[] result = new String[roles.length];
|
||||
for (int i = 0; i < roles.length; i++) {
|
||||
result[i] = ROLE_PREFIX + roles[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current user is authorized by evaluating if the
|
||||
* {@link Authentication} contains a specified authority.
|
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
||||
* @param object the {@link T} object to check
|
||||
* @return an {@link AuthorizationDecision}
|
||||
*/
|
||||
@Override
|
||||
public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
|
||||
boolean granted = isGranted(authentication.get());
|
||||
return new AuthorizationDecision(granted);
|
||||
}
|
||||
|
||||
private boolean isGranted(Authentication authentication) {
|
||||
return authentication != null && authentication.isAuthenticated() && isAuthorized(authentication);
|
||||
}
|
||||
|
||||
private boolean isAuthorized(Authentication authentication) {
|
||||
for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
|
||||
String authority = grantedAuthority.getAuthority();
|
||||
if (this.authorities.contains(authority)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AuthorityAuthorizationManager[authorities=" + this.authorities + "]";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* An Authorization manager which can determine if an {@link Authentication} has access to
|
||||
* a specific object.
|
||||
*
|
||||
* @param <T> the type of object that the authorization check is being done one.
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface AuthorizationManager<T> {
|
||||
|
||||
/**
|
||||
* Determines if access should be granted for a specific authentication and object.
|
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
||||
* @param object the {@link T} object to check
|
||||
* @throws AccessDeniedException if access is not granted
|
||||
*/
|
||||
default void verify(Supplier<Authentication> authentication, T object) {
|
||||
AuthorizationDecision decision = check(authentication, object);
|
||||
if (decision != null && !decision.isGranted()) {
|
||||
throw new AccessDeniedException("Access Denied");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if access is granted for a specific authentication and object.
|
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
||||
* @param object the {@link T} object to check
|
||||
* @return an {@link AuthorizationDecision} or null if no decision could be made
|
||||
*/
|
||||
@Nullable
|
||||
AuthorizationDecision check(Supplier<Authentication> authentication, T object);
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link AuthenticatedAuthorizationManager}.
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
public class AuthenticatedAuthorizationManagerTests {
|
||||
|
||||
@Test
|
||||
public void authenticatedWhenUserNotAnonymousAndAuthenticatedThenGrantedDecision() {
|
||||
AuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();
|
||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN",
|
||||
"ROLE_USER");
|
||||
Object object = new Object();
|
||||
|
||||
assertThat(manager.check(authentication, object).isGranted()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticatedWhenUserNullThenDeniedDecision() {
|
||||
AuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();
|
||||
Supplier<Authentication> authentication = () -> null;
|
||||
Object object = new Object();
|
||||
|
||||
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticatedWhenUserAnonymousThenDeniedDecision() {
|
||||
AuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();
|
||||
Supplier<Authentication> authentication = () -> new AnonymousAuthenticationToken("key", "principal",
|
||||
AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
|
||||
Object object = new Object();
|
||||
|
||||
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticatedWhenUserNotAuthenticatedThenDeniedDecision() {
|
||||
AuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();
|
||||
TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", "password", "ROLE_ADMIN",
|
||||
"ROLE_USER");
|
||||
authentication.setAuthenticated(false);
|
||||
Object object = new Object();
|
||||
|
||||
assertThat(manager.check(() -> authentication, object).isGranted()).isFalse();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
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 AuthorityAuthorizationManager}.
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
public class AuthorityAuthorizationManagerTests {
|
||||
|
||||
@Test
|
||||
public void hasRoleWhenNullThenException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasRole(null))
|
||||
.withMessage("role cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAuthorityWhenNullThenException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAuthority(null))
|
||||
.withMessage("authority cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAnyRoleWhenNullThenException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole(null))
|
||||
.withMessage("roles cannot be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAnyRoleWhenEmptyThenException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole(new String[] {}))
|
||||
.withMessage("roles cannot be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAnyRoleWhenContainNullThenException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole("ADMIN", null, "USER"))
|
||||
.withMessage("roles cannot contain null values");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAnyAuthorityWhenNullThenException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority(null))
|
||||
.withMessage("authorities cannot be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAnyAuthorityWhenEmptyThenException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority(new String[] {}))
|
||||
.withMessage("authorities cannot be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAnyAuthorityWhenContainNullThenException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority("ADMIN", null, "USER"))
|
||||
.withMessage("authorities cannot contain null values");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasRoleWhenUserHasRoleThenGrantedDecision() {
|
||||
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasRole("ADMIN");
|
||||
|
||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN",
|
||||
"ROLE_USER");
|
||||
Object object = new Object();
|
||||
|
||||
assertThat(manager.check(authentication, object).isGranted()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasRoleWhenUserHasNotRoleThenDeniedDecision() {
|
||||
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasRole("ADMIN");
|
||||
|
||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||
Object object = new Object();
|
||||
|
||||
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAuthorityWhenUserHasAuthorityThenGrantedDecision() {
|
||||
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAuthority("ADMIN");
|
||||
|
||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ADMIN",
|
||||
"USER");
|
||||
Object object = new Object();
|
||||
|
||||
assertThat(manager.check(authentication, object).isGranted()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAuthorityWhenUserHasNotAuthorityThenDeniedDecision() {
|
||||
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAuthority("ADMIN");
|
||||
|
||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "USER");
|
||||
Object object = new Object();
|
||||
|
||||
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAnyRoleWhenUserHasAnyRoleThenGrantedDecision() {
|
||||
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyRole("ADMIN", "USER");
|
||||
|
||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||
Object object = new Object();
|
||||
|
||||
assertThat(manager.check(authentication, object).isGranted()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAnyRoleWhenUserHasNotAnyRoleThenDeniedDecision() {
|
||||
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyRole("ADMIN", "USER");
|
||||
|
||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
|
||||
"ROLE_ANONYMOUS");
|
||||
Object object = new Object();
|
||||
|
||||
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAnyAuthorityWhenUserHasAnyAuthorityThenGrantedDecision() {
|
||||
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyAuthority("ADMIN", "USER");
|
||||
|
||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "USER");
|
||||
Object object = new Object();
|
||||
|
||||
assertThat(manager.check(authentication, object).isGranted()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAnyAuthorityWhenUserHasNotAnyAuthorityThenDeniedDecision() {
|
||||
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyAuthority("ADMIN", "USER");
|
||||
|
||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ANONYMOUS");
|
||||
Object object = new Object();
|
||||
|
||||
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link AuthorizationManager}.
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
public class AuthorizationManagerTests {
|
||||
|
||||
@Test
|
||||
public void verifyWhenCheckReturnedGrantedDecisionThenPasses() {
|
||||
AuthorizationManager<Object> manager = (a, o) -> new AuthorizationDecision(true);
|
||||
|
||||
Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_1", "ROLE_2");
|
||||
Object object = new Object();
|
||||
|
||||
manager.verify(() -> authentication, object);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyWhenCheckReturnedNullThenPasses() {
|
||||
AuthorizationManager<Object> manager = (a, o) -> null;
|
||||
|
||||
Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_1", "ROLE_2");
|
||||
Object object = new Object();
|
||||
|
||||
manager.verify(() -> authentication, object);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyWhenCheckReturnedDeniedDecisionThenAccessDeniedException() {
|
||||
AuthorizationManager<Object> manager = (a, o) -> new AuthorizationDecision(false);
|
||||
|
||||
Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_1", "ROLE_2");
|
||||
Object object = new Object();
|
||||
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(() -> manager.verify(() -> authentication, object)).withMessage("Access Denied");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.web.access.intercept;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
/**
|
||||
* An authorization filter that restricts access to the URL using
|
||||
* {@link AuthorizationManager}.
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
public class AuthorizationFilter extends OncePerRequestFilter {
|
||||
|
||||
private final AuthorizationManager<HttpServletRequest> authorizationManager;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
* @param authorizationManager the {@link AuthorizationManager} to use
|
||||
*/
|
||||
public AuthorizationFilter(AuthorizationManager<HttpServletRequest> authorizationManager) {
|
||||
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
|
||||
this.authorizationManager = authorizationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
this.authorizationManager.verify(this::getAuthentication, request);
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private Authentication getAuthentication() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication == null) {
|
||||
throw new AuthenticationCredentialsNotFoundException(
|
||||
"An Authentication object was not found in the SecurityContext");
|
||||
}
|
||||
return authentication;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.web.access.intercept;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher.MatchResult;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link AuthorizationManager} which delegates to a specific
|
||||
* {@link AuthorizationManager} based on a {@link RequestMatcher} evaluation.
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
public final class DelegatingAuthorizationManager implements AuthorizationManager<HttpServletRequest> {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings;
|
||||
|
||||
private DelegatingAuthorizationManager(
|
||||
Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings) {
|
||||
Assert.notEmpty(mappings, "mappings cannot be empty");
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to a specific {@link AuthorizationManager} based on a
|
||||
* {@link RequestMatcher} evaluation.
|
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
||||
* @param request the {@link HttpServletRequest} to check
|
||||
* @return an {@link AuthorizationDecision}. If there is no {@link RequestMatcher}
|
||||
* matching the request, or the {@link AuthorizationManager} could not decide, then
|
||||
* null is returned
|
||||
*/
|
||||
@Override
|
||||
public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest request) {
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace(LogMessage.format("Authorizing %s", request));
|
||||
}
|
||||
for (Map.Entry<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mapping : this.mappings
|
||||
.entrySet()) {
|
||||
|
||||
RequestMatcher matcher = mapping.getKey();
|
||||
MatchResult matchResult = matcher.matcher(request);
|
||||
if (matchResult.isMatch()) {
|
||||
AuthorizationManager<RequestAuthorizationContext> manager = mapping.getValue();
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace(LogMessage.format("Checking authorization on %s using %s", request, manager));
|
||||
}
|
||||
return manager.check(authentication,
|
||||
new RequestAuthorizationContext(request, matchResult.getVariables()));
|
||||
}
|
||||
}
|
||||
this.logger.trace("Abstaining since did not find matching RequestMatcher");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a builder for {@link DelegatingAuthorizationManager}.
|
||||
* @return the new {@link Builder} instance
|
||||
*/
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for {@link DelegatingAuthorizationManager}.
|
||||
*/
|
||||
public static final class Builder {
|
||||
|
||||
private final Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Maps a {@link RequestMatcher} to an {@link AuthorizationManager}.
|
||||
* @param matcher the {@link RequestMatcher} to use
|
||||
* @param manager the {@link AuthorizationManager} to use
|
||||
* @return the {@link Builder} for further customizations
|
||||
*/
|
||||
public Builder add(RequestMatcher matcher, AuthorizationManager<RequestAuthorizationContext> manager) {
|
||||
Assert.notNull(matcher, "matcher cannot be null");
|
||||
Assert.notNull(manager, "manager cannot be null");
|
||||
this.mappings.put(matcher, manager);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link DelegatingAuthorizationManager} instance.
|
||||
* @return the {@link DelegatingAuthorizationManager} instance
|
||||
*/
|
||||
public DelegatingAuthorizationManager build() {
|
||||
return new DelegatingAuthorizationManager(this.mappings);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.web.access.intercept;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* An {@link HttpServletRequest} authorization context.
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
public final class RequestAuthorizationContext {
|
||||
|
||||
private final HttpServletRequest request;
|
||||
|
||||
private final Map<String, String> variables;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
* @param request the {@link HttpServletRequest} to use
|
||||
*/
|
||||
public RequestAuthorizationContext(HttpServletRequest request) {
|
||||
this(request, Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
* @param request the {@link HttpServletRequest} to use
|
||||
* @param variables a map containing key-value pairs representing extracted variable
|
||||
* names and variable values
|
||||
*/
|
||||
public RequestAuthorizationContext(HttpServletRequest request, Map<String, String> variables) {
|
||||
this.request = request;
|
||||
this.variables = variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link HttpServletRequest}.
|
||||
* @return the {@link HttpServletRequest} to use
|
||||
*/
|
||||
public HttpServletRequest getRequest() {
|
||||
return this.request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the extracted variable values where the key is the variable name and the
|
||||
* value is the variable value.
|
||||
* @return a map containing key-value pairs representing extracted variable names and
|
||||
* variable values
|
||||
*/
|
||||
public Map<String, String> getVariables() {
|
||||
return this.variables;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.web.access.intercept;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.willThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link AuthorizationFilter}.
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
public class AuthorizationFilterTests {
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterWhenAuthorizationManagerVerifyPassesThenNextFilter() throws Exception {
|
||||
AuthorizationManager<HttpServletRequest> mockAuthorizationManager = mock(AuthorizationManager.class);
|
||||
AuthorizationFilter filter = new AuthorizationFilter(mockAuthorizationManager);
|
||||
TestingAuthenticationToken authenticationToken = new TestingAuthenticationToken("user", "password");
|
||||
|
||||
SecurityContext securityContext = new SecurityContextImpl();
|
||||
securityContext.setAuthentication(authenticationToken);
|
||||
SecurityContextHolder.setContext(securityContext);
|
||||
|
||||
MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path");
|
||||
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
|
||||
FilterChain mockFilterChain = mock(FilterChain.class);
|
||||
|
||||
filter.doFilter(mockRequest, mockResponse, mockFilterChain);
|
||||
|
||||
ArgumentCaptor<Supplier<Authentication>> authenticationCaptor = ArgumentCaptor.forClass(Supplier.class);
|
||||
verify(mockAuthorizationManager).verify(authenticationCaptor.capture(), eq(mockRequest));
|
||||
Supplier<Authentication> authentication = authenticationCaptor.getValue();
|
||||
assertThat(authentication.get()).isEqualTo(authenticationToken);
|
||||
|
||||
verify(mockFilterChain).doFilter(mockRequest, mockResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterWhenAuthorizationManagerVerifyThrowsAccessDeniedExceptionThenStopFilterChain() {
|
||||
AuthorizationManager<HttpServletRequest> mockAuthorizationManager = mock(AuthorizationManager.class);
|
||||
AuthorizationFilter filter = new AuthorizationFilter(mockAuthorizationManager);
|
||||
TestingAuthenticationToken authenticationToken = new TestingAuthenticationToken("user", "password");
|
||||
|
||||
SecurityContext securityContext = new SecurityContextImpl();
|
||||
securityContext.setAuthentication(authenticationToken);
|
||||
SecurityContextHolder.setContext(securityContext);
|
||||
|
||||
MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path");
|
||||
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
|
||||
FilterChain mockFilterChain = mock(FilterChain.class);
|
||||
|
||||
willThrow(new AccessDeniedException("Access Denied")).given(mockAuthorizationManager).verify(any(),
|
||||
eq(mockRequest));
|
||||
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(() -> filter.doFilter(mockRequest, mockResponse, mockFilterChain))
|
||||
.withMessage("Access Denied");
|
||||
|
||||
ArgumentCaptor<Supplier<Authentication>> authenticationCaptor = ArgumentCaptor.forClass(Supplier.class);
|
||||
verify(mockAuthorizationManager).verify(authenticationCaptor.capture(), eq(mockRequest));
|
||||
Supplier<Authentication> authentication = authenticationCaptor.getValue();
|
||||
assertThat(authentication.get()).isEqualTo(authenticationToken);
|
||||
|
||||
verifyNoInteractions(mockFilterChain);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterWhenAuthenticationNullThenAuthenticationCredentialsNotFoundException() {
|
||||
AuthorizationFilter filter = new AuthorizationFilter(AuthenticatedAuthorizationManager.authenticated());
|
||||
MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path");
|
||||
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
|
||||
FilterChain mockFilterChain = mock(FilterChain.class);
|
||||
|
||||
assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
|
||||
.isThrownBy(() -> filter.doFilter(mockRequest, mockResponse, mockFilterChain))
|
||||
.withMessage("An Authentication object was not found in the SecurityContext");
|
||||
|
||||
verifyNoInteractions(mockFilterChain);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.web.access.intercept;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link DelegatingAuthorizationManager}.
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
*/
|
||||
public class DelegatingAuthorizationManagerTests {
|
||||
|
||||
@Test
|
||||
public void buildWhenMappingsEmptyThenException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> DelegatingAuthorizationManager.builder().build())
|
||||
.withMessage("mappings cannot be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addWhenMatcherNullThenException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> DelegatingAuthorizationManager.builder()
|
||||
.add(null, (a, o) -> new AuthorizationDecision(true)).build())
|
||||
.withMessage("matcher cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addWhenManagerNullThenException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> DelegatingAuthorizationManager.builder().add(new MvcRequestMatcher(null, "/grant"), null).build())
|
||||
.withMessage("manager cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkWhenMultipleMappingsConfiguredThenDelegatesMatchingManager() {
|
||||
DelegatingAuthorizationManager manager = DelegatingAuthorizationManager.builder()
|
||||
.add(new MvcRequestMatcher(null, "/grant"), (a, o) -> new AuthorizationDecision(true))
|
||||
.add(new MvcRequestMatcher(null, "/deny"), (a, o) -> new AuthorizationDecision(false))
|
||||
.add(new MvcRequestMatcher(null, "/neutral"), (a, o) -> null).build();
|
||||
|
||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||
|
||||
AuthorizationDecision grant = manager.check(authentication, new MockHttpServletRequest(null, "/grant"));
|
||||
assertThat(grant).isNotNull();
|
||||
assertThat(grant.isGranted()).isTrue();
|
||||
|
||||
AuthorizationDecision deny = manager.check(authentication, new MockHttpServletRequest(null, "/deny"));
|
||||
assertThat(deny).isNotNull();
|
||||
assertThat(deny.isGranted()).isFalse();
|
||||
|
||||
AuthorizationDecision neutral = manager.check(authentication, new MockHttpServletRequest(null, "/neutral"));
|
||||
assertThat(neutral).isNull();
|
||||
|
||||
AuthorizationDecision abstain = manager.check(authentication, new MockHttpServletRequest(null, "/abstain"));
|
||||
assertThat(abstain).isNull();
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user