Skip to content

Commit

Permalink
NIFI-14025 Corrected LDAP Provider Trust Store Configuration
Browse files Browse the repository at this point in the history
- Fixed LDAP Provider support for configuring a Trust Store without a Key Store

Signed-off-by: Pierre Villard <pierre.villard.fr@gmail.com>

This closes apache#9544.
  • Loading branch information
exceptionfactory authored and pvillard31 committed Nov 20, 2024
1 parent e327dcd commit a3f4f7b
Show file tree
Hide file tree
Showing 6 changed files with 354 additions and 190 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@
<version>7.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-security-cert-builder</artifactId>
<version>2.1.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
</dependencies>
<name>nifi-ldap-iaa-providers</name>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@
*/
package org.apache.nifi.ldap;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
Expand All @@ -38,8 +32,8 @@
import org.apache.nifi.authentication.exception.ProviderCreationException;
import org.apache.nifi.authentication.exception.ProviderDestructionException;
import org.apache.nifi.configuration.NonComponentConfigurationContext;
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
import org.apache.nifi.ldap.ssl.LdapSslContextProvider;
import org.apache.nifi.ldap.ssl.StandardLdapSslContextProvider;
import org.apache.nifi.util.FormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -249,92 +243,9 @@ private void setTimeout(final LoginIdentityProviderConfigurationContext configur
}

private static SSLContext getConfiguredSslContext(final NonComponentConfigurationContext configurationContext) {
final String rawProtocol = configurationContext.getProperty(ProviderProperty.TLS_PROTOCOL.getProperty());

SSLContext sslContext = null;
try {
final KeyStore trustStore = getTrustStore(configurationContext);
if (trustStore == null) {
logger.debug("Truststore not configured");
} else {
final StandardSslContextBuilder sslContextBuilder = new StandardSslContextBuilder();
sslContextBuilder.protocol(rawProtocol);
sslContextBuilder.trustStore(trustStore);

final KeyStore keyStore = getKeyStore(configurationContext);
if (keyStore == null) {
logger.debug("Keystore not configured");
} else {
final String keyStorePassword = configurationContext.getProperty(ProviderProperty.KEYSTORE_PASSWORD.getProperty());
final char[] keyPassword = keyStorePassword.toCharArray();

sslContextBuilder.keyStore(keyStore);
sslContextBuilder.keyPassword(keyPassword);
sslContext = sslContextBuilder.build();
}
}
} catch (final Exception e) {
logger.error("Encountered an error configuring TLS for LDAP identity provider: {}", e.getLocalizedMessage());
throw new ProviderCreationException("Error configuring TLS for LDAP identity provider", e);
}

return sslContext;
}

private static KeyStore getKeyStore(final NonComponentConfigurationContext configurationContext) throws IOException {
final String rawKeystore = configurationContext.getProperty(ProviderProperty.KEYSTORE.getProperty());
final String rawKeystorePassword = configurationContext.getProperty(ProviderProperty.KEYSTORE_PASSWORD.getProperty());
final String rawKeystoreType = configurationContext.getProperty(ProviderProperty.KEYSTORE_TYPE.getProperty());

final KeyStore keyStore;

if (rawKeystore == null || rawKeystore.isBlank()) {
keyStore = null;
} else if (rawKeystorePassword == null) {
throw new ProviderCreationException("Keystore Password not configured");
} else {
final StandardKeyStoreBuilder builder = new StandardKeyStoreBuilder();
builder.type(rawKeystoreType);

final char[] keyStorePassword = rawKeystorePassword.toCharArray();
builder.password(keyStorePassword);

final Path trustStorePath = Paths.get(rawKeystore);
try (InputStream trustStoreStream = Files.newInputStream(trustStorePath)) {
builder.inputStream(trustStoreStream);
keyStore = builder.build();
}
}

return keyStore;
}

private static KeyStore getTrustStore(final NonComponentConfigurationContext configurationContext) throws IOException {
final String rawTruststore = configurationContext.getProperty(ProviderProperty.TRUSTSTORE.getProperty());
final String rawTruststorePassword = configurationContext.getProperty(ProviderProperty.TRUSTSTORE_PASSWORD.getProperty());
final String rawTruststoreType = configurationContext.getProperty(ProviderProperty.TRUSTSTORE_TYPE.getProperty());

final KeyStore trustStore;

if (rawTruststore == null || rawTruststore.isBlank()) {
trustStore = null;
} else if (rawTruststorePassword == null) {
throw new ProviderCreationException("Truststore Password not configured");
} else {
final StandardKeyStoreBuilder builder = new StandardKeyStoreBuilder();
builder.type(rawTruststoreType);

final char[] trustStorePassword = rawTruststorePassword.toCharArray();
builder.password(trustStorePassword);

final Path trustStorePath = Paths.get(rawTruststore);
try (InputStream trustStoreStream = Files.newInputStream(trustStorePath)) {
builder.inputStream(trustStoreStream);
trustStore = builder.build();
}
}

return trustStore;
final LdapSslContextProvider ldapSslContextProvider = new StandardLdapSslContextProvider();
final Map<String, String> contextProperties = configurationContext.getProperties();
return ldapSslContextProvider.createContext(contextProperties);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -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.ldap.ssl;

import javax.net.ssl.SSLContext;
import java.util.Map;

/**
* Abstraction for creating an SSLContext from LDAP configuration properties
*/
public interface LdapSslContextProvider {
/**
* Create SSLContext from configuration properties
*
* @param properties Provider properties
* @return SSLContext
*/
SSLContext createContext(Map<String, String> properties);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* 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.ldap.ssl;

import org.apache.nifi.authentication.exception.ProviderCreationException;
import org.apache.nifi.ldap.ProviderProperty;
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.util.Map;
import java.util.Objects;

/**
* Standard implementation of LDAP SSLContext Provider supporting common properties
*/
public class StandardLdapSslContextProvider implements LdapSslContextProvider {
private static final Logger logger = LoggerFactory.getLogger(StandardLdapSslContextProvider.class);

private static final String DEFAULT_PROTOCOL = "TLS";

/**
* Create SSLContext using configured properties defaulting to system trust store when trust store properties not configured
*
* @param properties Provider properties
* @return SSLContext initialized using configured properties
*/
@Override
public SSLContext createContext(final Map<String, String> properties) {
Objects.requireNonNull(properties, "Properties required");

final String rawProtocol = properties.get(ProviderProperty.TLS_PROTOCOL.getProperty());
final String protocol;
if (rawProtocol == null || rawProtocol.isBlank()) {
protocol = DEFAULT_PROTOCOL;
} else {
protocol = rawProtocol;
}

try {
final SSLContext sslContext;
final StandardSslContextBuilder sslContextBuilder = new StandardSslContextBuilder();
sslContextBuilder.protocol(protocol);

final KeyStore trustStore = getTrustStore(properties);
if (trustStore == null) {
logger.debug("LDAP TLS Truststore not configured");
} else {
sslContextBuilder.trustStore(trustStore);
}

final KeyStore keyStore = getKeyStore(properties);
if (keyStore == null) {
logger.debug("LDAP TLS Keystore not configured");
} else {
final String keyStorePassword = properties.get(ProviderProperty.KEYSTORE_PASSWORD.getProperty());
final char[] keyPassword = keyStorePassword.toCharArray();

sslContextBuilder.keyStore(keyStore);
sslContextBuilder.keyPassword(keyPassword);
}

sslContext = sslContextBuilder.build();
return sslContext;
} catch (final Exception e) {
throw new ProviderCreationException("Error configuring TLS for LDAP Provider", e);
}
}

private KeyStore getKeyStore(final Map<String, String> properties) throws IOException {
final String rawKeystore = properties.get(ProviderProperty.KEYSTORE.getProperty());
final String rawKeystorePassword = properties.get(ProviderProperty.KEYSTORE_PASSWORD.getProperty());
final String rawKeystoreType = properties.get(ProviderProperty.KEYSTORE_TYPE.getProperty());

final KeyStore keyStore;

if (rawKeystore == null || rawKeystore.isBlank()) {
keyStore = null;
} else if (rawKeystorePassword == null) {
throw new ProviderCreationException("Keystore Password not configured");
} else {
final StandardKeyStoreBuilder builder = new StandardKeyStoreBuilder();
builder.type(rawKeystoreType);

final char[] keyStorePassword = rawKeystorePassword.toCharArray();
builder.password(keyStorePassword);

final Path trustStorePath = Paths.get(rawKeystore);
try (InputStream trustStoreStream = Files.newInputStream(trustStorePath)) {
builder.inputStream(trustStoreStream);
keyStore = builder.build();
}
}

return keyStore;
}

private KeyStore getTrustStore(final Map<String, String> properties) throws IOException {
final String rawTruststore = properties.get(ProviderProperty.TRUSTSTORE.getProperty());
final String rawTruststorePassword = properties.get(ProviderProperty.TRUSTSTORE_PASSWORD.getProperty());
final String rawTruststoreType = properties.get(ProviderProperty.TRUSTSTORE_TYPE.getProperty());

final KeyStore trustStore;

if (rawTruststore == null || rawTruststore.isBlank()) {
trustStore = null;
} else if (rawTruststorePassword == null) {
throw new ProviderCreationException("Truststore Password not configured");
} else {
final StandardKeyStoreBuilder builder = new StandardKeyStoreBuilder();
builder.type(rawTruststoreType);

final char[] trustStorePassword = rawTruststorePassword.toCharArray();
builder.password(trustStorePassword);

final Path trustStorePath = Paths.get(rawTruststore);
try (InputStream trustStoreStream = Files.newInputStream(trustStorePath)) {
builder.inputStream(trustStoreStream);
trustStore = builder.build();
}
}

return trustStore;
}
}
Loading

0 comments on commit a3f4f7b

Please sign in to comment.