Add Annotated Support for WebTestClient

Fixes gh-4457
This commit is contained in:
Rob Winch 2017-07-15 21:55:23 -05:00
parent f50812c385
commit 544f39f826
5 changed files with 300 additions and 48 deletions

View File

@ -25,6 +25,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.test.context.TestSecurityContextHolder;
import org.springframework.test.web.reactive.server.MockServerConfigurer;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.reactive.server.WebTestClientConfigurer;
@ -39,6 +40,7 @@ import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Test utilities for working with Spring Security and
@ -56,7 +58,10 @@ public class SecurityMockServerConfigurers {
public static MockServerConfigurer springSecurity() {
return new MockServerConfigurer() {
public void beforeServerCreated(WebHttpHandlerBuilder builder) {
builder.filters( filters -> filters.add(0, new MutatorFilter()));
builder.filters( filters -> {
filters.add(0, new MutatorFilter());
filters.add(0, new SetupMutatorFilter(createMutator( () -> TestSecurityContextHolder.getContext().getAuthentication())));
});
}
};
}
@ -68,7 +73,7 @@ public class SecurityMockServerConfigurers {
* @return the {@link WebTestClientConfigurer} to use
*/
public static <T extends WebTestClientConfigurer & MockServerConfigurer> T mockPrincipal(Principal principal) {
return (T) new MutatorWebTestClientConfigurer(m -> m.mutate().principal(Mono.just(principal)).build());
return (T) new MutatorWebTestClientConfigurer(createMutator(() -> principal));
}
/**
@ -115,6 +120,10 @@ public class SecurityMockServerConfigurers {
return new UserExchangeMutator(username);
}
private static Function<ServerWebExchange, ServerWebExchange> createMutator(Supplier<Principal> principal) {
return m -> principal.get() == null ? m : m.mutate().principal(Mono.just(principal.get())).build();
}
/**
* Updates the WebServerExchange using {@code {@link SecurityMockServerConfigurers#mockUser(UserDetails)}. Defaults to use a
* password of "password" and granted authorities of "ROLE_USER".

View File

@ -0,0 +1,80 @@
/*
*
* * Copyright 2002-2017 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.test.web.reactive.server;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity;
/**
* @author Rob Winch
* @since 5.0
*/
abstract class AbstractMockServerConfigurersTests {
protected PrincipalController controller = new PrincipalController();
protected User.UserBuilder userBuilder = User
.withUsername("user")
.password("password")
.roles("USER");
protected void assertPrincipalCreatedFromUserDetails(Principal principal, UserDetails originalUserDetails) {
assertThat(principal).isInstanceOf(UsernamePasswordAuthenticationToken.class);
UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) principal;
assertThat(authentication.getCredentials()).isEqualTo(originalUserDetails.getPassword());
assertThat(authentication.getAuthorities()).containsOnlyElementsOf(originalUserDetails.getAuthorities());
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
assertThat(userDetails.getPassword()).isEqualTo(authentication.getCredentials());
assertThat(authentication.getAuthorities()).containsOnlyElementsOf(userDetails.getAuthorities());
}
@RestController
protected static class PrincipalController {
Principal principal;
@RequestMapping("/**")
public Principal get(Principal principal) {
this.principal = principal;
return principal;
}
public Principal removePrincipal() {
Principal result = this.principal;
this.principal = null;
return result;
}
public void assertPrincipalIsEqualTo(Principal expected) {
assertThat(this.principal).isEqualTo(expected);
this.principal = null;
}
}
}

View File

@ -0,0 +1,118 @@
/*
*
* * Copyright 2002-2017 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.test.web.reactive.server;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.test.context.TestSecurityContextHolder;
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import java.security.Principal;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockPrincipal;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity;
/**
* @author Rob Winch
* @since 5.0
*/
@RunWith(SpringRunner.class)
@SecurityTestExecutionListeners
public class SecurityMockServerConfigurersAnnotatedTests extends AbstractMockServerConfigurersTests {
WebTestClient client = WebTestClient
.bindToController(controller)
.apply(springSecurity())
.configureClient()
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.build();
@Test
@WithMockUser
public void withMockUserWhenOnMethodThenSuccess() {
client
.get()
.exchange()
.expectStatus().isOk();
Authentication authentication = TestSecurityContextHolder.getContext().getAuthentication();
controller.assertPrincipalIsEqualTo(authentication);
}
@Test
@WithMockUser
public void withMockUserWhenGlobalMockPrincipalThenOverridesAnnotation() {
Principal principal = () -> "principal";
client = WebTestClient
.bindToController(controller)
.apply(springSecurity())
.apply(mockPrincipal(principal))
.configureClient()
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.build();
client
.get()
.exchange()
.expectStatus().isOk();
controller.assertPrincipalIsEqualTo(principal);
}
@Test
@WithMockUser
public void withMockUserWhenMutateWithMockPrincipalThenOverridesAnnotation() {
Principal principal = () -> "principal";
client
.mutateWith(mockPrincipal(principal))
.get()
.exchange()
.expectStatus().isOk();
controller.assertPrincipalIsEqualTo(principal);
}
@Test
@WithMockUser
public void withMockUserWhenMutateWithMockPrincipalAndNoMutateThenOverridesAnnotationAndUsesAnnotation() {
Principal principal = () -> "principal";
client
.mutateWith(mockPrincipal(principal))
.get()
.exchange()
.expectStatus().isOk();
controller.assertPrincipalIsEqualTo(principal);
client
.get()
.exchange()
.expectStatus().isOk();
principal = controller.removePrincipal();
assertPrincipalCreatedFromUserDetails(principal, userBuilder.build());
}
}

View File

@ -0,0 +1,90 @@
/*
*
* * Copyright 2002-2017 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.test.web.reactive.server;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.test.context.TestSecurityContextHolder;
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import java.security.Principal;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity;
/**
* @author Rob Winch
* @since 5.0
*/
@WithMockUser
@RunWith(SpringRunner.class)
@SecurityTestExecutionListeners
public class SecurityMockServerConfigurersClassAnnotatedTests extends AbstractMockServerConfigurersTests {
WebTestClient client = WebTestClient
.bindToController(controller)
.apply(springSecurity())
.configureClient()
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.build();
@Test
public void wheMockUserWhenClassAnnotatedThenSuccess() {
client
.get()
.exchange()
.expectStatus().isOk()
.expectBody(String.class).consumeWith( response -> assertThat(response.getResponseBody()).contains("\"username\":\"user\""));
Authentication authentication = TestSecurityContextHolder.getContext().getAuthentication();
controller.assertPrincipalIsEqualTo(authentication);
}
@Test
@WithMockUser("method-user")
public void withMockUserWhenClassAndMethodAnnotationThenMethodOverrides() {
client
.get()
.exchange()
.expectStatus().isOk()
.expectBody(String.class).consumeWith( response -> assertThat(response.getResponseBody()).contains("\"username\":\"method-user\""));
Authentication authentication = TestSecurityContextHolder.getContext().getAuthentication();
controller.assertPrincipalIsEqualTo(authentication);
}
@Test
public void withMockUserWhenMutateWithThenMustateWithOverrides() {
client
.mutateWith(mockUser("mutateWith-mockUser"))
.get()
.exchange()
.expectStatus().isOk()
.expectBody(String.class).consumeWith( response -> assertThat(response.getResponseBody()).contains("\"username\":\"mutateWith-mockUser\""));
Principal principal = controller.removePrincipal();
assertPrincipalCreatedFromUserDetails(principal, userBuilder.username("mutateWith-mockUser").build());
}
}

View File

@ -22,25 +22,19 @@ import org.junit.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.*;
/**
* @author Rob Winch
* @since 5.0
*/
public class SecurityMockServerConfigurersTests {
PrincipalController controller = new PrincipalController();
public class SecurityMockServerConfigurersTests extends AbstractMockServerConfigurersTests {
WebTestClient client = WebTestClient
.bindToController(controller)
.apply(springSecurity())
@ -48,11 +42,6 @@ public class SecurityMockServerConfigurersTests {
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.build();
User.UserBuilder userBuilder = User
.withUsername("user")
.password("password")
.roles("USER");
@Test
public void mockPrincipalWhenLocalThenSuccess() {
Principal principal = () -> "principal";
@ -197,38 +186,4 @@ public class SecurityMockServerConfigurersTests {
assertPrincipalCreatedFromUserDetails(actual, userBuilder.build());
}
private void assertPrincipalCreatedFromUserDetails(Principal principal, UserDetails originalUserDetails) {
assertThat(principal).isInstanceOf(UsernamePasswordAuthenticationToken.class);
UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) principal;
assertThat(authentication.getCredentials()).isEqualTo(originalUserDetails.getPassword());
assertThat(authentication.getAuthorities()).containsOnlyElementsOf(originalUserDetails.getAuthorities());
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
assertThat(userDetails.getPassword()).isEqualTo(authentication.getCredentials());
assertThat(authentication.getAuthorities()).containsOnlyElementsOf(userDetails.getAuthorities());
}
@RestController
static class PrincipalController {
Principal principal;
@RequestMapping("/**")
public Principal get(Principal principal) {
this.principal = principal;
return principal;
}
public Principal removePrincipal() {
Principal result = this.principal;
this.principal = null;
return result;
}
public void assertPrincipalIsEqualTo(Principal expected) {
assertThat(this.principal).isEqualTo(expected);
this.principal = null;
}
}
}