DSL nested builder for HTTP security

DSL nested builder for HTTP security

Fixes gh-5557
This commit is contained in:
Rob Winch
2019-07-12 16:09:19 -05:00
committed by GitHub
61 changed files with 5281 additions and 519 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -18,18 +18,22 @@ package org.springframework.security.config.annotation.web.configurers;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.PasswordEncodedUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* @author Rob Winch
@@ -44,7 +48,7 @@ public class AnonymousConfigurerTests {
@Test
public void requestWhenAnonymousTwiceInvokedThenDoesNotOverride() throws Exception {
this.spring.register(InvokeTwiceDoesNotOverride.class).autowire();
this.spring.register(InvokeTwiceDoesNotOverride.class, PrincipalController.class).autowire();
this.mockMvc.perform(get("/"))
.andExpect(content().string("principal"));
@@ -63,13 +67,99 @@ public class AnonymousConfigurerTests {
.and()
.anonymous();
}
}
@RestController
static class PrincipalController {
@GetMapping("/")
String principal(@AuthenticationPrincipal String principal) {
return principal;
}
@Test
public void requestWhenAnonymousPrincipalInLambdaThenPrincipalUsed() throws Exception {
this.spring.register(AnonymousPrincipalInLambdaConfig.class, PrincipalController.class).autowire();
this.mockMvc.perform(get("/"))
.andExpect(content().string("principal"));
}
@EnableWebSecurity
@EnableWebMvc
static class AnonymousPrincipalInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.anonymous(anonymous ->
anonymous
.principal("principal")
);
// @formatter:on
}
}
@Test
public void requestWhenAnonymousDisabledInLambdaThenRespondsWithForbidden() throws Exception {
this.spring.register(AnonymousDisabledInLambdaConfig.class, PrincipalController.class).autowire();
this.mockMvc.perform(get("/"))
.andExpect(status().isForbidden());
}
@EnableWebSecurity
static class AnonymousDisabledInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().permitAll()
)
.anonymous(AbstractHttpConfigurer::disable);
// @formatter:on
}
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication()
.withUser(PasswordEncodedUser.user());
// @formatter:on
}
}
@Test
public void requestWhenAnonymousWithDefaultsInLambdaThenRespondsWithOk() throws Exception {
this.spring.register(AnonymousWithDefaultsInLambdaConfig.class, PrincipalController.class).autowire();
this.mockMvc.perform(get("/"))
.andExpect(status().isOk());
}
@EnableWebSecurity
static class AnonymousWithDefaultsInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().permitAll()
)
.anonymous(withDefaults());
// @formatter:on
}
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication()
.withUser(PasswordEncodedUser.user());
// @formatter:on
}
}
@RestController
static class PrincipalController {
@GetMapping("/")
String principal(@AuthenticationPrincipal String principal) {
return principal;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2019 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.
@@ -49,6 +49,7 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.spy;
import static org.springframework.security.config.Customizer.withDefaults;
/**
* @author Rob Winch
@@ -113,6 +114,39 @@ public class AuthorizeRequestsTests {
}
}
@Test
public void postWhenPostDenyAllInLambdaThenRespondsWithForbidden() throws Exception {
loadConfig(AntMatchersNoPatternsInLambdaConfig.class);
this.request.setMethod("POST");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
}
@EnableWebSecurity
@Configuration
static class AntMatchersNoPatternsInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.antMatchers(HttpMethod.POST).denyAll()
);
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication();
// @formatter:on
}
}
// SEC-2256
@Test
public void antMatchersPathVariables() throws Exception {
@@ -314,6 +348,66 @@ public class AuthorizeRequestsTests {
}
}
@Test
public void requestWhenMvcMatcherDenyAllThenRespondsWithUnauthorized() throws Exception {
loadConfig(MvcMatcherInLambdaConfig.class);
this.request.setRequestURI("/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setRequestURI("/path.html");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/path/");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic(withDefaults())
.authorizeRequests(authorizeRequests ->
authorizeRequests
.mvcMatchers("/path").denyAll()
);
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication();
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
@Test
public void mvcMatcherServletPath() throws Exception {
loadConfig(MvcMatcherServletPathConfig.class);
@@ -391,6 +485,85 @@ public class AuthorizeRequestsTests {
}
}
@Test
public void requestWhenMvcMatcherServletPathDenyAllThenMatchesOnServletPath() throws Exception {
loadConfig(MvcMatcherServletPathInLambdaConfig.class);
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path.html");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path/");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/foo");
this.request.setRequestURI("/foo/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
setup();
this.request.setServletPath("/");
this.request.setRequestURI("/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherServletPathInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic(withDefaults())
.authorizeRequests(authorizeRequests ->
authorizeRequests
.mvcMatchers("/path").servletPath("/spring").denyAll()
);
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication();
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
@Test
public void mvcMatcherPathVariables() throws Exception {
loadConfig(MvcMatcherPathVariablesConfig.class);
@@ -441,6 +614,58 @@ public class AuthorizeRequestsTests {
}
}
@Test
public void requestWhenMvcMatcherPathVariablesThenMatchesOnPathVariables() throws Exception {
loadConfig(MvcMatcherPathVariablesInLambdaConfig.class);
this.request.setRequestURI("/user/user");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
this.setup();
this.request.setRequestURI("/user/deny");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherPathVariablesInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic(withDefaults())
.authorizeRequests(authorizeRequests ->
authorizeRequests
.mvcMatchers("/user/{userName}").access("#userName == 'user'")
);
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication();
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
@EnableWebSecurity
@Configuration
@EnableWebMvc

View File

@@ -135,4 +135,27 @@ public class ChannelSecurityConfigurerTests {
// @formatter:on
}
}
@Test
public void requestWhenRequiresChannelConfiguredInLambdaThenRedirectsToHttps() throws Exception {
this.spring.register(RequiresChannelInLambdaConfig.class).autowire();
mvc.perform(get("/"))
.andExpect(redirectedUrl("https://localhost/"));
}
@EnableWebSecurity
static class RequiresChannelInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.requiresChannel(requiresChannel ->
requiresChannel
.anyRequest().requiresSecure()
);
// @formatter:on
}
}
}

View File

@@ -42,6 +42,7 @@ import java.util.Arrays;
import java.util.Collections;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
@@ -131,6 +132,56 @@ public class CorsConfigurerTests {
}
}
@Test
public void getWhenDefaultsInLambdaAndCrossOriginAnnotationThenRespondsWithCorsHeaders() throws Exception {
this.spring.register(MvcCorsInLambdaConfig.class).autowire();
this.mvc.perform(get("/")
.header(HttpHeaders.ORIGIN, "https://example.com"))
.andExpect(header().exists("Access-Control-Allow-Origin"))
.andExpect(header().exists("X-Content-Type-Options"));
}
@Test
public void optionsWhenDefaultsInLambdaAndCrossOriginAnnotationThenRespondsWithCorsHeaders() throws Exception {
this.spring.register(MvcCorsInLambdaConfig.class).autowire();
this.mvc.perform(options("/")
.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())
.header(HttpHeaders.ORIGIN, "https://example.com"))
.andExpect(status().isOk())
.andExpect(header().exists("Access-Control-Allow-Origin"))
.andExpect(header().exists("X-Content-Type-Options"));
}
@EnableWebMvc
@EnableWebSecurity
static class MvcCorsInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().authenticated()
)
.cors(withDefaults());
// @formatter:on
}
@RestController
@CrossOrigin(methods = {
RequestMethod.GET, RequestMethod.POST
})
static class CorsController {
@RequestMapping("/")
String hello() {
return "Hello";
}
}
}
@Test
public void getWhenCorsConfigurationSourceBeanThenRespondsWithCorsHeaders() throws Exception {
this.spring.register(ConfigSourceConfig.class).autowire();
@@ -180,6 +231,58 @@ public class CorsConfigurerTests {
}
}
@Test
public void getWhenMvcCorsInLambdaConfigAndCorsConfigurationSourceBeanThenRespondsWithCorsHeaders()
throws Exception {
this.spring.register(ConfigSourceInLambdaConfig.class).autowire();
this.mvc.perform(get("/")
.header(HttpHeaders.ORIGIN, "https://example.com"))
.andExpect(header().exists("Access-Control-Allow-Origin"))
.andExpect(header().exists("X-Content-Type-Options"));
}
@Test
public void optionsWhenMvcCorsInLambdaConfigAndCorsConfigurationSourceBeanThenRespondsWithCorsHeaders()
throws Exception {
this.spring.register(ConfigSourceInLambdaConfig.class).autowire();
this.mvc.perform(options("/")
.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())
.header(HttpHeaders.ORIGIN, "https://example.com"))
.andExpect(status().isOk())
.andExpect(header().exists("Access-Control-Allow-Origin"))
.andExpect(header().exists("X-Content-Type-Options"));
}
@EnableWebSecurity
static class ConfigSourceInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().authenticated()
)
.cors(withDefaults());
// @formatter:on
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(Collections.singletonList("*"));
corsConfiguration.setAllowedMethods(Arrays.asList(
RequestMethod.GET.name(),
RequestMethod.POST.name()));
source.registerCorsConfiguration("/**", corsConfiguration);
return source;
}
}
@Test
public void getWhenCorsFilterBeanThenRespondsWithCorsHeaders() throws Exception {
this.spring.register(CorsFilterConfig.class).autowire();
@@ -228,4 +331,54 @@ public class CorsConfigurerTests {
return new CorsFilter(source);
}
}
@Test
public void getWhenConfigSourceInLambdaConfigAndCorsFilterBeanThenRespondsWithCorsHeaders() throws Exception {
this.spring.register(CorsFilterInLambdaConfig.class).autowire();
this.mvc.perform(get("/")
.header(HttpHeaders.ORIGIN, "https://example.com"))
.andExpect(header().exists("Access-Control-Allow-Origin"))
.andExpect(header().exists("X-Content-Type-Options"));
}
@Test
public void optionsWhenConfigSourceInLambdaConfigAndCorsFilterBeanThenRespondsWithCorsHeaders() throws Exception {
this.spring.register(CorsFilterInLambdaConfig.class).autowire();
this.mvc.perform(options("/")
.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())
.header(HttpHeaders.ORIGIN, "https://example.com"))
.andExpect(status().isOk())
.andExpect(header().exists("Access-Control-Allow-Origin"))
.andExpect(header().exists("X-Content-Type-Options"));
}
@EnableWebSecurity
static class CorsFilterInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().authenticated()
)
.cors(withDefaults());
// @formatter:on
}
@Bean
CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(Collections.singletonList("*"));
corsConfiguration.setAllowedMethods(Arrays.asList(
RequestMethod.GET.name(),
RequestMethod.POST.name()));
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(source);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -75,6 +75,36 @@ public class CsrfConfigurerIgnoringRequestMatchersTests {
}
}
@Test
public void requestWhenIgnoringRequestMatchersInLambdaThenAugmentedByConfiguredRequestMatcher()
throws Exception {
this.spring.register(IgnoringRequestInLambdaMatchers.class, BasicController.class).autowire();
this.mvc.perform(get("/path"))
.andExpect(status().isForbidden());
this.mvc.perform(post("/path"))
.andExpect(status().isOk());
}
@EnableWebSecurity
static class IgnoringRequestInLambdaMatchers extends WebSecurityConfigurerAdapter {
RequestMatcher requestMatcher =
request -> HttpMethod.POST.name().equals(request.getMethod());
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf(csrf ->
csrf
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/path"))
.ignoringRequestMatchers(this.requestMatcher)
);
// @formatter:on
}
}
@Test
public void requestWhenIgnoringRequestMatcherThenUnionsWithConfiguredIgnoringAntMatchers()
throws Exception {
@@ -107,6 +137,40 @@ public class CsrfConfigurerIgnoringRequestMatchersTests {
}
}
@Test
public void requestWhenIgnoringRequestMatcherInLambdaThenUnionsWithConfiguredIgnoringAntMatchers()
throws Exception {
this.spring.register(IgnoringPathsAndMatchersInLambdaConfig.class, BasicController.class).autowire();
this.mvc.perform(put("/csrf"))
.andExpect(status().isForbidden());
this.mvc.perform(post("/csrf"))
.andExpect(status().isOk());
this.mvc.perform(put("/no-csrf"))
.andExpect(status().isOk());
}
@EnableWebSecurity
static class IgnoringPathsAndMatchersInLambdaConfig extends WebSecurityConfigurerAdapter {
RequestMatcher requestMatcher =
request -> HttpMethod.POST.name().equals(request.getMethod());
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf(csrf ->
csrf
.ignoringAntMatchers("/no-csrf")
.ignoringRequestMatchers(this.requestMatcher)
);
// @formatter:on
}
}
@RestController
public static class BasicController {
@RequestMapping("/path")

View File

@@ -55,6 +55,7 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
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.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
@@ -210,6 +211,26 @@ public class CsrfConfigurerTests {
}
}
@Test
public void postWhenCsrfDisabledInLambdaThenRespondsWithOk() throws Exception {
this.spring.register(DisableCsrfInLambdaConfig.class, BasicController.class).autowire();
this.mvc.perform(post("/"))
.andExpect(status().isOk());
}
@EnableWebSecurity
static class DisableCsrfInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf(AbstractHttpConfigurer::disable);
// @formatter:on
}
}
// SEC-2498
@Test
public void loginWhenCsrfDisabledThenRedirectsToPreviousPostRequest() throws Exception {
@@ -386,6 +407,40 @@ public class CsrfConfigurerTests {
}
}
@Test
public void requireCsrfProtectionMatcherInLambdaWhenRequestDoesNotMatchThenRespondsWithOk() throws Exception {
RequireCsrfProtectionMatcherInLambdaConfig.MATCHER = mock(RequestMatcher.class);
this.spring.register(RequireCsrfProtectionMatcherInLambdaConfig.class, BasicController.class).autowire();
when(RequireCsrfProtectionMatcherInLambdaConfig.MATCHER.matches(any()))
.thenReturn(false);
this.mvc.perform(get("/"))
.andExpect(status().isOk());
}
@Test
public void requireCsrfProtectionMatcherInLambdaWhenRequestMatchesThenRespondsWithForbidden() throws Exception {
RequireCsrfProtectionMatcherInLambdaConfig.MATCHER = mock(RequestMatcher.class);
when(RequireCsrfProtectionMatcherInLambdaConfig.MATCHER.matches(any())).thenReturn(true);
this.spring.register(RequireCsrfProtectionMatcherInLambdaConfig.class, BasicController.class).autowire();
this.mvc.perform(get("/"))
.andExpect(status().isForbidden());
}
@EnableWebSecurity
static class RequireCsrfProtectionMatcherInLambdaConfig extends WebSecurityConfigurerAdapter {
static RequestMatcher MATCHER;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf(csrf -> csrf.requireCsrfProtectionMatcher(MATCHER));
// @formatter:on
}
}
@Test
public void getWhenCustomCsrfTokenRepositoryThenRepositoryIsUsed() throws Exception {
CsrfTokenRepositoryConfig.REPO = mock(CsrfTokenRepository.class);
@@ -454,6 +509,32 @@ public class CsrfConfigurerTests {
}
}
@Test
public void getWhenCustomCsrfTokenRepositoryInLambdaThenRepositoryIsUsed() throws Exception {
CsrfTokenRepositoryInLambdaConfig.REPO = mock(CsrfTokenRepository.class);
when(CsrfTokenRepositoryInLambdaConfig.REPO.loadToken(any()))
.thenReturn(new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", "token"));
this.spring.register(CsrfTokenRepositoryInLambdaConfig.class, BasicController.class).autowire();
this.mvc.perform(get("/"))
.andExpect(status().isOk());
verify(CsrfTokenRepositoryInLambdaConfig.REPO).loadToken(any(HttpServletRequest.class));
}
@EnableWebSecurity
static class CsrfTokenRepositoryInLambdaConfig extends WebSecurityConfigurerAdapter {
static CsrfTokenRepository REPO;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.formLogin(withDefaults())
.csrf(csrf -> csrf.csrfTokenRepository(REPO));
// @formatter:on
}
}
@Test
public void getWhenCustomAccessDeniedHandlerThenHandlerIsUsed() throws Exception {
AccessDeniedHandlerConfig.DENIED_HANDLER = mock(AccessDeniedHandler.class);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -86,6 +86,48 @@ public class ExceptionHandlingConfigurerAccessDeniedHandlerTests {
}
}
@Test
@WithMockUser(roles = "ANYTHING")
public void getWhenAccessDeniedOverriddenInLambdaThenCustomizesResponseByRequest()
throws Exception {
this.spring.register(RequestMatcherBasedAccessDeniedHandlerInLambdaConfig.class).autowire();
this.mvc.perform(get("/hello"))
.andExpect(status().isIAmATeapot());
this.mvc.perform(get("/goodbye"))
.andExpect(status().isForbidden());
}
@EnableWebSecurity
static class RequestMatcherBasedAccessDeniedHandlerInLambdaConfig extends WebSecurityConfigurerAdapter {
AccessDeniedHandler teapotDeniedHandler =
(request, response, exception) ->
response.setStatus(HttpStatus.I_AM_A_TEAPOT.value());
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().denyAll()
)
.exceptionHandling(exceptionHandling ->
exceptionHandling
.defaultAccessDeniedHandlerFor(
this.teapotDeniedHandler,
new AntPathRequestMatcher("/hello/**")
)
.defaultAccessDeniedHandlerFor(
new AccessDeniedHandlerImpl(),
AnyRequestMatcher.INSTANCE
)
);
// @formatter:on
}
}
@Test
@WithMockUser(roles = "ANYTHING")
public void getWhenAccessDeniedOverriddenByOnlyOneHandlerThenAllRequestsUseThatHandler()

View File

@@ -42,6 +42,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.logout;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
@@ -195,6 +196,82 @@ public class FormLoginConfigurerTests {
}
}
@Test
public void loginWhenFormLoginDefaultsInLambdaThenHasDefaultUsernameAndPasswordParameterNames() throws Exception {
this.spring.register(FormLoginInLambdaConfig.class).autowire();
this.mockMvc.perform(formLogin().user("username", "user").password("password", "password"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("/"));
}
@Test
public void loginWhenFormLoginDefaultsInLambdaThenHasDefaultFailureUrl() throws Exception {
this.spring.register(FormLoginInLambdaConfig.class).autowire();
this.mockMvc.perform(formLogin().user("invalid"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("/login?error"));
}
@Test
public void loginWhenFormLoginDefaultsInLambdaThenHasDefaultSuccessUrl() throws Exception {
this.spring.register(FormLoginInLambdaConfig.class).autowire();
this.mockMvc.perform(formLogin())
.andExpect(status().isFound())
.andExpect(redirectedUrl("/"));
}
@Test
public void getLoginPageWhenFormLoginDefaultsInLambdaThenNotSecured() throws Exception {
this.spring.register(FormLoginInLambdaConfig.class).autowire();
this.mockMvc.perform(get("/login"))
.andExpect(status().isOk());
}
@Test
public void loginWhenFormLoginDefaultsInLambdaThenSecured() throws Exception {
this.spring.register(FormLoginInLambdaConfig.class).autowire();
this.mockMvc.perform(post("/login"))
.andExpect(status().isForbidden());
}
@Test
public void requestProtectedWhenFormLoginDefaultsInLambdaThenRedirectsToLogin() throws Exception {
this.spring.register(FormLoginInLambdaConfig.class).autowire();
this.mockMvc.perform(get("/private"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("http://localhost/login"));
}
@EnableWebSecurity
static class FormLoginInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().hasRole("USER")
)
.formLogin(withDefaults());
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication()
.withUser(PasswordEncodedUser.user());
// @formatter:on
}
}
@Test
public void getLoginPageWhenFormLoginPermitAllThenPermittedAndNoRedirect() throws Exception {
this.spring.register(FormLoginConfigPermitAll.class).autowire();
@@ -297,6 +374,34 @@ public class FormLoginConfigurerTests {
}
}
@Test
public void getLoginPageWhenCustomLoginPageInLambdaThenPermittedAndNoRedirect() throws Exception {
this.spring.register(FormLoginDefaultsInLambdaConfig.class).autowire();
this.mockMvc.perform(get("/authenticate"))
.andExpect(redirectedUrl(null));
}
@EnableWebSecurity
static class FormLoginDefaultsInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().hasRole("USER")
)
.formLogin(formLogin ->
formLogin
.loginPage("/authenticate")
.permitAll()
)
.logout(LogoutConfigurer::permitAll);
// @formatter:on
}
}
@Test
public void loginWhenCustomLoginProcessingUrlThenRedirectsToHome() throws Exception {
this.spring.register(FormLoginLoginProcessingUrlConfig.class).autowire();
@@ -340,6 +445,51 @@ public class FormLoginConfigurerTests {
}
}
@Test
public void loginWhenCustomLoginProcessingUrlInLambdaThenRedirectsToHome() throws Exception {
this.spring.register(FormLoginLoginProcessingUrlInLambdaConfig.class).autowire();
this.mockMvc.perform(formLogin("/loginCheck"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("/"));
}
@EnableWebSecurity
static class FormLoginLoginProcessingUrlInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().authenticated()
)
.formLogin(formLogin ->
formLogin
.loginProcessingUrl("/loginCheck")
.loginPage("/login")
.defaultSuccessUrl("/", true)
.permitAll()
)
.logout(logout ->
logout
.logoutSuccessUrl("/login")
.logoutUrl("/logout")
.deleteCookies("JSESSIONID")
);
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication()
.withUser(PasswordEncodedUser.user());
// @formatter:on
}
}
@Test
public void requestWhenCustomPortMapperThenPortMapperUsed() throws Exception {
FormLoginUsesPortMapperConfig.PORT_MAPPER = mock(PortMapper.class);

View File

@@ -36,6 +36,7 @@ import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
@@ -87,6 +88,36 @@ public class HeadersConfigurerTests {
}
}
@Test
public void getWhenHeadersConfiguredInLambdaThenDefaultHeadersInResponse() throws Exception {
this.spring.register(HeadersInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
.andExpect(header().string(HttpHeaders.X_CONTENT_TYPE_OPTIONS, "nosniff"))
.andExpect(header().string(HttpHeaders.X_FRAME_OPTIONS, XFrameOptionsMode.DENY.name()))
.andExpect(header().string(HttpHeaders.STRICT_TRANSPORT_SECURITY, "max-age=31536000 ; includeSubDomains"))
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate"))
.andExpect(header().string(HttpHeaders.EXPIRES, "0"))
.andExpect(header().string(HttpHeaders.PRAGMA, "no-cache"))
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block"))
.andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder(
HttpHeaders.X_CONTENT_TYPE_OPTIONS, HttpHeaders.X_FRAME_OPTIONS, HttpHeaders.STRICT_TRANSPORT_SECURITY,
HttpHeaders.CACHE_CONTROL, HttpHeaders.EXPIRES, HttpHeaders.PRAGMA, HttpHeaders.X_XSS_PROTECTION);
}
@EnableWebSecurity
static class HeadersInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.headers(withDefaults());
// @formatter:on
}
}
@Test
public void getWhenHeaderDefaultsDisabledAndContentTypeConfiguredThenOnlyContentTypeHeaderInResponse()
throws Exception {
@@ -112,6 +143,33 @@ public class HeadersConfigurerTests {
}
}
@Test
public void getWhenOnlyContentTypeConfiguredInLambdaThenOnlyContentTypeHeaderInResponse()
throws Exception {
this.spring.register(ContentTypeOptionsInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/"))
.andExpect(header().string(HttpHeaders.X_CONTENT_TYPE_OPTIONS, "nosniff"))
.andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_CONTENT_TYPE_OPTIONS);
}
@EnableWebSecurity
static class ContentTypeOptionsInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.headers(headers ->
headers
.defaultsDisabled()
.contentTypeOptions(withDefaults())
);
// @formatter:on
}
}
@Test
public void getWhenHeaderDefaultsDisabledAndFrameOptionsConfiguredThenOnlyFrameOptionsHeaderInResponse()
throws Exception {
@@ -190,6 +248,36 @@ public class HeadersConfigurerTests {
}
}
@Test
public void getWhenOnlyCacheControlConfiguredInLambdaThenCacheControlAndExpiresAndPragmaHeadersInResponse()
throws Exception {
this.spring.register(CacheControlInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate"))
.andExpect(header().string(HttpHeaders.EXPIRES, "0"))
.andExpect(header().string(HttpHeaders.PRAGMA, "no-cache"))
.andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder(HttpHeaders.CACHE_CONTROL,
HttpHeaders.EXPIRES, HttpHeaders.PRAGMA);
}
@EnableWebSecurity
static class CacheControlInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.headers(headers ->
headers
.defaultsDisabled()
.cacheControl(withDefaults())
);
// @formatter:on
}
}
@Test
public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredThenOnlyXssProtectionHeaderInResponse()
throws Exception {
@@ -215,6 +303,33 @@ public class HeadersConfigurerTests {
}
}
@Test
public void getWhenOnlyXssProtectionConfiguredInLambdaThenOnlyXssProtectionHeaderInResponse()
throws Exception {
this.spring.register(XssProtectionInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block"))
.andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);
}
@EnableWebSecurity
static class XssProtectionInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.headers(headers ->
headers
.defaultsDisabled()
.xssProtection(withDefaults())
);
// @formatter:on
}
}
@Test
public void getWhenFrameOptionsSameOriginConfiguredThenFrameOptionsHeaderHasValueSameOrigin() throws Exception {
this.spring.register(HeadersCustomSameOriginConfig.class).autowire();
@@ -237,6 +352,31 @@ public class HeadersConfigurerTests {
}
}
@Test
public void getWhenFrameOptionsSameOriginConfiguredInLambdaThenFrameOptionsHeaderHasValueSameOrigin()
throws Exception {
this.spring.register(HeadersCustomSameOriginInLambdaConfig.class).autowire();
this.mvc.perform(get("/").secure(true))
.andExpect(header().string(HttpHeaders.X_FRAME_OPTIONS, XFrameOptionsMode.SAMEORIGIN.name()))
.andReturn();
}
@EnableWebSecurity
static class HeadersCustomSameOriginInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.headers(headers ->
headers
.frameOptions(frameOptionsConfig -> frameOptionsConfig.sameOrigin())
);
// @formatter:on
}
}
@Test
public void getWhenHeaderDefaultsDisabledAndPublicHpkpWithNoPinThenNoHeadersInResponse() throws Exception {
this.spring.register(HpkpConfigNoPins.class).autowire();
@@ -465,6 +605,38 @@ public class HeadersConfigurerTests {
}
}
@Test
public void getWhenHpkpWithReportUriInLambdaThenPublicKeyPinsReportOnlyHeaderWithReportUriInResponse()
throws Exception {
this.spring.register(HpkpWithReportUriInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
.andExpect(header().string(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY,
"max-age=5184000 ; pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\" ; report-uri=\"https://example.net/pkp-report\""))
.andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY);
}
@EnableWebSecurity
static class HpkpWithReportUriInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.headers(headers ->
headers
.defaultsDisabled()
.httpPublicKeyPinning(hpkp ->
hpkp
.addSha256Pins("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=")
.reportUri("https://example.net/pkp-report")
)
);
// @formatter:on
}
}
@Test
public void getWhenContentSecurityPolicyConfiguredThenContentSecurityPolicyHeaderInResponse() throws Exception {
this.spring.register(ContentSecurityPolicyDefaultConfig.class).autowire();
@@ -515,6 +687,38 @@ public class HeadersConfigurerTests {
}
}
@Test
public void getWhenContentSecurityPolicyWithReportOnlyInLambdaThenContentSecurityPolicyReportOnlyHeaderInResponse()
throws Exception {
this.spring.register(ContentSecurityPolicyReportOnlyInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
.andExpect(header().string(HttpHeaders.CONTENT_SECURITY_POLICY_REPORT_ONLY,
"default-src 'self'; script-src trustedscripts.example.com"))
.andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.CONTENT_SECURITY_POLICY_REPORT_ONLY);
}
@EnableWebSecurity
static class ContentSecurityPolicyReportOnlyInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.headers(headers ->
headers
.defaultsDisabled()
.contentSecurityPolicy(csp ->
csp
.policyDirectives("default-src 'self'; script-src trustedscripts.example.com")
.reportOnly()
)
);
// @formatter:on
}
}
@Test
public void configureWhenContentSecurityPolicyEmptyThenException() {
assertThatThrownBy(() -> this.spring.register(ContentSecurityPolicyInvalidConfig.class).autowire())
@@ -536,6 +740,58 @@ public class HeadersConfigurerTests {
}
}
@Test
public void configureWhenContentSecurityPolicyEmptyInLambdaThenException() {
assertThatThrownBy(() -> this.spring.register(ContentSecurityPolicyInvalidInLambdaConfig.class).autowire())
.isInstanceOf(BeanCreationException.class)
.hasRootCauseInstanceOf(IllegalArgumentException.class);
}
@EnableWebSecurity
static class ContentSecurityPolicyInvalidInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.headers(headers ->
headers
.defaultsDisabled()
.contentSecurityPolicy(csp ->
csp.policyDirectives("")
)
);
// @formatter:on
}
}
@Test
public void configureWhenContentSecurityPolicyNoPolicyDirectivesInLambdaThenDefaultHeaderValue() throws Exception {
this.spring.register(ContentSecurityPolicyNoDirectivesInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
.andExpect(header().string(HttpHeaders.CONTENT_SECURITY_POLICY,
"default-src 'self'"))
.andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.CONTENT_SECURITY_POLICY);
}
@EnableWebSecurity
static class ContentSecurityPolicyNoDirectivesInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.headers(headers ->
headers
.defaultsDisabled()
.contentSecurityPolicy(withDefaults())
);
// @formatter:on
}
}
@Test
public void getWhenReferrerPolicyConfiguredThenReferrerPolicyHeaderInResponse() throws Exception {
this.spring.register(ReferrerPolicyDefaultConfig.class).autowire();
@@ -560,6 +816,32 @@ public class HeadersConfigurerTests {
}
}
@Test
public void getWhenReferrerPolicyInLambdaThenReferrerPolicyHeaderInResponse() throws Exception {
this.spring.register(ReferrerPolicyDefaultInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
.andExpect(header().string("Referrer-Policy", ReferrerPolicy.NO_REFERRER.getPolicy()))
.andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly("Referrer-Policy");
}
@EnableWebSecurity
static class ReferrerPolicyDefaultInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.headers(headers ->
headers
.defaultsDisabled()
.referrerPolicy()
);
// @formatter:on
}
}
@Test
public void getWhenReferrerPolicyConfiguredWithCustomValueThenReferrerPolicyHeaderWithCustomValueInResponse()
throws Exception {
@@ -585,6 +867,34 @@ public class HeadersConfigurerTests {
}
}
@Test
public void getWhenReferrerPolicyConfiguredWithCustomValueInLambdaThenCustomValueInResponse() throws Exception {
this.spring.register(ReferrerPolicyCustomInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
.andExpect(header().string("Referrer-Policy", ReferrerPolicy.SAME_ORIGIN.getPolicy()))
.andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly("Referrer-Policy");
}
@EnableWebSecurity
static class ReferrerPolicyCustomInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.headers(headers ->
headers
.defaultsDisabled()
.referrerPolicy(referrerPolicy ->
referrerPolicy.policy(ReferrerPolicy.SAME_ORIGIN)
)
);
// @formatter:on
}
}
@Test
public void getWhenFeaturePolicyConfiguredThenFeaturePolicyHeaderInResponse() throws Exception {
this.spring.register(FeaturePolicyConfig.class).autowire();
@@ -656,4 +966,32 @@ public class HeadersConfigurerTests {
// @formatter:on
}
}
@Test
public void getWhenHstsConfiguredWithPreloadInLambdaThenStrictTransportSecurityHeaderWithPreloadInResponse()
throws Exception {
this.spring.register(HstsWithPreloadInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
.andExpect(header().string(HttpHeaders.STRICT_TRANSPORT_SECURITY,
"max-age=31536000 ; includeSubDomains ; preload"))
.andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.STRICT_TRANSPORT_SECURITY);
}
@EnableWebSecurity
static class HstsWithPreloadInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.headers(headers ->
headers
.defaultsDisabled()
.httpStrictTransportSecurity(hstsConfig -> hstsConfig.preload(true))
);
// @formatter:on
}
}
}

View File

@@ -107,9 +107,10 @@ public class HttpBasicConfigurerTests {
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
// @formatter:on
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 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.
@@ -38,6 +38,7 @@ import org.springframework.web.context.support.AnnotationConfigWebApplicationCon
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.config.Customizer.withDefaults;
/**
* @author Rob Winch
@@ -195,6 +196,62 @@ public class HttpSecurityRequestMatchersTests {
}
}
@Test
public void requestMatchersWhenMvcMatcherInLambdaThenPathIsSecured() throws Exception {
loadConfig(RequestMatchersMvcMatcherInLambdaConfig.class);
this.request.setServletPath("/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/path.html");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/path/");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class RequestMatchersMvcMatcherInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers(requestMatchers ->
requestMatchers
.mvcMatchers("/path")
)
.httpBasic(withDefaults())
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().denyAll()
);
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
@Test
public void requestMatchersMvcMatcherServletPath() throws Exception {
loadConfig(RequestMatchersMvcMatcherServeltPathConfig.class);
@@ -260,6 +317,66 @@ public class HttpSecurityRequestMatchersTests {
}
}
@Test
public void requestMatcherWhensMvcMatcherServletPathInLambdaThenPathIsSecured() throws Exception {
loadConfig(RequestMatchersMvcMatcherServletPathInLambdaConfig.class);
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("");
this.request.setRequestURI("/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
setup();
this.request.setServletPath("/other");
this.request.setRequestURI("/other/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class RequestMatchersMvcMatcherServletPathInLambdaConfig
extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers(requestMatchers ->
requestMatchers
.mvcMatchers("/path").servletPath("/spring")
.mvcMatchers("/never-match")
)
.httpBasic(withDefaults())
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().denyAll()
);
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
public void loadConfig(Class<?>... configs) {
this.context = new AnnotationConfigWebApplicationContext();
this.context.register(configs);

View File

@@ -25,6 +25,9 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter;
import org.springframework.test.web.servlet.MockMvc;
@@ -125,4 +128,115 @@ public class JeeConfigurerTests {
// @formatter:on
}
}
@Test
public void requestWhenJeeMappableRolesInLambdaThenAuthenticatedWithMappableRoles() throws Exception {
this.spring.register(JeeMappableRolesConfig.class).autowire();
Principal user = mock(Principal.class);
when(user.getName()).thenReturn("user");
this.mvc.perform(get("/")
.principal(user)
.with(request -> {
request.addUserRole("ROLE_ADMIN");
request.addUserRole("ROLE_USER");
return request;
}))
.andExpect(authenticated().withRoles("USER"));
}
@EnableWebSecurity
public static class JeeMappableRolesConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().hasRole("USER")
)
.jee(jee ->
jee
.mappableRoles("USER")
);
// @formatter:on
}
}
@Test
public void requestWhenJeeMappableAuthoritiesInLambdaThenAuthenticatedWithMappableAuthorities() throws Exception {
this.spring.register(JeeMappableAuthoritiesConfig.class).autowire();
Principal user = mock(Principal.class);
when(user.getName()).thenReturn("user");
this.mvc.perform(get("/")
.principal(user)
.with(request -> {
request.addUserRole("ROLE_ADMIN");
request.addUserRole("ROLE_USER");
return request;
}))
.andExpect(authenticated().withAuthorities(AuthorityUtils.createAuthorityList("ROLE_USER")));
}
@EnableWebSecurity
public static class JeeMappableAuthoritiesConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().hasRole("USER")
)
.jee(jee ->
jee
.mappableAuthorities("ROLE_USER")
);
// @formatter:on
}
}
@Test
public void requestWhenCustomAuthenticatedUserDetailsServiceInLambdaThenCustomAuthenticatedUserDetailsServiceUsed()
throws Exception {
this.spring.register(JeeCustomAuthenticatedUserDetailsServiceConfig.class).autowire();
Principal user = mock(Principal.class);
User userDetails = new User("user", "N/A", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_USER"));
when(user.getName()).thenReturn("user");
when(JeeCustomAuthenticatedUserDetailsServiceConfig.authenticationUserDetailsService.loadUserDetails(any()))
.thenReturn(userDetails);
this.mvc.perform(get("/")
.principal(user)
.with(request -> {
request.addUserRole("ROLE_ADMIN");
request.addUserRole("ROLE_USER");
return request;
}))
.andExpect(authenticated().withRoles("USER"));
}
@EnableWebSecurity
public static class JeeCustomAuthenticatedUserDetailsServiceConfig extends WebSecurityConfigurerAdapter {
static AuthenticationUserDetailsService authenticationUserDetailsService =
mock(AuthenticationUserDetailsService.class);
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().hasRole("USER")
)
.jee(jee ->
jee
.authenticatedUserDetailsService(authenticationUserDetailsService)
);
// @formatter:on
}
}
}

View File

@@ -37,10 +37,15 @@ import org.springframework.test.web.servlet.MockMvc;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
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.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
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.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -77,6 +82,26 @@ public class LogoutConfigurerTests {
}
}
@Test
public void configureWhenDefaultLogoutSuccessHandlerForHasNullLogoutHandlerInLambdaThenException() {
assertThatThrownBy(() -> this.spring.register(NullLogoutSuccessHandlerInLambdaConfig.class).autowire())
.isInstanceOf(BeanCreationException.class)
.hasRootCauseInstanceOf(IllegalArgumentException.class);
}
@EnableWebSecurity
static class NullLogoutSuccessHandlerInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.logout(logout ->
logout.defaultLogoutSuccessHandlerFor(null, mock(RequestMatcher.class))
);
// @formatter:on
}
}
@Test
public void configureWhenDefaultLogoutSuccessHandlerForHasNullMatcherThenException() {
assertThatThrownBy(() -> this.spring.register(NullMatcherConfig.class).autowire())
@@ -96,6 +121,26 @@ public class LogoutConfigurerTests {
}
}
@Test
public void configureWhenDefaultLogoutSuccessHandlerForHasNullMatcherInLambdaThenException() {
assertThatThrownBy(() -> this.spring.register(NullMatcherInLambdaConfig.class).autowire())
.isInstanceOf(BeanCreationException.class)
.hasRootCauseInstanceOf(IllegalArgumentException.class);
}
@EnableWebSecurity
static class NullMatcherInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.logout(logout ->
logout.defaultLogoutSuccessHandlerFor(mock(LogoutSuccessHandler.class), null)
);
// @formatter:on
}
}
@Test
public void configureWhenRegisteringObjectPostProcessorThenInvokedOnLogoutFilter() {
this.spring.register(ObjectPostProcessorConfig.class).autowire();
@@ -263,6 +308,29 @@ public class LogoutConfigurerTests {
}
}
@Test
public void logoutWhenCustomLogoutUrlInLambdaThenRedirectsToLogin() throws Exception {
this.spring.register(CsrfDisabledAndCustomLogoutInLambdaConfig.class).autowire();
this.mvc.perform(get("/custom/logout"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("/login?logout"));
}
@EnableWebSecurity
static class CsrfDisabledAndCustomLogoutInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf()
.disable()
.logout(logout -> logout.logoutUrl("/custom/logout"));
// @formatter:on
}
}
// SEC-3170
@Test
public void configureWhenLogoutHandlerNullThenException() {
@@ -283,6 +351,24 @@ public class LogoutConfigurerTests {
}
}
@Test
public void configureWhenLogoutHandlerNullInLambdaThenException() {
assertThatThrownBy(() -> this.spring.register(NullLogoutHandlerInLambdaConfig.class).autowire())
.isInstanceOf(BeanCreationException.class)
.hasRootCauseInstanceOf(IllegalArgumentException.class);
}
@EnableWebSecurity
static class NullLogoutHandlerInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.logout(logout -> logout.addLogoutHandler(null));
// @formatter:on
}
}
// SEC-3170
@Test
public void rememberMeWhenRememberMeServicesNotLogoutHandlerThenRedirectsToLogin() throws Exception {

View File

@@ -125,9 +125,10 @@ public class NamespaceHttpBasicTests {
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests()
.anyRequest().hasRole("USER")
.and()
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().hasRole("USER")
)
.httpBasic(withDefaults());
// @formatter:on
}
@@ -174,9 +175,10 @@ public class NamespaceHttpBasicTests {
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests()
.anyRequest().hasRole("USER")
.and()
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().hasRole("USER")
)
.httpBasic(httpBasicConfig -> httpBasicConfig.realmName("Custom Realm"));
// @formatter:on
}
@@ -310,9 +312,10 @@ public class NamespaceHttpBasicTests {
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests()
.anyRequest().hasRole("USER")
.and()
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().hasRole("USER")
)
.httpBasic(httpBasicConfig ->
httpBasicConfig.authenticationEntryPoint(this.authenticationEntryPoint));
// @formatter:on

View File

@@ -41,9 +41,11 @@ import org.springframework.test.web.servlet.ResultMatcher;
import static org.assertj.core.api.Assertions.assertThat;
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.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests to verify that all the functionality of <logout> attributes is present
@@ -83,6 +85,23 @@ public class NamespaceHttpLogoutTests {
}
}
@Test
@WithMockUser
public void logoutWhenDisabledInLambdaThenRespondsWithNotFound() throws Exception {
this.spring.register(HttpLogoutDisabledInLambdaConfig.class).autowire();
this.mvc.perform(post("/logout").with(csrf()).with(user("user")))
.andExpect(status().isNotFound());
}
@EnableWebSecurity
static class HttpLogoutDisabledInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.logout(AbstractHttpConfigurer::disable);
}
}
/**
* http/logout custom
*/
@@ -112,6 +131,35 @@ public class NamespaceHttpLogoutTests {
}
}
@Test
@WithMockUser
public void logoutWhenUsingVariousCustomizationsInLambdaThenMatchesNamespace() throws Exception {
this.spring.register(CustomHttpLogoutInLambdaConfig.class).autowire();
this.mvc.perform(post("/custom-logout").with(csrf()))
.andExpect(authenticated(false))
.andExpect(redirectedUrl("/logout-success"))
.andExpect(result -> assertThat(result.getResponse().getCookies()).hasSize(1))
.andExpect(cookie().maxAge("remove", 0))
.andExpect(session(Objects::nonNull));
}
@EnableWebSecurity
static class CustomHttpLogoutInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.logout(logout ->
logout.deleteCookies("remove")
.invalidateHttpSession(false)
.logoutUrl("/custom-logout")
.logoutSuccessUrl("/logout-success")
);
// @formatter:on
}
}
/**
* http/logout@success-handler-ref
*/
@@ -141,6 +189,32 @@ public class NamespaceHttpLogoutTests {
}
}
@Test
@WithMockUser
public void logoutWhenUsingSuccessHandlerRefInLambdaThenMatchesNamespace() throws Exception {
this.spring.register(SuccessHandlerRefHttpLogoutInLambdaConfig.class).autowire();
this.mvc.perform(post("/logout").with(csrf()))
.andExpect(authenticated(false))
.andExpect(redirectedUrl("/SuccessHandlerRefHttpLogoutConfig"))
.andExpect(noCookies())
.andExpect(session(Objects::isNull));
}
@EnableWebSecurity
static class SuccessHandlerRefHttpLogoutInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
SimpleUrlLogoutSuccessHandler logoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();
logoutSuccessHandler.setDefaultTargetUrl("/SuccessHandlerRefHttpLogoutConfig");
// @formatter:off
http
.logout(logout -> logout.logoutSuccessHandler(logoutSuccessHandler));
// @formatter:on
}
}
ResultMatcher authenticated(boolean authenticated) {
return result -> assertThat(
Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())

View File

@@ -83,6 +83,32 @@ public class NamespaceHttpServerAccessDeniedHandlerTests {
return new UsernamePasswordAuthenticationToken("user", null, AuthorityUtils.NO_AUTHORITIES);
}
@Test
public void requestWhenCustomAccessDeniedPageInLambdaThenForwardedToCustomPage() throws Exception {
this.spring.register(AccessDeniedPageInLambdaConfig.class).autowire();
this.mvc.perform(get("/")
.with(authentication(user())))
.andExpect(status().isForbidden())
.andExpect(forwardedUrl("/AccessDeniedPageConfig"));
}
@EnableWebSecurity
static class AccessDeniedPageInLambdaConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().denyAll()
)
.exceptionHandling(exceptionHandling ->
exceptionHandling.accessDeniedPage("/AccessDeniedPageConfig")
);
// @formatter:on
}
}
@Test
public void requestWhenCustomAccessDeniedHandlerThenBehaviorMatchesNamespace() throws Exception {
this.spring.register(AccessDeniedHandlerRefConfig.class).autowire();
@@ -109,6 +135,40 @@ public class NamespaceHttpServerAccessDeniedHandlerTests {
}
}
@Test
public void requestWhenCustomAccessDeniedHandlerInLambdaThenBehaviorMatchesNamespace() throws Exception {
this.spring.register(AccessDeniedHandlerRefInLambdaConfig.class).autowire();
this.mvc.perform(get("/")
.with(authentication(user())));
verify(AccessDeniedHandlerRefInLambdaConfig.accessDeniedHandler)
.handle(any(HttpServletRequest.class), any(HttpServletResponse.class), any(AccessDeniedException.class));
}
@EnableWebSecurity
static class AccessDeniedHandlerRefInLambdaConfig extends WebSecurityConfigurerAdapter {
static AccessDeniedHandler accessDeniedHandler = mock(AccessDeniedHandler.class);
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().denyAll()
)
.exceptionHandling(exceptionHandling ->
exceptionHandling.accessDeniedHandler(accessDeniedHandler())
);
// @formatter:on
}
@Bean
AccessDeniedHandler accessDeniedHandler() {
return accessDeniedHandler;
}
}
private <T> T verifyBean(Class<T> beanClass) {
return verify(this.spring.getContext().getBean(beanClass));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -22,8 +22,11 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.web.PortMapperImpl;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Collections;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
@@ -61,4 +64,58 @@ public class PortMapperConfigurerTests {
.portMapper();
}
}
@Test
public void requestWhenPortMapperHttpMapsToInLambdaThenRedirectsToHttpsPort() throws Exception {
this.spring.register(HttpMapsToInLambdaConfig.class).autowire();
this.mockMvc.perform(get("http://localhost:543"))
.andExpect(redirectedUrl("https://localhost:123"));
}
@EnableWebSecurity
static class HttpMapsToInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.requiresChannel(requiresChannel ->
requiresChannel
.anyRequest().requiresSecure()
)
.portMapper(portMapper ->
portMapper
.http(543).mapsTo(123)
);
// @formatter:on
}
}
@Test
public void requestWhenCustomPortMapperInLambdaThenRedirectsToHttpsPort() throws Exception {
this.spring.register(CustomPortMapperInLambdaConfig.class).autowire();
this.mockMvc.perform(get("http://localhost:543"))
.andExpect(redirectedUrl("https://localhost:123"));
}
@EnableWebSecurity
static class CustomPortMapperInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
PortMapperImpl customPortMapper = new PortMapperImpl();
customPortMapper.setPortMappings(Collections.singletonMap("543", "123"));
// @formatter:off
http
.requiresChannel(requiresChannel ->
requiresChannel
.anyRequest().requiresSecure()
)
.portMapper(portMapper ->
portMapper
.portMapper(customPortMapper)
);
// @formatter:on
}
}
}

View File

@@ -51,6 +51,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
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.httpBasic;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
@@ -299,6 +300,45 @@ public class RememberMeConfigurerTests {
}
}
@Test
public void loginWhenRememberMeConfiguredInLambdaThenRespondsWithRememberMeCookie() throws Exception {
this.spring.register(RememberMeInLambdaConfig.class).autowire();
this.mvc.perform(post("/login")
.with(csrf())
.param("username", "user")
.param("password", "password")
.param("remember-me", "true"))
.andExpect(cookie().exists("remember-me"));
}
@EnableWebSecurity
static class RememberMeInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().hasRole("USER")
)
.formLogin(withDefaults())
.rememberMe(withDefaults());
// @formatter:on
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication()
.withUser(PasswordEncodedUser.user());
// @formatter:on
}
}
@Test
public void loginWhenRememberMeTrueAndCookieDomainThenRememberMeCookieHasDomain() throws Exception {
this.spring.register(RememberMeCookieDomainConfig.class).autowire();
@@ -337,6 +377,46 @@ public class RememberMeConfigurerTests {
}
}
@Test
public void loginWhenRememberMeTrueAndCookieDomainInLambdaThenRememberMeCookieHasDomain() throws Exception {
this.spring.register(RememberMeCookieDomainInLambdaConfig.class).autowire();
this.mvc.perform(post("/login")
.with(csrf())
.param("username", "user")
.param("password", "password")
.param("remember-me", "true"))
.andExpect(cookie().exists("remember-me"))
.andExpect(cookie().domain("remember-me", "spring.io"));
}
@EnableWebSecurity
static class RememberMeCookieDomainInLambdaConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().hasRole("USER")
)
.formLogin(withDefaults())
.rememberMe(rememberMe ->
rememberMe
.rememberMeCookieDomain("spring.io")
);
// @formatter:on
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication()
.withUser(PasswordEncodedUser.user());
// @formatter:on
}
}
@Test
public void configureWhenRememberMeCookieNameAndRememberMeServicesThenException() {
assertThatThrownBy(() -> this.spring.register(RememberMeCookieNameAndRememberMeServicesConfig.class).autowire())

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -33,6 +33,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.test.web.servlet.MockMvc;
@@ -42,6 +43,7 @@ import static org.mockito.ArgumentMatchers.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.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
@@ -271,6 +273,93 @@ public class RequestCacheConfigurerTests {
}
}
@Test
public void getWhenRequestCacheIsDisabledInLambdaThenExceptionTranslationFilterDoesNotStoreRequest() throws Exception {
this.spring.register(RequestCacheDisabledInLambdaConfig.class, DefaultSecurityConfig.class).autowire();
MockHttpSession session = (MockHttpSession)
this.mvc.perform(get("/bob"))
.andReturn().getRequest().getSession();
this.mvc.perform(formLogin(session))
.andExpect(redirectedUrl("/"));
}
@EnableWebSecurity
static class RequestCacheDisabledInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.requestCache(RequestCacheConfigurer::disable);
// @formatter:on
}
}
@Test
public void getWhenRequestCacheInLambdaThenRedirectedToCachedPage() throws Exception {
this.spring.register(RequestCacheInLambdaConfig.class, DefaultSecurityConfig.class).autowire();
MockHttpSession session = (MockHttpSession)
this.mvc.perform(get("/bob"))
.andReturn().getRequest().getSession();
this.mvc.perform(formLogin(session))
.andExpect(redirectedUrl("http://localhost/bob"));
}
@EnableWebSecurity
static class RequestCacheInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.requestCache(withDefaults());
// @formatter:on
}
}
@Test
public void getWhenCustomRequestCacheInLambdaThenCustomRequestCacheUsed() throws Exception {
this.spring.register(CustomRequestCacheInLambdaConfig.class, DefaultSecurityConfig.class).autowire();
MockHttpSession session = (MockHttpSession)
this.mvc.perform(get("/bob"))
.andReturn().getRequest().getSession();
this.mvc.perform(formLogin(session))
.andExpect(redirectedUrl("/"));
}
@EnableWebSecurity
static class CustomRequestCacheInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.requestCache(requestCache ->
requestCache
.requestCache(new NullRequestCache())
);
// @formatter:on
}
}
@EnableWebSecurity
static class DefaultSecurityConfig {

View File

@@ -71,4 +71,37 @@ public class RequestMatcherConfigurerTests {
// @formatter:on
}
}
@Test
public void authorizeRequestsWhenInvokedMultipleTimesInLambdaThenChainsPaths() throws Exception {
this.spring.register(AuthorizeRequestInLambdaConfig.class).autowire();
this.mvc.perform(get("/oauth/abc"))
.andExpect(status().isForbidden());
this.mvc.perform(get("/api/abc"))
.andExpect(status().isForbidden());
}
@EnableWebSecurity
static class AuthorizeRequestInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers(requestMatchers ->
requestMatchers
.antMatchers("/api/**")
)
.requestMatchers(requestMatchers ->
requestMatchers
.antMatchers("/oauth/**")
)
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().denyAll()
);
// @formatter:on
}
}
}

View File

@@ -28,14 +28,22 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.userdetails.PasswordEncodedUser;
import org.springframework.security.web.context.HttpRequestResponseHolder;
import org.springframework.security.web.context.NullSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import javax.servlet.http.HttpSession;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
/**
@@ -151,4 +159,97 @@ public class SecurityContextConfigurerTests {
// @formatter:on
}
}
@Test
public void requestWhenSecurityContextWithDefaultsInLambdaThenSessionIsCreated() throws Exception {
this.spring.register(SecurityContextWithDefaultsInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(formLogin()).andReturn();
HttpSession session = mvcResult.getRequest().getSession(false);
assertThat(session).isNotNull();
}
@EnableWebSecurity
static class SecurityContextWithDefaultsInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.formLogin(withDefaults())
.securityContext(withDefaults());
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication()
.withUser(PasswordEncodedUser.user());
// @formatter:on
}
}
@Test
public void requestWhenSecurityContextDisabledInLambdaThenContextNotSavedInSession() throws Exception {
this.spring.register(SecurityContextDisabledInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(formLogin()).andReturn();
HttpSession session = mvcResult.getRequest().getSession(false);
assertThat(session).isNull();
}
@EnableWebSecurity
static class SecurityContextDisabledInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.formLogin(withDefaults())
.securityContext(AbstractHttpConfigurer::disable);
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication()
.withUser(PasswordEncodedUser.user());
// @formatter:on
}
}
@Test
public void requestWhenNullSecurityContextRepositoryInLambdaThenContextNotSavedInSession() throws Exception {
this.spring.register(NullSecurityContextRepositoryInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(formLogin()).andReturn();
HttpSession session = mvcResult.getRequest().getSession(false);
assertThat(session).isNull();
}
@EnableWebSecurity
static class NullSecurityContextRepositoryInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.formLogin(withDefaults())
.securityContext(securityContext ->
securityContext
.securityContextRepository(new NullSecurityContextRepository())
);
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication()
.withUser(PasswordEncodedUser.user());
// @formatter:on
}
}
}

View File

@@ -47,6 +47,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atLeastOnce;
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.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
@@ -230,6 +231,53 @@ public class ServletApiConfigurerTests {
}
}
@Test
public void requestWhenServletApiWithDefaultsInLambdaThenUsesDefaultRolePrefix() throws Exception {
this.spring.register(ServletApiWithDefaultsInLambdaConfig.class, AdminController.class).autowire();
this.mvc.perform(get("/admin")
.with(user("user").authorities(AuthorityUtils.createAuthorityList("ROLE_ADMIN"))))
.andExpect(status().isOk());
}
@EnableWebSecurity
static class ServletApiWithDefaultsInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.servletApi(withDefaults());
// @formatter:on
}
}
@Test
public void requestWhenRolePrefixInLambdaThenUsesCustomRolePrefix() throws Exception {
this.spring.register(RolePrefixInLambdaConfig.class, AdminController.class).autowire();
this.mvc.perform(get("/admin")
.with(user("user").authorities(AuthorityUtils.createAuthorityList("PERMISSION_ADMIN"))))
.andExpect(status().isOk());
this.mvc.perform(get("/admin")
.with(user("user").authorities(AuthorityUtils.createAuthorityList("ROLE_ADMIN"))))
.andExpect(status().isForbidden());
}
@EnableWebSecurity
static class RolePrefixInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.servletApi(servletApi ->
servletApi
.rolePrefix("PERMISSION_")
);
// @formatter:on
}
}
@RestController
static class AdminController {
@GetMapping("/admin")

View File

@@ -54,6 +54,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
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.httpBasic;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -201,6 +202,51 @@ public class SessionManagementConfigurerTests {
}
}
@Test
public void authenticateWhenNewSessionFixationProtectionInLambdaThenCreatesNewSession() throws Exception {
this.spring.register(SFPNewSessionInLambdaConfig.class).autowire();
MockHttpSession givenSession = new MockHttpSession();
String givenSessionId = givenSession.getId();
givenSession.setAttribute("name", "value");
MockHttpSession resultingSession = (MockHttpSession)
this.mvc.perform(get("/auth")
.session(givenSession)
.with(httpBasic("user", "password")))
.andExpect(status().isNotFound())
.andReturn().getRequest().getSession(false);
assertThat(givenSessionId).isNotEqualTo(resultingSession.getId());
assertThat(resultingSession.getAttribute("name")).isNull();
}
@EnableWebSecurity
static class SFPNewSessionInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.sessionManagement(sessionManagement ->
sessionManagement
.sessionFixation(sessionFixation ->
sessionFixation.newSession()
)
)
.httpBasic(withDefaults());
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication()
.withUser(PasswordEncodedUser.user());
// @formatter:on
}
}
@Test
public void loginWhenUserLoggedInAndMaxSessionsIsOneThenLoginPrevented() throws Exception {
this.spring.register(ConcurrencyControlConfig.class).autowire();
@@ -262,6 +308,76 @@ public class SessionManagementConfigurerTests {
}
}
@Test
public void loginWhenUserLoggedInAndMaxSessionsOneInLambdaThenLoginPrevented() throws Exception {
this.spring.register(ConcurrencyControlInLambdaConfig.class).autowire();
this.mvc.perform(post("/login")
.with(csrf())
.param("username", "user")
.param("password", "password"));
this.mvc.perform(post("/login")
.with(csrf())
.param("username", "user")
.param("password", "password"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("/login?error"));
}
@EnableWebSecurity
static class ConcurrencyControlInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.formLogin(withDefaults())
.sessionManagement(sessionManagement ->
sessionManagement
.sessionConcurrency(sessionConcurrency ->
sessionConcurrency
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
)
);
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication()
.withUser(PasswordEncodedUser.user());
// @formatter:on
}
}
@Test
public void requestWhenSessionCreationPolicyStateLessInLambdaThenNoSessionCreated() throws Exception {
this.spring.register(SessionCreationPolicyStateLessInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/"))
.andReturn();
HttpSession session = mvcResult.getRequest().getSession(false);
assertThat(session).isNull();
}
@EnableWebSecurity
static class SessionCreationPolicyStateLessInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.sessionManagement(sessionManagement ->
sessionManagement
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
);
// @formatter:on
}
}
@Test
public void configureWhenRegisteringObjectPostProcessorThenInvokedOnSessionManagementFilter() {
ObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);

View File

@@ -38,6 +38,7 @@ import java.security.cert.X509Certificate;
import static org.mockito.ArgumentMatchers.any;
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.x509;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -122,6 +123,69 @@ public class X509ConfigurerTests {
}
}
@Test
public void x509WhenConfiguredInLambdaThenUsesDefaults() throws Exception {
this.spring.register(DefaultsInLambdaConfig.class).autowire();
X509Certificate certificate = loadCert("rod.cer");
this.mvc.perform(get("/")
.with(x509(certificate)))
.andExpect(authenticated().withUsername("rod"));
}
@EnableWebSecurity
static class DefaultsInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.x509(withDefaults());
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication()
.withUser("rod").password("password").roles("USER", "ADMIN");
// @formatter:on
}
}
@Test
public void x509WhenSubjectPrincipalRegexInLambdaThenUsesRegexToExtractPrincipal() throws Exception {
this.spring.register(SubjectPrincipalRegexInLambdaConfig.class).autowire();
X509Certificate certificate = loadCert("rodatexampledotcom.cer");
this.mvc.perform(get("/")
.with(x509(certificate)))
.andExpect(authenticated().withUsername("rod"));
}
@EnableWebSecurity
static class SubjectPrincipalRegexInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.x509(x509 ->
x509
.subjectPrincipalRegex("CN=(.*?)@example.com(?:,|$)")
);
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication()
.withUser("rod").password("password").roles("USER", "ADMIN");
// @formatter:on
}
}
private <T extends Certificate> T loadCert(String location) {
try (InputStream is = new ClassPathResource(location).getInputStream()) {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");

View File

@@ -65,6 +65,7 @@ import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -141,6 +142,19 @@ public class OAuth2ClientConfigurerTests {
"redirect_uri=http://localhost/client-1");
}
@Test
public void configureWhenOauth2ClientInLambdaThenRedirectForAuthorization() throws Exception {
this.spring.register(OAuth2ClientInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mockMvc.perform(get("/oauth2/authorization/registration-1"))
.andExpect(status().is3xxRedirection())
.andReturn();
assertThat(mvcResult.getResponse().getRedirectedUrl()).matches("https://provider.com/oauth2/authorize\\?" +
"response_type=code&client_id=client-1&" +
"scope=user&state=.{15,}&" +
"redirect_uri=http://localhost/client-1");
}
@Test
public void configureWhenAuthorizationCodeResponseSuccessThenAuthorizedClientSaved() throws Exception {
this.spring.register(OAuth2ClientConfig.class).autowire();
@@ -248,4 +262,30 @@ public class OAuth2ClientConfigurerTests {
}
}
}
@EnableWebSecurity
@EnableWebMvc
static class OAuth2ClientInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().authenticated()
)
.oauth2Client(withDefaults());
// @formatter:on
}
@Bean
public ClientRegistrationRepository clientRegistrationRepository() {
return clientRegistrationRepository;
}
@Bean
public OAuth2AuthorizedClientRepository authorizedClientRepository() {
return authorizedClientRepository;
}
}
}

View File

@@ -176,6 +176,25 @@ public class OAuth2LoginConfigurerTests {
.isInstanceOf(OAuth2UserAuthority.class).hasToString("ROLE_USER");
}
@Test
public void requestWhenOauth2LoginInLambdaThenAuthenticationContainsOauth2UserAuthority() throws Exception {
loadConfig(OAuth2LoginInLambdaConfig.class);
OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest();
this.authorizationRequestRepository.saveAuthorizationRequest(
authorizationRequest, this.request, this.response);
this.request.setParameter("code", "code123");
this.request.setParameter("state", authorizationRequest.getState());
this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);
Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1);
assertThat(authentication.getAuthorities()).first()
.isInstanceOf(OAuth2UserAuthority.class).hasToString("ROLE_USER");
}
// gh-6009
@Test
public void oauth2LoginWhenSuccessThenAuthenticationSuccessEventPublished() throws Exception {
@@ -303,6 +322,29 @@ public class OAuth2LoginConfigurerTests {
assertThat(this.response.getRedirectedUrl()).isEqualTo("https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=clientId&scope=openid+profile+email&state=state&redirect_uri=http%3A%2F%2Flocalhost%2Flogin%2Foauth2%2Fcode%2Fgoogle&custom-param1=custom-value1");
}
@Test
public void requestWhenOauth2LoginWithCustomAuthorizationRequestParametersThenParametersInRedirectedUrl()
throws Exception {
loadConfig(OAuth2LoginConfigCustomAuthorizationRequestResolverInLambda.class);
OAuth2AuthorizationRequestResolver resolver = this.context.getBean(
OAuth2LoginConfigCustomAuthorizationRequestResolverInLambda.class).resolver;
OAuth2AuthorizationRequest result = OAuth2AuthorizationRequest.authorizationCode()
.authorizationUri("https://accounts.google.com/authorize")
.clientId("client-id")
.state("adsfa")
.authorizationRequestUri("https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=clientId&scope=openid+profile+email&state=state&redirect_uri=http%3A%2F%2Flocalhost%2Flogin%2Foauth2%2Fcode%2Fgoogle&custom-param1=custom-value1")
.build();
when(resolver.resolve(any())).thenReturn(result);
String requestUri = "/oauth2/authorization/google";
this.request = new MockHttpServletRequest("GET", requestUri);
this.request.setServletPath(requestUri);
this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);
assertThat(this.response.getRedirectedUrl()).isEqualTo("https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=clientId&scope=openid+profile+email&state=state&redirect_uri=http%3A%2F%2Flocalhost%2Flogin%2Foauth2%2Fcode%2Fgoogle&custom-param1=custom-value1");
}
// gh-5347
@Test
public void oauth2LoginWithOneClientConfiguredThenRedirectForAuthorization() throws Exception {
@@ -374,6 +416,19 @@ public class OAuth2LoginConfigurerTests {
assertThat(this.response.getRedirectedUrl()).matches("http://localhost/custom-login");
}
@Test
public void requestWhenOauth2LoginWithCustomLoginPageInLambdaThenRedirectCustomLoginPage() throws Exception {
loadConfig(OAuth2LoginConfigCustomLoginPageInLambda.class);
String requestUri = "/";
this.request = new MockHttpServletRequest("GET", requestUri);
this.request.setServletPath(requestUri);
this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);
assertThat(this.response.getRedirectedUrl()).matches("http://localhost/custom-login");
}
@Test
public void oidcLogin() throws Exception {
// setup application context
@@ -400,6 +455,32 @@ public class OAuth2LoginConfigurerTests {
.isInstanceOf(OidcUserAuthority.class).hasToString("ROLE_USER");
}
@Test
public void requestWhenOauth2LoginInLambdaAndOidcThenAuthenticationContainsOidcUserAuthority() throws Exception {
// setup application context
loadConfig(OAuth2LoginInLambdaConfig.class, JwtDecoderFactoryConfig.class);
// setup authorization request
OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest("openid");
this.authorizationRequestRepository.saveAuthorizationRequest(
authorizationRequest, this.request, this.response);
// setup authentication parameters
this.request.setParameter("code", "code123");
this.request.setParameter("state", authorizationRequest.getState());
// perform test
this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);
// assertions
Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1);
assertThat(authentication.getAuthorities()).first()
.isInstanceOf(OidcUserAuthority.class).hasToString("ROLE_USER");
}
@Test
public void oidcLoginCustomWithConfigurer() throws Exception {
// setup application context
@@ -521,6 +602,30 @@ public class OAuth2LoginConfigurerTests {
}
}
@EnableWebSecurity
static class OAuth2LoginInLambdaConfig extends CommonLambdaWebSecurityConfigurerAdapter
implements ApplicationListener<AuthenticationSuccessEvent> {
static List<AuthenticationSuccessEvent> EVENTS = new ArrayList<>();
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.oauth2Login(oauth2Login ->
oauth2Login
.clientRegistrationRepository(
new InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION))
);
// @formatter:on
super.configure(http);
}
@Override
public void onApplicationEvent(AuthenticationSuccessEvent event) {
EVENTS.add(event);
}
}
@EnableWebSecurity
static class OAuth2LoginConfigCustomWithConfigurer extends CommonWebSecurityConfigurerAdapter {
@Override
@@ -586,6 +691,28 @@ public class OAuth2LoginConfigurerTests {
}
}
@EnableWebSecurity
static class OAuth2LoginConfigCustomAuthorizationRequestResolverInLambda extends CommonLambdaWebSecurityConfigurerAdapter {
private ClientRegistrationRepository clientRegistrationRepository =
new InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION);
OAuth2AuthorizationRequestResolver resolver = mock(OAuth2AuthorizationRequestResolver.class);
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Login(oauth2Login ->
oauth2Login
.clientRegistrationRepository(this.clientRegistrationRepository)
.authorizationEndpoint(authorizationEndpoint ->
authorizationEndpoint
.authorizationRequestResolver(this.resolver)
)
);
super.configure(http);
}
}
@EnableWebSecurity
static class OAuth2LoginConfigMultipleClients extends CommonWebSecurityConfigurerAdapter {
@Override
@@ -612,6 +739,23 @@ public class OAuth2LoginConfigurerTests {
}
}
@EnableWebSecurity
static class OAuth2LoginConfigCustomLoginPageInLambda extends CommonLambdaWebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.oauth2Login(oauth2Login ->
oauth2Login
.clientRegistrationRepository(
new InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION))
.loginPage("/custom-login")
);
// @formatter:on
super.configure(http);
}
}
@EnableWebSecurity
static class OAuth2LoginConfigWithOidcLogoutSuccessHandler extends CommonWebSecurityConfigurerAdapter {
@Override
@@ -667,6 +811,45 @@ public class OAuth2LoginConfigurerTests {
}
}
private static abstract class CommonLambdaWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().authenticated()
)
.securityContext(securityContext ->
securityContext
.securityContextRepository(securityContextRepository())
)
.oauth2Login(oauth2Login ->
oauth2Login
.tokenEndpoint(tokenEndpoint ->
tokenEndpoint
.accessTokenResponseClient(createOauth2AccessTokenResponseClient())
)
.userInfoEndpoint(userInfoEndpoint ->
userInfoEndpoint
.userService(createOauth2UserService())
.oidcUserService(createOidcUserService())
)
);
// @formatter:on
}
@Bean
SecurityContextRepository securityContextRepository() {
return new HttpSessionSecurityContextRepository();
}
@Bean
HttpSessionOAuth2AuthorizationRequestRepository oauth2AuthorizationRequestRepository() {
return new HttpSessionOAuth2AuthorizationRequestRepository();
}
}
@Configuration
static class JwtDecoderFactoryConfig {

View File

@@ -127,6 +127,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.oauth2.core.TestOAuth2AccessTokens.noScopes;
import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withJwkSetUri;
import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withPublicKey;
@@ -184,6 +185,19 @@ public class OAuth2ResourceServerConfigurerTests {
.andExpect(content().string("ok"));
}
@Test
public void getWhenUsingDefaultsInLambdaWithValidBearerTokenThenAcceptsRequest()
throws Exception {
this.spring.register(RestOperationsConfig.class, DefaultInLambdaConfig.class, BasicController.class).autowire();
mockRestOperations(jwks("Default"));
String token = this.token("ValidNoScopes");
this.mvc.perform(get("/").with(bearerToken(token)))
.andExpect(status().isOk())
.andExpect(content().string("ok"));
}
@Test
public void getWhenUsingJwkSetUriThenAcceptsRequest() throws Exception {
this.spring.register(WebServerConfig.class, JwkSetUriConfig.class, BasicController.class).autowire();
@@ -195,6 +209,16 @@ public class OAuth2ResourceServerConfigurerTests {
.andExpect(content().string("ok"));
}
@Test
public void getWhenUsingJwkSetUriInLambdaThenAcceptsRequest() throws Exception {
this.spring.register(WebServerConfig.class, JwkSetUriInLambdaConfig.class, BasicController.class).autowire();
mockWebServer(jwks("Default"));
String token = this.token("ValidNoScopes");
this.mvc.perform(get("/").with(bearerToken(token)))
.andExpect(status().isOk())
.andExpect(content().string("ok"));
}
@Test
public void getWhenUsingDefaultsWithExpiredBearerTokenThenInvalidToken()
@@ -756,6 +780,23 @@ public class OAuth2ResourceServerConfigurerTests {
.andExpect(content().string(JWT_SUBJECT));
}
@Test
public void requestWhenCustomJwtDecoderInLambdaOnDslThenUsed()
throws Exception {
this.spring.register(CustomJwtDecoderInLambdaOnDsl.class, BasicController.class).autowire();
CustomJwtDecoderInLambdaOnDsl config = this.spring.getContext().getBean(CustomJwtDecoderInLambdaOnDsl.class);
JwtDecoder decoder = config.decoder();
when(decoder.decode(anyString())).thenReturn(JWT);
this.mvc.perform(get("/authenticated")
.with(bearerToken(JWT_TOKEN)))
.andExpect(status().isOk())
.andExpect(content().string(JWT_SUBJECT));
}
@Test
public void requestWhenCustomJwtDecoderExposedAsBeanThenUsed()
throws Exception {
@@ -1067,6 +1108,17 @@ public class OAuth2ResourceServerConfigurerTests {
.andExpect(content().string("test-subject"));
}
@Test
public void getWhenOpaqueTokenInLambdaAndIntrospectingThenOk() throws Exception {
this.spring.register(RestOperationsConfig.class, OpaqueTokenInLambdaConfig.class, BasicController.class).autowire();
mockRestOperations(json("Active"));
this.mvc.perform(get("/authenticated")
.with(bearerToken("token")))
.andExpect(status().isOk())
.andExpect(content().string("test-subject"));
}
@Test
public void getWhenIntrospectionFailsThenUnauthorized() throws Exception {
this.spring.register(RestOperationsConfig.class, OpaqueTokenConfig.class).autowire();
@@ -1104,6 +1156,20 @@ public class OAuth2ResourceServerConfigurerTests {
verifyBean(AuthenticationProvider.class).authenticate(any(Authentication.class));
}
@Test
public void getWhenCustomIntrospectionAuthenticationManagerInLambdaThenUsed() throws Exception {
this.spring.register(OpaqueTokenAuthenticationManagerInLambdaConfig.class, BasicController.class).autowire();
when(bean(AuthenticationProvider.class).authenticate(any(Authentication.class)))
.thenReturn(INTROSPECTION_AUTHENTICATION_TOKEN);
this.mvc.perform(get("/authenticated")
.with(bearerToken("token")))
.andExpect(status().isOk())
.andExpect(content().string("mock-test-subject"));
verifyBean(AuthenticationProvider.class).authenticate(any(Authentication.class));
}
@Test
public void configureWhenOnlyIntrospectionUrlThenException() throws Exception {
assertThatCode(() -> this.spring.register(OpaqueTokenHalfConfiguredConfig.class).autowire())
@@ -1311,6 +1377,26 @@ public class OAuth2ResourceServerConfigurerTests {
}
}
@EnableWebSecurity
static class DefaultInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.antMatchers("/requires-read-scope").access("hasAuthority('SCOPE_message:read')")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer
.jwt(withDefaults())
);
// @formatter:on
}
}
@EnableWebSecurity
static class JwkSetUriConfig extends WebSecurityConfigurerAdapter {
@Value("${mockwebserver.url:https://example.org}")
@@ -1331,6 +1417,31 @@ public class OAuth2ResourceServerConfigurerTests {
}
}
@EnableWebSecurity
static class JwkSetUriInLambdaConfig extends WebSecurityConfigurerAdapter {
@Value("${mockwebserver.url:https://example.org}")
String jwkSetUri;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.antMatchers("/requires-read-scope").access("hasAuthority('SCOPE_message:read')")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer
.jwt(jwt ->
jwt
.jwkSetUri(this.jwkSetUri)
)
);
// @formatter:on
}
}
@EnableWebSecurity
static class CsrfDisabledConfig extends WebSecurityConfigurerAdapter {
@Value("${mockwebserver.url:https://example.org}")
@@ -1677,6 +1788,33 @@ public class OAuth2ResourceServerConfigurerTests {
}
}
@EnableWebSecurity
static class CustomJwtDecoderInLambdaOnDsl extends WebSecurityConfigurerAdapter {
JwtDecoder decoder = mock(JwtDecoder.class);
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer
.jwt(jwt ->
jwt
.decoder(decoder())
)
);
// @formatter:on
}
JwtDecoder decoder() {
return this.decoder;
}
}
@EnableWebSecurity
static class CustomJwtDecoderAsBean extends WebSecurityConfigurerAdapter {
@Override
@@ -1831,6 +1969,25 @@ public class OAuth2ResourceServerConfigurerTests {
}
}
@EnableWebSecurity
static class OpaqueTokenInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.antMatchers("/requires-read-scope").hasAuthority("SCOPE_message:read")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer
.opaqueToken(withDefaults())
);
// @formatter:on
}
}
@EnableWebSecurity
static class OpaqueTokenAuthenticationManagerConfig extends WebSecurityConfigurerAdapter {
@Override
@@ -1852,6 +2009,32 @@ public class OAuth2ResourceServerConfigurerTests {
}
}
@EnableWebSecurity
static class OpaqueTokenAuthenticationManagerInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer
.opaqueToken(opaqueToken ->
opaqueToken
.authenticationManager(authenticationProvider()::authenticate)
)
);
// @formatter:on
}
@Bean
public AuthenticationProvider authenticationProvider() {
return mock(AuthenticationProvider.class);
}
}
@EnableWebSecurity
static class OpaqueAndJwtConfig extends WebSecurityConfigurerAdapter {
@Override

View File

@@ -16,8 +16,13 @@
package org.springframework.security.config.annotation.web.configurers.openid;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.junit.Rule;
import org.junit.Test;
import org.openid4java.consumer.ConsumerManager;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.message.AuthRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.ObjectPostProcessor;
@@ -26,13 +31,23 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.openid.OpenIDAttribute;
import org.springframework.security.openid.OpenIDAuthenticationFilter;
import org.springframework.security.openid.OpenIDAuthenticationProvider;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.openid4java.discovery.yadis.YadisResolver.YADIS_XRDS_LOCATION;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -128,4 +143,167 @@ public class OpenIDLoginConfigurerTests {
// @formatter:on
}
}
@Test
public void requestWhenOpenIdLoginPageInLambdaThenRedirectsToLoginPAge() throws Exception {
this.spring.register(OpenIdLoginPageInLambdaConfig.class).autowire();
this.mvc.perform(get("/"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("http://localhost/login/custom"));
}
@EnableWebSecurity
static class OpenIdLoginPageInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().authenticated()
)
.openidLogin(openIdLogin ->
openIdLogin
.loginPage("/login/custom")
);
// @formatter:on
}
}
@Test
public void requestWhenAttributeExchangeConfiguredThenFetchAttributesMatchAttributeList() throws Exception {
OpenIdAttributesInLambdaConfig.CONSUMER_MANAGER = mock(ConsumerManager.class);
AuthRequest mockAuthRequest = mock(AuthRequest.class);
DiscoveryInformation mockDiscoveryInformation = mock(DiscoveryInformation.class);
when(mockAuthRequest.getDestinationUrl(anyBoolean())).thenReturn("mockUrl");
when(OpenIdAttributesInLambdaConfig.CONSUMER_MANAGER.associate(any()))
.thenReturn(mockDiscoveryInformation);
when(OpenIdAttributesInLambdaConfig.CONSUMER_MANAGER.authenticate(any(DiscoveryInformation.class), any(), any()))
.thenReturn(mockAuthRequest);
this.spring.register(OpenIdAttributesInLambdaConfig.class).autowire();
try ( MockWebServer server = new MockWebServer() ) {
String endpoint = server.url("/").toString();
server.enqueue(new MockResponse()
.addHeader(YADIS_XRDS_LOCATION, endpoint));
server.enqueue(new MockResponse()
.setBody(String.format("<XRDS><XRD><Service><URI>%s</URI></Service></XRD></XRDS>", endpoint)));
MvcResult mvcResult = this.mvc.perform(get("/login/openid")
.param(OpenIDAuthenticationFilter.DEFAULT_CLAIMED_IDENTITY_FIELD, endpoint))
.andExpect(status().isFound())
.andReturn();
Object attributeObject = mvcResult.getRequest().getSession().getAttribute("SPRING_SECURITY_OPEN_ID_ATTRIBUTES_FETCH_LIST");
assertThat(attributeObject).isInstanceOf(List.class);
List<OpenIDAttribute> attributeList = (List<OpenIDAttribute>) attributeObject;
assertThat(attributeList.stream().anyMatch(attribute ->
"nickname".equals(attribute.getName())
&& "https://schema.openid.net/namePerson/friendly".equals(attribute.getType())))
.isTrue();
assertThat(attributeList.stream().anyMatch(attribute ->
"email".equals(attribute.getName())
&& "https://schema.openid.net/contact/email".equals(attribute.getType())
&& attribute.isRequired()
&& attribute.getCount() == 2))
.isTrue();
}
}
@EnableWebSecurity
static class OpenIdAttributesInLambdaConfig extends WebSecurityConfigurerAdapter {
static ConsumerManager CONSUMER_MANAGER;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().permitAll()
)
.openidLogin(openIdLogin ->
openIdLogin
.consumerManager(CONSUMER_MANAGER)
.attributeExchange(attributeExchange ->
attributeExchange
.identifierPattern(".*")
.attribute(nicknameAttribute ->
nicknameAttribute
.name("nickname")
.type("https://schema.openid.net/namePerson/friendly")
)
.attribute(emailAttribute ->
emailAttribute
.name("email")
.type("https://schema.openid.net/contact/email")
.required(true)
.count(2)
)
)
);
// @formatter:on
}
}
@Test
public void requestWhenAttributeNameNotSpecifiedThenAttributeNameDefaulted()
throws Exception {
OpenIdAttributesNullNameConfig.CONSUMER_MANAGER = mock(ConsumerManager.class);
AuthRequest mockAuthRequest = mock(AuthRequest.class);
DiscoveryInformation mockDiscoveryInformation = mock(DiscoveryInformation.class);
when(mockAuthRequest.getDestinationUrl(anyBoolean())).thenReturn("mockUrl");
when(OpenIdAttributesNullNameConfig.CONSUMER_MANAGER.associate(any()))
.thenReturn(mockDiscoveryInformation);
when(OpenIdAttributesNullNameConfig.CONSUMER_MANAGER.authenticate(any(DiscoveryInformation.class), any(), any()))
.thenReturn(mockAuthRequest);
this.spring.register(OpenIdAttributesNullNameConfig.class).autowire();
try ( MockWebServer server = new MockWebServer() ) {
String endpoint = server.url("/").toString();
server.enqueue(new MockResponse()
.addHeader(YADIS_XRDS_LOCATION, endpoint));
server.enqueue(new MockResponse()
.setBody(String.format("<XRDS><XRD><Service><URI>%s</URI></Service></XRD></XRDS>", endpoint)));
MvcResult mvcResult = this.mvc.perform(get("/login/openid")
.param(OpenIDAuthenticationFilter.DEFAULT_CLAIMED_IDENTITY_FIELD, endpoint))
.andExpect(status().isFound())
.andReturn();
Object attributeObject = mvcResult.getRequest().getSession().getAttribute("SPRING_SECURITY_OPEN_ID_ATTRIBUTES_FETCH_LIST");
assertThat(attributeObject).isInstanceOf(List.class);
List<OpenIDAttribute> attributeList = (List<OpenIDAttribute>) attributeObject;
assertThat(attributeList).hasSize(1);
assertThat(attributeList.get(0).getName()).isEqualTo("default-attribute");
}
}
@EnableWebSecurity
static class OpenIdAttributesNullNameConfig extends WebSecurityConfigurerAdapter {
static ConsumerManager CONSUMER_MANAGER;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().permitAll()
)
.openidLogin(openIdLogin ->
openIdLogin
.consumerManager(CONSUMER_MANAGER)
.attributeExchange(attributeExchange ->
attributeExchange
.identifierPattern(".*")
.attribute(withDefaults())
)
);
// @formatter:on
}
}
}