Skip to content

Commit

Permalink
feat(#1089): kubernetes client config with auth token
Browse files Browse the repository at this point in the history
- Allow configuration with new oauthToken
- Add restrictions to allow config only with oauthToken or username and password
- Update Tests
- Update Documentation
  • Loading branch information
valisol authored and bbortt committed Jan 10, 2024
1 parent 4b05640 commit cad790d
Show file tree
Hide file tree
Showing 15 changed files with 406 additions and 75 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2016 the original author or authors.
* Copyright 2006-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,10 +16,10 @@

package org.citrusframework.kubernetes.client;

import org.citrusframework.endpoint.AbstractEndpointBuilder;
import org.citrusframework.kubernetes.message.KubernetesMessageConverter;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.fabric8.kubernetes.client.ConfigBuilder;
import org.citrusframework.endpoint.AbstractEndpointBuilder;
import org.citrusframework.kubernetes.message.KubernetesMessageConverter;

/**
* @author Christoph Deppisch
Expand Down Expand Up @@ -82,6 +82,16 @@ public KubernetesClientBuilder password(String password) {
return this;
}

/**
* Sets the authentication token.
* @param oauthToken
* @return
*/
public KubernetesClientBuilder oauthToken(String oauthToken) {
config.withOauthToken(oauthToken);
return this;
}

/**
* Sets the client namespace.
* @param namespace
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2024 the original author or 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 org.citrusframework.kubernetes.config;

import static org.citrusframework.util.StringUtils.hasText;
import static org.citrusframework.util.StringUtils.isEmpty;

public class CredentialValidator {

public CredentialValidator() {
throw new IllegalArgumentException("Utility class shall not be instantiated!");
}

/**
* Validates the given credentials based on the following rules. Returns {@code true} if and only if none of the
* following conditions are met:
* <ul>
* <li>{@code username} is set AND {@code oauthToken} is set</li>
* <li>{@code password} is set AND {@code oauthToken} is set</li>
* <li>{@code username} is <b>not</b> set AND {@code password} is set</li>
* </ul>
* All other combinations are valid.
*
* @param username The username.
* @param password The password.
* @param oauthToken The OAuth token.
* @return true if the combination is valid, false otherwise.
*/
public static boolean isValid(String username, String password, String oauthToken) {
return (!hasText(username) || !hasText(oauthToken))
&& (!hasText(password) || !hasText(oauthToken))
&& (!isEmpty(username) || !hasText(password));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2016 the original author or authors.
* Copyright 2006-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,13 +16,13 @@

package org.citrusframework.kubernetes.config.annotation;

import org.citrusframework.annotations.CitrusEndpointConfig;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.citrusframework.annotations.CitrusEndpointConfig;

/**
* @author Christoph Deppisch
* @since 2.7
Expand Down Expand Up @@ -56,6 +56,12 @@
*/
String password() default "";

/**
* Authentication Token
* @return
*/
String oauthToken() default "";

/**
* Namespace
* @return
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2016 the original author or authors.
* Copyright 2006-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -22,7 +22,9 @@
import org.citrusframework.kubernetes.client.KubernetesClientBuilder;
import org.citrusframework.kubernetes.message.KubernetesMessageConverter;
import org.citrusframework.spi.ReferenceResolver;
import org.citrusframework.util.StringUtils;

import static org.citrusframework.kubernetes.config.CredentialValidator.isValid;
import static org.citrusframework.util.StringUtils.hasText;

/**
* @author Christoph Deppisch
Expand All @@ -34,35 +36,43 @@ public class KubernetesClientConfigParser implements AnnotationConfigParser<Kube
public KubernetesClient parse(KubernetesClientConfig annotation, ReferenceResolver referenceResolver) {
KubernetesClientBuilder builder = new KubernetesClientBuilder();

if (StringUtils.hasText(annotation.url())) {
if (!isValid(annotation.username(), annotation.password(), annotation.oauthToken())) {
throw new IllegalArgumentException("Parameters not set correctly - check if either an oauthToke or password and username is set");
}

if (hasText(annotation.url())) {
builder.url(annotation.url());
}

if (StringUtils.hasText(annotation.version())) {
if (hasText(annotation.version())) {
builder.version(annotation.version());
}

if (StringUtils.hasText(annotation.username())) {
if (hasText(annotation.username())) {
builder.username(annotation.username());
}

if (StringUtils.hasText(annotation.password())) {
if (hasText(annotation.password())) {
builder.password(annotation.password());
}

if (StringUtils.hasText(annotation.namespace())) {
if (hasText(annotation.oauthToken())) {
builder.oauthToken(annotation.oauthToken());
}

if (hasText(annotation.namespace())) {
builder.namespace(annotation.namespace());
}

if (StringUtils.hasText(annotation.certFile())) {
if (hasText(annotation.certFile())) {
builder.certFile(annotation.certFile());
}

if (StringUtils.hasText(annotation.messageConverter())) {
if (hasText(annotation.messageConverter())) {
builder.messageConverter(referenceResolver.resolve(annotation.messageConverter(), KubernetesMessageConverter.class));
}

if (StringUtils.hasText(annotation.objectMapper())) {
if (hasText(annotation.objectMapper())) {
builder.objectMapper(referenceResolver.resolve(annotation.objectMapper(), ObjectMapper.class));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2016 the original author or authors.
* Copyright 2006-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,17 +16,21 @@

package org.citrusframework.kubernetes.config.xml;

import org.citrusframework.config.util.BeanDefinitionParserUtils;
import io.fabric8.kubernetes.client.Config;
import org.citrusframework.config.xml.AbstractEndpointParser;
import org.citrusframework.endpoint.Endpoint;
import org.citrusframework.endpoint.EndpointConfiguration;
import org.citrusframework.kubernetes.client.KubernetesClient;
import org.citrusframework.kubernetes.endpoint.KubernetesEndpointConfiguration;
import io.fabric8.kubernetes.client.Config;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;

import static org.citrusframework.config.util.BeanDefinitionParserUtils.registerBean;
import static org.citrusframework.config.util.BeanDefinitionParserUtils.setPropertyReference;
import static org.citrusframework.config.util.BeanDefinitionParserUtils.setPropertyValue;
import static org.citrusframework.kubernetes.config.CredentialValidator.isValid;

/**
* Bean definition parser for kubernetes client instance.
*
Expand All @@ -39,21 +43,30 @@ public class KubernetesClientParser extends AbstractEndpointParser {
protected void parseEndpointConfiguration(BeanDefinitionBuilder endpointConfiguration, Element element, ParserContext parserContext) {
super.parseEndpointConfiguration(endpointConfiguration, element, parserContext);

String username = element.getAttribute("username");
String password = element.getAttribute("password");
String oauthToken = element.getAttribute("oauthToken");

if (!isValid(username, password, oauthToken)) {
throw new IllegalArgumentException("Parameters not set correctly - check if either an oauthToke or password and username is set");
}

BeanDefinitionBuilder configBuilder = BeanDefinitionBuilder.genericBeanDefinition(Config.class);
BeanDefinitionParserUtils.setPropertyValue(configBuilder, element.getAttribute("url"), "masterUrl");
BeanDefinitionParserUtils.setPropertyValue(configBuilder, element.getAttribute("version"), "apiVersion");
BeanDefinitionParserUtils.setPropertyValue(configBuilder, element.getAttribute("username"), "username");
BeanDefinitionParserUtils.setPropertyValue(configBuilder, element.getAttribute("password"), "password");
BeanDefinitionParserUtils.setPropertyValue(configBuilder, element.getAttribute("namespace"), "namespace");
BeanDefinitionParserUtils.setPropertyValue(configBuilder, element.getAttribute("cert-file"), "caCertFile");
setPropertyValue(configBuilder, element.getAttribute("url"), "masterUrl");
setPropertyValue(configBuilder, element.getAttribute("version"), "apiVersion");
setPropertyValue(configBuilder, username, "username");
setPropertyValue(configBuilder, password, "password");
setPropertyValue(configBuilder, oauthToken, "oauthToken");
setPropertyValue(configBuilder, element.getAttribute("namespace"), "namespace");
setPropertyValue(configBuilder, element.getAttribute("cert-file"), "caCertFile");

String clientConfigId = element.getAttribute(ID_ATTRIBUTE) + "Config";
BeanDefinitionParserUtils.registerBean(clientConfigId, configBuilder.getBeanDefinition(), parserContext, shouldFireEvents());
registerBean(clientConfigId, configBuilder.getBeanDefinition(), parserContext, shouldFireEvents());

endpointConfiguration.addPropertyReference("kubernetesClientConfig", clientConfigId);

BeanDefinitionParserUtils.setPropertyReference(endpointConfiguration, element.getAttribute("message-converter"), "messageConverter");
BeanDefinitionParserUtils.setPropertyReference(endpointConfiguration, element.getAttribute("object-mapper"), "objectMapper");
setPropertyReference(endpointConfiguration, element.getAttribute("message-converter"), "messageConverter");
setPropertyReference(endpointConfiguration, element.getAttribute("object-mapper"), "objectMapper");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2006-2016 the original author or authors.
~ Copyright 2006-2024 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -30,11 +30,11 @@
<xs:attribute name="version" type="xs:string"/>
<xs:attribute name="username" type="xs:string"/>
<xs:attribute name="password" type="xs:string"/>
<xs:attribute name="oauthToken" type="xs:string"/>
<xs:attribute name="namespace" type="xs:string"/>
<xs:attribute name="message-converter" type="xs:string"/>
<xs:attribute name="object-mapper" type="xs:string"/>
<xs:attribute name="cert-file" type="xs:string"/>
</xs:complexType>
</xs:element>

</xs:schema>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.citrusframework.kubernetes.config;

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;

public class CredentialValidatorTest {

private static final String IS_VALID_DATA_PROVIDER = "is-valid";

@DataProvider(name = IS_VALID_DATA_PROVIDER)
public static Object[][] primeNumbers() {
return new Object[][]{
{"user1", "pass1", null, true},
{null, null, "token1", true},
{"user1", null, "token1", false},
{null, "pass1", "token1", false},
{"user1", "pass1", "token1", false},
{null, "pass1", null, false},
{null, null, null, true}
};
}

@Test(dataProvider = IS_VALID_DATA_PROVIDER)
public void isValid(String username, String password, String oauthToken, boolean expectedResult) {
assertEquals(CredentialValidator.isValid(username, password, oauthToken), expectedResult);
}
}
Loading

0 comments on commit cad790d

Please sign in to comment.