diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurityTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurityTests.java index 62f4e1c23e..11d6433ff3 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurityTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurityTests.java @@ -16,9 +16,8 @@ package org.springframework.security.config.annotation.web.reactive; +import org.junit.Rule; import org.junit.Test; -import org.junit.experimental.runners.Enclosed; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.core.Ordered; @@ -26,18 +25,18 @@ import org.springframework.core.annotation.Order; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.config.test.SpringTestRule; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; -import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.ReactiveUserDetailsService; +import org.springframework.security.core.userdetails.User; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.test.web.reactive.server.WebTestClientBuilder; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.WebFilterChainProxy; import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher; -import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.FluxExchangeResult; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.util.LinkedMultiValueMap; @@ -56,228 +55,202 @@ import static org.springframework.web.reactive.function.client.ExchangeFilterFun * @author Rob Winch * @since 5.0 */ -@RunWith(Enclosed.class) public class EnableWebFluxSecurityTests { - @RunWith(SpringRunner.class) - public static class Defaults { - @Autowired WebFilterChainProxy springSecurityFilterChain; + @Rule + public final SpringTestRule spring = new SpringTestRule(); - @Test - public void defaultRequiresAuthentication() { - WebTestClient client = WebTestClientBuilder - .bindToWebFilters(this.springSecurityFilterChain) - .build(); + @Autowired + WebFilterChainProxy springSecurityFilterChain; - client.get() - .uri("/") - .exchange() - .expectStatus().isUnauthorized() - .expectBody().isEmpty(); - } + @Test + public void defaultRequiresAuthentication() { + this.spring.register(Config.class).autowire(); - @Test - public void authenticateWhenBasicThenNoSession() { - WebTestClient client = WebTestClientBuilder - .bindToWebFilters(this.springSecurityFilterChain) - .filter(basicAuthentication()) - .build(); + WebTestClient client = WebTestClientBuilder + .bindToWebFilters(this.springSecurityFilterChain) + .build(); - FluxExchangeResult result = client.get() - .attributes(basicAuthenticationCredentials("user", "password")).exchange() - .expectStatus() - .isOk() - .returnResult(String.class); - result.assertWithDiagnostics(() -> assertThat(result.getResponseCookies().isEmpty())); - } + client.get() + .uri("/") + .exchange() + .expectStatus().isUnauthorized() + .expectBody().isEmpty(); + } - @Test - public void defaultPopulatesReactorContext() { - Principal currentPrincipal = new TestingAuthenticationToken("user", "password", "ROLE_USER"); - WebTestClient client = WebTestClientBuilder.bindToWebFilters( - (exchange, chain) -> - chain.filter(exchange.mutate().principal(Mono.just(currentPrincipal)).build()), - this.springSecurityFilterChain, - (exchange,chain) -> - Mono.subscriberContext() - .flatMap( c -> c.>get(Authentication.class)) - .flatMap( principal -> exchange.getResponse() - .writeWith(Mono.just(toDataBuffer(principal.getName())))) - ).build(); + @Test + public void authenticateWhenBasicThenNoSession() { + this.spring.register(Config.class).autowire(); - client - .get() - .uri("/") - .exchange() - .expectStatus().isOk() - .expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo(currentPrincipal.getName())); - } - - @Test - public void defaultPopulatesReactorContextWhenAuthenticating() { - WebTestClient client = WebTestClientBuilder.bindToWebFilters( - this.springSecurityFilterChain, - (exchange,chain) -> - Mono.subscriberContext() - .flatMap( c -> c.>get(Authentication.class)) - .flatMap( principal -> exchange.getResponse() - .writeWith(Mono.just(toDataBuffer(principal.getName())))) - ) + WebTestClient client = WebTestClientBuilder + .bindToWebFilters(this.springSecurityFilterChain) .filter(basicAuthentication()) .build(); - client - .get() - .uri("/") - .attributes(basicAuthenticationCredentials("user","password")) - .exchange() - .expectStatus().isOk() - .expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo("user")); - } + FluxExchangeResult result = client.get() + .attributes(basicAuthenticationCredentials("user", "password")).exchange() + .expectStatus() + .isOk() + .returnResult(String.class); + result.assertWithDiagnostics(() -> assertThat(result.getResponseCookies().isEmpty())); + } - @EnableWebFluxSecurity - static class Config { - @Bean - public ReactiveUserDetailsService userDetailsRepository() { - return new MapReactiveUserDetailsService(User.withUsername("user") - .password("password") - .roles("USER") - .build() - ); - } + @Test + public void defaultPopulatesReactorContext() { + this.spring.register(Config.class).autowire(); + Principal currentPrincipal = new TestingAuthenticationToken("user", "password", "ROLE_USER"); + WebTestClient client = WebTestClientBuilder.bindToWebFilters( + (exchange, chain) -> + chain.filter(exchange.mutate().principal(Mono.just(currentPrincipal)).build()), + this.springSecurityFilterChain, + (exchange,chain) -> + Mono.subscriberContext() + .flatMap( c -> c.>get(Authentication.class)) + .flatMap( principal -> exchange.getResponse() + .writeWith(Mono.just(toDataBuffer(principal.getName())))) + ).build(); + + client + .get() + .uri("/") + .exchange() + .expectStatus().isOk() + .expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo(currentPrincipal.getName())); + } + + @Test + public void defaultPopulatesReactorContextWhenAuthenticating() { + this.spring.register(Config.class).autowire(); + WebTestClient client = WebTestClientBuilder.bindToWebFilters( + this.springSecurityFilterChain, + (exchange,chain) -> + Mono.subscriberContext() + .flatMap( c -> c.>get(Authentication.class)) + .flatMap( principal -> exchange.getResponse() + .writeWith(Mono.just(toDataBuffer(principal.getName())))) + ) + .filter(basicAuthentication()) + .build(); + + client + .get() + .uri("/") + .attributes(basicAuthenticationCredentials("user","password")) + .exchange() + .expectStatus().isOk() + .expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo("user")); + } + + @EnableWebFluxSecurity + static class Config { + @Bean + public ReactiveUserDetailsService userDetailsRepository() { + return new MapReactiveUserDetailsService(User.withUsername("user") + .password("password") + .roles("USER") + .build() + ); } } - @RunWith(SpringRunner.class) - public static class CustomPasswordEncoder { - @Autowired WebFilterChainProxy springSecurityFilterChain; + @Test + public void passwordEncoderBeanIsUsed() { + this.spring.register(CustomPasswordEncoderConfig.class).autowire(); + WebTestClient client = WebTestClientBuilder.bindToWebFilters( + this.springSecurityFilterChain, + (exchange,chain) -> + Mono.subscriberContext() + .flatMap( c -> c.>get(Authentication.class)) + .flatMap( principal -> exchange.getResponse() + .writeWith(Mono.just(toDataBuffer(principal.getName())))) + ) + .filter(basicAuthentication()) + .build(); - @Test - public void passwordEncoderBeanIsUsed() { - WebTestClient client = WebTestClientBuilder.bindToWebFilters( - this.springSecurityFilterChain, - (exchange,chain) -> - Mono.subscriberContext() - .flatMap( c -> c.>get(Authentication.class)) - .flatMap( principal -> exchange.getResponse() - .writeWith(Mono.just(toDataBuffer(principal.getName())))) - ) - .filter(basicAuthentication()) - .build(); + client + .get() + .uri("/") + .attributes(basicAuthenticationCredentials("user","password")) + .exchange() + .expectStatus().isOk() + .expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo("user")); + } - client - .get() - .uri("/") - .attributes(basicAuthenticationCredentials("user","password")) - .exchange() - .expectStatus().isOk() - .expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo("user")); + @EnableWebFluxSecurity + static class CustomPasswordEncoderConfig { + @Bean + public ReactiveUserDetailsService userDetailsRepository(PasswordEncoder encoder) { + return new MapReactiveUserDetailsService(User.withUsername("user") + .password(encoder.encode("password")) + .roles("USER") + .build() + ); } - @EnableWebFluxSecurity - static class Config { - @Bean - public ReactiveUserDetailsService userDetailsRepository(PasswordEncoder encoder) { - return new MapReactiveUserDetailsService(User.withUsername("user") - .password(encoder.encode("password")) - .roles("USER") - .build() - ); - } - - @Bean - public static PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } + @Bean + public static PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); } } - - @RunWith(SpringRunner.class) - public static class FormLoginTests { - @Autowired WebFilterChainProxy springSecurityFilterChain; - @Test - public void formLoginWorks() { - WebTestClient client = WebTestClientBuilder.bindToWebFilters( - this.springSecurityFilterChain, - (exchange,chain) -> - Mono.subscriberContext() - .flatMap( c -> c.>get(Authentication.class)) - .flatMap( principal -> exchange.getResponse() - .writeWith(Mono.just(toDataBuffer(principal.getName())))) - ) - .build(); + @Test + public void formLoginWorks() { + this.spring.register(Config.class).autowire(); + WebTestClient client = WebTestClientBuilder.bindToWebFilters( + this.springSecurityFilterChain, + (exchange,chain) -> + Mono.subscriberContext() + .flatMap( c -> c.>get(Authentication.class)) + .flatMap( principal -> exchange.getResponse() + .writeWith(Mono.just(toDataBuffer(principal.getName())))) + ) + .build(); - MultiValueMap data = new LinkedMultiValueMap<>(); - data.add("username", "user"); - data.add("password", "password"); - client - .post() - .uri("/login") - .body(BodyInserters.fromFormData(data)) - .exchange() - .expectStatus().is3xxRedirection() - .expectHeader().valueMatches("Location", "/"); - } - - @EnableWebFluxSecurity - static class Config { - @Bean - public ReactiveUserDetailsService userDetailsRepository() { - return new MapReactiveUserDetailsService(User.withUsername("user") - .password("password") - .roles("USER") - .build() - ); - } - } + MultiValueMap data = new LinkedMultiValueMap<>(); + data.add("username", "user"); + data.add("password", "password"); + client + .post() + .uri("/login") + .body(BodyInserters.fromFormData(data)) + .exchange() + .expectStatus().is3xxRedirection() + .expectHeader().valueMatches("Location", "/"); } - @RunWith(SpringRunner.class) - public static class MultiServerHttpSecurity { - @Autowired WebFilterChainProxy springSecurityFilterChain; + @Test + public void multiWorks() { + this.spring.register(MultiSecurityHttpConfig.class).autowire(); + WebTestClient client = WebTestClientBuilder.bindToWebFilters(this.springSecurityFilterChain).build(); - @Test - public void multiWorks() { - WebTestClient client = WebTestClientBuilder.bindToWebFilters(this.springSecurityFilterChain).build(); + client.get() + .uri("/api/test") + .exchange() + .expectStatus().isUnauthorized() + .expectBody().isEmpty(); - client.get() - .uri("/api/test") - .exchange() - .expectStatus().isUnauthorized() - .expectBody().isEmpty(); + client.get() + .uri("/test") + .exchange() + .expectStatus().isOk(); + } - client.get() - .uri("/test") - .exchange() - .expectStatus().isOk(); + @EnableWebFluxSecurity + static class MultiSecurityHttpConfig { + @Order(Ordered.HIGHEST_PRECEDENCE) @Bean public SecurityWebFilterChain apiHttpSecurity( + ServerHttpSecurity http) { + http.securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/**")) + .authorizeExchange().anyExchange().denyAll(); + return http.build(); } - @EnableWebFluxSecurity - static class Config { - @Order(Ordered.HIGHEST_PRECEDENCE) - @Bean - public SecurityWebFilterChain apiHttpSecurity(ServerHttpSecurity http) { - http - .securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/**")) - .authorizeExchange() - .anyExchange().denyAll(); - return http.build(); - } + @Bean public SecurityWebFilterChain httpSecurity(ServerHttpSecurity http) { + return http.build(); + } - @Bean - public SecurityWebFilterChain httpSecurity(ServerHttpSecurity http) { - return http.build(); - } - - @Bean - public ReactiveUserDetailsService userDetailsRepository() { - return new MapReactiveUserDetailsService(User.withUsername("user") - .password("password") - .roles("USER") - .build() - ); - } + @Bean public ReactiveUserDetailsService userDetailsRepository() { + return new MapReactiveUserDetailsService( + User.withUsername("user").password("password").roles("USER").build()); } }