Skip to content

Commit

Permalink
fix (kubernetes-client-api) : Track sources from where OAuthToken get…
Browse files Browse the repository at this point in the history
…s set (fabric8io#4802)

+ Add enum OAuthTokenSource in Config which tell us about the source from
  which Config's OAuthToken was set.
+ Skip refresh in case current OAuthTokenSource is set to USER (the
  scenario reported in the linked issue)

Signed-off-by: Rohan Kumar <[email protected]>
  • Loading branch information
rohanKanojia committed Mar 2, 2023
1 parent dfa920e commit 358ce68
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* Fix #4794: improving the semantics of manually calling informer stop
* Fix #4797: OkHttpClientFactory.additionalConfig can be used to override the default OkHttp Dispatcher
* Fix #4798: fix leader election release on cancel
* Fix #4802: config.refresh() erases token specified when building initial config
* Fix #4815: (java-generator) create target download directory if it doesn't exist
* Fix #4846: allowed for pod read / copy operations to distinguish when the target doesn't exist
* Fix #4818: [java-generator] Escape `*/` in generated JavaDocs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ public class Config {
private int connectionTimeout = 10 * 1000;
private int maxConcurrentRequests = DEFAULT_MAX_CONCURRENT_REQUESTS;
private int maxConcurrentRequestsPerHost = DEFAULT_MAX_CONCURRENT_REQUESTS_PER_HOST;
private OAuthTokenSource oAuthTokenSource;

private RequestConfig requestConfig = new RequestConfig();

Expand Down Expand Up @@ -330,7 +331,7 @@ public Config(String masterUrl, String apiVersion, String namespace, boolean tru
loggingInterval, maxConcurrentRequests, maxConcurrentRequestsPerHost, false, httpProxy, httpsProxy, noProxy,
errorMessages, userAgent, tlsVersions, websocketTimeout, websocketPingInterval, proxyUsername, proxyPassword,
trustStoreFile, trustStorePassphrase, keyStoreFile, keyStorePassphrase, impersonateUsername, impersonateGroups,
impersonateExtras, null, null, DEFAULT_REQUEST_RETRY_BACKOFFLIMIT, DEFAULT_REQUEST_RETRY_BACKOFFINTERVAL,
impersonateExtras, null, null, null, DEFAULT_REQUEST_RETRY_BACKOFFLIMIT, DEFAULT_REQUEST_RETRY_BACKOFFINTERVAL,
DEFAULT_UPLOAD_REQUEST_TIMEOUT);
}

Expand All @@ -344,7 +345,8 @@ public Config(String masterUrl, String apiVersion, String namespace, boolean tru
String userAgent, TlsVersion[] tlsVersions, long websocketTimeout, long websocketPingInterval, String proxyUsername,
String proxyPassword, String trustStoreFile, String trustStorePassphrase, String keyStoreFile, String keyStorePassphrase,
String impersonateUsername, String[] impersonateGroups, Map<String, List<String>> impersonateExtras,
OAuthTokenProvider oauthTokenProvider, Map<String, String> customHeaders, int requestRetryBackoffLimit,
OAuthTokenProvider oauthTokenProvider, OAuthTokenSource oAuthTokenSource, Map<String, String> customHeaders,
int requestRetryBackoffLimit,
int requestRetryBackoffInterval, int uploadRequestTimeout) {
this.apiVersion = apiVersion;
this.namespace = namespace;
Expand All @@ -361,6 +363,7 @@ public Config(String masterUrl, String apiVersion, String namespace, boolean tru
this.username = username;
this.password = password;
this.oauthToken = oauthToken;
this.oAuthTokenSource = determineOAuthTokenSource(oauthToken, oAuthTokenSource, oauthTokenProvider);
this.websocketPingInterval = websocketPingInterval;
this.connectionTimeout = connectionTimeout;

Expand Down Expand Up @@ -426,7 +429,11 @@ public static void configFromSysPropsOrEnvVars(Config config) {
Utils.getSystemPropertyOrEnvVar(KUBERNETES_KEYSTORE_PASSPHRASE_PROPERTY, config.getKeyStorePassphrase()));
config.setKeyStoreFile(Utils.getSystemPropertyOrEnvVar(KUBERNETES_KEYSTORE_FILE_PROPERTY, config.getKeyStoreFile()));

config.setOauthToken(Utils.getSystemPropertyOrEnvVar(KUBERNETES_OAUTH_TOKEN_SYSTEM_PROPERTY, config.getOauthToken()));
String tokenFromPropertyOrEnvVar = Utils.getSystemPropertyOrEnvVar(KUBERNETES_OAUTH_TOKEN_SYSTEM_PROPERTY, "");
if (!tokenFromPropertyOrEnvVar.isEmpty()) {
config.setOauthToken(tokenFromPropertyOrEnvVar);
config.setOAuthTokenSource(OAuthTokenSource.SYSTEM_PROPERTIES_OR_ENVIRONMENT_VARIABLES);
}
config.setUsername(Utils.getSystemPropertyOrEnvVar(KUBERNETES_AUTH_BASIC_USERNAME_SYSTEM_PROPERTY, config.getUsername()));
config.setPassword(Utils.getSystemPropertyOrEnvVar(KUBERNETES_AUTH_BASIC_PASSWORD_SYSTEM_PROPERTY, config.getPassword()));

Expand Down Expand Up @@ -552,6 +559,7 @@ private static boolean tryServiceAccount(Config config) {
String serviceTokenCandidate = new String(Files.readAllBytes(saTokenPathFile.toPath()));
LOGGER.debug("Found service account token at: [{}].", saTokenPathLocation);
config.setOauthToken(serviceTokenCandidate);
config.setOAuthTokenSource(OAuthTokenSource.SERVICEACCOUNT_TOKEN_FILE);
String txt = "Configured service account doesn't have access. Service account may have been revoked.";
config.getErrorMessages().put(401, "Unauthorized! " + txt);
config.getErrorMessages().put(403, "Forbidden!" + txt);
Expand Down Expand Up @@ -632,6 +640,9 @@ public static Config fromKubeconfig(String context, String kubeconfigContents, S
* @return
*/
public Config refresh() {
if (this.oAuthTokenSource != null && this.oAuthTokenSource.equals(OAuthTokenSource.USER)) {
return this;
}
final String currentContextName = this.getCurrentContext() != null ? this.getCurrentContext().getName() : null;
if (this.autoConfigure) {
return Config.autoConfigure(currentContextName);
Expand Down Expand Up @@ -731,6 +742,7 @@ private static boolean loadFromKubeconfig(Config config, String context, String
config.setClientKeyData(currentAuthInfo.getClientKeyData());
config.setClientKeyAlgo(getKeyAlgorithm(config.getClientKeyFile(), config.getClientKeyData()));
config.setOauthToken(currentAuthInfo.getToken());
config.setOAuthTokenSource(OAuthTokenSource.KUBECONFIG);
config.setUsername(currentAuthInfo.getUsername());
config.setPassword(currentAuthInfo.getPassword());

Expand All @@ -740,9 +752,11 @@ private static boolean loadFromKubeconfig(Config config, String context, String
if (!Utils.isNullOrEmpty(currentAuthInfo.getAuthProvider().getConfig().get(ACCESS_TOKEN))) {
// GKE token
config.setOauthToken(currentAuthInfo.getAuthProvider().getConfig().get(ACCESS_TOKEN));
config.setOAuthTokenSource(OAuthTokenSource.KUBECONFIG);
} else if (!Utils.isNullOrEmpty(currentAuthInfo.getAuthProvider().getConfig().get(ID_TOKEN))) {
// OpenID Connect token
config.setOauthToken(currentAuthInfo.getAuthProvider().getConfig().get(ID_TOKEN));
config.setOAuthTokenSource(OAuthTokenSource.KUBECONFIG);
}
}
} else if (config.getOauthTokenProvider() == null) { // https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins
Expand All @@ -751,6 +765,7 @@ private static boolean loadFromKubeconfig(Config config, String context, String
ExecCredential ec = getExecCredentialFromExecConfig(exec, configFile);
if (ec != null && ec.status != null && ec.status.token != null) {
config.setOauthToken(ec.status.token);
config.setOAuthTokenSource(OAuthTokenSource.KUBECONFIG);
} else {
LOGGER.warn("No token returned");
}
Expand Down Expand Up @@ -968,6 +983,7 @@ public String getOauthToken() {

public void setOauthToken(String oauthToken) {
this.oauthToken = oauthToken;
this.oAuthTokenSource = OAuthTokenSource.USER;
}

@JsonProperty("password")
Expand Down Expand Up @@ -1405,6 +1421,14 @@ public boolean getAutoConfigure() {
return autoConfigure;
}

public void setOAuthTokenSource(OAuthTokenSource oAuthTokenSource) {
this.oAuthTokenSource = oAuthTokenSource;
}

public OAuthTokenSource getOAuthTokenSource() {
return oAuthTokenSource;
}

/**
* Returns all the {@link NamedContext}s that exist in the kube config
*
Expand Down Expand Up @@ -1476,4 +1500,18 @@ public void setAutoConfigure(boolean autoConfigure) {
this.autoConfigure = autoConfigure;
}

private static OAuthTokenSource determineOAuthTokenSource(String oAuthToken, OAuthTokenSource oAuthTokenSource,
OAuthTokenProvider oAuthTokenProvider) {
if (oAuthTokenProvider != null) {
return OAuthTokenSource.OAUTHTOKEN_PROVIDER;
}
if (oAuthToken != null) {
if (Utils.getSystemPropertyOrEnvVar(KUBERNETES_OAUTH_TOKEN_SYSTEM_PROPERTY) != null) {
return OAuthTokenSource.SYSTEM_PROPERTIES_OR_ENVIRONMENT_VARIABLES;
} else {
return OAuthTokenSource.USER;
}
}
return oAuthTokenSource;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* 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.fabric8.kubernetes.client;

public enum OAuthTokenSource {
OAUTHTOKEN_PROVIDER,
USER,
SYSTEM_PROPERTIES_OR_ENVIRONMENT_VARIABLES,
KUBECONFIG,
SERVICEACCOUNT_TOKEN_FILE
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import java.util.List;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
Expand All @@ -49,7 +50,7 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.condition.OS.WINDOWS;

public class ConfigTest {
class ConfigTest {

private static final String TEST_KUBECONFIG_FILE = Utils.filePath(ConfigTest.class.getResource("/test-kubeconfig"));
private static final String TEST_EC_KUBECONFIG_FILE = Utils.filePath(ConfigTest.class.getResource("/test-ec-kubeconfig"));
Expand Down Expand Up @@ -148,9 +149,11 @@ void testWithSystemProperties() {

Config config = new Config();
assertConfig(config);
assertThat(config.getOAuthTokenSource()).isEqualTo(OAuthTokenSource.SYSTEM_PROPERTIES_OR_ENVIRONMENT_VARIABLES);

config = new ConfigBuilder().build();
assertConfig(config);
assertThat(config.getOAuthTokenSource()).isEqualTo(OAuthTokenSource.SYSTEM_PROPERTIES_OR_ENVIRONMENT_VARIABLES);
}

@Test
Expand Down Expand Up @@ -230,6 +233,7 @@ void testWithBuilderAndSystemProperties() {
.build();

assertConfig(config);
assertThat(config.getOAuthTokenSource()).isEqualTo(OAuthTokenSource.SYSTEM_PROPERTIES_OR_ENVIRONMENT_VARIABLES);
}

@Test
Expand Down Expand Up @@ -281,6 +285,7 @@ void testWithKubeConfig() {
assertTrue(config.getCaCertFile().endsWith("testns/ca.pem".replace("/", File.separator)));
assertTrue(new File(config.getCaCertFile()).isAbsolute());
assertEquals(new File(TEST_KUBECONFIG_FILE), config.getFile());
assertThat(config.getOAuthTokenSource()).isEqualTo(OAuthTokenSource.KUBECONFIG);
}

@Test
Expand Down Expand Up @@ -497,6 +502,7 @@ void shouldBeUsedTokenSuppliedByProvider() {
.build();

assertEquals("PROVIDER_TOKEN", config.getOauthToken());
assertThat(config.getOAuthTokenSource()).isEqualTo(OAuthTokenSource.OAUTHTOKEN_PROVIDER);
}

@Test
Expand All @@ -515,6 +521,7 @@ void testKubeConfigWithAuthConfigProvider() throws URISyntaxException {
assertEquals(
"eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw",
config.getOauthToken());
assertEquals(OAuthTokenSource.KUBECONFIG, config.getOAuthTokenSource());
}

@Test
Expand All @@ -529,7 +536,7 @@ void testEmptyConfig() {
assertNotNull(emptyConfig);
assertEquals("https://kubernetes.default.svc", emptyConfig.getMasterUrl());
assertTrue(emptyConfig.getContexts().isEmpty());
assertNull(emptyConfig.getCurrentContext());
assertThat(emptyConfig.getCurrentContext()).isNull();
assertEquals(64, emptyConfig.getMaxConcurrentRequests());
assertEquals(5, emptyConfig.getMaxConcurrentRequestsPerHost());
assertFalse(emptyConfig.isTrustCerts());
Expand All @@ -551,6 +558,7 @@ void testEmptyConfig() {
assertEquals(1, emptyConfig.getTlsVersions().length);
assertTrue(emptyConfig.getErrorMessages().isEmpty());
assertNotNull(emptyConfig.getUserAgent());
assertThat(emptyConfig.getOAuthTokenSource()).isNull();
}

private void assertConfig(Config config) {
Expand Down Expand Up @@ -750,4 +758,20 @@ void getHomeDir_shouldReturnUserHomeProp_WhenHomeEnvVariablesAreNotSet() {
System.setProperty("user.home", userHomePropToRestore);
}
}

@Test
void refresh_whenOAuthTokenSourceSetToUser_thenConfigUnchanged() {
// Given
Config config = new ConfigBuilder()
.withOauthToken("token-from-user")
.build();

// When
Config updatedConfig = config.refresh();

// Then
assertThat(updatedConfig)
.isSameAs(config)
.hasFieldOrPropertyWithValue("oauthToken", "token-from-user");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.OAuthTokenProvider;
import io.fabric8.kubernetes.client.OAuthTokenSource;
import io.fabric8.kubernetes.client.http.TlsVersion;
import io.fabric8.kubernetes.client.readiness.Readiness;
import io.fabric8.kubernetes.client.utils.URLUtils;
Expand Down Expand Up @@ -85,6 +86,7 @@ public OpenShiftConfig(String openShiftUrl, String oapiVersion, String masterUrl
long websocketPingInterval, String proxyUsername, String proxyPassword, String trustStoreFile,
String trustStorePassphrase, String keyStoreFile, String keyStorePassphrase, String impersonateUsername,
String[] impersonateGroups, Map<String, List<String>> impersonateExtras, OAuthTokenProvider oauthTokenProvider,
OAuthTokenSource oAuthTokenSource,
Map<String, String> customHeaders, int requestRetryBackoffLimit, int requestRetryBackoffInterval,
int uploadRequestTimeout, long buildTimeout,
boolean disableApiGroupCheck) {
Expand All @@ -97,7 +99,8 @@ public OpenShiftConfig(String openShiftUrl, String oapiVersion, String masterUrl
noProxy,
errorMessages, userAgent, tlsVersions, websocketTimeout, websocketPingInterval, proxyUsername, proxyPassword,
trustStoreFile, trustStorePassphrase, keyStoreFile, keyStorePassphrase, impersonateUsername, impersonateGroups,
impersonateExtras, oauthTokenProvider, customHeaders, requestRetryBackoffLimit, requestRetryBackoffInterval,
impersonateExtras, oauthTokenProvider, oAuthTokenSource, customHeaders, requestRetryBackoffLimit,
requestRetryBackoffInterval,
uploadRequestTimeout);
this.setOapiVersion(oapiVersion);
this.setBuildTimeout(buildTimeout);
Expand Down Expand Up @@ -135,7 +138,7 @@ public OpenShiftConfig(Config kubernetesConfig, String openShiftUrl, String oapi
kubernetesConfig.getTrustStorePassphrase(), kubernetesConfig.getKeyStoreFile(),
kubernetesConfig.getKeyStorePassphrase(), kubernetesConfig.getImpersonateUsername(),
kubernetesConfig.getImpersonateGroups(), kubernetesConfig.getImpersonateExtras(),
kubernetesConfig.getOauthTokenProvider(), kubernetesConfig.getCustomHeaders(),
kubernetesConfig.getOauthTokenProvider(), kubernetesConfig.getOAuthTokenSource(), kubernetesConfig.getCustomHeaders(),
kubernetesConfig.getRequestRetryBackoffLimit(), kubernetesConfig.getRequestRetryBackoffInterval(),
kubernetesConfig.getUploadRequestTimeout(),
buildTimeout,
Expand Down

0 comments on commit 358ce68

Please sign in to comment.