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..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 @@ -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,17 @@ */ package hec.army.usace.hec.cwbi.auth.http.client; -import java.io.IOException; +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.auth.OAuth2TokenProvider; +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 implements OAuth2TokenProvider { +public final class CwbiAuthTokenProvider extends CwbiAuthTokenProviderBase { - private OAuth2Token oauth2Token; - private final String url; - private final String clientId; private final SSLSocketFactory sslSocketFactory; + private final String url; /** * Provider for OAuth2Tokens. @@ -44,52 +43,16 @@ public final class CwbiAuthTokenProvider implements OAuth2TokenProvider { * @param sslSocketFactory - ssl socket factory */ public CwbiAuthTokenProvider(String tokenUrl, String clientId, SSLSocketFactory sslSocketFactory) { + super(clientId); + this.sslSocketFactory = Objects.requireNonNull(sslSocketFactory, "Missing required 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; + ApiConnectionInfo getUrl() { + return new ApiConnectionInfoBuilder(url) + .withSslSocketData(new SslSocketData(sslSocketFactory, CwbiAuthTrustManager.getTrustManager())) + .build(); } - //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..e542092a --- /dev/null +++ b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/CwbiAuthTokenProviderBase.java @@ -0,0 +1,78 @@ +/* + * 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 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 { + protected OAuth2Token oauth2Token; + protected final String clientId; + + protected CwbiAuthTokenProviderBase(String clientId) { + this.clientId = Objects.requireNonNull(clientId, "Missing required clientId"); + } + + abstract ApiConnectionInfo 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() + .withUrl(getUrl()) + .withClientId(clientId) + .fetchToken(); + } + + @Override + public synchronized OAuth2Token refreshToken() throws IOException { + OAuth2Token token = new RefreshTokenRequestBuilder() + .withRefreshToken(oauth2Token.getRefreshToken()) + .withUrl(getUrl()) + .withClientId(clientId) + .fetchToken(); + oauth2Token = token; + return token; + } + + //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/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/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..84ab56ad --- /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 mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; + +public final class DiscoveredCwbiAuthTokenProvider extends CwbiAuthTokenProviderBase +{ + private final TokenUrlDiscoveryService tokenUrlDiscoveryService; + private ApiConnectionInfo url; + + public DiscoveredCwbiAuthTokenProvider(String clientId, TokenUrlDiscoveryService tokenUrlDiscoveryService) + { + super(clientId); + this.tokenUrlDiscoveryService = Objects.requireNonNull(tokenUrlDiscoveryService, "Missing required tokenUrlDiscoveryService"); + } + + @Override + synchronized ApiConnectionInfo 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/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/DirectGrantX509TokenRequestFluentBuilder.java b/cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenUrlDiscoveryService.java similarity index 82% rename from cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/DirectGrantX509TokenRequestFluentBuilder.java rename to cwbi-auth-http-client/src/main/java/hec/army/usace/hec/cwbi/auth/http/client/TokenUrlDiscoveryService.java index def0f1a1..fc3c537b 100644 --- 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/TokenUrlDiscoveryService.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,9 +23,8 @@ */ package hec.army.usace.hec.cwbi.auth.http.client; -import javax.net.ssl.SSLSocketFactory; +import mil.army.usace.hec.cwms.http.client.ApiConnectionInfo; -public interface DirectGrantX509TokenRequestFluentBuilder { - - TokenRequestFluentBuilder withSSlSocketFactory(SSLSocketFactory sslSocketFactory); +public interface TokenUrlDiscoveryService { + 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 19cc7e35..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,16 +23,17 @@ */ package hec.army.usace.hec.cwbi.auth.http.client; -import java.io.IOException; +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; -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; /** @@ -43,58 +44,24 @@ public class MockCwbiAuthTokenProvider implements OAuth2TokenProvider { * @param sslSocketFactory - ssl socket factory */ public MockCwbiAuthTokenProvider(String url, String clientId, SSLSocketFactory sslSocketFactory) { + super(clientId); + this.sslSocketFactory = Objects.requireNonNull(sslSocketFactory, "Missing required 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 - String getUrl() { - return url; + @Override + ApiConnectionInfo getUrl() { + return new ApiConnectionInfoBuilder(url) + .withSslSocketData(new SslSocketData(sslSocketFactory, CwbiAuthTrustManager.getTrustManager())) + .build(); } //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..b4565502 --- /dev/null +++ b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/MockDiscoveredCwbiAuthTokenProvider.java @@ -0,0 +1,65 @@ +/* + * 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; +import mil.army.usace.hec.cwms.http.client.auth.OAuth2Token; + +public class MockDiscoveredCwbiAuthTokenProvider extends CwbiAuthTokenProviderBase { + + private final TokenUrlDiscoveryService tokenUrlDiscoveryService; + private ApiConnectionInfo url; + + /** + * Provider for OAuth2Tokens. + * + * @param clientId - client name + * @param tokenUrlDiscoveryService - service to discover the token URL + */ + public MockDiscoveredCwbiAuthTokenProvider(String clientId, TokenUrlDiscoveryService tokenUrlDiscoveryService) { + super(clientId); + 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 + 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 b274c51d..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 @@ -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; @@ -96,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()); } @@ -170,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 new file mode 100644 index 00000000..cfbbbfc0 --- /dev/null +++ b/cwbi-auth-http-client/src/test/java/hec/army/usace/hec/cwbi/auth/http/client/TestDiscoveredCwbiTokenProvider.java @@ -0,0 +1,228 @@ +/* + * 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 hec.army.usace.hec.cwbi.auth.http.client.trustmanagers.CwbiAuthTrustManager; +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.concurrent.ExecutorService; +import java.util.concurrent.Executors; +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; +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() { + SSLSocketFactory sslSocketFactory = getTestSslSocketFactory(); + SslSocketData sslSocketData = new SslSocketData(sslSocketFactory, CwbiAuthTrustManager.getTrustManager()); + String baseUrl = String.format("http://localhost:%s", mockHttpServer.getPort()); + return new ApiConnectionInfoBuilder(baseUrl) + .withSslSocketData(sslSocketData) + .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() { + 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", null)); + assertThrows(NullPointerException.class, () -> new DiscoveredCwbiAuthTokenProvider(null, new MockTokenUrlDiscoveryService(new ApiConnectionInfoBuilder("").build()))); + } + + @Test + void testGetToken() throws IOException { + String resource = "oauth2token.json"; + launchMockServerWithResource(resource); + ApiConnectionInfo tokenUrl = buildConnectionInfo(); + MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(tokenUrl); + DiscoveredCwbiAuthTokenProvider tokenProvider = new DiscoveredCwbiAuthTokenProvider("cumulus", 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); + ApiConnectionInfo tokenUrl = buildConnectionInfo(); + MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(tokenUrl); + MockDiscoveredCwbiAuthTokenProvider tokenProvider = new MockDiscoveredCwbiAuthTokenProvider("cumulus", 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); + ApiConnectionInfo tokenUrl = buildConnectionInfo(); + MockTokenUrlDiscoveryService tokenUrlDiscoveryService = new MockTokenUrlDiscoveryService(tokenUrl); + MockDiscoveredCwbiAuthTokenProvider tokenProvider = new MockDiscoveredCwbiAuthTokenProvider("cumulus", 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(); + 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()); + assertSame(tokenUrlDiscoveryService, tokenProvider.getDiscoveryService()); + } + + 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 implements TokenUrlDiscoveryService { + private final ApiConnectionInfo tokenUrl; + + private MockTokenUrlDiscoveryService(ApiConnectionInfo tokenUrl) { + this.tokenUrl = tokenUrl; + } + + @Override + 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);