From 51d00742f3939dbcd3b59f7c16aa9f64f1957130 Mon Sep 17 00:00:00 2001 From: Steve Riesenberg Date: Mon, 9 May 2022 10:03:27 -0500 Subject: [PATCH] Polish tests using AuthorizationCodeGrantFlow --- .../gettingStarted/SecurityConfigTests.java | 108 ++--------------- .../src/test/java/sample/jpa/JpaTests.java | 109 ++---------------- 2 files changed, 18 insertions(+), 199 deletions(-) diff --git a/docs/src/docs/asciidoc/examples/src/test/java/sample/gettingStarted/SecurityConfigTests.java b/docs/src/docs/asciidoc/examples/src/test/java/sample/gettingStarted/SecurityConfigTests.java index 1a46d839..0db6144c 100644 --- a/docs/src/docs/asciidoc/examples/src/test/java/sample/gettingStarted/SecurityConfigTests.java +++ b/docs/src/docs/asciidoc/examples/src/test/java/sample/gettingStarted/SecurityConfigTests.java @@ -15,17 +15,12 @@ */ package sample.gettingStarted; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import org.assertj.core.api.ObjectAssert; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import sample.AuthorizationCodeGrantFlow; import sample.test.SpringTestContext; import sample.test.SpringTestContextExtension; @@ -34,13 +29,9 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration; -import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.OAuth2TokenType; -import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames; import org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationConsentService; @@ -53,21 +44,9 @@ import org.springframework.security.oauth2.server.authorization.client.InMemoryR import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** * Tests for the Getting Started section of the reference documentation. @@ -76,10 +55,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @ExtendWith(SpringTestContextExtension.class) public class SecurityConfigTests { - private static final Pattern HIDDEN_STATE_INPUT_PATTERN = Pattern.compile(".+.+"); - private static final TypeReference> TOKEN_RESPONSE_TYPE_REFERENCE = new TypeReference>() { - }; - public final SpringTestContext spring = new SpringTestContext(this); @Autowired @@ -104,15 +79,20 @@ public class SecurityConfigTests { RegisteredClient registeredClient = this.registeredClientRepository.findByClientId("messaging-client"); assertThat(registeredClient).isNotNull(); - String state = performAuthorizationCodeRequest(registeredClient); + AuthorizationCodeGrantFlow authorizationCodeGrantFlow = new AuthorizationCodeGrantFlow(this.mockMvc); + authorizationCodeGrantFlow.setUsername("user"); + authorizationCodeGrantFlow.addScope("message.read"); + authorizationCodeGrantFlow.addScope("message.write"); + + String state = authorizationCodeGrantFlow.authorize(registeredClient); assertThatAuthorization(state, OAuth2ParameterNames.STATE).isNotNull(); assertThatAuthorization(state, null).isNotNull(); - String authorizationCode = performAuthorizationConsentRequest(registeredClient, state); + String authorizationCode = authorizationCodeGrantFlow.submitConsent(registeredClient, state); assertThatAuthorization(authorizationCode, OAuth2ParameterNames.CODE).isNotNull(); assertThatAuthorization(authorizationCode, null).isNotNull(); - Map tokenResponse = performTokenRequest(registeredClient, authorizationCode); + Map tokenResponse = authorizationCodeGrantFlow.getTokenResponse(registeredClient, authorizationCode); String accessToken = (String) tokenResponse.get(OAuth2ParameterNames.ACCESS_TOKEN); assertThatAuthorization(accessToken, OAuth2ParameterNames.ACCESS_TOKEN).isNotNull(); assertThatAuthorization(accessToken, null).isNotNull(); @@ -143,76 +123,6 @@ public class SecurityConfigTests { return this.authorizationService.findByToken(token, tokenType == null ? null : new OAuth2TokenType(tokenType)); } - private String performAuthorizationCodeRequest(RegisteredClient registeredClient) throws Exception { - MultiValueMap parameters = new LinkedMultiValueMap<>(); - parameters.set(OAuth2ParameterNames.RESPONSE_TYPE, OAuth2AuthorizationResponseType.CODE.getValue()); - parameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()); - parameters.set(OAuth2ParameterNames.REDIRECT_URI, registeredClient.getRedirectUris().iterator().next()); - parameters.set(OAuth2ParameterNames.SCOPE, - StringUtils.collectionToDelimitedString(registeredClient.getScopes(), " ")); - parameters.set(OAuth2ParameterNames.STATE, "state"); - - MvcResult mvcResult = this.mockMvc.perform(get("/oauth2/authorize") - .params(parameters) - .with(user("user").roles("USER"))) - .andExpect(status().isOk()) - .andExpect(header().string("content-type", containsString(MediaType.TEXT_HTML_VALUE))) - .andReturn(); - String responseHtml = mvcResult.getResponse().getContentAsString(); - Matcher matcher = HIDDEN_STATE_INPUT_PATTERN.matcher(responseHtml); - - return matcher.matches() ? matcher.group(1) : null; - } - - private String performAuthorizationConsentRequest(RegisteredClient registeredClient, String state) throws Exception { - MultiValueMap parameters = new LinkedMultiValueMap<>(); - parameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()); - parameters.set(OAuth2ParameterNames.STATE, state); - parameters.add(OAuth2ParameterNames.SCOPE, "message.read"); - parameters.add(OAuth2ParameterNames.SCOPE, "message.write"); - - MvcResult mvcResult = this.mockMvc.perform(post("/oauth2/authorize") - .params(parameters) - .with(user("user").roles("USER"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); - String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); - assertThat(redirectedUrl).isNotNull(); - assertThat(redirectedUrl).matches("http://127.0.0.1:8080/authorized\\?code=.{15,}&state=state"); - - String locationHeader = URLDecoder.decode(redirectedUrl, StandardCharsets.UTF_8.name()); - UriComponents uriComponents = UriComponentsBuilder.fromUriString(locationHeader).build(); - - return uriComponents.getQueryParams().getFirst("code"); - } - - private Map performTokenRequest(RegisteredClient registeredClient, String authorizationCode) throws Exception { - MultiValueMap parameters = new LinkedMultiValueMap<>(); - parameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()); - parameters.set(OAuth2ParameterNames.CODE, authorizationCode); - parameters.set(OAuth2ParameterNames.REDIRECT_URI, registeredClient.getRedirectUris().iterator().next()); - - HttpHeaders basicAuth = new HttpHeaders(); - basicAuth.setBasicAuth(registeredClient.getClientId(), "secret"); - - MvcResult mvcResult = this.mockMvc.perform(post("/oauth2/token") - .params(parameters) - .headers(basicAuth)) - .andExpect(status().isOk()) - .andExpect(header().string(HttpHeaders.CONTENT_TYPE, containsString(MediaType.APPLICATION_JSON_VALUE))) - .andExpect(jsonPath("$.access_token").isNotEmpty()) - .andExpect(jsonPath("$.token_type").isNotEmpty()) - .andExpect(jsonPath("$.expires_in").isNotEmpty()) - .andExpect(jsonPath("$.refresh_token").isNotEmpty()) - .andExpect(jsonPath("$.scope").isNotEmpty()) - .andExpect(jsonPath("$.id_token").isNotEmpty()) - .andReturn(); - - ObjectMapper objectMapper = new ObjectMapper(); - String responseJson = mvcResult.getResponse().getContentAsString(); - return objectMapper.readValue(responseJson, TOKEN_RESPONSE_TYPE_REFERENCE); - } - @EnableWebSecurity @EnableAutoConfiguration @ComponentScan diff --git a/docs/src/docs/asciidoc/examples/src/test/java/sample/jpa/JpaTests.java b/docs/src/docs/asciidoc/examples/src/test/java/sample/jpa/JpaTests.java index 605ea56d..7e6cc842 100644 --- a/docs/src/docs/asciidoc/examples/src/test/java/sample/jpa/JpaTests.java +++ b/docs/src/docs/asciidoc/examples/src/test/java/sample/jpa/JpaTests.java @@ -15,14 +15,8 @@ */ package sample.jpa; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jose.jwk.source.ImmutableJWKSet; import com.nimbusds.jose.jwk.source.JWKSource; @@ -30,6 +24,7 @@ import com.nimbusds.jose.proc.SecurityContext; import org.assertj.core.api.ObjectAssert; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import sample.AuthorizationCodeGrantFlow; import sample.jose.TestJwks; import sample.test.SpringTestContext; import sample.test.SpringTestContextExtension; @@ -39,13 +34,9 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration; -import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.OAuth2TokenType; -import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames; import org.springframework.security.oauth2.jwt.JwtDecoder; @@ -56,21 +47,9 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static sample.util.RegisteredClients.messagingClient; /** @@ -80,11 +59,6 @@ import static sample.util.RegisteredClients.messagingClient; */ @ExtendWith(SpringTestContextExtension.class) public class JpaTests { - - private static final Pattern HIDDEN_STATE_INPUT_PATTERN = Pattern.compile(".+.+"); - private static final TypeReference> TOKEN_RESPONSE_TYPE_REFERENCE = new TypeReference>() { - }; - public final SpringTestContext spring = new SpringTestContext(this); @Autowired @@ -109,15 +83,20 @@ public class JpaTests { RegisteredClient registeredClient = messagingClient(); this.registeredClientRepository.save(registeredClient); - String state = performAuthorizationCodeRequest(registeredClient); + AuthorizationCodeGrantFlow authorizationCodeGrantFlow = new AuthorizationCodeGrantFlow(this.mockMvc); + authorizationCodeGrantFlow.setUsername("user"); + authorizationCodeGrantFlow.addScope("message.read"); + authorizationCodeGrantFlow.addScope("message.write"); + + String state = authorizationCodeGrantFlow.authorize(registeredClient); assertThatAuthorization(state, OAuth2ParameterNames.STATE).isNotNull(); assertThatAuthorization(state, null).isNotNull(); - String authorizationCode = performAuthorizationConsentRequest(registeredClient, state); + String authorizationCode = authorizationCodeGrantFlow.submitConsent(registeredClient, state); assertThatAuthorization(authorizationCode, OAuth2ParameterNames.CODE).isNotNull(); assertThatAuthorization(authorizationCode, null).isNotNull(); - Map tokenResponse = performTokenRequest(registeredClient, authorizationCode); + Map tokenResponse = authorizationCodeGrantFlow.getTokenResponse(registeredClient, authorizationCode); String accessToken = (String) tokenResponse.get(OAuth2ParameterNames.ACCESS_TOKEN); assertThatAuthorization(accessToken, OAuth2ParameterNames.ACCESS_TOKEN).isNotNull(); assertThatAuthorization(accessToken, null).isNotNull(); @@ -148,76 +127,6 @@ public class JpaTests { return this.authorizationService.findByToken(token, tokenType == null ? null : new OAuth2TokenType(tokenType)); } - private String performAuthorizationCodeRequest(RegisteredClient registeredClient) throws Exception { - MultiValueMap parameters = new LinkedMultiValueMap<>(); - parameters.set(OAuth2ParameterNames.RESPONSE_TYPE, OAuth2AuthorizationResponseType.CODE.getValue()); - parameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()); - parameters.set(OAuth2ParameterNames.REDIRECT_URI, registeredClient.getRedirectUris().iterator().next()); - parameters.set(OAuth2ParameterNames.SCOPE, - StringUtils.collectionToDelimitedString(registeredClient.getScopes(), " ")); - parameters.set(OAuth2ParameterNames.STATE, "state"); - - MvcResult mvcResult = this.mockMvc.perform(get("/oauth2/authorize") - .params(parameters) - .with(user("user").roles("USER"))) - .andExpect(status().isOk()) - .andExpect(header().string("content-type", containsString(MediaType.TEXT_HTML_VALUE))) - .andReturn(); - String responseHtml = mvcResult.getResponse().getContentAsString(); - Matcher matcher = HIDDEN_STATE_INPUT_PATTERN.matcher(responseHtml); - - return matcher.matches() ? matcher.group(1) : null; - } - - private String performAuthorizationConsentRequest(RegisteredClient registeredClient, String state) throws Exception { - MultiValueMap parameters = new LinkedMultiValueMap<>(); - parameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()); - parameters.set(OAuth2ParameterNames.STATE, state); - parameters.add(OAuth2ParameterNames.SCOPE, "message.read"); - parameters.add(OAuth2ParameterNames.SCOPE, "message.write"); - - MvcResult mvcResult = this.mockMvc.perform(post("/oauth2/authorize") - .params(parameters) - .with(user("user").roles("USER"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); - String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); - assertThat(redirectedUrl).isNotNull(); - assertThat(redirectedUrl).matches("http://127.0.0.1:8080/authorized\\?code=.{15,}&state=state"); - - String locationHeader = URLDecoder.decode(redirectedUrl, StandardCharsets.UTF_8.name()); - UriComponents uriComponents = UriComponentsBuilder.fromUriString(locationHeader).build(); - - return uriComponents.getQueryParams().getFirst("code"); - } - - private Map performTokenRequest(RegisteredClient registeredClient, String authorizationCode) throws Exception { - MultiValueMap parameters = new LinkedMultiValueMap<>(); - parameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()); - parameters.set(OAuth2ParameterNames.CODE, authorizationCode); - parameters.set(OAuth2ParameterNames.REDIRECT_URI, registeredClient.getRedirectUris().iterator().next()); - - HttpHeaders basicAuth = new HttpHeaders(); - basicAuth.setBasicAuth(registeredClient.getClientId(), "secret"); - - MvcResult mvcResult = this.mockMvc.perform(post("/oauth2/token") - .params(parameters) - .headers(basicAuth)) - .andExpect(status().isOk()) - .andExpect(header().string(HttpHeaders.CONTENT_TYPE, containsString(MediaType.APPLICATION_JSON_VALUE))) - .andExpect(jsonPath("$.access_token").isNotEmpty()) - .andExpect(jsonPath("$.token_type").isNotEmpty()) - .andExpect(jsonPath("$.expires_in").isNotEmpty()) - .andExpect(jsonPath("$.refresh_token").isNotEmpty()) - .andExpect(jsonPath("$.scope").isNotEmpty()) - .andExpect(jsonPath("$.id_token").isNotEmpty()) - .andReturn(); - - ObjectMapper objectMapper = new ObjectMapper(); - String responseJson = mvcResult.getResponse().getContentAsString(); - return objectMapper.readValue(responseJson, TOKEN_RESPONSE_TYPE_REFERENCE); - } - @EnableWebSecurity @EnableAutoConfiguration @ComponentScan