From 80d68d19c707077e7de95f4fbb633cd93d769d70 Mon Sep 17 00:00:00 2001 From: Min Jin Date: Thu, 12 Dec 2024 14:10:09 -0800 Subject: [PATCH] merge TokeFile with Refresh authentication Signed-off-by: Min Jin --- .../credentials/RefreshAuthentication.java | 14 ++++- .../credentials/TokenFileAuthentication.java | 57 +++++-------------- .../exception/TokenAquisitionException.java | 19 +++++++ .../TokenFileAuthenticationTest.java | 29 +++------- 4 files changed, 52 insertions(+), 67 deletions(-) create mode 100644 util/src/main/java/io/kubernetes/client/util/exception/TokenAquisitionException.java diff --git a/util/src/main/java/io/kubernetes/client/util/credentials/RefreshAuthentication.java b/util/src/main/java/io/kubernetes/client/util/credentials/RefreshAuthentication.java index f03c610cb1..72eb4e6c29 100644 --- a/util/src/main/java/io/kubernetes/client/util/credentials/RefreshAuthentication.java +++ b/util/src/main/java/io/kubernetes/client/util/credentials/RefreshAuthentication.java @@ -20,6 +20,7 @@ import java.time.Instant; import java.util.function.Supplier; +import io.kubernetes.client.util.exception.TokenAquisitionException; import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -28,7 +29,6 @@ // TODO: prefer OpenAPI backed Auentication once it is available. see details in // https://github.com/OpenAPITools/openapi-generator/pull/6036. currently, the // workaround is to hijack the http request. -// TODO: Merge this with TokenFileAuthentication. public class RefreshAuthentication implements Authentication, Interceptor { private Instant expiry; private Duration refreshPeriod; @@ -43,14 +43,22 @@ public RefreshAuthentication(Supplier tokenSupplier, Duration refreshPer public RefreshAuthentication(Supplier tokenSupplier, Duration refreshPeriod, Clock clock) { this.expiry = Instant.MIN; this.refreshPeriod = refreshPeriod; - this.token = tokenSupplier.get(); + try { + this.token = tokenSupplier.get(); + } catch (RuntimeException e) { + throw new TokenAquisitionException(e); + } this.tokenSupplier = tokenSupplier; this.clock = clock; } private String getToken() { if (Instant.now(this.clock).isAfter(this.expiry)) { - this.token = tokenSupplier.get(); + try { + this.token = tokenSupplier.get(); + } catch (RuntimeException e) { + throw new TokenAquisitionException(e); + } expiry = Instant.now(this.clock).plusSeconds(refreshPeriod.toSeconds()); } return this.token; diff --git a/util/src/main/java/io/kubernetes/client/util/credentials/TokenFileAuthentication.java b/util/src/main/java/io/kubernetes/client/util/credentials/TokenFileAuthentication.java index 2afe0df7d7..10ef8e3579 100644 --- a/util/src/main/java/io/kubernetes/client/util/credentials/TokenFileAuthentication.java +++ b/util/src/main/java/io/kubernetes/client/util/credentials/TokenFileAuthentication.java @@ -12,63 +12,32 @@ */ package io.kubernetes.client.util.credentials; -import io.kubernetes.client.openapi.ApiClient; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; -import java.time.Instant; -import okhttp3.Interceptor; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; +import java.time.Duration; // TODO: prefer OpenAPI backed Auentication once it is available. see details in // https://github.com/OpenAPITools/openapi-generator/pull/6036. currently, the // workaround is to hijack the http request. -public class TokenFileAuthentication implements Authentication, Interceptor { - private String file; - private String token; - private Instant expiry; - +public class TokenFileAuthentication extends RefreshAuthentication { public TokenFileAuthentication(String file) { - this.expiry = Instant.MIN; - this.file = file; - } - - private String getToken() { - if (Instant.now().isAfter(this.expiry)) { - try { - this.token = - new String(Files.readAllBytes(Paths.get(this.file)), Charset.defaultCharset()).trim(); - expiry = Instant.now().plusSeconds(60); - } catch (IOException ie) { - throw new RuntimeException("Cannot read file: " + this.file); - } - } - - return this.token; + this(file, Duration.ofMinutes(1)); } - public void setExpiry(Instant expiry) { - this.expiry = expiry; + public TokenFileAuthentication(String file, Duration refreshPeriod) { + super(() -> { + return getToken(file); + }, refreshPeriod); } - public void setFile(String file) { - this.file = file; - } - - @Override - public void provide(ApiClient client) { - OkHttpClient withInterceptor = client.getHttpClient().newBuilder().addInterceptor(this).build(); - client.setHttpClient(withInterceptor); + private static String getToken(String file) { + try { + return new String(Files.readAllBytes(Paths.get(file)), Charset.defaultCharset()).trim(); + } catch (IOException e) { + throw new RuntimeException("Cannot read file: " + file); + } } - @Override - public Response intercept(Interceptor.Chain chain) throws IOException { - Request request = chain.request(); - Request newRequest; - newRequest = request.newBuilder().header("Authorization", "Bearer " + getToken()).build(); - return chain.proceed(newRequest); - } } diff --git a/util/src/main/java/io/kubernetes/client/util/exception/TokenAquisitionException.java b/util/src/main/java/io/kubernetes/client/util/exception/TokenAquisitionException.java new file mode 100644 index 0000000000..b04df65f96 --- /dev/null +++ b/util/src/main/java/io/kubernetes/client/util/exception/TokenAquisitionException.java @@ -0,0 +1,19 @@ +/* +Copyright 2024 The Kubernetes Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package io.kubernetes.client.util.exception; + +public class TokenAquisitionException extends RuntimeException { + public TokenAquisitionException(Exception exception) { + super(exception); + } +} diff --git a/util/src/test/java/io/kubernetes/client/util/credentials/TokenFileAuthenticationTest.java b/util/src/test/java/io/kubernetes/client/util/credentials/TokenFileAuthenticationTest.java index 39814d7d8e..0199aa2211 100644 --- a/util/src/test/java/io/kubernetes/client/util/credentials/TokenFileAuthenticationTest.java +++ b/util/src/test/java/io/kubernetes/client/util/credentials/TokenFileAuthenticationTest.java @@ -42,17 +42,19 @@ class TokenFileAuthenticationTest { static WireMockExtension apiServer = WireMockExtension.newInstance().options(options().dynamicPort()).build(); + private ApiClient apiClient; + @BeforeEach void setup() { - final ApiClient client = new ApiClient(); - client.setBasePath("http://localhost:" + apiServer.getPort()); - this.auth = new TokenFileAuthentication(SERVICEACCOUNT_TOKEN1_PATH); - this.auth.provide(client); - Configuration.setDefaultApiClient(client); + this.apiClient = new ApiClient(); + this.apiClient.setBasePath("http://localhost:" + apiServer.getPort()); + Configuration.setDefaultApiClient(this.apiClient); } @Test - void tokenProvided() throws ApiException { + void tokenProvidedTokenNormalFileReadShouldWork() throws ApiException { + Authentication authn = new TokenFileAuthentication(SERVICEACCOUNT_TOKEN1_PATH); + authn.provide(this.apiClient); apiServer.stubFor( get(urlPathEqualTo("/api/v1/pods")).willReturn(okForContentType("application/json", "{\"items\":[]}"))); @@ -63,19 +65,6 @@ void tokenProvided() throws ApiException { 1, getRequestedFor(urlPathEqualTo("/api/v1/pods")) .withHeader("Authorization", equalTo("Bearer token1"))); - - this.auth.setFile(SERVICEACCOUNT_TOKEN2_PATH); - api.listPodForAllNamespaces().execute(); - apiServer.verify( - 2, - getRequestedFor(urlPathEqualTo("/api/v1/pods")) - .withHeader("Authorization", equalTo("Bearer token1"))); - - this.auth.setExpiry(Instant.now().minusSeconds(1)); - api.listPodForAllNamespaces().execute(); - apiServer.verify( - 1, - getRequestedFor(urlPathEqualTo("/api/v1/pods")) - .withHeader("Authorization", equalTo("Bearer token2"))); } + }