From f647f419bb2cc9fea7422ee39ebad3afe8da44b5 Mon Sep 17 00:00:00 2001 From: Bryson Spilman Date: Mon, 14 Apr 2025 15:06:18 -0700 Subject: [PATCH 1/3] CWMS-2055 - Updates to allow for a token provider with a discovered token url that the client implements --- .../http/client/CwbiAuthTokenProvider.java | 50 +--- .../client/CwbiAuthTokenProviderBase.java | 88 +++++++ .../DiscoveredCwbiAuthTokenProvider.java | 49 ++++ .../http/client/TokenUrlDiscoveryService.java | 47 ++++ .../client/MockCwbiAuthTokenProvider.java | 54 +--- .../MockDiscoveredCwbiAuthTokenProvider.java | 56 +++++ .../http/client/TestCwbiTokenProvider.java | 3 - .../TestDiscoveredCwbiTokenProvider.java | 231 ++++++++++++++++++ .../client/TestTokenUrlDiscoveryService.java | 57 +++++ 9 files changed, 535 insertions(+), 100 deletions(-) create mode 100644 cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProviderBase.java create mode 100644 cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DiscoveredCwbiAuthTokenProvider.java create mode 100644 cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenUrlDiscoveryService.java create mode 100644 cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockDiscoveredCwbiAuthTokenProvider.java create mode 100644 cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDiscoveredCwbiTokenProvider.java create mode 100644 cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestTokenUrlDiscoveryService.java diff --git a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProvider.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProvider.java index 7aa0089d..f33d6c02 100644 --- a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProvider.java +++ b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProvider.java @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2024 Hydrologic Engineering Center + * 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 @@ -23,18 +23,14 @@ */ package hec.army.usace.hec.cwbi.auth.http.client; -import java.io.IOException; import java.util.Objects; import javax.net.ssl.SSLSocketFactory; import mil.army.usace.hec.cwms.http.client.auth.OAuth2Token; -import mil.army.usace.hec.cwms.http.client.auth.OAuth2TokenProvider; -public final class CwbiAuthTokenProvider implements OAuth2TokenProvider { +public final class CwbiAuthTokenProvider extends CwbiAuthTokenProviderBase { private OAuth2Token oauth2Token; private final String url; - private final String clientId; - private final SSLSocketFactory sslSocketFactory; /** * Provider for OAuth2Tokens. @@ -44,52 +40,12 @@ public final class CwbiAuthTokenProvider implements OAuth2TokenProvider { * @param sslSocketFactory - ssl socket factory */ public CwbiAuthTokenProvider(String tokenUrl, String clientId, SSLSocketFactory sslSocketFactory) { + super(clientId, sslSocketFactory); this.url = Objects.requireNonNull(tokenUrl, "Missing required tokenUrl"); - this.clientId = Objects.requireNonNull(clientId, "Missing required clientId"); - this.sslSocketFactory =Objects.requireNonNull(sslSocketFactory, "Missing required KeyManager"); } @Override - public synchronized void clear() { - oauth2Token = null; - } - - @Override - public synchronized OAuth2Token getToken() throws IOException { - if (oauth2Token == null) { - oauth2Token = newToken(); - } - return oauth2Token; - } - - @Override - public OAuth2Token newToken() throws IOException { - return new DirectGrantX509TokenRequestBuilder() - .withSSlSocketFactory(sslSocketFactory) - .withUrl(url) - .withClientId(clientId) - .fetchToken(); - } - - @Override - public synchronized OAuth2Token refreshToken() throws IOException { - OAuth2Token token = new RefreshTokenRequestBuilder() - .withSSlSocketFactory(sslSocketFactory) - .withRefreshToken(oauth2Token.getRefreshToken()) - .withUrl(url) - .withClientId(clientId) - .fetchToken(); - oauth2Token = token; - return token; - } - - //package scoped for testing String getUrl() { return url; } - - //package scoped for testing - String getClientId() { - return clientId; - } } diff --git a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProviderBase.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProviderBase.java new file mode 100644 index 00000000..9341f9ea --- /dev/null +++ b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProviderBase.java @@ -0,0 +1,88 @@ +/* + * 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 hec.army.usace.hec.cwbi.auth.http.client; + +import java.io.IOException; +import java.util.Objects; +import javax.net.ssl.SSLSocketFactory; +import mil.army.usace.hec.cwms.http.client.auth.OAuth2Token; +import mil.army.usace.hec.cwms.http.client.auth.OAuth2TokenProvider; + +abstract class CwbiAuthTokenProviderBase implements OAuth2TokenProvider { + + private final SSLSocketFactory sslSocketFactory; + protected OAuth2Token oauth2Token; + protected final String clientId; + + protected CwbiAuthTokenProviderBase(String clientId, SSLSocketFactory sslSocketFactory) { + this.clientId = Objects.requireNonNull(clientId, "Missing required clientId"); + this.sslSocketFactory =Objects.requireNonNull(sslSocketFactory, "Missing required KeyManager"); + } + + abstract String getUrl(); + + @Override + public synchronized void clear() { + oauth2Token = null; + } + + @Override + public synchronized OAuth2Token getToken() throws IOException { + if (oauth2Token == null) { + oauth2Token = newToken(); + } + return oauth2Token; + } + + @Override + public OAuth2Token newToken() throws IOException { + return new DirectGrantX509TokenRequestBuilder() + .withSSlSocketFactory(sslSocketFactory) + .withUrl(getUrl()) + .withClientId(clientId) + .fetchToken(); + } + + @Override + public synchronized OAuth2Token refreshToken() throws IOException { + OAuth2Token token = new RefreshTokenRequestBuilder() + .withSSlSocketFactory(sslSocketFactory) + .withRefreshToken(oauth2Token.getRefreshToken()) + .withUrl(getUrl()) + .withClientId(clientId) + .fetchToken(); + oauth2Token = token; + return token; + } + + //package scoped for testing + String getClientId() { + return clientId; + } + + //package scoped for testing + SSLSocketFactory getSslSocketFactory() { + return sslSocketFactory; + } +} diff --git a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DiscoveredCwbiAuthTokenProvider.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DiscoveredCwbiAuthTokenProvider.java new file mode 100644 index 00000000..e304d98e --- /dev/null +++ b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DiscoveredCwbiAuthTokenProvider.java @@ -0,0 +1,49 @@ +/* + * 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 hec.army.usace.hec.cwbi.auth.http.client; + +import java.util.Objects; +import javax.net.ssl.SSLSocketFactory; + +public final class DiscoveredCwbiAuthTokenProvider extends CwbiAuthTokenProviderBase +{ + private final TokenUrlDiscoveryService tokenUrlDiscoveryService; + private String url; + + public DiscoveredCwbiAuthTokenProvider(String clientId, SSLSocketFactory sslSocketFactory, TokenUrlDiscoveryService tokenUrlDiscoveryService) + { + super(clientId, sslSocketFactory); + this.tokenUrlDiscoveryService = Objects.requireNonNull(tokenUrlDiscoveryService, "Missing required tokenUrlDiscoveryService"); + } + + @Override + synchronized String getUrl() + { + if(url == null) + { + url = tokenUrlDiscoveryService.discoverTokenUrl(); + } + return url; + } +} diff --git a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenUrlDiscoveryService.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenUrlDiscoveryService.java new file mode 100644 index 00000000..e57e2603 --- /dev/null +++ b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenUrlDiscoveryService.java @@ -0,0 +1,47 @@ +/* + * 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 hec.army.usace.hec.cwbi.auth.http.client; + +import java.util.Objects; +import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; + +public abstract class TokenUrlDiscoveryService { + + private final ApiConnectionInfo apiConnectionInfo; + + protected TokenUrlDiscoveryService(ApiConnectionInfo apiConnectionInfo) + { + this.apiConnectionInfo = Objects.requireNonNull(apiConnectionInfo, "apiConnectionInfo must not be null"); + } + protected abstract String retrieveTokenUrl(ApiConnectionInfo apiConnectionInfo); + + public final String discoverTokenUrl() { + return retrieveTokenUrl(apiConnectionInfo); + } + + //package scoped for testing + ApiConnectionInfo getApiConnectionInfo() { + return apiConnectionInfo; + } +} diff --git a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockCwbiAuthTokenProvider.java b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockCwbiAuthTokenProvider.java index 19cc7e35..18cda797 100644 --- a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockCwbiAuthTokenProvider.java +++ b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockCwbiAuthTokenProvider.java @@ -23,17 +23,12 @@ */ package hec.army.usace.hec.cwbi.auth.http.client; -import java.io.IOException; import javax.net.ssl.SSLSocketFactory; import mil.army.usace.hec.cwms.http.client.auth.OAuth2Token; -import mil.army.usace.hec.cwms.http.client.auth.OAuth2TokenProvider; -public class MockCwbiAuthTokenProvider implements OAuth2TokenProvider { +public class MockCwbiAuthTokenProvider extends CwbiAuthTokenProviderBase { - private OAuth2Token oauth2Token; private final String url; - private final String clientId; - private final SSLSocketFactory sslSocketFactory; /** * Provider for OAuth2Tokens. @@ -43,59 +38,18 @@ public class MockCwbiAuthTokenProvider implements OAuth2TokenProvider { * @param sslSocketFactory - ssl socket factory */ public MockCwbiAuthTokenProvider(String url, String clientId, SSLSocketFactory sslSocketFactory) { + super(clientId, sslSocketFactory); this.url = url; - this.clientId = clientId; - this.sslSocketFactory = sslSocketFactory; - } - - @Override - public void clear() { - oauth2Token = null; - } - - @Override - public OAuth2Token getToken() throws IOException { - if (oauth2Token == null) { - oauth2Token = newToken(); - } - return oauth2Token; - } - - @Override - public OAuth2Token refreshToken() throws IOException { - OAuth2Token token = new RefreshTokenRequestBuilder() - .withRefreshToken(oauth2Token.getRefreshToken()) - .withUrl(url) - .withClientId(clientId) - .fetchToken(); - oauth2Token = token; - return token; - } - - @Override - public OAuth2Token newToken() throws IOException { - return new DirectGrantX509TokenRequestBuilder() - .withSSlSocketFactory(sslSocketFactory) - .withUrl(url) - .withClientId(clientId) - .fetchToken(); } + //used to manually set token for testing void setOAuth2Token(OAuth2Token token) { oauth2Token = token; } //package scoped for testing + @Override String getUrl() { return url; } - - //package scoped for testing - String getClientId() { - return clientId; - } - - SSLSocketFactory getSslSocketFactory() { - return sslSocketFactory; - } } diff --git a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockDiscoveredCwbiAuthTokenProvider.java b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockDiscoveredCwbiAuthTokenProvider.java new file mode 100644 index 00000000..7d734550 --- /dev/null +++ b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockDiscoveredCwbiAuthTokenProvider.java @@ -0,0 +1,56 @@ +/* + * 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 hec.army.usace.hec.cwbi.auth.http.client; + +import java.util.Objects; +import javax.net.ssl.SSLSocketFactory; +import mil.army.usace.hec.cwms.http.client.auth.OAuth2Token; + +public class MockDiscoveredCwbiAuthTokenProvider extends CwbiAuthTokenProviderBase { + + private final TokenUrlDiscoveryService tokenUrlDiscoveryService; + + /** + * Provider for OAuth2Tokens. + * + * @param clientId - client name + * @param sslSocketFactory - ssl socket factory + * @param tokenUrlDiscoveryService - service to discover the token URL + */ + public MockDiscoveredCwbiAuthTokenProvider(String clientId, SSLSocketFactory sslSocketFactory, TokenUrlDiscoveryService tokenUrlDiscoveryService) { + super(clientId, sslSocketFactory); + this.tokenUrlDiscoveryService = Objects.requireNonNull(tokenUrlDiscoveryService, "Missing required tokenUrlDiscoveryService"); + } + + //used to manually set token for testing + void setOAuth2Token(OAuth2Token token) { + oauth2Token = token; + } + + //package scoped for testing + @Override + String getUrl() { + return tokenUrlDiscoveryService.discoverTokenUrl(); + } +} diff --git a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestCwbiTokenProvider.java b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestCwbiTokenProvider.java index b274c51d..8a1decd8 100644 --- a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestCwbiTokenProvider.java +++ b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestCwbiTokenProvider.java @@ -44,12 +44,9 @@ import mil.army.usace.hec.cwms.http.client.ApiConnectionInfoBuilder; import mil.army.usace.hec.cwms.http.client.auth.OAuth2Token; import org.junit.jupiter.api.AfterEach; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDiscoveredCwbiTokenProvider.java b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDiscoveredCwbiTokenProvider.java new file mode 100644 index 00000000..ec256bca --- /dev/null +++ b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDiscoveredCwbiTokenProvider.java @@ -0,0 +1,231 @@ +/* + * 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 hec.army.usace.hec.cwbi.auth.http.client; + +import static hec.army.usace.hec.cwbi.auth.http.client.trustmanagers.CwbiAuthTrustManager.TOKEN_URL; +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLSocketFactory; +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.OAuth2Token; +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class TestDiscoveredCwbiTokenProvider { + + static MockHttpServer mockHttpServer; + + static ExecutorService executorService; + + @BeforeAll + static void setUpExecutorService() { + executorService = Executors.newFixedThreadPool(1); + } + + @BeforeEach + void setUp() throws IOException { + mockHttpServer = MockHttpServer.create(); + } + + @AfterEach + void tearDown() throws IOException { + mockHttpServer.shutdown(); + } + + ApiConnectionInfo buildConnectionInfo() { + String baseUrl = String.format("http://localhost:%s", mockHttpServer.getPort()); + return new ApiConnectionInfoBuilder(baseUrl).build(); + } + + protected void launchMockServerWithResource(String resource) throws IOException { + URL resourceUrl = getClass().getClassLoader().getResource(resource); + if (resourceUrl == null) { + throw new IOException("Failed to get resource: " + resource); + } + Path path = new File(resourceUrl.getFile()).toPath(); + String collect = String.join("\n", Files.readAllLines(path)); + mockHttpServer.enqueue(collect); + mockHttpServer.start(); + } + + @Test + void testBuildTokenProvider() throws Exception { + SSLSocketFactory sslSocketFactory = CwbiAuthSslSocketFactory.buildSSLSocketFactory( + Collections.singletonList(getTestKeyManager())); + ApiConnectionInfo apiConnectionInfo = buildConnectionInfo(); + MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(apiConnectionInfo, TOKEN_URL); + DiscoveredCwbiAuthTokenProvider tokenProvider = new DiscoveredCwbiAuthTokenProvider("cumulus", sslSocketFactory, tokenUrlDiscoveryService); + assertEquals(TOKEN_URL, tokenProvider.getUrl()); + assertEquals("cumulus", tokenProvider.getClientId()); + } + + @Test + void testNulls() { + assertThrows(NullPointerException.class, () -> new DiscoveredCwbiAuthTokenProvider("cumulus", getTestSslSocketFactory(), null)); + assertThrows(NullPointerException.class, () -> new DiscoveredCwbiAuthTokenProvider("cumulus", null, new MockTokenUrlDiscoveryService(buildConnectionInfo(), TOKEN_URL))); + assertThrows(NullPointerException.class, () -> new DiscoveredCwbiAuthTokenProvider(null, getTestSslSocketFactory(), new MockTokenUrlDiscoveryService(buildConnectionInfo(), TOKEN_URL))); + } + + private KeyManager getTestKeyManager() { + return new KeyManager() { + }; + } + + @Test + void testGetToken() throws IOException { + String resource = "oauth2token.json"; + launchMockServerWithResource(resource); + String tokenUrl = buildConnectionInfo().getApiRoot(); + MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(buildConnectionInfo(), tokenUrl); + DiscoveredCwbiAuthTokenProvider tokenProvider = new DiscoveredCwbiAuthTokenProvider("cumulus", getTestSslSocketFactory(), tokenUrlDiscoveryService); + OAuth2Token token = tokenProvider.getToken(); + assertEquals("MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3", token.getAccessToken()); + assertEquals("Bearer", token.getTokenType()); + assertEquals(3600, token.getExpiresIn()); + assertEquals("IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk", token.getRefreshToken()); + assertEquals("create", token.getScope()); + } + + @Test + void testClear() throws IOException { + String resource = "oauth2token.json"; + launchMockServerWithResource(resource); + String tokenUrl = buildConnectionInfo().getApiRoot(); + MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(buildConnectionInfo(), tokenUrl); + MockDiscoveredCwbiAuthTokenProvider tokenProvider = new MockDiscoveredCwbiAuthTokenProvider("cumulus", getTestSslSocketFactory(), tokenUrlDiscoveryService); + OAuth2Token token = new OAuth2Token(); + token.setAccessToken("abc123"); + token.setTokenType("Bearer"); + token.setExpiresIn(3600); + token.setRefreshToken("123abc"); + tokenProvider.setOAuth2Token(token); + OAuth2Token token1 = tokenProvider.getToken(); + OAuth2Token token2 = tokenProvider.getToken(); + assertSame(token1, token2); + tokenProvider.clear(); + assertNotSame(token1, tokenProvider.getToken()); + } + + @Test + void testRefreshToken() throws IOException { + String resource = "oauth2token.json"; + launchMockServerWithResource(resource); + String tokenUrl = buildConnectionInfo().getApiRoot(); + MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(buildConnectionInfo(), tokenUrl); + MockDiscoveredCwbiAuthTokenProvider tokenProvider = new MockDiscoveredCwbiAuthTokenProvider("cumulus", getTestSslSocketFactory(), tokenUrlDiscoveryService); + OAuth2Token token = new OAuth2Token(); + token.setAccessToken("abc123"); + token.setTokenType("Bearer"); + token.setExpiresIn(3600); + token.setRefreshToken("123abc"); + tokenProvider.setOAuth2Token(token); + + OAuth2Token refreshedToken = tokenProvider.refreshToken(); + assertEquals("MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3", refreshedToken.getAccessToken()); + assertEquals("Bearer", refreshedToken.getTokenType()); + assertEquals(3600, refreshedToken.getExpiresIn()); + assertEquals("IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk", refreshedToken.getRefreshToken()); + assertEquals("create", refreshedToken.getScope()); + } + + @Test + void testConstructor() { + SSLSocketFactory sslSocketFactory = getTestSslSocketFactory(); + MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(buildConnectionInfo(), "test.com"); + MockDiscoveredCwbiAuthTokenProvider tokenProvider = new MockDiscoveredCwbiAuthTokenProvider("clientId", sslSocketFactory, tokenUrlDiscoveryService); + assertEquals("test.com", tokenProvider.getUrl()); + assertEquals("clientId", tokenProvider.getClientId()); + assertEquals(sslSocketFactory, tokenProvider.getSslSocketFactory()); + } + + private SSLSocketFactory getTestSslSocketFactory() { + return new SSLSocketFactory() { + @Override + public String[] getDefaultCipherSuites() { + return new String[0]; + } + + @Override + public String[] getSupportedCipherSuites() { + return new String[0]; + } + + @Override + public Socket createSocket(Socket socket, String s, int i, boolean b) { + return null; + } + + @Override + public Socket createSocket(String s, int i) { + return null; + } + + @Override + public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) { + return null; + } + + @Override + public Socket createSocket(InetAddress inetAddress, int i) { + return null; + } + + @Override + public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) { + return null; + } + }; + } + + private static class MockTokenUrlDiscoveryService extends TokenUrlDiscoveryService { + private final String tokenUrl; + + private MockTokenUrlDiscoveryService(ApiConnectionInfo apiConnectionInfo, String tokenUrl) { + super(apiConnectionInfo); + this.tokenUrl = tokenUrl; + } + + @Override + protected String retrieveTokenUrl(ApiConnectionInfo apiConnectionInfo) { + return this.tokenUrl; + } + } +} diff --git a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestTokenUrlDiscoveryService.java b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestTokenUrlDiscoveryService.java new file mode 100644 index 00000000..fda561ea --- /dev/null +++ b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestTokenUrlDiscoveryService.java @@ -0,0 +1,57 @@ +/* + * 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 hec.army.usace.hec.cwbi.auth.http.client; + +import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; +import mil.army.usace.hec.cwms.http.client.ApiConnectionInfoBuilder; +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import org.junit.jupiter.api.Test; + +final class TestTokenUrlDiscoveryService { + + @Test + void testDiscoverTokenUrl() { + ApiConnectionInfo apiConnectionInfo = new ApiConnectionInfoBuilder("http://localhost:8080").build(); + TokenUrlDiscoveryService tokenUrlDiscoveryService = new TokenUrlDiscoveryService(apiConnectionInfo) { + @Override + protected String retrieveTokenUrl(ApiConnectionInfo apiConnectionInfo) { + return "http://token.url"; + } + }; + assertSame(apiConnectionInfo, tokenUrlDiscoveryService.getApiConnectionInfo()); + assertEquals("http://token.url", tokenUrlDiscoveryService.discoverTokenUrl()); + } + + @Test + void testNulls() { + Assertions.assertThrows(NullPointerException.class, () -> new TokenUrlDiscoveryService(null) { + @Override + protected String retrieveTokenUrl(ApiConnectionInfo apiConnectionInfo) { + return null; + } + }); + } +} From a7eecdec94bf9d19133fecd5d239f1671d3e898f Mon Sep 17 00:00:00 2001 From: Bryson Spilman Date: Tue, 15 Apr 2025 14:03:56 -0700 Subject: [PATCH 2/3] CWMS-2055 - Update TokenUrlDiscoveryService to be an interface --- .../http/client/TokenUrlDiscoveryService.java | 23 +------- .../MockDiscoveredCwbiAuthTokenProvider.java | 9 ++- .../TestDiscoveredCwbiTokenProvider.java | 24 ++++---- .../client/TestTokenUrlDiscoveryService.java | 57 ------------------- 4 files changed, 20 insertions(+), 93 deletions(-) delete mode 100644 cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestTokenUrlDiscoveryService.java diff --git a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenUrlDiscoveryService.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenUrlDiscoveryService.java index e57e2603..727d034a 100644 --- a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenUrlDiscoveryService.java +++ b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenUrlDiscoveryService.java @@ -23,25 +23,6 @@ */ package hec.army.usace.hec.cwbi.auth.http.client; -import java.util.Objects; -import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; - -public abstract class TokenUrlDiscoveryService { - - private final ApiConnectionInfo apiConnectionInfo; - - protected TokenUrlDiscoveryService(ApiConnectionInfo apiConnectionInfo) - { - this.apiConnectionInfo = Objects.requireNonNull(apiConnectionInfo, "apiConnectionInfo must not be null"); - } - protected abstract String retrieveTokenUrl(ApiConnectionInfo apiConnectionInfo); - - public final String discoverTokenUrl() { - return retrieveTokenUrl(apiConnectionInfo); - } - - //package scoped for testing - ApiConnectionInfo getApiConnectionInfo() { - return apiConnectionInfo; - } +public interface TokenUrlDiscoveryService { + String discoverTokenUrl(); } diff --git a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockDiscoveredCwbiAuthTokenProvider.java b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockDiscoveredCwbiAuthTokenProvider.java index 7d734550..53ac51de 100644 --- a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockDiscoveredCwbiAuthTokenProvider.java +++ b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockDiscoveredCwbiAuthTokenProvider.java @@ -30,6 +30,7 @@ public class MockDiscoveredCwbiAuthTokenProvider extends CwbiAuthTokenProviderBase { private final TokenUrlDiscoveryService tokenUrlDiscoveryService; + private String url; /** * Provider for OAuth2Tokens. @@ -50,7 +51,11 @@ void setOAuth2Token(OAuth2Token token) { //package scoped for testing @Override - String getUrl() { - return tokenUrlDiscoveryService.discoverTokenUrl(); + synchronized String getUrl() { + if(url == null) + { + url = tokenUrlDiscoveryService.discoverTokenUrl(); + } + return url; } } diff --git a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDiscoveredCwbiTokenProvider.java b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDiscoveredCwbiTokenProvider.java index ec256bca..8f52b755 100644 --- a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDiscoveredCwbiTokenProvider.java +++ b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDiscoveredCwbiTokenProvider.java @@ -90,8 +90,7 @@ protected void launchMockServerWithResource(String resource) throws IOException void testBuildTokenProvider() throws Exception { SSLSocketFactory sslSocketFactory = CwbiAuthSslSocketFactory.buildSSLSocketFactory( Collections.singletonList(getTestKeyManager())); - ApiConnectionInfo apiConnectionInfo = buildConnectionInfo(); - MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(apiConnectionInfo, TOKEN_URL); + MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(TOKEN_URL); DiscoveredCwbiAuthTokenProvider tokenProvider = new DiscoveredCwbiAuthTokenProvider("cumulus", sslSocketFactory, tokenUrlDiscoveryService); assertEquals(TOKEN_URL, tokenProvider.getUrl()); assertEquals("cumulus", tokenProvider.getClientId()); @@ -100,8 +99,8 @@ void testBuildTokenProvider() throws Exception { @Test void testNulls() { assertThrows(NullPointerException.class, () -> new DiscoveredCwbiAuthTokenProvider("cumulus", getTestSslSocketFactory(), null)); - assertThrows(NullPointerException.class, () -> new DiscoveredCwbiAuthTokenProvider("cumulus", null, new MockTokenUrlDiscoveryService(buildConnectionInfo(), TOKEN_URL))); - assertThrows(NullPointerException.class, () -> new DiscoveredCwbiAuthTokenProvider(null, getTestSslSocketFactory(), new MockTokenUrlDiscoveryService(buildConnectionInfo(), TOKEN_URL))); + assertThrows(NullPointerException.class, () -> new DiscoveredCwbiAuthTokenProvider("cumulus", null, new MockTokenUrlDiscoveryService(TOKEN_URL))); + assertThrows(NullPointerException.class, () -> new DiscoveredCwbiAuthTokenProvider(null, getTestSslSocketFactory(), new MockTokenUrlDiscoveryService(TOKEN_URL))); } private KeyManager getTestKeyManager() { @@ -114,7 +113,7 @@ void testGetToken() throws IOException { String resource = "oauth2token.json"; launchMockServerWithResource(resource); String tokenUrl = buildConnectionInfo().getApiRoot(); - MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(buildConnectionInfo(), tokenUrl); + MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(tokenUrl); DiscoveredCwbiAuthTokenProvider tokenProvider = new DiscoveredCwbiAuthTokenProvider("cumulus", getTestSslSocketFactory(), tokenUrlDiscoveryService); OAuth2Token token = tokenProvider.getToken(); assertEquals("MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3", token.getAccessToken()); @@ -129,7 +128,7 @@ void testClear() throws IOException { String resource = "oauth2token.json"; launchMockServerWithResource(resource); String tokenUrl = buildConnectionInfo().getApiRoot(); - MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(buildConnectionInfo(), tokenUrl); + MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(tokenUrl); MockDiscoveredCwbiAuthTokenProvider tokenProvider = new MockDiscoveredCwbiAuthTokenProvider("cumulus", getTestSslSocketFactory(), tokenUrlDiscoveryService); OAuth2Token token = new OAuth2Token(); token.setAccessToken("abc123"); @@ -149,7 +148,7 @@ void testRefreshToken() throws IOException { String resource = "oauth2token.json"; launchMockServerWithResource(resource); String tokenUrl = buildConnectionInfo().getApiRoot(); - MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(buildConnectionInfo(), tokenUrl); + MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(tokenUrl); MockDiscoveredCwbiAuthTokenProvider tokenProvider = new MockDiscoveredCwbiAuthTokenProvider("cumulus", getTestSslSocketFactory(), tokenUrlDiscoveryService); OAuth2Token token = new OAuth2Token(); token.setAccessToken("abc123"); @@ -169,7 +168,7 @@ void testRefreshToken() throws IOException { @Test void testConstructor() { SSLSocketFactory sslSocketFactory = getTestSslSocketFactory(); - MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(buildConnectionInfo(), "test.com"); + MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService("test.com"); MockDiscoveredCwbiAuthTokenProvider tokenProvider = new MockDiscoveredCwbiAuthTokenProvider("clientId", sslSocketFactory, tokenUrlDiscoveryService); assertEquals("test.com", tokenProvider.getUrl()); assertEquals("clientId", tokenProvider.getClientId()); @@ -215,17 +214,16 @@ public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddre }; } - private static class MockTokenUrlDiscoveryService extends TokenUrlDiscoveryService { + private static class MockTokenUrlDiscoveryService implements TokenUrlDiscoveryService { private final String tokenUrl; - private MockTokenUrlDiscoveryService(ApiConnectionInfo apiConnectionInfo, String tokenUrl) { - super(apiConnectionInfo); + private MockTokenUrlDiscoveryService(String tokenUrl) { this.tokenUrl = tokenUrl; } @Override - protected String retrieveTokenUrl(ApiConnectionInfo apiConnectionInfo) { - return this.tokenUrl; + public String discoverTokenUrl() { + return tokenUrl; } } } diff --git a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestTokenUrlDiscoveryService.java b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestTokenUrlDiscoveryService.java deleted file mode 100644 index fda561ea..00000000 --- a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestTokenUrlDiscoveryService.java +++ /dev/null @@ -1,57 +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 hec.army.usace.hec.cwbi.auth.http.client; - -import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; -import mil.army.usace.hec.cwms.http.client.ApiConnectionInfoBuilder; -import org.junit.jupiter.api.Assertions; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; -import org.junit.jupiter.api.Test; - -final class TestTokenUrlDiscoveryService { - - @Test - void testDiscoverTokenUrl() { - ApiConnectionInfo apiConnectionInfo = new ApiConnectionInfoBuilder("http://localhost:8080").build(); - TokenUrlDiscoveryService tokenUrlDiscoveryService = new TokenUrlDiscoveryService(apiConnectionInfo) { - @Override - protected String retrieveTokenUrl(ApiConnectionInfo apiConnectionInfo) { - return "http://token.url"; - } - }; - assertSame(apiConnectionInfo, tokenUrlDiscoveryService.getApiConnectionInfo()); - assertEquals("http://token.url", tokenUrlDiscoveryService.discoverTokenUrl()); - } - - @Test - void testNulls() { - Assertions.assertThrows(NullPointerException.class, () -> new TokenUrlDiscoveryService(null) { - @Override - protected String retrieveTokenUrl(ApiConnectionInfo apiConnectionInfo) { - return null; - } - }); - } -} From 9572035b363d01344e4bca07775bd5c0a3892aa4 Mon Sep 17 00:00:00 2001 From: Bryson Spilman Date: Tue, 15 Apr 2025 17:17:51 -0700 Subject: [PATCH 3/3] CWMS-2055 - Update TokenUrlDiscoveryService to return ApiConnectionInfo. Refactors code to pass ApiConnectionInfo for token url. This moved responsibility of creation of ssl socket data to client for discovered token url. --- .../http/client/CwbiAuthTokenProvider.java | 17 ++++-- .../client/CwbiAuthTokenProviderBase.java | 16 +---- .../DirectGrantX509TokenRequestBuilder.java | 46 +++++--------- ...ectGrantX509TokenRequestFluentBuilder.java | 31 ---------- .../DiscoveredCwbiAuthTokenProvider.java | 10 +-- .../client/RefreshTokenRequestBuilder.java | 30 +-------- .../RefreshTokenRequestFluentBuilder.java | 3 - .../auth/http/client/TokenRequestBuilder.java | 7 ++- .../client/TokenRequestFluentBuilder.java | 4 +- .../http/client/TokenUrlDiscoveryService.java | 4 +- .../client/MockCwbiAuthTokenProvider.java | 21 +++++-- .../MockDiscoveredCwbiAuthTokenProvider.java | 16 +++-- .../http/client/TestCwbiTokenProvider.java | 4 +- ...estDirectGrantX509TokenRequestBuilder.java | 34 +++++------ .../TestDiscoveredCwbiTokenProvider.java | 61 +++++++++---------- .../TestRefreshTokenRequestBuilder.java | 36 ++++++----- 16 files changed, 141 insertions(+), 199 deletions(-) delete mode 100644 cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DirectGrantX509TokenRequestFluentBuilder.java diff --git a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProvider.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProvider.java index f33d6c02..36e6a52b 100644 --- a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProvider.java +++ b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProvider.java @@ -23,13 +23,16 @@ */ package hec.army.usace.hec.cwbi.auth.http.client; +import hec.army.usace.hec.cwbi.auth.http.client.trustmanagers.CwbiAuthTrustManager; import java.util.Objects; import javax.net.ssl.SSLSocketFactory; -import mil.army.usace.hec.cwms.http.client.auth.OAuth2Token; +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; public final class CwbiAuthTokenProvider extends CwbiAuthTokenProviderBase { - private OAuth2Token oauth2Token; + private final SSLSocketFactory sslSocketFactory; private final String url; /** @@ -40,12 +43,16 @@ public final class CwbiAuthTokenProvider extends CwbiAuthTokenProviderBase { * @param sslSocketFactory - ssl socket factory */ public CwbiAuthTokenProvider(String tokenUrl, String clientId, SSLSocketFactory sslSocketFactory) { - super(clientId, sslSocketFactory); + super(clientId); + this.sslSocketFactory = Objects.requireNonNull(sslSocketFactory, "Missing required sslSocketFactory"); this.url = Objects.requireNonNull(tokenUrl, "Missing required tokenUrl"); } @Override - String getUrl() { - return url; + ApiConnectionInfo getUrl() { + return new ApiConnectionInfoBuilder(url) + .withSslSocketData(new SslSocketData(sslSocketFactory, CwbiAuthTrustManager.getTrustManager())) + .build(); } + } diff --git a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProviderBase.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProviderBase.java index 9341f9ea..e542092a 100644 --- a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProviderBase.java +++ b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProviderBase.java @@ -25,22 +25,19 @@ import java.io.IOException; import java.util.Objects; -import javax.net.ssl.SSLSocketFactory; +import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; import mil.army.usace.hec.cwms.http.client.auth.OAuth2Token; import mil.army.usace.hec.cwms.http.client.auth.OAuth2TokenProvider; abstract class CwbiAuthTokenProviderBase implements OAuth2TokenProvider { - - private final SSLSocketFactory sslSocketFactory; protected OAuth2Token oauth2Token; protected final String clientId; - protected CwbiAuthTokenProviderBase(String clientId, SSLSocketFactory sslSocketFactory) { + protected CwbiAuthTokenProviderBase(String clientId) { this.clientId = Objects.requireNonNull(clientId, "Missing required clientId"); - this.sslSocketFactory =Objects.requireNonNull(sslSocketFactory, "Missing required KeyManager"); } - abstract String getUrl(); + abstract ApiConnectionInfo getUrl(); @Override public synchronized void clear() { @@ -58,7 +55,6 @@ public synchronized OAuth2Token getToken() throws IOException { @Override public OAuth2Token newToken() throws IOException { return new DirectGrantX509TokenRequestBuilder() - .withSSlSocketFactory(sslSocketFactory) .withUrl(getUrl()) .withClientId(clientId) .fetchToken(); @@ -67,7 +63,6 @@ public OAuth2Token newToken() throws IOException { @Override public synchronized OAuth2Token refreshToken() throws IOException { OAuth2Token token = new RefreshTokenRequestBuilder() - .withSSlSocketFactory(sslSocketFactory) .withRefreshToken(oauth2Token.getRefreshToken()) .withUrl(getUrl()) .withClientId(clientId) @@ -80,9 +75,4 @@ public synchronized OAuth2Token refreshToken() throws IOException { String getClientId() { return clientId; } - - //package scoped for testing - SSLSocketFactory getSslSocketFactory() { - return sslSocketFactory; - } } diff --git a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DirectGrantX509TokenRequestBuilder.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DirectGrantX509TokenRequestBuilder.java index 461347f8..faface92 100644 --- a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DirectGrantX509TokenRequestBuilder.java +++ b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DirectGrantX509TokenRequestBuilder.java @@ -23,54 +23,36 @@ */ package hec.army.usace.hec.cwbi.auth.http.client; -import hec.army.usace.hec.cwbi.auth.http.client.trustmanagers.CwbiAuthTrustManager; -import mil.army.usace.hec.cwms.http.client.ApiConnectionInfoBuilder; import mil.army.usace.hec.cwms.http.client.HttpRequestBuilderImpl; import mil.army.usace.hec.cwms.http.client.HttpRequestResponse; -import mil.army.usace.hec.cwms.http.client.SslSocketData; import mil.army.usace.hec.cwms.http.client.auth.OAuth2Token; import mil.army.usace.hec.cwms.http.client.request.HttpRequestExecutor; -import javax.net.ssl.SSLSocketFactory; import java.io.IOException; -import java.util.Objects; -public final class DirectGrantX509TokenRequestBuilder implements DirectGrantX509TokenRequestFluentBuilder { - - private SslSocketData sslSocketData; +public final class DirectGrantX509TokenRequestBuilder extends TokenRequestBuilder { @Override - public TokenRequestFluentBuilder withSSlSocketFactory(SSLSocketFactory sslSocketFactory) { - this.sslSocketData = new SslSocketData(Objects.requireNonNull(sslSocketFactory, "Missing required SSLSocketFactory"), - CwbiAuthTrustManager.getTrustManager()); - return new TokenRequestBuilderImpl(); - } - - private class TokenRequestBuilderImpl extends TokenRequestBuilder { - - @Override - OAuth2Token retrieveToken() throws IOException { - OAuth2Token retVal = null; - String formBody = new UrlEncodedFormData() + OAuth2Token retrieveToken() throws IOException { + OAuth2Token retVal = null; + String formBody = new UrlEncodedFormData() .addPassword("") .addGrantType("password") .addScopes("openid", "profile") .addClientId(getClientId()) .addUsername("") .buildEncodedString(); - HttpRequestExecutor executor = - new HttpRequestBuilderImpl(new ApiConnectionInfoBuilder(getUrl()) - .withSslSocketData(sslSocketData).build()) - .post() - .withBody(formBody) - .withMediaType(MEDIA_TYPE); - try (HttpRequestResponse response = executor.execute()) { - String body = response.getBody(); - if (body != null) { - retVal = OAuth2ObjectMapper.mapJsonToObject(body, OAuth2Token.class); - } + HttpRequestExecutor executor = + new HttpRequestBuilderImpl(getUrl()) + .post() + .withBody(formBody) + .withMediaType(MEDIA_TYPE); + try (HttpRequestResponse response = executor.execute()) { + String body = response.getBody(); + if (body != null) { + retVal = OAuth2ObjectMapper.mapJsonToObject(body, OAuth2Token.class); } - return retVal; } + return retVal; } } diff --git a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DirectGrantX509TokenRequestFluentBuilder.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DirectGrantX509TokenRequestFluentBuilder.java deleted file mode 100644 index def0f1a1..00000000 --- a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DirectGrantX509TokenRequestFluentBuilder.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2024 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 hec.army.usace.hec.cwbi.auth.http.client; - -import javax.net.ssl.SSLSocketFactory; - -public interface DirectGrantX509TokenRequestFluentBuilder { - - TokenRequestFluentBuilder withSSlSocketFactory(SSLSocketFactory sslSocketFactory); -} diff --git a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DiscoveredCwbiAuthTokenProvider.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DiscoveredCwbiAuthTokenProvider.java index e304d98e..84ab56ad 100644 --- a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DiscoveredCwbiAuthTokenProvider.java +++ b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DiscoveredCwbiAuthTokenProvider.java @@ -24,21 +24,21 @@ package hec.army.usace.hec.cwbi.auth.http.client; import java.util.Objects; -import javax.net.ssl.SSLSocketFactory; +import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; public final class DiscoveredCwbiAuthTokenProvider extends CwbiAuthTokenProviderBase { private final TokenUrlDiscoveryService tokenUrlDiscoveryService; - private String url; + private ApiConnectionInfo url; - public DiscoveredCwbiAuthTokenProvider(String clientId, SSLSocketFactory sslSocketFactory, TokenUrlDiscoveryService tokenUrlDiscoveryService) + public DiscoveredCwbiAuthTokenProvider(String clientId, TokenUrlDiscoveryService tokenUrlDiscoveryService) { - super(clientId, sslSocketFactory); + super(clientId); this.tokenUrlDiscoveryService = Objects.requireNonNull(tokenUrlDiscoveryService, "Missing required tokenUrlDiscoveryService"); } @Override - synchronized String getUrl() + synchronized ApiConnectionInfo getUrl() { if(url == null) { diff --git a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/RefreshTokenRequestBuilder.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/RefreshTokenRequestBuilder.java index 78c911c5..6e319f6a 100644 --- a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/RefreshTokenRequestBuilder.java +++ b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/RefreshTokenRequestBuilder.java @@ -1,12 +1,8 @@ package hec.army.usace.hec.cwbi.auth.http.client; -import hec.army.usace.hec.cwbi.auth.http.client.trustmanagers.CwbiAuthTrustManager; -import java.util.Optional; -import javax.net.ssl.SSLSocketFactory; -import mil.army.usace.hec.cwms.http.client.ApiConnectionInfoBuilder; +import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; import mil.army.usace.hec.cwms.http.client.HttpRequestBuilderImpl; import mil.army.usace.hec.cwms.http.client.HttpRequestResponse; -import mil.army.usace.hec.cwms.http.client.SslSocketData; import mil.army.usace.hec.cwms.http.client.auth.OAuth2Token; import mil.army.usace.hec.cwms.http.client.request.HttpRequestExecutor; @@ -16,7 +12,6 @@ public final class RefreshTokenRequestBuilder implements RefreshTokenRequestFluentBuilder { private String refreshToken; - private SSLSocketFactory sslSocketFactory; /** * Retrieved token via a refresh token. @@ -29,32 +24,13 @@ public TokenRequestFluentBuilder withRefreshToken(String refreshToken) { return new RefreshTokenRequestExecutor(); } - /** - * Set the SSLSocketFactory for the refresh request should it be needed. - * @param sslSocketFactory - SSLSocketFactory to use - * @return Builder for http request - */ - @Override - public RefreshTokenRequestBuilder withSSlSocketFactory(SSLSocketFactory sslSocketFactory) { - this.sslSocketFactory = sslSocketFactory; - return this; - } - - //package scoped for testing - Optional getSslSocketFactory() { - return Optional.ofNullable(sslSocketFactory); - } - - private class RefreshTokenRequestExecutor extends TokenRequestBuilder { + class RefreshTokenRequestExecutor extends TokenRequestBuilder { @Override OAuth2Token retrieveToken() throws IOException { OAuth2Token retVal = null; - SslSocketData sslSocketData = getSslSocketFactory().map(sf -> new SslSocketData(sf, CwbiAuthTrustManager.getTrustManager())) - .orElse(null); HttpRequestExecutor executor = - new HttpRequestBuilderImpl(new ApiConnectionInfoBuilder(getUrl()) - .withSslSocketData(sslSocketData).build()) + new HttpRequestBuilderImpl(getUrl()) .post() .withBody(new UrlEncodedFormData() .addRefreshToken(refreshToken) diff --git a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/RefreshTokenRequestFluentBuilder.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/RefreshTokenRequestFluentBuilder.java index a86afea2..18976a64 100644 --- a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/RefreshTokenRequestFluentBuilder.java +++ b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/RefreshTokenRequestFluentBuilder.java @@ -1,8 +1,5 @@ package hec.army.usace.hec.cwbi.auth.http.client; -import javax.net.ssl.SSLSocketFactory; - public interface RefreshTokenRequestFluentBuilder { TokenRequestFluentBuilder withRefreshToken(String refreshToken); - RefreshTokenRequestBuilder withSSlSocketFactory(SSLSocketFactory sslSocketFactory); } diff --git a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenRequestBuilder.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenRequestBuilder.java index 7e2d24fb..14989734 100644 --- a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenRequestBuilder.java +++ b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenRequestBuilder.java @@ -25,17 +25,18 @@ import java.io.IOException; import java.util.Objects; +import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; import mil.army.usace.hec.cwms.http.client.auth.OAuth2Token; abstract class TokenRequestBuilder implements TokenRequestFluentBuilder { static final String MEDIA_TYPE = "application/x-www-form-urlencoded"; - private String url; + private ApiConnectionInfo url; private String clientId; abstract OAuth2Token retrieveToken() throws IOException; - String getUrl() { + ApiConnectionInfo getUrl() { return url; } @@ -44,7 +45,7 @@ String getClientId() { } @Override - public RequestClientId withUrl(String url) { + public RequestClientId withUrl(ApiConnectionInfo url) { this.url = Objects.requireNonNull(url, "Missing required URL"); return new RequestClientIdImpl(); } diff --git a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenRequestFluentBuilder.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenRequestFluentBuilder.java index 2a4e2800..53555786 100644 --- a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenRequestFluentBuilder.java +++ b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenRequestFluentBuilder.java @@ -23,7 +23,9 @@ */ package hec.army.usace.hec.cwbi.auth.http.client; +import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; + public interface TokenRequestFluentBuilder { - RequestClientId withUrl(String url); + RequestClientId withUrl(ApiConnectionInfo url); } diff --git a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenUrlDiscoveryService.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenUrlDiscoveryService.java index 727d034a..fc3c537b 100644 --- a/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenUrlDiscoveryService.java +++ b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenUrlDiscoveryService.java @@ -23,6 +23,8 @@ */ package hec.army.usace.hec.cwbi.auth.http.client; +import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; + public interface TokenUrlDiscoveryService { - String discoverTokenUrl(); + ApiConnectionInfo discoverTokenUrl(); } diff --git a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockCwbiAuthTokenProvider.java b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockCwbiAuthTokenProvider.java index 18cda797..7687d325 100644 --- a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockCwbiAuthTokenProvider.java +++ b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockCwbiAuthTokenProvider.java @@ -23,12 +23,18 @@ */ package hec.army.usace.hec.cwbi.auth.http.client; +import hec.army.usace.hec.cwbi.auth.http.client.trustmanagers.CwbiAuthTrustManager; +import java.util.Objects; import javax.net.ssl.SSLSocketFactory; +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.OAuth2Token; public class MockCwbiAuthTokenProvider extends CwbiAuthTokenProviderBase { private final String url; + private final SSLSocketFactory sslSocketFactory; /** * Provider for OAuth2Tokens. @@ -38,7 +44,8 @@ public class MockCwbiAuthTokenProvider extends CwbiAuthTokenProviderBase { * @param sslSocketFactory - ssl socket factory */ public MockCwbiAuthTokenProvider(String url, String clientId, SSLSocketFactory sslSocketFactory) { - super(clientId, sslSocketFactory); + super(clientId); + this.sslSocketFactory = Objects.requireNonNull(sslSocketFactory, "Missing required sslSocketFactory"); this.url = url; } @@ -47,9 +54,15 @@ void setOAuth2Token(OAuth2Token token) { oauth2Token = token; } - //package scoped for testing @Override - String getUrl() { - return url; + ApiConnectionInfo getUrl() { + return new ApiConnectionInfoBuilder(url) + .withSslSocketData(new SslSocketData(sslSocketFactory, CwbiAuthTrustManager.getTrustManager())) + .build(); + } + + //package scoped for testing + SSLSocketFactory getSslSocketFactory() { + return sslSocketFactory; } } diff --git a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockDiscoveredCwbiAuthTokenProvider.java b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockDiscoveredCwbiAuthTokenProvider.java index 53ac51de..b4565502 100644 --- a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockDiscoveredCwbiAuthTokenProvider.java +++ b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockDiscoveredCwbiAuthTokenProvider.java @@ -24,23 +24,22 @@ package hec.army.usace.hec.cwbi.auth.http.client; import java.util.Objects; -import javax.net.ssl.SSLSocketFactory; +import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; import mil.army.usace.hec.cwms.http.client.auth.OAuth2Token; public class MockDiscoveredCwbiAuthTokenProvider extends CwbiAuthTokenProviderBase { private final TokenUrlDiscoveryService tokenUrlDiscoveryService; - private String url; + private ApiConnectionInfo url; /** * Provider for OAuth2Tokens. * * @param clientId - client name - * @param sslSocketFactory - ssl socket factory * @param tokenUrlDiscoveryService - service to discover the token URL */ - public MockDiscoveredCwbiAuthTokenProvider(String clientId, SSLSocketFactory sslSocketFactory, TokenUrlDiscoveryService tokenUrlDiscoveryService) { - super(clientId, sslSocketFactory); + public MockDiscoveredCwbiAuthTokenProvider(String clientId, TokenUrlDiscoveryService tokenUrlDiscoveryService) { + super(clientId); this.tokenUrlDiscoveryService = Objects.requireNonNull(tokenUrlDiscoveryService, "Missing required tokenUrlDiscoveryService"); } @@ -51,11 +50,16 @@ void setOAuth2Token(OAuth2Token token) { //package scoped for testing @Override - synchronized String getUrl() { + synchronized ApiConnectionInfo getUrl() { if(url == null) { url = tokenUrlDiscoveryService.discoverTokenUrl(); } return url; } + + //package scoped for testing + TokenUrlDiscoveryService getDiscoveryService() { + return tokenUrlDiscoveryService; + } } diff --git a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestCwbiTokenProvider.java b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestCwbiTokenProvider.java index 8a1decd8..9cfdf837 100644 --- a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestCwbiTokenProvider.java +++ b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestCwbiTokenProvider.java @@ -93,7 +93,7 @@ void testBuildTokenProvider() throws IOException { SSLSocketFactory sslSocketFactory = CwbiAuthSslSocketFactory.buildSSLSocketFactory( Collections.singletonList(getTestKeyManager())); CwbiAuthTokenProvider tokenProvider = new CwbiAuthTokenProvider(TOKEN_URL, "cumulus", sslSocketFactory); - assertEquals(TOKEN_URL, tokenProvider.getUrl()); + assertEquals(TOKEN_URL, tokenProvider.getUrl().getApiRoot()); assertEquals("cumulus", tokenProvider.getClientId()); } @@ -167,7 +167,7 @@ void testRefreshToken() throws IOException { void testConstructor() { SSLSocketFactory sslSocketFactory = getTestSslSocketFactory(); MockCwbiAuthTokenProvider tokenProvider = new MockCwbiAuthTokenProvider("test.com", "clientId", sslSocketFactory); - assertEquals("test.com", tokenProvider.getUrl()); + assertEquals("test.com", tokenProvider.getUrl().getApiRoot()); assertEquals("clientId", tokenProvider.getClientId()); assertEquals(sslSocketFactory, tokenProvider.getSslSocketFactory()); } diff --git a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDirectGrantX509TokenRequestBuilder.java b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDirectGrantX509TokenRequestBuilder.java index 9130e6ef..7bedfbda 100644 --- a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDirectGrantX509TokenRequestBuilder.java +++ b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDirectGrantX509TokenRequestBuilder.java @@ -23,6 +23,9 @@ */ package hec.army.usace.hec.cwbi.auth.http.client; +import hec.army.usace.hec.cwbi.auth.http.client.trustmanagers.CwbiAuthTrustManager; +import mil.army.usace.hec.cwms.http.client.ApiConnectionInfoBuilder; +import mil.army.usace.hec.cwms.http.client.SslSocketData; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -46,43 +49,36 @@ class TestDirectGrantX509TokenRequestBuilder { @Test void testRetrieveTokenMissingParams() { + SslSocketData sslSocketData = new SslSocketData(getTestSslSocketFactory(), CwbiAuthTrustManager.getTrustManager()); assertThrows(NullPointerException.class, () -> { OAuth2Token token = new DirectGrantX509TokenRequestBuilder() - .withSSlSocketFactory(null) - .withUrl("https://test.com") - .withClientId("cumulus") + .withUrl(new ApiConnectionInfoBuilder("https://test.com") + .withSslSocketData(sslSocketData) + .build()) + .withClientId(null) .fetchToken(); assertNull(token); }); assertThrows(NullPointerException.class, () -> { new DirectGrantX509TokenRequestBuilder() - .withSSlSocketFactory(getTestSslSocketFactory()) .withUrl(null); }); - - assertThrows(NullPointerException.class, () -> { - OAuth2Token token = new DirectGrantX509TokenRequestBuilder() - .withSSlSocketFactory(getTestSslSocketFactory()) - .withUrl("https://test.com") - .withClientId(null) - .fetchToken(); - assertNull(token); - }); - } @Test void testDirectGrantX509TokenRequestBuilder() throws IOException { - MockWebServer mockWebServer = new MockWebServer(); - try { + + try (MockWebServer mockWebServer = new MockWebServer()) { + SslSocketData sslSocketData = new SslSocketData(getTestSslSocketFactory(), CwbiAuthTrustManager.getTrustManager()); String body = readJsonFile(); mockWebServer.enqueue(new MockResponse().setBody(body).setResponseCode(200)); mockWebServer.start(); String baseUrl = String.format("http://localhost:%s", mockWebServer.getPort()); OAuth2Token token = new DirectGrantX509TokenRequestBuilder() - .withSSlSocketFactory(getTestSslSocketFactory()) - .withUrl(baseUrl) + .withUrl(new ApiConnectionInfoBuilder(baseUrl) + .withSslSocketData(sslSocketData) + .build()) .withClientId("cumulus") .fetchToken(); assertNotNull(token); @@ -91,8 +87,6 @@ void testDirectGrantX509TokenRequestBuilder() throws IOException { assertEquals(3600, token.getExpiresIn()); assertEquals("create", token.getScope()); assertEquals("IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk", token.getRefreshToken()); - } finally { - mockWebServer.shutdown(); } } diff --git a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDiscoveredCwbiTokenProvider.java b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDiscoveredCwbiTokenProvider.java index 8f52b755..cfbbbfc0 100644 --- a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDiscoveredCwbiTokenProvider.java +++ b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDiscoveredCwbiTokenProvider.java @@ -23,7 +23,7 @@ */ package hec.army.usace.hec.cwbi.auth.http.client; -import static hec.army.usace.hec.cwbi.auth.http.client.trustmanagers.CwbiAuthTrustManager.TOKEN_URL; +import hec.army.usace.hec.cwbi.auth.http.client.trustmanagers.CwbiAuthTrustManager; import java.io.File; import java.io.IOException; import java.net.InetAddress; @@ -31,14 +31,13 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import javax.net.ssl.KeyManager; import javax.net.ssl.SSLSocketFactory; 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.SslSocketData; import mil.army.usace.hec.cwms.http.client.auth.OAuth2Token; import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -71,8 +70,12 @@ void tearDown() throws IOException { } ApiConnectionInfo buildConnectionInfo() { + SSLSocketFactory sslSocketFactory = getTestSslSocketFactory(); + SslSocketData sslSocketData = new SslSocketData(sslSocketFactory, CwbiAuthTrustManager.getTrustManager()); String baseUrl = String.format("http://localhost:%s", mockHttpServer.getPort()); - return new ApiConnectionInfoBuilder(baseUrl).build(); + return new ApiConnectionInfoBuilder(baseUrl) + .withSslSocketData(sslSocketData) + .build(); } protected void launchMockServerWithResource(String resource) throws IOException { @@ -87,34 +90,27 @@ protected void launchMockServerWithResource(String resource) throws IOException } @Test - void testBuildTokenProvider() throws Exception { - SSLSocketFactory sslSocketFactory = CwbiAuthSslSocketFactory.buildSSLSocketFactory( - Collections.singletonList(getTestKeyManager())); - MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(TOKEN_URL); - DiscoveredCwbiAuthTokenProvider tokenProvider = new DiscoveredCwbiAuthTokenProvider("cumulus", sslSocketFactory, tokenUrlDiscoveryService); - assertEquals(TOKEN_URL, tokenProvider.getUrl()); + void testBuildTokenProvider() { + ApiConnectionInfo tokenUrl = buildConnectionInfo(); + MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(tokenUrl); + DiscoveredCwbiAuthTokenProvider tokenProvider = new DiscoveredCwbiAuthTokenProvider("cumulus", tokenUrlDiscoveryService); + assertEquals(tokenUrl.getApiRoot(), tokenProvider.getUrl().getApiRoot()); assertEquals("cumulus", tokenProvider.getClientId()); } @Test void testNulls() { - assertThrows(NullPointerException.class, () -> new DiscoveredCwbiAuthTokenProvider("cumulus", getTestSslSocketFactory(), null)); - assertThrows(NullPointerException.class, () -> new DiscoveredCwbiAuthTokenProvider("cumulus", null, new MockTokenUrlDiscoveryService(TOKEN_URL))); - assertThrows(NullPointerException.class, () -> new DiscoveredCwbiAuthTokenProvider(null, getTestSslSocketFactory(), new MockTokenUrlDiscoveryService(TOKEN_URL))); - } - - private KeyManager getTestKeyManager() { - return new KeyManager() { - }; + assertThrows(NullPointerException.class, () -> new DiscoveredCwbiAuthTokenProvider("cumulus", null)); + assertThrows(NullPointerException.class, () -> new DiscoveredCwbiAuthTokenProvider(null, new MockTokenUrlDiscoveryService(new ApiConnectionInfoBuilder("").build()))); } @Test void testGetToken() throws IOException { String resource = "oauth2token.json"; launchMockServerWithResource(resource); - String tokenUrl = buildConnectionInfo().getApiRoot(); + ApiConnectionInfo tokenUrl = buildConnectionInfo(); MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(tokenUrl); - DiscoveredCwbiAuthTokenProvider tokenProvider = new DiscoveredCwbiAuthTokenProvider("cumulus", getTestSslSocketFactory(), tokenUrlDiscoveryService); + DiscoveredCwbiAuthTokenProvider tokenProvider = new DiscoveredCwbiAuthTokenProvider("cumulus", tokenUrlDiscoveryService); OAuth2Token token = tokenProvider.getToken(); assertEquals("MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3", token.getAccessToken()); assertEquals("Bearer", token.getTokenType()); @@ -127,9 +123,9 @@ void testGetToken() throws IOException { void testClear() throws IOException { String resource = "oauth2token.json"; launchMockServerWithResource(resource); - String tokenUrl = buildConnectionInfo().getApiRoot(); + ApiConnectionInfo tokenUrl = buildConnectionInfo(); MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(tokenUrl); - MockDiscoveredCwbiAuthTokenProvider tokenProvider = new MockDiscoveredCwbiAuthTokenProvider("cumulus", getTestSslSocketFactory(), tokenUrlDiscoveryService); + MockDiscoveredCwbiAuthTokenProvider tokenProvider = new MockDiscoveredCwbiAuthTokenProvider("cumulus", tokenUrlDiscoveryService); OAuth2Token token = new OAuth2Token(); token.setAccessToken("abc123"); token.setTokenType("Bearer"); @@ -147,9 +143,9 @@ void testClear() throws IOException { void testRefreshToken() throws IOException { String resource = "oauth2token.json"; launchMockServerWithResource(resource); - String tokenUrl = buildConnectionInfo().getApiRoot(); + ApiConnectionInfo tokenUrl = buildConnectionInfo(); MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(tokenUrl); - MockDiscoveredCwbiAuthTokenProvider tokenProvider = new MockDiscoveredCwbiAuthTokenProvider("cumulus", getTestSslSocketFactory(), tokenUrlDiscoveryService); + MockDiscoveredCwbiAuthTokenProvider tokenProvider = new MockDiscoveredCwbiAuthTokenProvider("cumulus", tokenUrlDiscoveryService); OAuth2Token token = new OAuth2Token(); token.setAccessToken("abc123"); token.setTokenType("Bearer"); @@ -168,11 +164,14 @@ void testRefreshToken() throws IOException { @Test void testConstructor() { SSLSocketFactory sslSocketFactory = getTestSslSocketFactory(); - MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService("test.com"); - MockDiscoveredCwbiAuthTokenProvider tokenProvider = new MockDiscoveredCwbiAuthTokenProvider("clientId", sslSocketFactory, tokenUrlDiscoveryService); - assertEquals("test.com", tokenProvider.getUrl()); + ApiConnectionInfo tokenUrl = new ApiConnectionInfoBuilder("test.com") + .withSslSocketData(new SslSocketData(sslSocketFactory, CwbiAuthTrustManager.getTrustManager())) + .build(); + MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(tokenUrl); + MockDiscoveredCwbiAuthTokenProvider tokenProvider = new MockDiscoveredCwbiAuthTokenProvider("clientId", tokenUrlDiscoveryService); + assertEquals("test.com", tokenProvider.getUrl().getApiRoot()); assertEquals("clientId", tokenProvider.getClientId()); - assertEquals(sslSocketFactory, tokenProvider.getSslSocketFactory()); + assertSame(tokenUrlDiscoveryService, tokenProvider.getDiscoveryService()); } private SSLSocketFactory getTestSslSocketFactory() { @@ -215,14 +214,14 @@ public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddre } private static class MockTokenUrlDiscoveryService implements TokenUrlDiscoveryService { - private final String tokenUrl; + private final ApiConnectionInfo tokenUrl; - private MockTokenUrlDiscoveryService(String tokenUrl) { + private MockTokenUrlDiscoveryService(ApiConnectionInfo tokenUrl) { this.tokenUrl = tokenUrl; } @Override - public String discoverTokenUrl() { + public ApiConnectionInfo discoverTokenUrl() { return tokenUrl; } } diff --git a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestRefreshTokenRequestBuilder.java b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestRefreshTokenRequestBuilder.java index ca9a74d3..9120348b 100644 --- a/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestRefreshTokenRequestBuilder.java +++ b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestRefreshTokenRequestBuilder.java @@ -24,7 +24,10 @@ package hec.army.usace.hec.cwbi.auth.http.client; import static hec.army.usace.hec.cwbi.auth.http.client.TestDirectGrantX509TokenRequestBuilder.getTestSslSocketFactory; -import javax.net.ssl.SSLSocketFactory; +import hec.army.usace.hec.cwbi.auth.http.client.trustmanagers.CwbiAuthTrustManager; +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 static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -45,35 +48,34 @@ class TestRefreshTokenRequestBuilder { @Test void testRefreshTokenRequestBuilder() throws IOException { - MockWebServer mockWebServer = new MockWebServer(); - try { + try (MockWebServer mockWebServer = new MockWebServer()) { + SslSocketData sslSocketData = new SslSocketData(getTestSslSocketFactory(), CwbiAuthTrustManager.getTrustManager()); String body = readJsonFile(); mockWebServer.enqueue(new MockResponse().setBody(body).setResponseCode(200)); mockWebServer.start(); String baseUrl = String.format("http://localhost:%s", mockWebServer.getPort()); - SSLSocketFactory sslSocketFactory = getTestSslSocketFactory(); - RefreshTokenRequestBuilder builder = new RefreshTokenRequestBuilder() - .withSSlSocketFactory(sslSocketFactory); - assertSame(sslSocketFactory, builder.getSslSocketFactory().orElse(null)); - OAuth2Token token = new RefreshTokenRequestBuilder() - .withSSlSocketFactory(sslSocketFactory) - .withRefreshToken("abcdefghijklmnopqrstuvwxyz0123456789") - .withUrl(baseUrl) + ApiConnectionInfo url = new ApiConnectionInfoBuilder(baseUrl) + .withSslSocketData(sslSocketData) + .build(); + TokenRequestFluentBuilder refreshBuilder = new RefreshTokenRequestBuilder() + .withRefreshToken("abcdefghijklmnopqrstuvwxyz0123456789"); + OAuth2Token token = refreshBuilder + .withUrl(url) .withClientId("cumulus") .fetchToken(); assertNotNull(token); + assertSame(url, ((RefreshTokenRequestBuilder.RefreshTokenRequestExecutor)refreshBuilder).getUrl()); assertEquals("MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3", token.getAccessToken()); assertEquals("Bearer", token.getTokenType()); assertEquals(3600, token.getExpiresIn()); assertEquals("create", token.getScope()); assertEquals("IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk", token.getRefreshToken()); - } finally { - mockWebServer.shutdown(); } } @Test void testRetrieveTokenMissingParams() { + SslSocketData sslSocketData = new SslSocketData(getTestSslSocketFactory(), CwbiAuthTrustManager.getTrustManager()); assertThrows(NullPointerException.class, () -> { new RefreshTokenRequestBuilder() .withRefreshToken("testToken") @@ -83,7 +85,9 @@ void testRetrieveTokenMissingParams() { assertThrows(NullPointerException.class, () -> { OAuth2Token token = new RefreshTokenRequestBuilder() .withRefreshToken("testToken") - .withUrl("https://test.com") + .withUrl(new ApiConnectionInfoBuilder("https://test.com") + .withSslSocketData(sslSocketData) + .build()) .withClientId(null) .fetchToken(); assertNull(token); @@ -93,7 +97,9 @@ void testRetrieveTokenMissingParams() { assertThrows(NullPointerException.class, () -> { OAuth2Token token = new RefreshTokenRequestBuilder() .withRefreshToken(null) - .withUrl("https://test.com") + .withUrl(new ApiConnectionInfoBuilder("https://test.com") + .withSslSocketData(sslSocketData) + .build()) .withClientId("cumulus") .fetchToken(); assertNull(token);