I have a spring-boot Webflux application and I am writing some tests using Spock and Groovy.
My Controllers are secured with OAuth opaque token which I need to mock a response from introspection. My test properties are:
spring.security.oauth2.resourceserver.opaquetoken.client-id=fake_client
spring.security.oauth2.resourceserver.opaquetoken.client-secret=fake_secret
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=http://localhost:8089/api/v1/oauth/token/introspect
My test uses WebClient
as below:
webClient.post()
.uri(URL.toString()))
.accept(MediaType.APPLICATION_JSON)
.headers(http -> http.setBearerAuth("bearer_token"))
.exchange()
.expectStatus()
.is2xxSuccessful()
CodePudding user response:
I found the solution.
You have to configure WireMockServer and then stub the response. Working solution below:
@ContextConfiguration
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class SomeTest extends Specification {
private static final int PORT = 8089;
private WireMockServer wireMockServer;
def setup() {
wireMockServer = new WireMockServer(options().port(PORT))
wireMockServer.start()
WireMock.configureFor("localhost", wireMockServer.port())
def stubIntrospection() {
stubFor(post("/api/v1/oauth/token/introspect")
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.withBodyFile("path-to-my-file.json")))
}
def "my test case" () {
stubIntrospection()
//my test here
}
}
CodePudding user response:
There is no difference between Spock and JUnit when mocking spring security-context. You can provide with mocked Authentication
for both unit (@WebFluxTest
) and integration (@SpringBootTest
) with either:
org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockOpaqueToken
mutator for WebTestClient@WithMockBearerTokenAuthentication
annotation.
Mocked BearerTokenAuthentication
, Spring's auth implementation for token introspection (unless you explicitly changed it in your conf), is put in test security-context directly by annotation or mutator: authorization header is not introspected (it is completely ignored and can be omitted).
Sample (with JUnit, just adapt to your test framework) in this project:
@Test
void securedRouteWithAuthorizedPersonnelViaMutatorIsOk() throws Exception {
api
.mutateWith(mockOpaqueToken()
.attributes(attributes -> attributes.put(StandardClaimNames.PREFERRED_USERNAME, "Ch4mpy"))
.authorities(new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL")))
.get().uri("https://localhost/secured-route")
.exchange()
.expectStatus().isOk();
}
@Test
@WithMockBearerTokenAuthentication(
authorities = "ROLE_AUTHORIZED_PERSONNEL",
attributes = @OpenIdClaims(preferredUsername = "Ch4mpy"))
void securedRouteWithAuthorizedPersonnelViaAnnotationIsOk() throws Exception {
api
.get().uri("https://localhost/secured-route")
.exchange()
.expectStatus().isOk();
}
When testing other secured @Component
type than @Controller
(like @Service
or @Repository
), only test annotation is usable as there is no HTTP request:
@Import({ SecurityConfig.class, SecretRepo.class })
class SecretRepoTest {
// auto-wire tested component
@Autowired
SecretRepo secretRepo;
@Test
void whenNotAuthenticatedThenThrows() {
// call tested components methods directly (do not use MockMvc nor
// WebTestClient)
assertThrows(Exception.class, () -> secretRepo.findSecretByUsername("ch4mpy").block());
}
@Test
@WithMockBearerTokenAuthentication(attributes = @OpenIdClaims(preferredUsername = "Tonton Pirate"))
void whenAuthenticatedAsSomeoneElseThenThrows() {
assertThrows(Exception.class, () -> secretRepo.findSecretByUsername("ch4mpy").block());
}
@Test
@WithMockBearerTokenAuthentication(attributes = @OpenIdClaims(preferredUsername = "ch4mpy"))
void whenAuthenticatedWithSameUsernameThenReturns() {
assertEquals("Don't ever tell it", secretRepo.findSecretByUsername("ch4mpy").block());
}
}
Annotation is available from
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-oauth2-test</artifactId>
<scope>test</scope>
</dependency>
Which is a transient dependency of
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-webflux-introspecting-test</artifactId>
<scope>test</scope>
</dependency>
I used the later in sample to also have WebTestClientSupport
, but first is enough for just test annotations.