NIFI-8445: Implemented HashiCorpVaultCommunicationService in nifi-vault-utils

This closes #5034

Signed-off-by: David Handermann <exceptionfactory@apache.org>
This commit is contained in:
Joe Gresock 2021-04-27 11:48:44 -04:00 committed by exceptionfactory
parent 51aae5bcf6
commit ed591e0f22
No known key found for this signature in database
GPG Key ID: 29B6A52D2AAE8DBA
17 changed files with 1301 additions and 0 deletions

View File

@ -0,0 +1,60 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons</artifactId>
<version>1.14.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-vault-utils</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-utils</artifactId>
<version>1.14.0-SNAPSHOT</version>
</dependency>
<!-- Runtime dependency to enable TLS client configuration -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-security-utils-api</artifactId>
<version>1.14.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-security-utils</artifactId>
<version>1.14.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.vault.hashicorp;
/**
* A service to handle all communication with an instance of HashiCorp Vault.
* @see <a href="https://www.vaultproject.io/">https://www.vaultproject.io/</a>
*/
public interface HashiCorpVaultCommunicationService {
/**
* Encrypts the given plaintext using Vault's Transit Secrets Engine.
*
* @see <a href="https://www.vaultproject.io/api-docs/secret/transit">https://www.vaultproject.io/api-docs/secret/transit</a>
* @param transitKey A named encryption key used in the Transit Secrets Engine. The key is expected to have
* already been configured in the Vault instance.
* @param plainText The plaintext to encrypt
* @return The cipher text
*/
String encrypt(String transitKey, byte[] plainText);
/**
* Decrypts the given cipher text using Vault's Transit Secrets Engine.
*
* @see <a href="https://www.vaultproject.io/api-docs/secret/transit">https://www.vaultproject.io/api-docs/secret/transit</a>
* @param transitKey A named encryption key used in the Transit Secrets Engine. The key is expected to have
* already been configured in the Vault instance.
* @param cipherText The cipher text to decrypt
* @return The decrypted plaintext
*/
byte[] decrypt(String transitKey, String cipherText);
}

View File

@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.vault.hashicorp;
/**
* Indicates a misconfiguration of the Vault client.
*/
public class HashiCorpVaultConfigurationException extends RuntimeException {
public HashiCorpVaultConfigurationException() {
}
public HashiCorpVaultConfigurationException(String message) {
super(message);
}
public HashiCorpVaultConfigurationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,94 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.vault.hashicorp;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.vault.hashicorp.config.HashiCorpVaultConfiguration;
import org.apache.nifi.vault.hashicorp.config.HashiCorpVaultProperties;
import org.springframework.vault.authentication.SimpleSessionManager;
import org.springframework.vault.client.ClientHttpRequestFactoryFactory;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.VaultTransitOperations;
import org.springframework.vault.support.Ciphertext;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.Plaintext;
import org.springframework.vault.support.SslConfiguration;
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
* Implements the VaultCommunicationService using Spring Vault
*/
public class StandardHashiCorpVaultCommunicationService implements HashiCorpVaultCommunicationService {
private static final String HTTPS = "https";
private final HashiCorpVaultConfiguration vaultConfiguration;
private final VaultTemplate vaultTemplate;
private final VaultTransitOperations transitOperations;
/**
* Creates a VaultCommunicationService that uses Spring Vault.
* @param vaultProperties Properties to configure the service
* @throws HashiCorpVaultConfigurationException If the configuration was invalid
*/
public StandardHashiCorpVaultCommunicationService(final HashiCorpVaultProperties vaultProperties) throws HashiCorpVaultConfigurationException {
this.vaultConfiguration = new HashiCorpVaultConfiguration(vaultProperties);
final SslConfiguration sslConfiguration = vaultProperties.getUri().contains(HTTPS)
? vaultConfiguration.sslConfiguration() : SslConfiguration.unconfigured();
final ClientOptions clientOptions = getClientOptions(vaultProperties);
vaultTemplate = new VaultTemplate(vaultConfiguration.vaultEndpoint(),
ClientHttpRequestFactoryFactory.create(clientOptions, sslConfiguration),
new SimpleSessionManager(vaultConfiguration.clientAuthentication()));
transitOperations = vaultTemplate.opsForTransit();
}
private static ClientOptions getClientOptions(HashiCorpVaultProperties vaultProperties) {
final ClientOptions clientOptions = new ClientOptions();
Duration readTimeoutDuration = clientOptions.getReadTimeout();
Duration connectionTimeoutDuration = clientOptions.getConnectionTimeout();
final Optional<String> configuredReadTimeout = vaultProperties.getReadTimeout();
if (configuredReadTimeout.isPresent()) {
readTimeoutDuration = getDuration(configuredReadTimeout.get());
}
final Optional<String> configuredConnectionTimeout = vaultProperties.getConnectionTimeout();
if (configuredConnectionTimeout.isPresent()) {
connectionTimeoutDuration = getDuration(configuredConnectionTimeout.get());
}
return new ClientOptions(connectionTimeoutDuration, readTimeoutDuration);
}
private static Duration getDuration(String formattedDuration) {
final double duration = FormatUtils.getPreciseTimeDuration(formattedDuration, TimeUnit.MILLISECONDS);
return Duration.ofMillis(Double.valueOf(duration).longValue());
}
@Override
public String encrypt(final String transitKey, final byte[] plainText) {
return transitOperations.encrypt(transitKey, Plaintext.of(plainText)).getCiphertext();
}
@Override
public byte[] decrypt(final String transitKey, final String cipherText) {
return transitOperations.decrypt(transitKey, Ciphertext.of(cipherText)).getPlaintext();
}
}

View File

@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.vault.hashicorp.config;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import java.nio.file.Paths;
/**
* Basic ApplicationContext that defines resources as FileSystemResource objects.
*/
public class HashiCorpVaultApplicationContext extends StaticApplicationContext {
public HashiCorpVaultApplicationContext(ConfigurableEnvironment env) {
this.setEnvironment(env);
}
@Override
public Resource getResource(String location) {
return new FileSystemResource(Paths.get(location));
}
}

View File

@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.vault.hashicorp.config;
import org.apache.nifi.vault.hashicorp.HashiCorpVaultConfigurationException;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.support.ResourcePropertySource;
import org.springframework.vault.config.EnvironmentVaultConfiguration;
import java.io.IOException;
import java.nio.file.Paths;
/**
* A Vault configuration that uses the NiFiVaultEnvironment.
*/
public class HashiCorpVaultConfiguration extends EnvironmentVaultConfiguration {
public HashiCorpVaultConfiguration(final HashiCorpVaultProperties vaultProperties) throws HashiCorpVaultConfigurationException {
final ConfigurableEnvironment env = new StandardEnvironment();
try {
env.getPropertySources().addFirst(new ResourcePropertySource(new FileSystemResource(Paths.get(vaultProperties.getAuthPropertiesFilename()))));
} catch (IOException e) {
throw new HashiCorpVaultConfigurationException("Could not load auth properties", e);
}
env.getPropertySources().addFirst(new HashiCorpVaultPropertySource(vaultProperties));
this.setApplicationContext(new HashiCorpVaultApplicationContext(env));
}
}

View File

@ -0,0 +1,246 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.vault.hashicorp.config;
import org.apache.nifi.vault.hashicorp.HashiCorpVaultConfigurationException;
import java.io.File;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.Optional;
/**
* Properties to configure the HashiCorpVaultCommunicationService. The only properties considered mandatory are uri and
* authPropertiesFilename. See the following link for valid vault authentication properties (default is
* vault.authentication=TOKEN, expecting a vault.token property to be supplied).
*
* @see <a href="https://docs.spring.io/spring-vault/docs/2.3.1/reference/html/#vault.core.environment-vault-configuration">
* https://docs.spring.io/spring-vault/docs/2.3.1/reference/html/#vault.core.environment-vault-configuration</a>
*/
public class HashiCorpVaultProperties {
public static final String HTTPS = "https";
private final String uri;
private final String authPropertiesFilename;
private final HashiCorpVaultSslProperties ssl;
private final Optional<String> connectionTimeout;
private final Optional<String> readTimeout;
private HashiCorpVaultProperties(final String uri, String keyStore, final String keyStoreType, final String keyStorePassword, final String trustStore,
final String trustStoreType, final String trustStorePassword, final String authPropertiesFilename,
final String enabledTlsCipherSuites, final String enabledTlsProtocols, final String connectionTimeout, final String readTimeout) {
Objects.requireNonNull(uri, "Vault URI is required");
Objects.requireNonNull(authPropertiesFilename, "Vault auth properties filename is required");
this.uri = uri;
this.authPropertiesFilename = authPropertiesFilename;
this.ssl = new HashiCorpVaultSslProperties(keyStore, keyStoreType, keyStorePassword, trustStore, trustStoreType, trustStorePassword,
enabledTlsCipherSuites, enabledTlsProtocols);
this.connectionTimeout = connectionTimeout == null ? Optional.empty() : Optional.of(connectionTimeout);
this.readTimeout = readTimeout == null ? Optional.empty() : Optional.of(readTimeout);
if (uri.startsWith(HTTPS)) {
Objects.requireNonNull(keyStore, "KeyStore is required with an https URI");
Objects.requireNonNull(keyStorePassword, "KeyStore password is required with an https URI");
Objects.requireNonNull(keyStoreType, "KeyStore type is required with an https URI");
Objects.requireNonNull(trustStore, "TrustStore is required with an https URI");
Objects.requireNonNull(trustStorePassword, "TrustStore password is required with an https URI");
Objects.requireNonNull(trustStoreType, "TrustStore type is required with an https URI");
}
validateAuthProperties();
}
private void validateAuthProperties() throws HashiCorpVaultConfigurationException {
final File authPropertiesFile = Paths.get(authPropertiesFilename).toFile();
if (!authPropertiesFile.exists()) {
throw new HashiCorpVaultConfigurationException(String.format("Auth properties file [%s] does not exist", authPropertiesFilename));
}
}
@HashiCorpVaultProperty
public String getUri() {
return uri;
}
@HashiCorpVaultProperty
public HashiCorpVaultSslProperties getSsl() {
return ssl;
}
public String getAuthPropertiesFilename() {
return authPropertiesFilename;
}
public Optional<String> getConnectionTimeout() {
return connectionTimeout;
}
public Optional<String> getReadTimeout() {
return readTimeout;
}
/**
* Builder for HashiCorpVaultProperties. The only properties that are considered mandatory are uri and authPropertiesFilename.
*/
public static class HashiCorpVaultPropertiesBuilder {
private String uri;
private String keyStore;
private String keyStoreType;
private String keyStorePassword;
private String trustStore;
private String trustStoreType;
private String trustStorePassword;
private String authPropertiesFilename;
private String enabledTlsCipherSuites;
private String enabledTlsProtocols;
private String connectionTimeout;
private String readTimeout;
/**
* Set the Vault URI (e.g., http://localhost:8200). If using https protocol, the KeyStore and TrustStore
* properties are expected to also be set.
* @param uri Vault's URI
* @return
*/
public HashiCorpVaultPropertiesBuilder setUri(String uri) {
this.uri = uri;
return this;
}
/**
* Sets the path to the keyStore.
* @param keyStore Path to the keyStore
* @return
*/
public HashiCorpVaultPropertiesBuilder setKeyStore(String keyStore) {
this.keyStore = keyStore;
return this;
}
/**
* Sets keyStore type (e.g., JKS, PKCS12).
* @param keyStoreType KeyStore type
* @return
*/
public HashiCorpVaultPropertiesBuilder setKeyStoreType(String keyStoreType) {
this.keyStoreType = keyStoreType;
return this;
}
/**
* Sets the keyStore password.
* @param keyStorePassword KeyStore password
* @return
*/
public HashiCorpVaultPropertiesBuilder setKeyStorePassword(String keyStorePassword) {
this.keyStorePassword = keyStorePassword;
return this;
}
/**
* Sets the path to the trustStore.
* @param trustStore Path to the trustStore
* @return
*/
public HashiCorpVaultPropertiesBuilder setTrustStore(String trustStore) {
this.trustStore = trustStore;
return this;
}
/**
* Sets the trustStore type (e.g., JKS, PKCS12).
* @param trustStoreType TrustStore type
* @return
*/
public HashiCorpVaultPropertiesBuilder setTrustStoreType(String trustStoreType) {
this.trustStoreType = trustStoreType;
return this;
}
/**
* Sets the trustStore passsword.
* @param trustStorePassword TrustStore password
* @return
*/
public HashiCorpVaultPropertiesBuilder setTrustStorePassword(String trustStorePassword) {
this.trustStorePassword = trustStorePassword;
return this;
}
/**
* Sets the path to the vault authentication properties file. See the following link for valid
* vault authentication properties (default is vault.authentication=TOKEN, expecting a vault.token property
* to be supplied).
* @see <a href="https://docs.spring.io/spring-vault/docs/2.3.1/reference/html/#vault.core.environment-vault-configuration">
* https://docs.spring.io/spring-vault/docs/2.3.1/reference/html/#vault.core.environment-vault-configuration</a>
* @param authPropertiesFilename The filename of a properties file containing Spring Vault authentication
* properties
* @return
*/
public HashiCorpVaultPropertiesBuilder setAuthPropertiesFilename(String authPropertiesFilename) {
this.authPropertiesFilename = authPropertiesFilename;
return this;
}
/**
* Sets an optional comma-separated list of enabled TLS cipher suites.
* @param enabledTlsCipherSuites Enabled TLS cipher suites (only these will be enabled)
* @return
*/
public HashiCorpVaultPropertiesBuilder setEnabledTlsCipherSuites(String enabledTlsCipherSuites) {
this.enabledTlsCipherSuites = enabledTlsCipherSuites;
return this;
}
/**
* Sets an optional comma-separated list of enabled TLS protocols.
* @param enabledTlsProtocols Enabled TLS protocols (only these will be enabled)
* @return
*/
public HashiCorpVaultPropertiesBuilder setEnabledTlsProtocols(String enabledTlsProtocols) {
this.enabledTlsProtocols = enabledTlsProtocols;
return this;
}
/**
* Sets the connection timeout for the HTTP client, using the standard NiFi duration format (e.g., 5 secs)
* @param connectionTimeout Connection timeout (default is 5 secs)
* @return
*/
public HashiCorpVaultPropertiesBuilder setConnectionTimeout(String connectionTimeout) {
this.connectionTimeout = connectionTimeout;
return this;
}
/**
* Sets the read timeout for the HTTP client, using the standard NiFi duration format (e.g., 15 secs).
* @param readTimeout Read timeout (default is 15 secs)
* @return
*/
public HashiCorpVaultPropertiesBuilder setReadTimeout(String readTimeout) {
this.readTimeout = readTimeout;
return this;
}
/**
* Build the VaultProperties.
* @return
*/
public HashiCorpVaultProperties build() {
return new HashiCorpVaultProperties(uri, keyStore, keyStoreType, keyStorePassword, trustStore, trustStoreType,
trustStorePassword, authPropertiesFilename, enabledTlsCipherSuites, enabledTlsProtocols, connectionTimeout, readTimeout);
}
}
}

View File

@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.vault.hashicorp.config;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks a vault property that should be mapped to a Spring Vault property key.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HashiCorpVaultProperty {
}

View File

@ -0,0 +1,60 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.vault.hashicorp.config;
import org.apache.nifi.vault.hashicorp.config.lookup.BeanPropertyLookup;
import org.apache.nifi.vault.hashicorp.config.lookup.PropertyLookup;
import org.springframework.core.env.PropertySource;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class HashiCorpVaultPropertySource extends PropertySource<HashiCorpVaultProperties> {
private static final String PREFIX = "vault";
private static final Pattern DASH_LETTER_PATTERN = Pattern.compile("-[a-z]");
private PropertyLookup propertyLookup;
public HashiCorpVaultPropertySource(HashiCorpVaultProperties source) {
super(HashiCorpVaultPropertySource.class.getName(), source);
propertyLookup = new BeanPropertyLookup(PREFIX, HashiCorpVaultProperties.class, HashiCorpVaultProperty.class);
}
@Override
public Object getProperty(final String key) {
Objects.requireNonNull(key, "Property key cannot be null");
return propertyLookup.getPropertyValue(getPropertyKey(key), getSource());
}
/**
* Converts key names from format test-value to testValue
* @param springPropertyKey A Spring Vault property key
* @return
*/
private String getPropertyKey(String springPropertyKey) {
final Matcher m = DASH_LETTER_PATTERN.matcher(springPropertyKey);
final StringBuffer result = new StringBuffer();
while (m.find()) {
m.appendReplacement(result, m.group(0).substring(1).toUpperCase());
}
m.appendTail(result);
return result.toString();
}
}

View File

@ -0,0 +1,81 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.vault.hashicorp.config;
public class HashiCorpVaultSslProperties {
private final String keyStore;
private final String keyStoreType;
private final String keyStorePassword;
private final String trustStore;
private final String trustStoreType;
private final String trustStorePassword;
private final String enabledCipherSuites;
private final String enabledProtocols;
public HashiCorpVaultSslProperties(String keyStore, String keyStoreType, String keyStorePassword, String trustStore,
String trustStoreType, String trustStorePassword,
String enabledCipherSuites, String enabledProtocols) {
this.keyStore = keyStore;
this.keyStoreType = keyStoreType;
this.keyStorePassword = keyStorePassword;
this.trustStore = trustStore;
this.trustStoreType = trustStoreType;
this.trustStorePassword = trustStorePassword;
this.enabledCipherSuites = enabledCipherSuites;
this.enabledProtocols = enabledProtocols;
}
@HashiCorpVaultProperty
public String getKeyStore() {
return keyStore;
}
@HashiCorpVaultProperty
public String getKeyStoreType() {
return keyStoreType;
}
@HashiCorpVaultProperty
public String getKeyStorePassword() {
return keyStorePassword;
}
@HashiCorpVaultProperty
public String getTrustStore() {
return trustStore;
}
@HashiCorpVaultProperty
public String getTrustStoreType() {
return trustStoreType;
}
@HashiCorpVaultProperty
public String getTrustStorePassword() {
return trustStorePassword;
}
@HashiCorpVaultProperty
public String getEnabledCipherSuites() {
return enabledCipherSuites;
}
@HashiCorpVaultProperty
public String getEnabledProtocols() {
return enabledProtocols;
}
}

View File

@ -0,0 +1,81 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.vault.hashicorp.config.lookup;
import org.apache.nifi.vault.hashicorp.HashiCorpVaultConfigurationException;
import org.springframework.beans.BeanUtils;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
/**
* A property lookup that indexes the properties of a Java bean.
*/
public class BeanPropertyLookup extends PropertyLookup {
private static final String SEPARATOR = ".";
private final Map<String, PropertyLookup> propertyLookupMap;
public BeanPropertyLookup(final String prefix, final Class<?> beanClass, final Class<? extends Annotation> propertyFilter) {
this(prefix, beanClass, propertyFilter, null);
}
private BeanPropertyLookup(final String prefix, final Class<?> beanClass, final Class<? extends Annotation> propertyFilter,
final PropertyDescriptor propertyDescriptor) {
super(propertyDescriptor);
propertyLookupMap = Arrays.stream(BeanUtils.getPropertyDescriptors(beanClass))
.filter(pd -> pd.getReadMethod().getAnnotation(propertyFilter) != null)
.collect(Collectors.toMap(
pd -> getPropertyKey(prefix, pd),
pd -> pd.getReadMethod().getReturnType().equals(String.class) ? new ValuePropertyLookup(pd)
: new BeanPropertyLookup(getPropertyKey(prefix, pd), pd.getReadMethod().getReturnType(), propertyFilter, pd)
));
}
private static String getPropertyKey(final String prefix, final PropertyDescriptor propertyDescriptor) {
return prefix == null ? propertyDescriptor.getDisplayName() : String.join(SEPARATOR, prefix, propertyDescriptor.getDisplayName());
}
@Override
public Object getPropertyValue(final String propertyKey, final Object obj) {
if (propertyLookupMap.containsKey(propertyKey)) {
final PropertyLookup propertyLookup = propertyLookupMap.get(propertyKey);
return propertyLookup.getPropertyValue(propertyKey, propertyLookup.getEnclosingObject(obj));
}
for(final Map.Entry<String, PropertyLookup> entry : propertyLookupMap.entrySet()) {
final String key = entry.getKey();
if (propertyKey.startsWith(key + SEPARATOR)) {
final PropertyLookup propertyLookup = entry.getValue();
return propertyLookup.getPropertyValue(propertyKey, propertyLookup.getEnclosingObject(obj));
}
}
return null;
}
@Override
public Object getEnclosingObject(Object obj) {
try {
return getPropertyDescriptor().getReadMethod().invoke(obj);
} catch (final IllegalAccessException | InvocationTargetException e) {
throw new HashiCorpVaultConfigurationException("Could not invoke " + getPropertyDescriptor().getDisplayName());
}
}
}

View File

@ -0,0 +1,50 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.vault.hashicorp.config.lookup;
import java.beans.PropertyDescriptor;
/**
* Provides a method of looking up property values.
*/
public abstract class PropertyLookup {
private final PropertyDescriptor propertyDescriptor;
protected PropertyLookup(final PropertyDescriptor propertyDescriptor) {
this.propertyDescriptor = propertyDescriptor;
}
/**
* Returns the value of a property.
* @param propertyKey The property key (e.g., object.child.propertyValue)
* @param obj The object from which to retrieve the property.
* @return The property value
*/
public abstract Object getPropertyValue(final String propertyKey, final Object obj);
/**
* Returns the enclosing object of the property.
* @param obj The top level object
* @return The appropriate object that contains the property
*/
public abstract Object getEnclosingObject(final Object obj);
protected PropertyDescriptor getPropertyDescriptor() {
return propertyDescriptor;
}
}

View File

@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.vault.hashicorp.config.lookup;
import org.apache.nifi.vault.hashicorp.HashiCorpVaultConfigurationException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
/**
* A simple property lookup that invokes the property descriptor to retrieve the value of the property.
*/
public class ValuePropertyLookup extends PropertyLookup {
public ValuePropertyLookup(final PropertyDescriptor propertyDescriptor) {
super(propertyDescriptor);
}
@Override
public Object getPropertyValue(final String propertyKey, final Object obj) {
try {
return getPropertyDescriptor().getReadMethod().invoke(obj);
} catch (final IllegalAccessException | InvocationTargetException e) {
throw new HashiCorpVaultConfigurationException("Could not get the value of " + propertyKey);
}
}
@Override
public Object getEnclosingObject(Object obj) {
return obj;
}
}

View File

@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.vault.hashicorp;
import org.apache.nifi.vault.hashicorp.config.HashiCorpVaultProperties;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
/**
* The simplest way to run this test is by installing Vault locally, then running:
*
* vault server -dev
* vault secrets enable transit
* vault write -f transit/keys/nifi
*
* Make note of the Root Token and create a properties file with the contents:
* vault.token=[Root Token]
*
* Then, set the system property -Dvault.auth.properties to the file path of the above properties file when
* running the integration test.
*/
public class StandardHashiCorpVaultCommunicationServiceIT {
private static final String TRANSIT_KEY = "nifi";
private HashiCorpVaultCommunicationService vcs;
@Before
public void init() {
vcs = new StandardHashiCorpVaultCommunicationService(new HashiCorpVaultProperties.HashiCorpVaultPropertiesBuilder()
.setAuthPropertiesFilename(System.getProperty("vault.auth.properties"))
.setUri("http://127.0.0.1:8200")
.build());
}
@Test
public void testEncryptDecrypt() {
this.runEncryptDecryptTest();
}
public void runEncryptDecryptTest() {
String plaintext = "this is the plaintext";
String ciphertext = vcs.encrypt(TRANSIT_KEY, plaintext.getBytes(StandardCharsets.UTF_8));
byte[] decrypted = vcs.decrypt(TRANSIT_KEY, ciphertext);
Assert.assertEquals(plaintext, new String(decrypted, StandardCharsets.UTF_8));
}
}

View File

@ -0,0 +1,195 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.vault.hashicorp;
import org.apache.nifi.vault.hashicorp.config.HashiCorpVaultConfiguration;
import org.apache.nifi.vault.hashicorp.config.HashiCorpVaultProperties;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.support.SslConfiguration;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class TestHashiCorpVaultConfiguration {
public static final String VAULT_AUTHENTICATION = "vault.authentication";
public static final String VAULT_TOKEN = "vault.token";
private static final String TEST_TOKEN_VALUE = "test-token";
private static final String TOKEN_VALUE = "TOKEN";
private static final String URI_VALUE = "http://localhost:8200";
private static final String KEYSTORE_PASSWORD_VALUE = "keystorePassword";
private static final String KEYSTORE_TYPE_VALUE = "keystoreType";
private static final String TRUSTSTORE_PASSWORD_VALUE = "truststorePassword";
private static final String TRUSTSTORE_TYPE_VALUE = "truststoreType";
public static final String TLS_V_1_3_VALUE = "TLSv1.3";
public static final String TEST_CIPHER_SUITE_VALUE = "Test cipher suite";
private static Path keystoreFile;
private static Path truststoreFile;
private HashiCorpVaultProperties.HashiCorpVaultPropertiesBuilder propertiesBuilder;
private static File authProps;
private HashiCorpVaultConfiguration config;
@BeforeClass
public static void initClass() throws IOException {
keystoreFile = Files.createTempFile("test", ".jks");
truststoreFile = Files.createTempFile("test", ".jks");
authProps = writeBasicVaultAuthProperties();
}
@AfterClass
public static void cleanUpClass() throws IOException {
Files.deleteIfExists(keystoreFile);
Files.deleteIfExists(truststoreFile);
Files.deleteIfExists(authProps.toPath());
}
@Before
public void init() throws IOException {
propertiesBuilder = new HashiCorpVaultProperties.HashiCorpVaultPropertiesBuilder()
.setUri(URI_VALUE)
.setAuthPropertiesFilename(authProps.getAbsolutePath());
}
public static File writeVaultAuthProperties(final Map<String, String> properties) throws IOException {
File authProps = File.createTempFile("vault-", ".properties");
writeProperties(properties, authProps);
return authProps;
}
/**
* Writes a new temp vault authentication properties file with the following properties:
* vault.authentication=TOKEN
* vault.token=test-token
* @return The created temp file
* @throws IOException If the file could not be written
*/
public static File writeBasicVaultAuthProperties() throws IOException {
Map<String, String> properties = new HashMap<>();
properties.put(VAULT_AUTHENTICATION, TOKEN_VALUE);
properties.put(VAULT_TOKEN, TEST_TOKEN_VALUE);
return writeVaultAuthProperties(properties);
}
public static void writeProperties(Map<String, String> props, File authProps) throws IOException {
Properties properties = new Properties();
for (Map.Entry<String, String> entry : props.entrySet()) {
properties.put(entry.getKey(), entry.getValue());
}
try (Writer writer = new FileWriter(authProps)) {
properties.store(writer, "Vault test authentication properties");
}
}
public void runTest() {
config = new HashiCorpVaultConfiguration(propertiesBuilder.build());
VaultEndpoint endpoint = config.vaultEndpoint();
Assert.assertEquals("localhost", endpoint.getHost());
Assert.assertEquals(8200, endpoint.getPort());
Assert.assertEquals("http", endpoint.getScheme());
ClientAuthentication clientAuthentication = config.clientAuthentication();
Assert.assertNotNull(clientAuthentication);
}
@Test
public void testBasicProperties() {
this.runTest();
}
@Test
public void testTlsProperties() throws IOException {
propertiesBuilder.setKeyStore(keystoreFile.toFile().getAbsolutePath());
propertiesBuilder.setKeyStorePassword(KEYSTORE_PASSWORD_VALUE);
propertiesBuilder.setKeyStoreType(KEYSTORE_TYPE_VALUE);
propertiesBuilder.setTrustStore(truststoreFile.toFile().getAbsolutePath());
propertiesBuilder.setTrustStorePassword(TRUSTSTORE_PASSWORD_VALUE);
propertiesBuilder.setTrustStoreType(TRUSTSTORE_TYPE_VALUE);
propertiesBuilder.setEnabledTlsProtocols(TLS_V_1_3_VALUE);
propertiesBuilder.setEnabledTlsCipherSuites(TEST_CIPHER_SUITE_VALUE);
this.runTest();
SslConfiguration sslConfiguration = config.sslConfiguration();
Assert.assertEquals(keystoreFile.toFile().getAbsolutePath(), sslConfiguration.getKeyStoreConfiguration().getResource().getFile().getAbsolutePath());
Assert.assertEquals(KEYSTORE_PASSWORD_VALUE, new String(sslConfiguration.getKeyStoreConfiguration().getStorePassword()));
Assert.assertEquals(KEYSTORE_TYPE_VALUE, sslConfiguration.getKeyStoreConfiguration().getStoreType());
Assert.assertEquals(truststoreFile.toFile().getAbsolutePath(), sslConfiguration.getTrustStoreConfiguration().getResource().getFile().getAbsolutePath());
Assert.assertEquals(TRUSTSTORE_PASSWORD_VALUE, new String(sslConfiguration.getTrustStoreConfiguration().getStorePassword()));
Assert.assertEquals(TRUSTSTORE_TYPE_VALUE, sslConfiguration.getTrustStoreConfiguration().getStoreType());
Assert.assertEquals(Arrays.asList(TLS_V_1_3_VALUE), sslConfiguration.getEnabledProtocols());
Assert.assertEquals(Arrays.asList(TEST_CIPHER_SUITE_VALUE), sslConfiguration.getEnabledCipherSuites());
}
@Test
public void testInvalidTLS() {
propertiesBuilder.setUri(URI_VALUE.replace("http", "https"));
Assert.assertThrows(NullPointerException.class, () -> this.runTest());
}
@Test
public void testMissingAuthToken() throws IOException {
File authProperties = null;
try {
final Map<String, String> props = new HashMap<>();
props.put(VAULT_AUTHENTICATION, TOKEN_VALUE);
authProperties = writeVaultAuthProperties(props);
propertiesBuilder.setAuthPropertiesFilename(authProperties.getAbsolutePath());
Assert.assertThrows(IllegalArgumentException.class, () -> this.runTest());
} finally {
if (authProperties != null) {
Files.deleteIfExists(authProperties.toPath());
}
}
}
@Test
public void testMissingAuthType() throws IOException {
File authProperties = null;
try {
final Map<String, String> props = new HashMap<>();
authProperties = writeVaultAuthProperties(props);
propertiesBuilder.setAuthPropertiesFilename(authProperties.getAbsolutePath());
Assert.assertThrows(IllegalArgumentException.class, () -> this.runTest());
} finally {
if (authProperties != null) {
Files.deleteIfExists(authProperties.toPath());
}
}
}
}

View File

@ -0,0 +1,125 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.vault.hashicorp;
import org.apache.nifi.security.util.KeyStoreUtils;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.vault.hashicorp.config.HashiCorpVaultProperties;
import org.apache.nifi.vault.hashicorp.config.HashiCorpVaultSslProperties;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Collectors;
public class TestStandardHashiCorpVaultCommunicationService {
public static final String URI_VALUE = "http://127.0.0.1:8200";
public static final String CIPHER_SUITE_VALUE = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384";
private HashiCorpVaultProperties properties;
private HashiCorpVaultSslProperties sslProperties;
private File authProps;
@Before
public void init() throws IOException {
authProps = TestHashiCorpVaultConfiguration.writeBasicVaultAuthProperties();
properties = Mockito.mock(HashiCorpVaultProperties.class);
sslProperties = Mockito.mock(HashiCorpVaultSslProperties.class);
Mockito.when(properties.getUri()).thenReturn(URI_VALUE);
Mockito.when(properties.getAuthPropertiesFilename()).thenReturn(authProps.getAbsolutePath());
Mockito.when(properties.getSsl()).thenReturn(sslProperties);
}
@After
public void cleanUp() throws IOException {
Files.deleteIfExists(authProps.toPath());
}
private HashiCorpVaultCommunicationService configureService() {
return new StandardHashiCorpVaultCommunicationService(properties);
}
@Test
public void testBasicConfiguration() {
this.configureService();
// Once to check if the URI is https, and once by VaultTemplate
Mockito.verify(properties, Mockito.times(2)).getUri();
// Once each to check if they are configured
Mockito.verify(properties, Mockito.times(1)).getConnectionTimeout();
Mockito.verify(properties, Mockito.times(1)).getReadTimeout();
// These should not be called because TLS is not configured
this.ensureTlsPropertiesAccessed(0);
}
private void ensureTlsPropertiesAccessed(int numberOfTimes) {
Mockito.verify(sslProperties, Mockito.times(numberOfTimes)).getKeyStore();
Mockito.verify(sslProperties, Mockito.times(numberOfTimes)).getKeyStoreType();
Mockito.verify(sslProperties, Mockito.times(numberOfTimes)).getKeyStorePassword();
Mockito.verify(sslProperties, Mockito.times(numberOfTimes)).getTrustStore();
Mockito.verify(sslProperties, Mockito.times(numberOfTimes)).getTrustStoreType();
Mockito.verify(sslProperties, Mockito.times(numberOfTimes)).getTrustStorePassword();
Mockito.verify(sslProperties, Mockito.times(numberOfTimes)).getEnabledProtocols();
Mockito.verify(sslProperties, Mockito.times(numberOfTimes)).getEnabledCipherSuites();
}
@Test
public void testTimeouts() {
Mockito.when(properties.getConnectionTimeout()).thenReturn(Optional.of("20 secs"));
Mockito.when(properties.getReadTimeout()).thenReturn(Optional.of("40 secs"));
this.configureService();
Mockito.verify(properties, Mockito.times(1)).getConnectionTimeout();
Mockito.verify(properties, Mockito.times(1)).getReadTimeout();
}
@Test
public void testTLS() throws GeneralSecurityException, IOException {
TlsConfiguration tlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore();
try {
Mockito.when(sslProperties.getKeyStore()).thenReturn(tlsConfiguration.getKeystorePath());
Mockito.when(sslProperties.getKeyStorePassword()).thenReturn(tlsConfiguration.getKeystorePassword());
Mockito.when(sslProperties.getKeyStoreType()).thenReturn(tlsConfiguration.getKeystoreType().getType());
Mockito.when(sslProperties.getTrustStore()).thenReturn(tlsConfiguration.getTruststorePath());
Mockito.when(sslProperties.getTrustStorePassword()).thenReturn(tlsConfiguration.getTruststorePassword());
Mockito.when(sslProperties.getTrustStoreType()).thenReturn(tlsConfiguration.getTruststoreType().getType());
Mockito.when(sslProperties.getEnabledProtocols()).thenReturn(Arrays.stream(tlsConfiguration.getEnabledProtocols())
.collect(Collectors.joining(",")));
Mockito.when(sslProperties.getEnabledCipherSuites()).thenReturn(CIPHER_SUITE_VALUE);
Mockito.when(properties.getUri()).thenReturn(URI_VALUE.replace("http", "https"));
this.configureService();
this.ensureTlsPropertiesAccessed(1);
} finally {
Files.deleteIfExists(Paths.get(tlsConfiguration.getKeystorePath()));
Files.deleteIfExists(Paths.get(tlsConfiguration.getTruststorePath()));
}
}
}

View File

@ -48,6 +48,7 @@
<module>nifi-socket-utils</module>
<module>nifi-utils</module>
<module>nifi-uuid5</module>
<module>nifi-vault-utils</module>
<module>nifi-web-utils</module>
<module>nifi-write-ahead-log</module>
</modules>