From c46068fee3216d509bfe3177c5a319d70df2a506 Mon Sep 17 00:00:00 2001 From: Michael Neilson Date: Wed, 5 Nov 2025 13:41:23 -0800 Subject: [PATCH 1/3] Update for latest cwms-data-api-client and OpenID Connect changes. --- .gitignore | 1 + build.gradle | 2 +- cumulus-client/build.gradle | 2 +- .../auth/CumulusTokenProviderFactory.java | 15 ++--- .../auth/CumulusTokenUrlDiscoveryService.java | 48 -------------- .../CumulusIdentityProviderController.java | 2 +- .../auth/TestCumulusTokenProviderFactory.java | 4 +- .../TestCumulusTokenUrlDiscoveryService.java | 66 ------------------- .../client/controllers/TestCumulusMock.java | 16 ++++- gradle/libs.versions.toml | 2 +- 10 files changed, 28 insertions(+), 130 deletions(-) delete mode 100644 cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/auth/CumulusTokenUrlDiscoveryService.java delete mode 100644 cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenUrlDiscoveryService.java diff --git a/.gitignore b/.gitignore index b227d28..dad901d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ build **/bin **/.idea/ lib/ +.vscode diff --git a/build.gradle b/build.gradle index 313bca7..a233f74 100644 --- a/build.gradle +++ b/build.gradle @@ -45,7 +45,7 @@ subprojects { } jacoco { - toolVersion = "0.8.7" + toolVersion = "0.8.11" } jacocoTestReport { diff --git a/cumulus-client/build.gradle b/cumulus-client/build.gradle index 8bc0289..57be0b5 100644 --- a/cumulus-client/build.gradle +++ b/cumulus-client/build.gradle @@ -9,8 +9,8 @@ dependencies { testImplementation(libs.mockito.junit.jupiter) testImplementation(libs.jackson.databind) testImplementation(libs.jackson.jsr310) + testImplementation(libs.okhttp.mockwebserver) - testRuntimeOnly(libs.okhttp.mockwebserver) testRuntimeOnly(libs.junit.engine) } diff --git a/cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/auth/CumulusTokenProviderFactory.java b/cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/auth/CumulusTokenProviderFactory.java index 4d99ac1..46046d4 100644 --- a/cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/auth/CumulusTokenProviderFactory.java +++ b/cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/auth/CumulusTokenProviderFactory.java @@ -24,17 +24,13 @@ package mil.army.usace.hec.cumulus.client.auth; import hec.army.usace.hec.cwbi.auth.http.client.CwbiAuthSslSocketFactory; -import hec.army.usace.hec.cwbi.auth.http.client.DiscoveredCwbiAuthTokenProvider; -import hec.army.usace.hec.cwbi.auth.http.client.trustmanagers.CwbiAuthTrustManager; +import hec.army.usace.hec.cwbi.auth.http.client.CwbiAuthTokenProvider; import java.io.IOException; import java.util.Collections; import java.util.Objects; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLSocketFactory; import mil.army.usace.hec.cumulus.client.controllers.CumulusConstants; -import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; -import mil.army.usace.hec.cwms.http.client.ApiConnectionInfoBuilder; -import mil.army.usace.hec.cwms.http.client.SslSocketData; import mil.army.usace.hec.cwms.http.client.auth.OAuth2TokenProvider; public final class CumulusTokenProviderFactory { @@ -44,10 +40,9 @@ private CumulusTokenProviderFactory() { public static OAuth2TokenProvider createTokenProvider(String url, KeyManager keyManager) throws IOException { SSLSocketFactory sslSocketFactory = CwbiAuthSslSocketFactory.buildSSLSocketFactory(Collections.singletonList(Objects.requireNonNull(keyManager, "Missing required KeyManager"))); - SslSocketData sslSocketData = new SslSocketData(Objects.requireNonNull(sslSocketFactory, "Missing required SSLSocketFactory"), - CwbiAuthTrustManager.getTrustManager()); - ApiConnectionInfo apiConnectionInfo = new ApiConnectionInfoBuilder(Objects.requireNonNull(url, "Missing required url")).build(); - CumulusTokenUrlDiscoveryService tokenUrlDiscoveryService = new CumulusTokenUrlDiscoveryService(apiConnectionInfo, sslSocketData); - return new DiscoveredCwbiAuthTokenProvider(CumulusConstants.CLIENT_ID, tokenUrlDiscoveryService); + + return new CwbiAuthTokenProvider(Objects.requireNonNull(url, "Missing required url"), + CumulusConstants.CLIENT_ID, + Objects.requireNonNull(sslSocketFactory, "Missing required SSLSocketFactory")); } } diff --git a/cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/auth/CumulusTokenUrlDiscoveryService.java b/cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/auth/CumulusTokenUrlDiscoveryService.java deleted file mode 100644 index 965aa86..0000000 --- a/cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/auth/CumulusTokenUrlDiscoveryService.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2025 Hydrologic Engineering Center - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package mil.army.usace.hec.cumulus.client.auth; - -import hec.army.usace.hec.cwbi.auth.http.client.TokenUrlDiscoveryService; -import java.io.IOException; -import java.util.Objects; -import mil.army.usace.hec.cumulus.client.controllers.CumulusIdentityProviderController; -import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; -import mil.army.usace.hec.cwms.http.client.SslSocketData; - -final class CumulusTokenUrlDiscoveryService implements TokenUrlDiscoveryService { - - private final ApiConnectionInfo webServiceUrl; - private final SslSocketData sslSocketData; - - CumulusTokenUrlDiscoveryService(ApiConnectionInfo webServiceUrl, SslSocketData sslSocketData) { - this.webServiceUrl = Objects.requireNonNull(webServiceUrl, "webServiceUrl cannot be null"); - this.sslSocketData = Objects.requireNonNull(sslSocketData, "sslSocketData cannot be null"); - } - - @Override - public ApiConnectionInfo discoverTokenUrl() throws IOException { - return new CumulusIdentityProviderController() - .retrieveTokenUrl(webServiceUrl, sslSocketData); - } -} diff --git a/cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/controllers/CumulusIdentityProviderController.java b/cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/controllers/CumulusIdentityProviderController.java index 3264f74..881d606 100644 --- a/cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/controllers/CumulusIdentityProviderController.java +++ b/cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/controllers/CumulusIdentityProviderController.java @@ -39,7 +39,7 @@ public final class CumulusIdentityProviderController extends OpenIdTokenControll private static final String CONFIG_ENDPOINT = "configuration"; @Override - protected String retrieveWellKnownEndpoint(ApiConnectionInfo apiConnectionInfo) throws IOException { + public String retrieveWellKnownEndpoint(ApiConnectionInfo apiConnectionInfo) throws IOException { IdentityProviderConfiguration configuration = retrieveConfiguration(apiConnectionInfo); return configuration.getWellKnownEndpoint(); } diff --git a/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenProviderFactory.java b/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenProviderFactory.java index 1e6fb59..60be8ab 100644 --- a/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenProviderFactory.java +++ b/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenProviderFactory.java @@ -36,9 +36,11 @@ final class TestCumulusTokenProviderFactory extends TestCumulusMock { @Test void testNotNull() throws IOException { - String resource = "cumulus/json/idPConfig.json"; + String resource = "cumulus/json/openIdConfig.json"; launchMockServerWithResource(resource); + enqueueAdditionalResource(resource); // The discovery isn't particularly efficient thus we need to enqueue twice. ApiConnectionInfo webServiceUrl = buildConnectionInfo(); + System.out.println("URL: " + webServiceUrl.getApiRoot()); OAuth2TokenProvider tokenProvider = CumulusTokenProviderFactory.createTokenProvider(webServiceUrl.getApiRoot(), new KeyManager() {}); assertNotNull(tokenProvider); } diff --git a/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenUrlDiscoveryService.java b/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenUrlDiscoveryService.java deleted file mode 100644 index faf81d6..0000000 --- a/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenUrlDiscoveryService.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2025 Hydrologic Engineering Center - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package mil.army.usace.hec.cumulus.client.auth; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import hec.army.usace.hec.cwbi.auth.http.client.trustmanagers.CwbiAuthTrustManager; -import java.io.IOException; -import javax.net.ssl.SSLSocketFactory; -import mil.army.usace.hec.cumulus.client.controllers.TestCumulusMock; -import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; -import mil.army.usace.hec.cwms.http.client.SslSocketData; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -final class TestCumulusTokenUrlDiscoveryService extends TestCumulusMock { - - @Test - void testCumulusTokenUrlDiscoveryService() throws IOException { - SSLSocketFactory mockSslSocketFactory = Mockito.mock(SSLSocketFactory.class); - String resource = "cumulus/json/idPConfig.json"; - String openIdConfig = "cumulus/json/openIdConfig.json"; - ObjectMapper mapper = new ObjectMapper(); - ObjectNode node = (ObjectNode) mapper.readTree(readResourceAsString(resource)); - ApiConnectionInfo webServiceUrl = buildConnectionInfo(); - node.put("well_known_endpoint", webServiceUrl.getApiRoot() + "/.well-known/openid-configuration"); - String updatedIdpConfig = mapper.writeValueAsString(node); - enqueueMockServer(updatedIdpConfig); - enqueueMockServer(readResourceAsString(openIdConfig)); - SslSocketData sslSocketData = new SslSocketData(mockSslSocketFactory, CwbiAuthTrustManager.getTrustManager()); - CumulusTokenUrlDiscoveryService discoveryService = new CumulusTokenUrlDiscoveryService(webServiceUrl, sslSocketData); - assertEquals("https://api.example.com/auth/realms/cwbi/protocol/openid-connect/token", discoveryService.discoverTokenUrl().getApiRoot()); - } - - @Test - void testNulls() { - SSLSocketFactory mockSslSocketFactory = Mockito.mock(SSLSocketFactory.class); - SslSocketData sslSocketData = new SslSocketData(mockSslSocketFactory, CwbiAuthTrustManager.getTrustManager()); - ApiConnectionInfo webServiceUrl = buildConnectionInfo(); - assertThrows(NullPointerException.class, () -> new CumulusTokenUrlDiscoveryService(null, sslSocketData)); - assertThrows(NullPointerException.class, () -> new CumulusTokenUrlDiscoveryService(webServiceUrl, null)); - } -} diff --git a/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/controllers/TestCumulusMock.java b/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/controllers/TestCumulusMock.java index 0543add..bcd406d 100644 --- a/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/controllers/TestCumulusMock.java +++ b/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/controllers/TestCumulusMock.java @@ -79,7 +79,11 @@ ApiConnectionInfo buildConnectionInfoWithAuth() { } protected static void enqueueMockServer(String body) { - mockHttpServer.enqueue(body); + mockHttpServer.enqueue((String)body); + } + + protected void enqueueAdditionalResource(String resource) throws IOException { + mockHttpServer.enqueue(readResourceAsString(resource)); } protected void launchMockServerWithResource(String resource) throws IOException { @@ -130,6 +134,16 @@ public OAuth2Token refreshToken() { public OAuth2Token newToken() throws IOException { return null; } + + @Override + public ApiConnectionInfo getAuthUrl() { + return null; + } + + @Override + public ApiConnectionInfo getTokenUrl() { + return null; + } }; } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5b8d5ba..4d75b4d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] #HEC Dependencies -cwms-http-client = "9.2.0" +cwms-http-client = "10.2.0" #Third Party Dependencies jackson = "2.13.0" From 5f58141bcc25ca7d1d31d81a317018e333c33e29 Mon Sep 17 00:00:00 2001 From: Michael Neilson Date: Wed, 5 Nov 2025 15:24:01 -0800 Subject: [PATCH 2/3] Correct behavior of token provider. Still need to update test. --- .../client/auth/CumulusTokenProviderFactory.java | 8 ++++++-- .../auth/TestCumulusTokenProviderFactory.java | 8 +++++--- hec-build-ext.gradle | 13 +++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 hec-build-ext.gradle diff --git a/cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/auth/CumulusTokenProviderFactory.java b/cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/auth/CumulusTokenProviderFactory.java index 46046d4..6195d23 100644 --- a/cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/auth/CumulusTokenProviderFactory.java +++ b/cumulus-client/src/main/java/mil/army/usace/hec/cumulus/client/auth/CumulusTokenProviderFactory.java @@ -31,6 +31,9 @@ import javax.net.ssl.KeyManager; import javax.net.ssl.SSLSocketFactory; import mil.army.usace.hec.cumulus.client.controllers.CumulusConstants; +import mil.army.usace.hec.cumulus.client.controllers.CumulusIdentityProviderController; +import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; +import mil.army.usace.hec.cwms.http.client.ApiConnectionInfoBuilder; import mil.army.usace.hec.cwms.http.client.auth.OAuth2TokenProvider; public final class CumulusTokenProviderFactory { @@ -40,8 +43,9 @@ private CumulusTokenProviderFactory() { public static OAuth2TokenProvider createTokenProvider(String url, KeyManager keyManager) throws IOException { SSLSocketFactory sslSocketFactory = CwbiAuthSslSocketFactory.buildSSLSocketFactory(Collections.singletonList(Objects.requireNonNull(keyManager, "Missing required KeyManager"))); - - return new CwbiAuthTokenProvider(Objects.requireNonNull(url, "Missing required url"), + ApiConnectionInfo configInfo = new ApiConnectionInfoBuilder(Objects.requireNonNull(url, "Missing required url")).build(); + final String wellKnownUrl = new CumulusIdentityProviderController().retrieveWellKnownEndpoint(configInfo); + return new CwbiAuthTokenProvider(wellKnownUrl, CumulusConstants.CLIENT_ID, Objects.requireNonNull(sslSocketFactory, "Missing required SSLSocketFactory")); } diff --git a/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenProviderFactory.java b/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenProviderFactory.java index 60be8ab..dd135cd 100644 --- a/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenProviderFactory.java +++ b/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenProviderFactory.java @@ -36,9 +36,11 @@ final class TestCumulusTokenProviderFactory extends TestCumulusMock { @Test void testNotNull() throws IOException { - String resource = "cumulus/json/openIdConfig.json"; - launchMockServerWithResource(resource); - enqueueAdditionalResource(resource); // The discovery isn't particularly efficient thus we need to enqueue twice. + final String idpConfig = "cumulus/json/idPConfig.json"; + final String openIdConfig = "cumulus/json/openIdConfig.json"; + launchMockServerWithResource(idpConfig); + enqueueAdditionalResource(openIdConfig); + enqueueAdditionalResource(openIdConfig); // The discovery isn't particularly efficient thus we need to enqueue twice. ApiConnectionInfo webServiceUrl = buildConnectionInfo(); System.out.println("URL: " + webServiceUrl.getApiRoot()); OAuth2TokenProvider tokenProvider = CumulusTokenProviderFactory.createTokenProvider(webServiceUrl.getApiRoot(), new KeyManager() {}); diff --git a/hec-build-ext.gradle b/hec-build-ext.gradle new file mode 100644 index 0000000..2c9f7b5 --- /dev/null +++ b/hec-build-ext.gradle @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024 + * United States Army Corps of Engineers - Hydrologic Engineering Center (USACE/HEC) + * All Rights Reserved. USACE PROPRIETARY/CONFIDENTIAL. + * Source may not be released without written approval from HEC + */ + +includeBuild("$gradle.ext.externalLibDir") { + dependencySubstitution { + substitute(module("mil.army.usace.hec:cumulus-client")).using(project(":cumulus-client")) + substitute(module("mil.army.usace.hec:cumulus-model")).using(project(":cumulus-model")) + } +} From e410048195a4f04de4d627299b917e621d75a339 Mon Sep 17 00:00:00 2001 From: Michael Neilson Date: Thu, 6 Nov 2025 13:12:52 -0800 Subject: [PATCH 3/3] Setup a more flexible cumulus/auth mock for test. --- .../auth/TestCumulusTokenProviderFactory.java | 100 ++++++++++++++++-- ...TestCumulusIdentityProviderController.java | 2 +- .../resources/cumulus/json/idPConfig.json | 2 +- .../resources/cumulus/json/openIdConfig.json | 42 ++++---- 4 files changed, 115 insertions(+), 31 deletions(-) diff --git a/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenProviderFactory.java b/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenProviderFactory.java index dd135cd..35eea35 100644 --- a/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenProviderFactory.java +++ b/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/auth/TestCumulusTokenProviderFactory.java @@ -23,25 +23,100 @@ */ package mil.army.usace.hec.cumulus.client.auth; +import java.io.File; import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; + import javax.net.ssl.KeyManager; -import mil.army.usace.hec.cumulus.client.controllers.TestCumulusMock; import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; +import mil.army.usace.hec.cwms.http.client.ApiConnectionInfoBuilder; +import mil.army.usace.hec.cwms.http.client.MockHttpServer; import mil.army.usace.hec.cwms.http.client.auth.OAuth2TokenProvider; +import okhttp3.HttpUrl; +import okhttp3.mockwebserver.Dispatcher; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.RecordedRequest; + import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -final class TestCumulusTokenProviderFactory extends TestCumulusMock { +final class TestCumulusTokenProviderFactory { + + static MockHttpServer mockCumulusServer; + static MockHttpServer mockAuthServer; + + @BeforeAll + static void setUp() throws IOException { + mockCumulusServer = MockHttpServer.create(); + mockAuthServer = MockHttpServer.create(); + mockCumulusServer.start(); + mockAuthServer.start(); + + mockCumulusServer.getMockServer().setDispatcher(new Dispatcher() { + + @Override + public MockResponse dispatch(RecordedRequest request) throws InterruptedException { + final HttpUrl url = request.getRequestUrl(); + final String path = url.encodedPath(); + System.out.println(path); + try { + if (path.endsWith("configuration")) { + return new MockResponse().setBody(getResource("cumulus/json/idPConfig.json") + .replace("PORT", ""+mockAuthServer.getPort())); + } + } catch (IOException ex) { + fail("Couldn't process mocked request", ex); + } + return new MockResponse().setResponseCode(404).setBody("Request not mocked."); + } + }); + + mockAuthServer.getMockServer().setDispatcher(new Dispatcher() { + @Override + public MockResponse dispatch(RecordedRequest request) throws InterruptedException { + final HttpUrl url = request.getRequestUrl(); + final String path = url.encodedPath(); + System.out.println("Got request for url: " + url); + System.out.println("path: " + path); + try { + if (path.endsWith("openid-configuration")) { + return new MockResponse().setBody(getResource("cumulus/json/openIdConfig.json") + .replace("PORT", ""+mockAuthServer.getPort())); + } + } catch (IOException ex) { + fail("Couldn't process mocked request", ex); + } + return new MockResponse().setResponseCode(404).setBody("Request not mocked."); + } + }); + } + + @AfterAll + static void tearDown() throws IOException { + mockCumulusServer.shutdown(); + mockAuthServer.shutdown(); + } + + ApiConnectionInfo buildCumulusInfo() { + String baseUrl = String.format("http://localhost:%s", mockCumulusServer.getPort()); + return new ApiConnectionInfoBuilder(baseUrl).build(); + } + + ApiConnectionInfo buildAuthInfo() { + String baseUrl = String.format("http://localhost:%s", mockAuthServer.getPort()); + return new ApiConnectionInfoBuilder(baseUrl).build(); + } @Test void testNotNull() throws IOException { - final String idpConfig = "cumulus/json/idPConfig.json"; - final String openIdConfig = "cumulus/json/openIdConfig.json"; - launchMockServerWithResource(idpConfig); - enqueueAdditionalResource(openIdConfig); - enqueueAdditionalResource(openIdConfig); // The discovery isn't particularly efficient thus we need to enqueue twice. - ApiConnectionInfo webServiceUrl = buildConnectionInfo(); + ApiConnectionInfo webServiceUrl = buildCumulusInfo(); System.out.println("URL: " + webServiceUrl.getApiRoot()); OAuth2TokenProvider tokenProvider = CumulusTokenProviderFactory.createTokenProvider(webServiceUrl.getApiRoot(), new KeyManager() {}); assertNotNull(tokenProvider); @@ -52,4 +127,13 @@ void testNulls() { assertThrows(NullPointerException.class, () -> CumulusTokenProviderFactory.createTokenProvider("test", null)); assertThrows(NullPointerException.class, () -> CumulusTokenProviderFactory.createTokenProvider(null, new KeyManager() {})); } + + protected static String getResource(String resource) throws IOException { + URL resourceUrl = TestCumulusTokenProviderFactory.class.getClassLoader().getResource(resource); + if (resourceUrl == null) { + throw new IOException("Failed to get resource: " + resource); + } + Path path = new File(resourceUrl.getFile()).toPath(); + return String.join("\n", Files.readAllLines(path)); + } } diff --git a/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/controllers/TestCumulusIdentityProviderController.java b/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/controllers/TestCumulusIdentityProviderController.java index 8badf09..47511f1 100644 --- a/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/controllers/TestCumulusIdentityProviderController.java +++ b/cumulus-client/src/test/java/mil/army/usace/hec/cumulus/client/controllers/TestCumulusIdentityProviderController.java @@ -27,6 +27,6 @@ void testRetrieveTokenUrl() throws IOException { enqueueMockServer(readResourceAsString(openIdConfig)); SslSocketData sslSocketData = new SslSocketData(mockSslSocketFactory, CwbiAuthTrustManager.getTrustManager()); ApiConnectionInfo tokenUrl = new CumulusIdentityProviderController().retrieveTokenUrl(buildConnectionInfo(), sslSocketData); - assertEquals("https://api.example.com/auth/realms/cwbi/protocol/openid-connect/token", tokenUrl.getApiRoot()); + assertEquals("http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/token", tokenUrl.getApiRoot()); } } diff --git a/cumulus-client/src/test/resources/cumulus/json/idPConfig.json b/cumulus-client/src/test/resources/cumulus/json/idPConfig.json index 70b912d..075c9e5 100644 --- a/cumulus-client/src/test/resources/cumulus/json/idPConfig.json +++ b/cumulus-client/src/test/resources/cumulus/json/idPConfig.json @@ -1,4 +1,4 @@ { "token_endpoint": "https://api.example.com/oauth2/token", - "well_known_endpoint": "https://api.example.com/.well-known/openid-configuration" + "well_known_endpoint": "http://localhost:PORT/.well-known/openid-configuration" } \ No newline at end of file diff --git a/cumulus-client/src/test/resources/cumulus/json/openIdConfig.json b/cumulus-client/src/test/resources/cumulus/json/openIdConfig.json index f82e984..7369014 100644 --- a/cumulus-client/src/test/resources/cumulus/json/openIdConfig.json +++ b/cumulus-client/src/test/resources/cumulus/json/openIdConfig.json @@ -1,14 +1,14 @@ { - "issuer": "https://api.example.com/auth/realms/cwbi", - "authorization_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/auth", - "token_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/token", - "introspection_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/token/introspect", - "userinfo_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/userinfo", - "end_session_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/logout", + "issuer": "http://localhost:PORT/auth/realms/cwbi", + "authorization_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/auth", + "token_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/token", + "introspection_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/token/introspect", + "userinfo_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/userinfo", + "end_session_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/logout", "frontchannel_logout_session_supported": true, "frontchannel_logout_supported": true, - "jwks_uri": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/certs", - "check_session_iframe": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/login-status-iframe.html", + "jwks_uri": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/certs", + "check_session_iframe": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/login-status-iframe.html", "grant_types_supported": [ "authorization_code", "implicit", "refresh_token", "password", "client_credentials", "urn:ietf:params:oauth:grant-type:device_code", "urn:openid:params:grant-type:ciba" @@ -46,7 +46,7 @@ "response_modes_supported": [ "query", "fragment", "form_post", "query.jwt", "fragment.jwt", "form_post.jwt", "jwt" ], - "registration_endpoint": "https://api.example.com/auth/realms/cwbi/clients-registrations/openid-connect", + "registration_endpoint": "http://localhost:PORT/auth/realms/cwbi/clients-registrations/openid-connect", "token_endpoint_auth_methods_supported": [ "private_key_jwt", "client_secret_basic", "client_secret_post", "tls_client_auth", "client_secret_jwt" @@ -89,7 +89,7 @@ "require_request_uri_registration": true, "code_challenge_methods_supported": ["plain", "S256"], "tls_client_certificate_bound_access_tokens": true, - "revocation_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/revoke", + "revocation_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/revoke", "revocation_endpoint_auth_methods_supported": [ "private_key_jwt", "client_secret_basic", "client_secret_post", "tls_client_auth", "client_secret_jwt" @@ -100,22 +100,22 @@ ], "backchannel_logout_supported": true, "backchannel_logout_session_supported": true, - "device_authorization_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/auth/device", + "device_authorization_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/auth/device", "backchannel_token_delivery_modes_supported": ["poll", "ping"], - "backchannel_authentication_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/ext/ciba/auth", + "backchannel_authentication_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/ext/ciba/auth", "backchannel_authentication_request_signing_alg_values_supported": [ "PS384", "ES384", "RS384", "ES256", "RS256", "ES512", "PS256", "PS512", "RS512" ], "require_pushed_authorization_requests": false, - "pushed_authorization_request_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/ext/par/request", + "pushed_authorization_request_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/ext/par/request", "mtls_endpoint_aliases": { - "token_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/token", - "revocation_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/revoke", - "introspection_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/token/introspect", - "device_authorization_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/auth/device", - "registration_endpoint": "https://api.example.com/auth/realms/cwbi/clients-registrations/openid-connect", - "userinfo_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/userinfo", - "pushed_authorization_request_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/ext/par/request", - "backchannel_authentication_endpoint": "https://api.example.com/auth/realms/cwbi/protocol/openid-connect/ext/ciba/auth" + "token_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/token", + "revocation_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/revoke", + "introspection_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/token/introspect", + "device_authorization_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/auth/device", + "registration_endpoint": "http://localhost:PORT/auth/realms/cwbi/clients-registrations/openid-connect", + "userinfo_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/userinfo", + "pushed_authorization_request_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/ext/par/request", + "backchannel_authentication_endpoint": "http://localhost:PORT/auth/realms/cwbi/protocol/openid-connect/ext/ciba/auth" } }