Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ switcher.snapshot.skipvalidation -> true/false Skip snapshotValidation() that ca
switcher.snapshot.updateinterval -> Enable the Snapshot Auto Update given an interval of time - e.g. 1s (s: seconds, m: minutes)
switcher.silent -> true/false Contingency in case of some problem with connectivity with the API
switcher.retry -> Time given to the module to re-establish connectivity with the API - e.g. 5s (s: seconds - m: minutes - h: hours)
switcher.truststore.path -> Path to the truststore file
switcher.truststore.password -> Truststore password

(Java 8 applications only)
switcher.regextimeout -> Time in ms given to Timed Match Worker used for offline Regex (ReDoS safety mechanism) - 3000 default value
Expand Down
8 changes: 4 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@
<maven.compiler.target>${java.version}</maven.compiler.target>

<!-- rest/json libs -->
<jersey-client.version>2.39</jersey-client.version>
<jersey-hk2.version>2.39</jersey-hk2.version>
<jersey-media-json-jackson.version>2.39</jersey-media-json-jackson.version>
<jersey-client.version>2.40</jersey-client.version>
<jersey-hk2.version>2.40</jersey-hk2.version>
<jersey-media-json-jackson.version>2.40</jersey-media-json-jackson.version>
<gson.version>2.10.1</gson.version>

<!-- utils -->
Expand All @@ -63,7 +63,7 @@

<!-- test -->
<okhttp.version>5.0.0-alpha.11</okhttp.version>
<junit.version>5.9.2</junit.version>
<junit.version>5.9.3</junit.version>
<junit-pioneer.version>1.9.1</junit-pioneer.version>

<!-- Plugins -->
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/github/switcherapi/client/ContextBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,14 @@ public ContextBuilder offlineMode(boolean offlineMode) {
return this;
}

public ContextBuilder truststorePath(String truststorePath) {
properties.setTruststorePath(truststorePath);
return this;
}

public ContextBuilder truststorePassword(String truststorePassword) {
properties.setTruststorePassword(truststorePassword);
return this;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ class SwitcherProperties {

private boolean offlineMode;

private String truststorePath;

private String truststorePassword;

public SwitcherProperties() {
this.environment = DEFAULT_ENV;
this.regexTimeout = DEFAULT_REGEX_TIMEOUT;
Expand All @@ -72,6 +76,8 @@ public void loadFromProperties(Properties prop) {
setOfflineMode(Boolean.parseBoolean(SwitcherUtils.resolveProperties(ContextKey.OFFLINE_MODE.getParam(), prop)));
setRetryAfter(SwitcherUtils.resolveProperties(ContextKey.RETRY_AFTER.getParam(), prop));
setRegexTimeout(SwitcherUtils.resolveProperties(ContextKey.REGEX_TIMEOUT.getParam(), prop));
setTruststorePath(SwitcherUtils.resolveProperties(ContextKey.TRUSTSTORE_PATH.getParam(), prop));
setTruststorePassword(SwitcherUtils.resolveProperties(ContextKey.TRUSTSTORE_PASSWORD.getParam(), prop));
}

public <T> T getValue(ContextKey contextKey, Class<T> type) {
Expand Down Expand Up @@ -209,4 +215,20 @@ public void setOfflineMode(boolean offlineMode) {
this.offlineMode = offlineMode;
}

public String getTruststorePath() {
return truststorePath;
}

public void setTruststorePath(String truststorePath) {
this.truststorePath = truststorePath;
}

public String getTruststorePassword() {
return truststorePassword;
}

public void setTruststorePassword(String truststorePassword) {
this.truststorePassword = truststorePassword;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,17 @@ public enum ContextKey {
/**
* (Number) Defines the Timed Match regex time out.
*/
REGEX_TIMEOUT("switcher.regextimeout", "regexTimeout");
REGEX_TIMEOUT("switcher.regextimeout", "regexTimeout"),

/**
* (Path) Defines the path for the trustsore file.
*/
TRUSTSTORE_PATH("switcher.truststore.path", "truststorePath"),

/**
* (String) Defines the password for the truststore file.
*/
TRUSTSTORE_PASSWORD("switcher.truststore.password", "truststorePassword");

private final String param;
private final String propField;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.github.switcherapi.client.remote;

import com.github.switcherapi.client.SwitcherContextBase;
import com.github.switcherapi.client.exception.SwitcherException;
import com.github.switcherapi.client.model.ContextKey;
import org.apache.commons.lang3.StringUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import javax.ws.rs.client.ClientBuilder;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;

public class ClientWSBuilder {

private static final String KEYSTORE_TYPE = "JKS";

private static final String PROTOCOL = "TLSv1.2";

private ClientWSBuilder() {
throw new IllegalStateException("Utility class");
}

public static ClientBuilder builder() {
if (StringUtils.isNotBlank(SwitcherContextBase.contextStr(ContextKey.TRUSTSTORE_PATH))) {
return builderSSL();
}

return ClientBuilder.newBuilder();
}

public static ClientBuilder builderSSL() {
try (InputStream readStream = new FileInputStream(SwitcherContextBase.contextStr(ContextKey.TRUSTSTORE_PATH))) {
final KeyStore trustStore = KeyStore.getInstance(KEYSTORE_TYPE);
trustStore.load(readStream, SwitcherContextBase.contextStr(ContextKey.TRUSTSTORE_PASSWORD).toCharArray());

final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);

final SSLContext sslContext = SSLContext.getInstance(PROTOCOL);
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);

return ClientBuilder.newBuilder().sslContext(sslContext);
} catch (Exception e) {
throw new SwitcherException("Error while building SSL context", e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import org.apache.logging.log4j.Logger;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
Expand All @@ -41,7 +40,7 @@ public class ClientWSImpl implements ClientWS {
private final Client client;

public ClientWSImpl() {
this.client = ClientBuilder.newClient();
this.client = ClientWSBuilder.builder().build();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public CriteriaResponse executeCriteria(final Switcher switcher) {

return response;
} catch (final SwitcherRemoteException e) {
logger.error("Failed to execute criteria - {}", e.getMessage());
logger.error("Failed to execute criteria - {}\nCause: {}", e.getMessage(), e.getCause());
return executeSilentCriteria(switcher, e);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.github.switcherapi.client.remote;

import com.github.switcherapi.client.ContextBuilder;
import com.github.switcherapi.client.SwitcherContextBase;
import com.github.switcherapi.client.exception.SwitcherException;
import org.junit.jupiter.api.Test;

import javax.net.ssl.SSLContext;
import javax.ws.rs.client.ClientBuilder;
import java.util.Objects;

import static org.junit.jupiter.api.Assertions.*;

class ClientWSBuilderTest {

@Test
void shouldCreateClientBuilder() {
// given
SwitcherContextBase.configure(ContextBuilder.builder()
.truststorePath("")
.truststorePassword(""));

// test
ClientBuilder clientBuilder = ClientWSBuilder.builder();
assertNotNull(clientBuilder);

SSLContext sslContext = clientBuilder.build().getSslContext();
assertNotNull(sslContext);
assertEquals("TLS", sslContext.getProtocol());
}

@Test
void shouldCreateClientBuilderSSL() {
// given
String truststorePath = Objects.requireNonNull(getClass().getClassLoader()
.getResource("keystore.jks")).getPath();

SwitcherContextBase.configure(ContextBuilder.builder()
.truststorePath(truststorePath)
.truststorePassword("changeit"));

// test
ClientBuilder clientBuilder = ClientWSBuilder.builder();
assertNotNull(clientBuilder);

SSLContext sslContext = clientBuilder.build().getSslContext();
assertNotNull(sslContext);
assertEquals("TLSv1.2", sslContext.getProtocol());
}

@Test
void shouldNotCreateClientBuilderSSL_invalidKeystorePassword() {
// given
String truststorePath = Objects.requireNonNull(getClass().getClassLoader()
.getResource("keystore.jks")).getPath();

SwitcherContextBase.configure(ContextBuilder.builder()
.truststorePath(truststorePath)
.truststorePassword("INVALID"));

// test
assertThrows(SwitcherException.class, ClientWSBuilder::builder);
}

@Test
void shouldNotCreateClientBuilderSSL_invalidKeystorePath() {
// given
SwitcherContextBase.configure(ContextBuilder.builder()
.truststorePath("INVALID")
.truststorePassword("changeit"));

// test
assertThrows(SwitcherException.class, ClientWSBuilder::builder);
}
}
Binary file added src/test/resources/keystore.jks
Binary file not shown.