package com.lambdaworks.redis;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import com.lambdaworks.redis.internal.LettuceAssert;
import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslProvider;
/**
* Options to configure SSL options for the connections kept to Redis servers.
*
* @author Mark Paluch
* @since 4.3
*/
public class SslOptions {
public static final SslProvider DEFAULT_SSL_PROVIDER = SslProvider.JDK;
private final SslProvider sslProvider;
private final URL truststore;
private final char[] truststorePassword;
protected SslOptions(Builder builder) {
this.sslProvider = builder.sslProvider;
this.truststore = builder.truststore;
this.truststorePassword = builder.truststorePassword;
}
protected SslOptions(SslOptions original) {
this.sslProvider = original.getSslProvider();
this.truststore = original.getTruststore();
this.truststorePassword = original.getTruststorePassword();
}
/**
* Create a copy of {@literal options}
*
* @param options the original
* @return A new instance of {@link SslOptions} containing the values of {@literal options}
*/
public static SslOptions copyOf(SslOptions options) {
return new SslOptions(options);
}
/**
* Returns a new {@link SslOptions.Builder} to construct {@link SslOptions}.
*
* @return a new {@link SslOptions.Builder} to construct {@link SslOptions}.
*/
public static SslOptions.Builder builder() {
return new SslOptions.Builder();
}
/**
* Create a new {@link SslOptions} using default settings.
*
* @return a new instance of default cluster client client options.
*/
public static SslOptions create() {
return builder().build();
}
/**
* Builder for {@link SslOptions}.
*/
public static class Builder {
private SslProvider sslProvider = DEFAULT_SSL_PROVIDER;
private URL truststore;
private char[] truststorePassword = new char[0];
private Builder() {
}
/**
* Use the JDK SSL provider for SSL connections.
*
* @return {@code this}
*/
public Builder jdkSslProvider() {
return sslProvider(SslProvider.JDK);
}
/**
* Use the OpenSSL provider for SSL connections. The OpenSSL provider requires the
* <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> dependency with the OpenSSL JNI
* binary.
*
* @return {@code this}
* @throws IllegalStateException if OpenSSL is not available
*/
public Builder openSslProvider() {
return sslProvider(SslProvider.OPENSSL);
}
private Builder sslProvider(SslProvider sslProvider) {
if (sslProvider == SslProvider.OPENSSL) {
if (!OpenSsl.isAvailable()) {
throw new IllegalStateException("OpenSSL SSL Provider is not available");
}
}
this.sslProvider = sslProvider;
return this;
}
/**
* Sets the Truststore file to load trusted certificates. The trust store file must be supported by
* {@link java.security.KeyStore} which is {@code jks} by default. Truststores are reloaded on each connection attempt
* that allows to replace certificates during runtime.
*
* @param truststore the truststore file, must not be {@literal null}.
* @return {@code this}
*/
public Builder truststore(File truststore) {
return truststore(truststore, "");
}
/**
* Sets the Truststore file to load trusted certificates. The trust store file must be supported by
* {@link java.security.KeyStore} which is {@code jks} by default. Truststores are reloaded on each connection attempt
* that allows to replace certificates during runtime.
*
* @param truststore the truststore file, must not be {@literal null}.
* @param truststorePassword the truststore password. May be empty to omit password and the truststore integrity check.
* @return {@code this}
*/
public Builder truststore(File truststore, String truststorePassword) {
LettuceAssert.notNull(truststore, "Truststore must not be null");
LettuceAssert.isTrue(truststore.exists(), String.format("Truststore file %s does not exist", truststore));
LettuceAssert.isTrue(truststore.isFile(), String.format("Truststore file %s is not a file", truststore));
try {
this.truststore = truststore.toURI().toURL();
} catch (MalformedURLException e) {
throw new IllegalArgumentException(e);
}
if (LettuceStrings.isNotEmpty(truststorePassword)) {
this.truststorePassword = truststorePassword.toCharArray();
} else {
this.truststorePassword = new char[0];
}
return this;
}
/**
* Sets the Truststore resource to load trusted certificates. The trust store file must be supported by
* {@link java.security.KeyStore} which is {@code jks} by default. Truststores are reloaded on each connection attempt
* that allows to replace certificates during runtime.
*
* @param truststore the truststore file, must not be {@literal null}.
* @return {@code this}
*/
public Builder truststore(URL truststore) {
return truststore(truststore, "");
}
/**
* Sets the Truststore resource to load trusted certificates. The trust store file must be supported by
* {@link java.security.KeyStore} which is {@code jks} by default. Truststores are reloaded on each connection attempt
* that allows to replace certificates during runtime.
*
* @param truststore the truststore file, must not be {@literal null}.
* @param truststorePassword the truststore password. May be empty to omit password and the truststore integrity check.
* @return {@code this}
*/
public Builder truststore(URL truststore, String truststorePassword) {
LettuceAssert.notNull(truststore, "Truststore must not be null");
this.truststore = truststore;
if (LettuceStrings.isNotEmpty(truststorePassword)) {
this.truststorePassword = truststorePassword.toCharArray();
} else {
this.truststorePassword = new char[0];
}
return this;
}
/**
* Create a new instance of {@link SslOptions}
*
* @return new instance of {@link SslOptions}
*/
public SslOptions build() {
return new SslOptions(this);
}
}
/**
*
* @return the configured {@link SslProvider}.
*/
public SslProvider getSslProvider() {
return sslProvider;
}
/**
*
* @return the truststore {@link URL}.
*/
public URL getTruststore() {
return truststore;
}
/**
*
* @return the password for the truststore. May be empty.
*/
public char[] getTruststorePassword() {
return truststorePassword;
}
}