From 9144d02fbedcb3533a33764de3b9520628538118 Mon Sep 17 00:00:00 2001 From: Enrico Vianello Date: Sun, 17 Mar 2024 22:13:21 +0100 Subject: [PATCH] Add more tests --- .../DefaultOidcConfigurationFetcher.java | 20 +-- .../jwk/OidcConfigurationFetcherTests.java | 168 +++++++++++++++--- 2 files changed, 158 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/utils/DefaultOidcConfigurationFetcher.java b/src/main/java/org/italiangrid/storm/webdav/oauth/utils/DefaultOidcConfigurationFetcher.java index 73fa6277..7a2c53c8 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/utils/DefaultOidcConfigurationFetcher.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/utils/DefaultOidcConfigurationFetcher.java @@ -84,22 +84,22 @@ public Map loadConfigurationForIssuer(String issuer) { new ParameterizedTypeReference>() {}; URI uri = UriComponentsBuilder.fromUriString(issuer + WELL_KNOWN_FRAGMENT).build().toUri(); - + ResponseEntity> response = null; try { RequestEntity request = RequestEntity.get(uri).build(); - Map conf = restTemplate.exchange(request, typeReference).getBody(); - metadataChecks(issuer, conf); - return conf; + response = restTemplate.exchange(request, typeReference); + if (response.getStatusCodeValue() != 200) { + throw new RuntimeException( + format("Received status code: %s", response.getStatusCodeValue())); + } + metadataChecks(issuer, response.getBody()); + return response.getBody(); } catch (RuntimeException e) { - final String errorMsg = - format("Unable to resolve OpenID configuration for issuer '%s' from '%s': %s", issuer, - uri, e.getMessage()); - + final String errorMsg = format("Unable to resolve OpenID configuration from '%s'", uri); if (LOG.isDebugEnabled()) { - LOG.error(errorMsg, e); + LOG.error("{}: {}", errorMsg, e.getMessage()); } - throw new OidcConfigurationResolutionError(errorMsg, e); } } diff --git a/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwk/OidcConfigurationFetcherTests.java b/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwk/OidcConfigurationFetcherTests.java index 779e7a52..71b0ec86 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwk/OidcConfigurationFetcherTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwk/OidcConfigurationFetcherTests.java @@ -17,10 +17,14 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.lenient; +import static org.springframework.http.HttpStatus.NOT_FOUND; +import static org.springframework.http.HttpStatus.OK; import java.io.File; import java.io.IOException; @@ -32,6 +36,7 @@ import org.italiangrid.storm.webdav.config.OAuthProperties; import org.italiangrid.storm.webdav.oauth.utils.DefaultOidcConfigurationFetcher; import org.italiangrid.storm.webdav.oauth.utils.OidcConfigurationFetcher; +import org.italiangrid.storm.webdav.oauth.utils.OidcConfigurationResolutionError; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -39,6 +44,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; @@ -52,7 +58,10 @@ public class OidcConfigurationFetcherTests { final static String ISSUER = "https://iam-dev.cloud.cnaf.infn.it/"; - final static String JWK_URI = "https://iam-dev.cloud.cnaf.infn.it/jwk"; + final static String JWK_URI = ISSUER + "jwk"; + + final static String ANOTHER_ISSUER = "https://iam.cloud.infn.it/"; + final static String ANOTHER_JWK_URI = ANOTHER_ISSUER + "jwk"; final static String KID = "rsa1"; @@ -67,62 +76,181 @@ public class OidcConfigurationFetcherTests { @Mock OAuthProperties oAuthProperties; + private Map getMapWithIssuerAndJwkUri(String issuer, String jwkUri) { + Map m = Maps.newHashMap(); + m.put("issuer", issuer); + m.put("jwks_uri", jwkUri); + return m; + } + @SuppressWarnings("unchecked") - private ResponseEntity> getMockedResponseFromWellKnown() { + private ResponseEntity> getWellKnownResponse(HttpStatus status, Map map) { - Map wellKnownMap = Maps.newHashMap(); - wellKnownMap.put("issuer", ISSUER); - wellKnownMap.put("jwks_uri", JWK_URI); ResponseEntity> mockedEntity = (ResponseEntity>) Mockito.mock(ResponseEntity.class); - lenient().when(mockedEntity.getBody()).thenReturn(wellKnownMap); + lenient().when(mockedEntity.getStatusCode()).thenReturn(status); + lenient().when(mockedEntity.getStatusCodeValue()).thenReturn(status.value()); + lenient().when(mockedEntity.getBody()).thenReturn(map); return mockedEntity; } - @SuppressWarnings("unchecked") - private ResponseEntity getMockedResponseFromJWKURI() throws IOException { + private String loadJwkFromFile() throws IOException { ClassLoader classLoader = getClass().getClassLoader(); File file = new File(classLoader.getResource("jwk/test-keystore.jwks").getFile()); - String data = FileUtils.readFileToString(file, "UTF-8"); + return FileUtils.readFileToString(file, "UTF-8"); + } + + @SuppressWarnings("unchecked") + private ResponseEntity getJWKURIResponse(HttpStatus status, String data) throws IOException { + ResponseEntity mockedEntity = (ResponseEntity) Mockito.mock(ResponseEntity.class); lenient().when(mockedEntity.getBody()).thenReturn(data); - lenient().when(mockedEntity.getStatusCodeValue()).thenReturn(200); + lenient().when(mockedEntity.getStatusCode()).thenReturn(status); + lenient().when(mockedEntity.getStatusCodeValue()).thenReturn(status.value()); return mockedEntity; } - - private OidcConfigurationFetcher getFetcher() throws RestClientException, IOException { - ResponseEntity> mockedResponseMapEntity = getMockedResponseFromWellKnown(); - lenient().when(restTemplate.exchange(any(), eq(typeReference))).thenReturn(mockedResponseMapEntity); - ResponseEntity mockedResponseStringEntity = getMockedResponseFromJWKURI(); - lenient().when(restTemplate.exchange(any(), eq(String.class))).thenReturn(mockedResponseStringEntity); + private OidcConfigurationFetcher getFetcher(ResponseEntity> wellKnownResponse, ResponseEntity jwkResponse) { + lenient().when(restTemplate.exchange(any(), eq(typeReference))).thenReturn(wellKnownResponse); + lenient().when(restTemplate.exchange(any(), eq(String.class))).thenReturn(jwkResponse); lenient().when(restBuilder.build()).thenReturn(restTemplate); lenient().when(restBuilder.setConnectTimeout(any())).thenReturn(restBuilder); lenient().when(restBuilder.setReadTimeout(any())).thenReturn(restBuilder); lenient().when(oAuthProperties.getRefreshTimeoutSeconds()).thenReturn(30); lenient().when(oAuthProperties.getRefreshPeriodMinutes()).thenReturn(1); - return new DefaultOidcConfigurationFetcher(restBuilder, oAuthProperties); } + private OidcConfigurationFetcher getSuccessfulFetcher() throws RestClientException, IOException { + + ResponseEntity> mockedResponseMapEntity = getWellKnownResponse(OK, getMapWithIssuerAndJwkUri(ISSUER, JWK_URI)); + ResponseEntity mockedResponseStringEntity = getJWKURIResponse(OK, loadJwkFromFile()); + return getFetcher(mockedResponseMapEntity, mockedResponseStringEntity); + } + + private OidcConfigurationFetcher getSuccessfulFetcherWithWrongIssuer() throws RestClientException, IOException { + + ResponseEntity> mockedResponseMapEntity = getWellKnownResponse(OK, getMapWithIssuerAndJwkUri(ANOTHER_ISSUER, ANOTHER_JWK_URI)); + ResponseEntity mockedResponseStringEntity = getJWKURIResponse(OK, loadJwkFromFile()); + return getFetcher(mockedResponseMapEntity, mockedResponseStringEntity); + } + + private OidcConfigurationFetcher getSuccessfulFetcherWithNoIssuer() throws RestClientException, IOException { + + Map map = getMapWithIssuerAndJwkUri(ANOTHER_ISSUER, ANOTHER_JWK_URI); + map.remove("issuer"); + ResponseEntity> mockedResponseMapEntity = getWellKnownResponse(OK, map); + ResponseEntity mockedResponseStringEntity = getJWKURIResponse(OK, loadJwkFromFile()); + return getFetcher(mockedResponseMapEntity, mockedResponseStringEntity); + } + + private OidcConfigurationFetcher getSuccessfulFetcherWithNoJwk() throws RestClientException, IOException { + + Map map = getMapWithIssuerAndJwkUri(ANOTHER_ISSUER, ANOTHER_JWK_URI); + map.remove("jwks_uri"); + ResponseEntity> mockedResponseMapEntity = getWellKnownResponse(OK, map); + ResponseEntity mockedResponseStringEntity = getJWKURIResponse(OK, loadJwkFromFile()); + return getFetcher(mockedResponseMapEntity, mockedResponseStringEntity); + } + + private OidcConfigurationFetcher getFetcherWithErrorOnFetch() throws RestClientException, IOException { + + ResponseEntity> mockedResponseMapEntity = getWellKnownResponse(NOT_FOUND, null); + return getFetcher(mockedResponseMapEntity, null); + } + + private OidcConfigurationFetcher getFetcherWithErrorOnGetJwk() throws RestClientException, IOException { + + ResponseEntity> mockedResponseMapEntity = getWellKnownResponse(OK, getMapWithIssuerAndJwkUri(ISSUER, JWK_URI)); + ResponseEntity mockedResponseStringEntity = getJWKURIResponse(NOT_FOUND, null); + return getFetcher(mockedResponseMapEntity, mockedResponseStringEntity); + } + @Test - public void fetchWellKnownEndpointTests() throws RestClientException, IOException { + public void fetchWellKnownEndpointWithSuccessTests() throws RestClientException, IOException { - OidcConfigurationFetcher fetcher = getFetcher(); + OidcConfigurationFetcher fetcher = getSuccessfulFetcher(); Map conf = fetcher.loadConfigurationForIssuer(ISSUER); assertNotNull(conf); assertThat(conf.get("issuer"), is(ISSUER)); assertThat(conf.get("jwks_uri"), is(JWK_URI)); } + @Test + public void fetchWellKnownEndpointWithErrorTests() throws RestClientException, IOException { + + OidcConfigurationFetcher fetcher = getFetcherWithErrorOnFetch(); + RuntimeException exception = assertThrows(RuntimeException.class, () -> { + fetcher.loadConfigurationForIssuer(ISSUER); + }); + String expectedMessage = "Unable to resolve OpenID configuration from '" + ISSUER + ".well-known/openid-configuration'"; + String actualMessage = exception.getMessage(); + + assertEquals(expectedMessage, actualMessage); + } + + @Test + public void fetchWellKnownEndpointWrongIssuerTests() throws RestClientException, IOException { + + OidcConfigurationFetcher fetcher = getSuccessfulFetcherWithWrongIssuer(); + OidcConfigurationResolutionError exception = assertThrows(OidcConfigurationResolutionError.class, () -> { + fetcher.loadConfigurationForIssuer(ISSUER); + }); + String expectedMessage = "Unable to resolve OpenID configuration from '" + ISSUER + ".well-known/openid-configuration'"; + String actualMessage = exception.getMessage(); + + assertEquals(expectedMessage, actualMessage); + + } + + @Test + public void fetchWellKnownEndpointNoIssuerTests() throws RestClientException, IOException { + + OidcConfigurationFetcher fetcher = getSuccessfulFetcherWithNoIssuer(); + OidcConfigurationResolutionError exception = assertThrows(OidcConfigurationResolutionError.class, () -> { + fetcher.loadConfigurationForIssuer(ISSUER); + }); + String expectedMessage = "Unable to resolve OpenID configuration from '" + ISSUER + ".well-known/openid-configuration'"; + String actualMessage = exception.getMessage(); + + assertEquals(expectedMessage, actualMessage); + } + + @Test + public void fetchWellKnownEndpointNoJwkTests() throws RestClientException, IOException { + + OidcConfigurationFetcher fetcher = getSuccessfulFetcherWithNoJwk(); + OidcConfigurationResolutionError exception = assertThrows(OidcConfigurationResolutionError.class, () -> { + fetcher.loadConfigurationForIssuer(ISSUER); + }); + String expectedMessage = "Unable to resolve OpenID configuration from '" + ISSUER + ".well-known/openid-configuration'"; + String actualMessage = exception.getMessage(); + + assertEquals(expectedMessage, actualMessage); + } + @Test public void fetchJWKEndpointTests() throws RestClientException, IOException, RemoteKeySourceException, ParseException { - OidcConfigurationFetcher fetcher = getFetcher(); + OidcConfigurationFetcher fetcher = getSuccessfulFetcher(); JWKSet key = JWKSet.parse(fetcher.loadJWKSourceForURL(URI.create(JWK_URI))); assertNotNull(key.getKeyByKeyId(KID)); assertThat(key.getKeyByKeyId(KID).getKeyType(), is(KeyType.RSA)); } + + @Test + public void fetcJWKEndpointWithErrorTests() throws RestClientException, IOException { + + OidcConfigurationFetcher fetcher = getFetcherWithErrorOnGetJwk(); + final URI jwkUri = URI.create(JWK_URI); + RemoteKeySourceException exception = assertThrows(RemoteKeySourceException.class, () -> { + fetcher.loadJWKSourceForURL(jwkUri); + }); + String expectedMessage = "Unable to get JWK from '" + jwkUri + "'"; + String actualMessage = exception.getMessage(); + + assertEquals(expectedMessage, actualMessage); + } }