From c6600ea25be5ebdb8f936e4d2fe05f65f413eee2 Mon Sep 17 00:00:00 2001 From: shawkins Date: Mon, 1 Nov 2021 20:44:38 -0400 Subject: [PATCH] adding default client construction this allows moving openid logic away from okhttp --- .../kubernetes/client/http/HttpClient.java | 10 +- .../kubernetes/client/http/HttpRequest.java | 19 ++ .../internal/okhttp/OkHttpClientFactory.java | 31 ++-- .../internal/okhttp/OkHttpClientImpl.java | 19 +- .../client/utils/HttpClientUtils.java | 8 +- .../OpenIDConnectionUtils.java | 163 +++++++----------- .../client/utils/TokenRefreshInterceptor.java | 2 +- .../kubernetes/client/utils/URLUtils.java | 15 +- .../IpAddressMatcherTest.java | 10 +- .../OpenIDConnectionUtilsTest.java | 48 ++---- 10 files changed, 160 insertions(+), 165 deletions(-) rename kubernetes-client/src/main/java/io/fabric8/kubernetes/client/{internal/okhttp => utils}/OpenIDConnectionUtils.java (68%) rename kubernetes-client/src/test/java/io/fabric8/kubernetes/client/{internal/okhttp => utils}/IpAddressMatcherTest.java (93%) rename kubernetes-client/src/test/java/io/fabric8/kubernetes/client/{internal/okhttp => utils}/OpenIDConnectionUtilsTest.java (81%) diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/http/HttpClient.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/http/HttpClient.java index 34eda321da5..1230083145a 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/http/HttpClient.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/http/HttpClient.java @@ -18,15 +18,20 @@ import io.fabric8.kubernetes.client.Config; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + import java.io.IOException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -public interface HttpClient { +public interface HttpClient extends AutoCloseable { public interface Factory { HttpClient createHttpClient(Config config); + + HttpClient.Builder newBuilder(); } @@ -45,8 +50,11 @@ public interface Builder { Builder addOrReplaceInterceptor(String name, Interceptor interceptor); Builder authenticatorNone(); + + Builder sslContext(SSLContext context, TrustManager[] trustManagers); } + @Override void close(); Builder newBuilder(); diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/http/HttpRequest.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/http/HttpRequest.java index 9556d81ed6b..898d7ff6a00 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/http/HttpRequest.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/http/HttpRequest.java @@ -17,8 +17,13 @@ package io.fabric8.kubernetes.client.http; import java.io.InputStream; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.stream.Collectors; public interface HttpRequest extends HttpHeaders { @@ -61,8 +66,22 @@ default Builder patch(String contentType, String patchForUpdate) { @Override Builder setHeader(String k, String v); + + default Builder post(Map formData) { + return post("application/x-www-form-urlencoded", formData.entrySet().stream().map((e) -> { + return formURLEncode(e.getKey()) + "=" + formURLEncode(e.getValue()); + }).collect(Collectors.joining("&"))); + } } + + static String formURLEncode(String value) { + try { + return URLEncoder.encode(value, StandardCharsets.UTF_8.displayName()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } URI uri(); diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/okhttp/OkHttpClientFactory.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/okhttp/OkHttpClientFactory.java index a02c9a225de..42c6ec5a746 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/okhttp/OkHttpClientFactory.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/okhttp/OkHttpClientFactory.java @@ -21,6 +21,7 @@ import io.fabric8.kubernetes.client.http.HttpClient; import io.fabric8.kubernetes.client.http.HttpClient.Builder; import io.fabric8.kubernetes.client.internal.SSLUtils; +import io.fabric8.kubernetes.client.internal.okhttp.OkHttpClientImpl.BuilderImpl; import io.fabric8.kubernetes.client.utils.HttpClientUtils; import okhttp3.ConnectionSpec; import okhttp3.Credentials; @@ -35,13 +36,11 @@ import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.Proxy; import java.net.URL; -import java.security.GeneralSecurityException; import java.util.Arrays; import java.util.Collections; import java.util.Locale; @@ -56,6 +55,11 @@ public class OkHttpClientFactory implements HttpClient.Factory { public HttpClient createHttpClient(Config config) { return createHttpClient(config, builder -> {}); } + + @Override + public Builder newBuilder() { + return OkHttpClientImpl.builder(); + } /** * Creates an HTTP client configured to access the Kubernetes API. @@ -76,20 +80,6 @@ public static io.fabric8.kubernetes.client.http.HttpClient createHttpClient(fina httpClientBuilder.hostnameVerifier((s, sslSession) -> true); } - TrustManager[] trustManagers = SSLUtils.trustManagers(config); - KeyManager[] keyManagers = SSLUtils.keyManagers(config); - - try { - SSLContext sslContext = SSLUtils.sslContext(keyManagers, trustManagers); - X509TrustManager trustManager = null; - if (trustManagers != null && trustManagers.length == 1) { - trustManager = (X509TrustManager) trustManagers[0]; - } - httpClientBuilder.sslSocketFactory(sslContext.getSocketFactory(), trustManager); - } catch (GeneralSecurityException e) { - throw new AssertionError(e); // The system has no TLS. Just give up. - } - Logger reqLogger = LoggerFactory.getLogger(HttpLoggingInterceptor.class); if (reqLogger.isTraceEnabled()) { HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); @@ -159,7 +149,14 @@ public static io.fabric8.kubernetes.client.http.HttpClient createHttpClient(fina additionalConfig.accept(httpClientBuilder); } - Builder builderWrapper = new OkHttpClientImpl(httpClientBuilder.build()).newBuilder(); + Builder builderWrapper = new OkHttpClientImpl.BuilderImpl(httpClientBuilder); + + TrustManager[] trustManagers = SSLUtils.trustManagers(config); + KeyManager[] keyManagers = SSLUtils.keyManagers(config); + + SSLContext sslContext = SSLUtils.sslContext(keyManagers, trustManagers); + builderWrapper.sslContext(sslContext, trustManagers); + HttpClientUtils.createApplicableInterceptors(config).forEach((s, i) -> builderWrapper.addOrReplaceInterceptor(s, i)); return builderWrapper.build(); } catch (Exception e) { diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/okhttp/OkHttpClientImpl.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/okhttp/OkHttpClientImpl.java index e2ff575e098..aa88be1e3b1 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/okhttp/OkHttpClientImpl.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/okhttp/OkHttpClientImpl.java @@ -32,6 +32,10 @@ import okhttp3.ResponseBody; import okhttp3.logging.HttpLoggingInterceptor; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + import java.io.IOException; import java.io.InputStream; import java.io.Reader; @@ -147,7 +151,7 @@ public String getName() { } } - static class BuilderImpl implements Builder { + public static class BuilderImpl implements Builder { private boolean streaming; private OkHttpClient.Builder builder; @@ -227,6 +231,17 @@ public Builder authenticatorNone() { builder.authenticator(Authenticator.NONE); return this; } + + @Override + public Builder sslContext(SSLContext context, TrustManager[] trustManagers) { + X509TrustManager trustManager = null; + if (trustManagers != null && trustManagers.length == 1) { + trustManager = (X509TrustManager) trustManagers[0]; + } + builder.sslSocketFactory(context.getSocketFactory(), trustManager); + return null; + } + } private final okhttp3.OkHttpClient httpClient; @@ -304,5 +319,5 @@ public okhttp3.OkHttpClient getOkHttpClient() { public HttpRequest.Builder newHttpRequestBuilder() { return new OkHttpRequestImpl.BuilderImpl(); } - + } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/HttpClientUtils.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/HttpClientUtils.java index 2c5755dcd16..4b194fa67eb 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/HttpClientUtils.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/HttpClientUtils.java @@ -22,6 +22,7 @@ import io.fabric8.kubernetes.client.http.HttpHeaders; import io.fabric8.kubernetes.client.http.Interceptor; import io.fabric8.kubernetes.client.internal.okhttp.OkHttpClientFactory; +import io.fabric8.kubernetes.client.internal.okhttp.OkHttpClientImpl; import java.net.MalformedURLException; import java.net.URL; @@ -126,6 +127,11 @@ public static String basicCredentials(String username, String password) { public static HttpClient createHttpClient(Config config) { // TODO: replace with reflection / service load and factory interface - return OkHttpClientFactory.createHttpClient(config, builder -> {}); + return new OkHttpClientFactory().createHttpClient(config); + } + + public static HttpClient.Builder createHttpClientBuilder() { + // TODO: replace with reflection / service load and factory interface + return new OkHttpClientFactory().newBuilder(); } } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/okhttp/OpenIDConnectionUtils.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/OpenIDConnectionUtils.java similarity index 68% rename from kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/okhttp/OpenIDConnectionUtils.java rename to kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/OpenIDConnectionUtils.java index 52dda566175..856c7e06b58 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/internal/okhttp/OpenIDConnectionUtils.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/OpenIDConnectionUtils.java @@ -13,32 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.fabric8.kubernetes.client.internal.okhttp; +package io.fabric8.kubernetes.client.utils; import com.fasterxml.jackson.core.JsonProcessingException; import io.fabric8.kubernetes.api.model.NamedContext; +import io.fabric8.kubernetes.client.http.HttpClient; +import io.fabric8.kubernetes.client.http.HttpRequest; +import io.fabric8.kubernetes.client.http.HttpResponse; import io.fabric8.kubernetes.client.internal.KubeConfigUtils; import io.fabric8.kubernetes.client.internal.SSLUtils; -import io.fabric8.kubernetes.client.utils.Serialization; -import io.fabric8.kubernetes.client.utils.URLUtils; -import io.fabric8.kubernetes.client.utils.Utils; -import okhttp3.FormBody; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; + import java.io.File; import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.KeyManagementException; import java.security.KeyStoreException; @@ -47,6 +39,7 @@ import java.security.cert.CertificateException; import java.security.spec.InvalidKeySpecException; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.Map; /** @@ -78,6 +71,7 @@ private OpenIDConnectionUtils() { } * Fetch OpenID Connect token from Kubeconfig, check whether it's still valid or not; If expired handle * token refresh with OpenID Connection provider APIs * + * @param client Http Client * @param currentAuthProviderConfig current AuthInfo's AuthProvider config as a map * @return access token for interacting with Kubernetes API */ @@ -97,7 +91,7 @@ public static String resolveOIDCTokenFromAuthConfig(Map currentA /** * Get OIDC Provider discovery token_endpoint and issue refresh request * - * @param client OkHttp Client + * @param client Http Client * @param wellKnownOpenIdConfiguration OIDC Provider Discovery Document * @param clientId client id as string * @param refreshToken refresh token @@ -106,7 +100,7 @@ public static String resolveOIDCTokenFromAuthConfig(Map currentA * @param shouldPersistUpdatedTokenInKubeConfig boolean value whether to modify kubeconfig file in disc or not * @return returns access token(either updated or old) depending upon response from provider */ - static String getOIDCProviderTokenEndpointAndRefreshToken(OkHttpClient client, Map wellKnownOpenIdConfiguration, String clientId, String refreshToken, String clientSecret, String accessToken, boolean shouldPersistUpdatedTokenInKubeConfig) { + static String getOIDCProviderTokenEndpointAndRefreshToken(HttpClient client, Map wellKnownOpenIdConfiguration, String clientId, String refreshToken, String clientSecret, String accessToken, boolean shouldPersistUpdatedTokenInKubeConfig) { String oidcTokenEndpoint = getParametersFromDiscoveryResponse(wellKnownOpenIdConfiguration, TOKEN_ENDPOINT_PARAM); try { String freshAccessToken = OpenIDConnectionUtils.refreshToken(client, oidcTokenEndpoint, clientId, refreshToken, clientSecret, shouldPersistUpdatedTokenInKubeConfig); @@ -133,7 +127,7 @@ static boolean isTokenRefreshSupported(Map currentAuthProviderCo /** * Issue Token refresh request * - * @param client okhttp client + * @param client http client * @param oidcTokenEndpoint OIDC provider token endpoint * @param clientId client id * @param refreshToken refresh token for token refreshing @@ -141,7 +135,7 @@ static boolean isTokenRefreshSupported(Map currentAuthProviderCo * @param shouldPersistUpdatedTokenInKubeConfig boolean value whether to update local kubeconfig file or not * @return access token received from OpenID Connection provider */ - static String refreshToken(OkHttpClient client, String oidcTokenEndpoint, String clientId, String refreshToken, String clientSecret, boolean shouldPersistUpdatedTokenInKubeConfig) { + static String refreshToken(HttpClient client, String oidcTokenEndpoint, String clientId, String refreshToken, String clientSecret, boolean shouldPersistUpdatedTokenInKubeConfig) { try { Map response = refreshOidcToken(client, clientId, refreshToken, clientSecret, oidcTokenEndpoint); @@ -166,7 +160,7 @@ static String refreshToken(OkHttpClient client, String oidcTokenEndpoint, String /** * Issue Token Refresh HTTP Request to OIDC Provider * - * @param client OkHttp Client for issuing HTTP request + * @param client Http Client for issuing HTTP request * @param clientId client id * @param refreshToken refresh token * @param clientSecret client secret @@ -174,19 +168,17 @@ static String refreshToken(OkHttpClient client, String oidcTokenEndpoint, String * @return response as HashMap * @throws IOException in case of any error in contacting OIDC provider */ - static Map refreshOidcToken(OkHttpClient client, String clientId, String refreshToken, String clientSecret, String tokenURL) throws IOException { - try (Response response = client.newCall(getTokenRefreshHttpRequest(tokenURL, clientId, refreshToken, clientSecret)).execute()) { - String responseBody; - if (response.body() != null) { - // Get response body as string - responseBody = response.body().string(); - if (response.isSuccessful()) { - // Deserialize response body into a Map and return - return convertJsonStringToMap(responseBody); - } else { - // Log error response body - LOGGER.warn("Response: {}", responseBody); - } + static Map refreshOidcToken(HttpClient client, String clientId, String refreshToken, String clientSecret, String tokenURL) throws IOException { + HttpRequest request = getTokenRefreshHttpRequest(client, tokenURL, clientId, refreshToken, clientSecret); + HttpResponse response = client.send(request, String.class); + if (response.body() != null) { + // Get response body as string + if (response.isSuccessful()) { + // Deserialize response body into a Map and return + return convertJsonStringToMap(response.body()); + } else { + // Log error response body + LOGGER.warn("Response: {}", response.body()); } } return Collections.emptyMap(); @@ -197,17 +189,19 @@ static Map refreshOidcToken(OkHttpClient client, String clientId * at a well-known URL which looks like this: https://[base-server-url]/.well-known/openid-configuration * This method performs an Http Get at this public URL and fetches response as a HashMap * - * @param client OkHttpClient for doing HTTP Get to well known URL of OpenID provider + * @param client HttpClient for doing HTTP Get to well known URL of OpenID provider * @param issuer OpenID Connect provider issuer URL * @return a HashMap of Discovery document */ - static Map getOIDCDiscoveryDocumentAsMap(OkHttpClient client, String issuer) { - try (Response response = client.newCall(getDiscoveryDocumentHttpRequest(issuer)).execute()) { + static Map getOIDCDiscoveryDocumentAsMap(HttpClient client, String issuer) { + HttpRequest request = client.newHttpRequestBuilder().uri(getWellKnownUrlForOpenIDIssuer(issuer)).build(); + try { + HttpResponse response = client.send(request, String.class); if (response.isSuccessful() && response.body() != null) { - return convertJsonStringToMap(response.body().string()); + return convertJsonStringToMap(response.body()); } else { // Don't produce an error that's too huge (e.g. if we get HTML back for some reason). - String responseBody = response.body() != null ? response.body().string() : null; + String responseBody = response.body(); LOGGER.warn("oidc: failed to query metadata endpoint: {} {}", response.code(), responseBody); } } catch (IOException e) { @@ -282,84 +276,59 @@ private static Map convertJsonStringToMap(String jsonString) thr return Serialization.jsonMapper().readValue(jsonString, Map.class); } - private static SSLContext getSSLContext(String idpCert) { + private static HttpClient getDefaultHttpClientWithPemCert(String idpCert) { SSLContext sslContext = null; + TrustManager[] trustManagers = null; + // fist, lets get the pem + String pemCert = new String(java.util.Base64.getDecoder().decode(idpCert)); - if (idpCert != null) { - // fist, lets get the pem - String pemCert = new String(java.util.Base64.getDecoder().decode(idpCert)); - - try { - TrustManager[] trustManagers = SSLUtils.trustManagers(pemCert, null, false, null, null); - KeyManager[] keyManagers = SSLUtils.keyManagers(pemCert, null, null, null, null, null, null, null); - sslContext = SSLUtils.sslContext(keyManagers, trustManagers); - } catch (KeyStoreException | - KeyManagementException | - InvalidKeySpecException | - NoSuchAlgorithmException | - IOException | - UnrecoverableKeyException | - CertificateException e) { - throw new RuntimeException("Could not import idp certificate", e); - } + try { + trustManagers = SSLUtils.trustManagers(pemCert, null, false, null, null); + KeyManager[] keyManagers = SSLUtils.keyManagers(pemCert, null, null, null, null, null, null, null); + sslContext = SSLUtils.sslContext(keyManagers, trustManagers); + } catch (KeyStoreException | + KeyManagementException | + InvalidKeySpecException | + NoSuchAlgorithmException | + IOException | + UnrecoverableKeyException | + CertificateException e) { + throw new RuntimeException("Could not import idp certificate", e); } - return sslContext; - } - - private static OkHttpClient getOkHttpClient(SSLContext sslContext, String pemCert) { - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); + + HttpClient.Builder clientBuilder = HttpClientUtils.createHttpClientBuilder(); if (sslContext != null) { - clientBuilder.sslSocketFactory(sslContext.getSocketFactory(), getTrustManagerForAllCerts(pemCert)); + clientBuilder.sslContext(sslContext, trustManagers); } return clientBuilder.build(); } - private static X509TrustManager getTrustManagerForAllCerts(String pemCert) { - X509TrustManager trustManager = null; - try { - TrustManager[] trustManagers = SSLUtils.trustManagers(pemCert, null, false, null, null); - if (trustManagers != null && trustManagers.length == 1) { - trustManager = (X509TrustManager) trustManagers[0]; - } - } catch (CertificateException | NoSuchAlgorithmException | KeyStoreException | IOException e) { - LOGGER.warn("Could not get trust manager"); - } - return trustManager; - } + private static HttpRequest getTokenRefreshHttpRequest(HttpClient client, String tokenEndpointUrl, String clientId, String refreshToken, String clientSecret) throws JsonProcessingException { + HttpRequest.Builder httpRequestBuilder = client.newHttpRequestBuilder().uri(tokenEndpointUrl); - private static Request getTokenRefreshHttpRequest(String tokenEndpointUrl, String clientId, String refreshToken, String clientSecret) throws JsonProcessingException { - HttpUrl.Builder httpUrlBuilder = HttpUrl.get(tokenEndpointUrl).newBuilder(); - - RequestBody requestBody = getRequestBodyContentForRefresh(clientId, refreshToken, clientSecret); - Request.Builder requestBuilder = new Request.Builder().post(requestBody).url(httpUrlBuilder.build()); + Map requestBody = getRequestBodyContentForRefresh(clientId, refreshToken, clientSecret); String credentials = java.util.Base64.getEncoder() .encodeToString((clientId + ':' + clientSecret).getBytes(StandardCharsets.UTF_8)); - requestBuilder.addHeader("Authorization", "Basic " + credentials); - requestBuilder.addHeader("Content-Type", "application/x-www-form-urlencoded"); - return requestBuilder.build(); - } - - private static Request getDiscoveryDocumentHttpRequest(String issuer) throws MalformedURLException { - URL wellKnown = new URL(getWellKnownUrlForOpenIDIssuer(issuer)); - return new Request.Builder() - .url(wellKnown) - .build(); + httpRequestBuilder.header("Authorization", "Basic " + credentials); + httpRequestBuilder.post(requestBody); + return httpRequestBuilder.build(); } - private static RequestBody getRequestBodyContentForRefresh(String clientId, String refreshToken, String clientSecret) { - return new FormBody.Builder() - .add(REFRESH_TOKEN_PARAM, refreshToken) - .add(GRANT_TYPE_PARAM, GRANT_TYPE_REFRESH_TOKEN) - .add(CLIENT_ID_PARAM, clientId) - .add(CLIENT_SECRET_PARAM, clientSecret) - .build(); + private static Map getRequestBodyContentForRefresh(String clientId, String refreshToken, String clientSecret) { + Map result = new LinkedHashMap<>(); + result.put(REFRESH_TOKEN_PARAM, refreshToken); + result.put(GRANT_TYPE_PARAM, GRANT_TYPE_REFRESH_TOKEN); + result.put(CLIENT_ID_PARAM, clientId); + result.put(CLIENT_SECRET_PARAM, clientSecret); + return result; } private static String getOIDCProviderTokenEndpointAndRefreshToken(String issuer, String clientId, String refreshToken, String clientSecret, String accessToken, String idpCert) { - OkHttpClient okHttpClient = getOkHttpClient(getSSLContext(idpCert), idpCert); - Map wellKnownOpenIdConfiguration = getOIDCDiscoveryDocumentAsMap(okHttpClient, issuer); - return getOIDCProviderTokenEndpointAndRefreshToken(okHttpClient, wellKnownOpenIdConfiguration, clientId, refreshToken, clientSecret, accessToken, true); + try (HttpClient newClient = getDefaultHttpClientWithPemCert(idpCert)) { + Map wellKnownOpenIdConfiguration = getOIDCDiscoveryDocumentAsMap(newClient, issuer); + return getOIDCProviderTokenEndpointAndRefreshToken(newClient, wellKnownOpenIdConfiguration, clientId, refreshToken, clientSecret, accessToken, true); + } } private static boolean persistKubeConfigWithUpdatedToken(Map updatedAuthProviderConfig) throws IOException { diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptor.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptor.java index 5749a7fb63b..b8e6f4d56c7 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptor.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptor.java @@ -19,7 +19,6 @@ import io.fabric8.kubernetes.client.http.BasicBuilder; import io.fabric8.kubernetes.client.http.HttpResponse; import io.fabric8.kubernetes.client.http.Interceptor; -import io.fabric8.kubernetes.client.internal.okhttp.OpenIDConnectionUtils; import java.net.HttpURLConnection; @@ -31,6 +30,7 @@ public class TokenRefreshInterceptor implements Interceptor { public static final String NAME = "TOKEN"; private final Config config; + public TokenRefreshInterceptor(Config config) { this.config = config; } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/URLUtils.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/URLUtils.java index 037019776f6..8b9f0d5b626 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/URLUtils.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/URLUtils.java @@ -15,13 +15,12 @@ */ package io.fabric8.kubernetes.client.utils; -import java.io.UnsupportedEncodingException; +import io.fabric8.kubernetes.client.http.HttpRequest; + import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; public class URLUtils { @@ -43,11 +42,7 @@ public URLBuilder addQueryParameter(String key, String value) { } else { url.append("&"); } - try { - url.append(encodeToUTF(key).replaceAll("[+]", "%20")).append("=").append(encodeToUTF(value).replaceAll("[+]", "%20")); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } + url.append(encodeToUTF(key).replaceAll("[+]", "%20")).append("=").append(encodeToUTF(value).replaceAll("[+]", "%20")); return this; } @@ -198,7 +193,7 @@ public static boolean isValidURL(String url) { } } - public static String encodeToUTF(String url) throws UnsupportedEncodingException { - return URLEncoder.encode(url, StandardCharsets.UTF_8.displayName()); + public static String encodeToUTF(String url) { + return HttpRequest.formURLEncode(url); } } diff --git a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/internal/okhttp/IpAddressMatcherTest.java b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/IpAddressMatcherTest.java similarity index 93% rename from kubernetes-client/src/test/java/io/fabric8/kubernetes/client/internal/okhttp/IpAddressMatcherTest.java rename to kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/IpAddressMatcherTest.java index b6a152c1d24..5ec46ffadf2 100644 --- a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/internal/okhttp/IpAddressMatcherTest.java +++ b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/IpAddressMatcherTest.java @@ -13,19 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.fabric8.kubernetes.client.internal.okhttp; +package io.fabric8.kubernetes.client.utils; import io.fabric8.kubernetes.client.Config; -import io.fabric8.kubernetes.client.utils.HttpClientUtils; -import io.fabric8.kubernetes.client.utils.IpAddressMatcher; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertFalse; import org.junit.jupiter.api.Test; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class IpAddressMatcherTest { @Test diff --git a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/internal/okhttp/OpenIDConnectionUtilsTest.java b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/OpenIDConnectionUtilsTest.java similarity index 81% rename from kubernetes-client/src/test/java/io/fabric8/kubernetes/client/internal/okhttp/OpenIDConnectionUtilsTest.java rename to kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/OpenIDConnectionUtilsTest.java index f6b267f1876..e8552507578 100644 --- a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/internal/okhttp/OpenIDConnectionUtilsTest.java +++ b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/OpenIDConnectionUtilsTest.java @@ -13,17 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.fabric8.kubernetes.client.internal.okhttp; +package io.fabric8.kubernetes.client.utils; import io.fabric8.kubernetes.api.model.NamedContext; +import io.fabric8.kubernetes.client.http.HttpClient; +import io.fabric8.kubernetes.client.http.HttpResponse; import io.fabric8.kubernetes.client.internal.KubeConfigUtils; -import okhttp3.Call; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Protocol; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.ResponseBody; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -36,23 +31,22 @@ import java.util.HashMap; import java.util.Map; -import static io.fabric8.kubernetes.client.internal.okhttp.OpenIDConnectionUtils.CLIENT_ID_KUBECONFIG; -import static io.fabric8.kubernetes.client.internal.okhttp.OpenIDConnectionUtils.CLIENT_SECRET_KUBECONFIG; -import static io.fabric8.kubernetes.client.internal.okhttp.OpenIDConnectionUtils.ID_TOKEN_KUBECONFIG; -import static io.fabric8.kubernetes.client.internal.okhttp.OpenIDConnectionUtils.ID_TOKEN_PARAM; -import static io.fabric8.kubernetes.client.internal.okhttp.OpenIDConnectionUtils.REFRESH_TOKEN_KUBECONFIG; -import static io.fabric8.kubernetes.client.internal.okhttp.OpenIDConnectionUtils.REFRESH_TOKEN_PARAM; -import static io.fabric8.kubernetes.client.internal.okhttp.OpenIDConnectionUtils.TOKEN_ENDPOINT_PARAM; +import static io.fabric8.kubernetes.client.utils.OpenIDConnectionUtils.CLIENT_ID_KUBECONFIG; +import static io.fabric8.kubernetes.client.utils.OpenIDConnectionUtils.CLIENT_SECRET_KUBECONFIG; +import static io.fabric8.kubernetes.client.utils.OpenIDConnectionUtils.ID_TOKEN_KUBECONFIG; +import static io.fabric8.kubernetes.client.utils.OpenIDConnectionUtils.ID_TOKEN_PARAM; +import static io.fabric8.kubernetes.client.utils.OpenIDConnectionUtils.REFRESH_TOKEN_KUBECONFIG; +import static io.fabric8.kubernetes.client.utils.OpenIDConnectionUtils.REFRESH_TOKEN_PARAM; +import static io.fabric8.kubernetes.client.utils.OpenIDConnectionUtils.TOKEN_ENDPOINT_PARAM; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class OpenIDConnectionUtilsTest { - OkHttpClient mockClient = Mockito.mock(OkHttpClient.class, Mockito.RETURNS_DEEP_STUBS); + HttpClient mockClient = Mockito.mock(HttpClient.class, Mockito.RETURNS_DEEP_STUBS); @Test void testLoadTokenURL() throws IOException { @@ -194,20 +188,14 @@ void testgetParametersFromDiscoveryResponse() { } private void mockOkHttpClient(int responseCode, String responseAsStr) throws IOException { - Call mockCall = mock(Call.class); - Response mockSuccessResponse = mockResponse(responseCode, responseAsStr); - when(mockCall.execute()) - .thenReturn(mockSuccessResponse); - when(mockClient.newCall(any())).thenReturn(mockCall); + HttpResponse mockSuccessResponse = mockResponse(responseCode, responseAsStr); + when(mockClient.send(any(), Mockito.eq(String.class))).thenReturn(mockSuccessResponse); } - private Response mockResponse(int responseCode, String responseBody) { - return new Response.Builder() - .request(new Request.Builder().url("http://mock").build()) - .protocol(Protocol.HTTP_1_1) - .code(responseCode) - .body(ResponseBody.create(MediaType.get("application/json"), responseBody)) - .message("mock") - .build(); + private HttpResponse mockResponse(int responseCode, String responseBody) { + HttpResponse response = Mockito.mock(HttpResponse.class, Mockito.CALLS_REAL_METHODS); + Mockito.when(response.code()).thenReturn(responseCode); + Mockito.when(response.body()).thenReturn(responseBody); + return response; } }