NIFI-12125 Refactored Framework SSLContext Loading (#8967)

- Added Framework SSLContext Provider for centralized loading
- Refactored Spring Framework XML to Java Configuration
- Removed Jetty StoreScanner
This commit is contained in:
David Handermann 2024-06-25 15:08:04 -05:00 committed by GitHub
parent 48e8457a8a
commit 42c0aa6aa7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
85 changed files with 2721 additions and 4769 deletions

View File

@ -84,25 +84,17 @@ jobs:
timeout-minutes: 120
runs-on: ${{ matrix.os }}
name: ${{ matrix.os }} Java ${{ matrix.version }}
env:
# default to Amazon Corretto JDK
JAVA_DISTRIBUTION: corretto
steps:
- name: System Information
run: |
hostname
if [ "${{ runner.os }}" = "macOS" ]; then top -l 1 | grep PhysMem && sysctl machdep.cpu; else cat /proc/cpuinfo && cat /proc/meminfo; fi
df
# Zulu JDK appears to be more reliable for MacOS
- name: Use Java distribution Zulu
if: ${{ runner.os == 'macOS' }}
run: echo "JAVA_DISTRIBUTION=zulu" >> "$GITHUB_ENV"
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Java ${{ env.JAVA_DISTRIBUTION }} ${{ matrix.version }}
- name: Set up Java ${{ matrix.version }}
uses: actions/setup-java@v4
with:
distribution: ${{ env.JAVA_DISTRIBUTION }}
distribution: 'zulu'
java-version: ${{ matrix.version }}
cache: 'maven'

View File

@ -14,9 +14,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.spring;
package org.apache.nifi.security.ssl;
import org.apache.nifi.controller.leader.election.StandaloneLeaderElectionManager;
import javax.net.ssl.X509ExtendedKeyManager;
public class MockLeaderElectionManager extends StandaloneLeaderElectionManager {
/**
* Listener abstraction for receiving instances of X.509 Extended Key Manager
*/
public interface KeyManagerListener {
/**
* Set X.509 Extended Key Manager
*
* @param keyManager Key Manager
*/
void setKeyManager(X509ExtendedKeyManager keyManager);
}

View File

@ -35,10 +35,14 @@ public class StandardSslContextBuilder implements SslContextBuilder {
private String protocol = DEFAULT_PROTOCOL;
private KeyManager keyManager;
private KeyStore keyStore;
private char[] keyPassword;
private TrustManager trustManager;
private KeyStore trustStore;
/**
@ -73,6 +77,17 @@ public class StandardSslContextBuilder implements SslContextBuilder {
return this;
}
/**
* Set Kay Manager takes precedence over Key Store
*
* @param keyManager Key Manager
* @return Builder
*/
public StandardSslContextBuilder keyManager(final KeyManager keyManager) {
this.keyManager = Objects.requireNonNull(keyManager, "Key Manager required");
return this;
}
/**
* Set Key Store with Private Key and Certificate Entry
*
@ -95,6 +110,17 @@ public class StandardSslContextBuilder implements SslContextBuilder {
return this;
}
/**
* Set Trust Manager takes precedence over Trust Store
*
* @param trustManager Trust Manager
* @return Builder
*/
public StandardSslContextBuilder trustManager(final TrustManager trustManager) {
this.trustManager = Objects.requireNonNull(trustManager, "Trust Manager required");
return this;
}
/**
* Set Trust Store with Certificate Entries
*
@ -109,10 +135,14 @@ public class StandardSslContextBuilder implements SslContextBuilder {
private KeyManager[] getKeyManagers() {
final KeyManager[] keyManagers;
if (keyStore == null) {
keyManagers = null;
if (keyManager == null) {
keyManagers = null;
} else {
keyManagers = new KeyManager[]{keyManager};
}
} else {
final X509ExtendedKeyManager keyManager = new StandardKeyManagerBuilder().keyStore(keyStore).keyPassword(keyPassword).build();
keyManagers = new KeyManager[]{keyManager};
final X509ExtendedKeyManager configuredKeyManager = new StandardKeyManagerBuilder().keyStore(keyStore).keyPassword(keyPassword).build();
keyManagers = new KeyManager[]{configuredKeyManager};
}
return keyManagers;
}
@ -120,10 +150,14 @@ public class StandardSslContextBuilder implements SslContextBuilder {
private TrustManager[] getTrustManagers() {
final TrustManager[] trustManagers;
if (trustStore == null) {
trustManagers = null;
if (trustManager == null) {
trustManagers = null;
} else {
trustManagers = new TrustManager[]{trustManager};
}
} else {
final X509TrustManager trustManager = new StandardTrustManagerBuilder().trustStore(trustStore).build();
trustManagers = new TrustManager[]{trustManager};
final X509TrustManager configuredTrustManager = new StandardTrustManagerBuilder().trustStore(trustStore).build();
trustManagers = new TrustManager[]{configuredTrustManager};
}
return trustManagers;
}

View File

@ -18,7 +18,7 @@ package org.apache.nifi.security.ssl;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.security.KeyStore;
import java.util.Arrays;
import java.util.Objects;
@ -31,20 +31,20 @@ public class StandardTrustManagerBuilder implements TrustManagerBuilder {
private KeyStore trustStore;
/**
* Build X.509 Trust Manager using configured properties
* Build X.509 Extended Trust Manager using configured properties
*
* @return X.509 Trust Manager
* @return X.509 Extended Trust Manager
*/
@Override
public X509TrustManager build() {
public X509ExtendedTrustManager build() {
final TrustManager[] trustManagers = getTrustManagers();
if (trustManagers == null) {
throw new BuilderConfigurationException("Trust Managers not found: Trust Store required");
}
final Optional<X509TrustManager> configuredTrustManager = Arrays.stream(trustManagers)
.filter(trustManager -> trustManager instanceof X509TrustManager)
.map(trustManager -> (X509TrustManager) trustManager)
final Optional<X509ExtendedTrustManager> configuredTrustManager = Arrays.stream(trustManagers)
.filter(trustManager -> trustManager instanceof X509ExtendedTrustManager)
.map(trustManager -> (X509ExtendedTrustManager) trustManager)
.findFirst();
return configuredTrustManager.orElseThrow(() -> new BuilderConfigurationException("X.509 Trust Manager not found"));

View File

@ -0,0 +1,92 @@
/*
* 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.security.ssl;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedKeyManager;
import java.net.Socket;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
/**
* Standard X.509 Extended Key Manager delegates to configurable wrapped Key Manager
*/
public class StandardX509ExtendedKeyManager extends X509ExtendedKeyManager implements KeyManagerListener {
private final AtomicReference<X509ExtendedKeyManager> keyManagerRef;
/**
* Standard X.509 Extended Key Manager with required delegate Key Manager
*
* @param keyManager Key Manager
*/
public StandardX509ExtendedKeyManager(final X509ExtendedKeyManager keyManager) {
Objects.requireNonNull(keyManager, "Key Manager required");
this.keyManagerRef = new AtomicReference<>(keyManager);
}
/**
* Set X.509 Extended Key Manager to be used for subsequent operations
*
* @param keyManager Key Manager
*/
@Override
public void setKeyManager(final X509ExtendedKeyManager keyManager) {
Objects.requireNonNull(keyManager, "Key Manager required");
keyManagerRef.lazySet(keyManager);
}
@Override
public String[] getClientAliases(final String keyType, final Principal[] issuers) {
return keyManagerRef.get().getClientAliases(keyType, issuers);
}
@Override
public String chooseClientAlias(final String[] keyType, final Principal[] issuers, final Socket socket) {
return keyManagerRef.get().chooseClientAlias(keyType, issuers, socket);
}
public String chooseEngineClientAlias(final String[] keyType, final Principal[] issuers, final SSLEngine engine) {
return keyManagerRef.get().chooseEngineClientAlias(keyType, issuers, engine);
}
@Override
public String[] getServerAliases(final String keyType, final Principal[] issuers) {
return keyManagerRef.get().getServerAliases(keyType, issuers);
}
@Override
public String chooseServerAlias(final String keyType, final Principal[] issuers, final Socket socket) {
return keyManagerRef.get().chooseServerAlias(keyType, issuers, socket);
}
public String chooseEngineServerAlias(final String keyType, final Principal[] issuers, final SSLEngine engine) {
return keyManagerRef.get().chooseEngineServerAlias(keyType, issuers, engine);
}
@Override
public X509Certificate[] getCertificateChain(final String alias) {
return keyManagerRef.get().getCertificateChain(alias);
}
@Override
public PrivateKey getPrivateKey(final String alias) {
return keyManagerRef.get().getPrivateKey(alias);
}
}

View File

@ -0,0 +1,88 @@
/*
* 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.security.ssl;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedTrustManager;
import java.net.Socket;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
/**
* Standard X.509 Extended Trust Manager delegates to configurable wrapped Trust Manager
*/
public class StandardX509ExtendedTrustManager extends X509ExtendedTrustManager implements TrustManagerListener {
private final AtomicReference<X509ExtendedTrustManager> trustManagerRef;
/**
* Standard X.509 Extended Trust Manager with required delegate Trust Manager
*
* @param trustManager Trust Manager
*/
public StandardX509ExtendedTrustManager(final X509ExtendedTrustManager trustManager) {
Objects.requireNonNull(trustManager, "Trust Manager required");
trustManagerRef = new AtomicReference<>(trustManager);
}
/**
* Set X.509 Extended Trust Manager to be used for subsequent operations
*
* @param trustManager Trust Manager
*/
@Override
public void setTrustManager(final X509ExtendedTrustManager trustManager) {
Objects.requireNonNull(trustManager, "Trust Manager required");
trustManagerRef.lazySet(trustManager);
}
@Override
public void checkClientTrusted(final X509Certificate[] chain, final String authType, final Socket socket) throws CertificateException {
trustManagerRef.get().checkClientTrusted(chain, authType, socket);
}
@Override
public void checkServerTrusted(final X509Certificate[] chain, final String authType, final Socket socket) throws CertificateException {
trustManagerRef.get().checkServerTrusted(chain, authType, socket);
}
@Override
public void checkClientTrusted(final X509Certificate[] chain, final String authType, final SSLEngine engine) throws CertificateException {
trustManagerRef.get().checkClientTrusted(chain, authType, engine);
}
@Override
public void checkServerTrusted(final X509Certificate[] chain, final String authType, final SSLEngine engine) throws CertificateException {
trustManagerRef.get().checkServerTrusted(chain, authType, engine);
}
@Override
public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
trustManagerRef.get().checkClientTrusted(chain, authType);
}
@Override
public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
trustManagerRef.get().checkServerTrusted(chain, authType);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return trustManagerRef.get().getAcceptedIssuers();
}
}

View File

@ -16,16 +16,16 @@
*/
package org.apache.nifi.security.ssl;
import javax.net.ssl.X509TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;
/**
* Builder interface for instances of java.security.ssl.X509TrustManager
* Builder interface for instances of java.security.ssl.X509ExtendedTrustManager
*/
public interface TrustManagerBuilder {
/**
* Build X.509 Trust Manager using configured properties
* Build X.509 Extended Trust Manager using configured properties
*
* @return X.509 Trust Manager
* @return X.509 Extended Trust Manager
*/
X509TrustManager build();
X509ExtendedTrustManager build();
}

View File

@ -14,23 +14,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.web.security.saml2.registration;
package org.apache.nifi.security.ssl;
import org.springframework.security.saml2.core.Saml2X509Credential;
import java.security.KeyStore;
import java.util.Collection;
import javax.net.ssl.X509ExtendedTrustManager;
/**
* SAML2 Credentials Provider translates Certificate and Key entries to SAML2 X.509 Credentials
* Listener abstraction for receiving instances of X.509 Extended Trust Manager
*/
public interface Saml2CredentialProvider {
public interface TrustManagerListener {
/**
* Get SAML2 X.509 Credentials from Key Store entries
* Set X.509 Extended Trust Manager
*
* @param keyStore Key Store containing credentials
* @param keyPassword Optional key password for loading Private Keys
* @return Collection of SAML2 Credentials
* @param trustManager Trust Manager
*/
Collection<Saml2X509Credential> getCredentials(KeyStore keyStore, char[] keyPassword);
void setTrustManager(X509ExtendedTrustManager trustManager);
}

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.security.ssl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.net.Socket;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedKeyManager;
import java.security.Principal;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class StandardX509ExtendedKeyManagerTest {
private static final String KEY_TYPE = "RSA";
private static final String ALIAS = "alias";
private static final Principal[] ISSUERS = new Principal[]{};
@Mock
private X509ExtendedKeyManager extendedKeyManager;
@Mock
private X509ExtendedKeyManager updatedKeyManager;
@Mock
private Socket socket;
@Mock
private SSLEngine sslEngine;
private StandardX509ExtendedKeyManager manager;
@BeforeEach
void setManager() {
manager = new StandardX509ExtendedKeyManager(extendedKeyManager);
}
@Test
void testGetClientAlias() {
manager.getClientAliases(KEY_TYPE, ISSUERS);
verify(extendedKeyManager).getClientAliases(eq(KEY_TYPE), eq(ISSUERS));
}
@Test
void testChooseClientAlias() {
final String[] keyTypes = new String[]{KEY_TYPE};
manager.chooseClientAlias(keyTypes, ISSUERS, socket);
verify(extendedKeyManager).chooseClientAlias(eq(keyTypes), eq(ISSUERS), eq(socket));
}
@Test
void testChooseEngineClientAlias() {
final String[] keyTypes = new String[]{KEY_TYPE};
manager.chooseEngineClientAlias(keyTypes, ISSUERS, sslEngine);
verify(extendedKeyManager).chooseEngineClientAlias(eq(keyTypes), eq(ISSUERS), eq(sslEngine));
}
@Test
void testChooseServerAlias() {
manager.chooseServerAlias(KEY_TYPE, ISSUERS, socket);
verify(extendedKeyManager).chooseServerAlias(eq(KEY_TYPE), eq(ISSUERS), eq(socket));
}
@Test
void testChooseEngineServerAlias() {
manager.chooseEngineServerAlias(KEY_TYPE, ISSUERS, sslEngine);
verify(extendedKeyManager).chooseEngineServerAlias(eq(KEY_TYPE), eq(ISSUERS), eq(sslEngine));
}
@Test
void testGetPrivateKey() {
manager.getPrivateKey(ALIAS);
verify(extendedKeyManager).getPrivateKey(eq(ALIAS));
}
@Test
void testGetCertificateChain() {
manager.getCertificateChain(ALIAS);
verify(extendedKeyManager).getCertificateChain(eq(ALIAS));
}
@Test
void testGetServerAliases() {
manager.getServerAliases(KEY_TYPE, ISSUERS);
verify(extendedKeyManager).getServerAliases(KEY_TYPE, ISSUERS);
manager.setKeyManager(updatedKeyManager);
manager.getServerAliases(KEY_TYPE, ISSUERS);
verify(updatedKeyManager).getServerAliases(KEY_TYPE, ISSUERS);
}
}

View File

@ -0,0 +1,116 @@
/*
* 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.security.ssl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedTrustManager;
import java.net.Socket;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class StandardX509ExtendedTrustManagerTest {
private static final String AUTH_TYPE = "AUTH";
private static final String ALIAS = "alias";
private static final X509Certificate[] certificates = new X509Certificate[]{};
@Mock
private X509ExtendedTrustManager extendedTrustManager;
@Mock
private X509ExtendedTrustManager updatedTrustManager;
@Mock
private Socket socket;
@Mock
private SSLEngine sslEngine;
private StandardX509ExtendedTrustManager manager;
@BeforeEach
void setManager() {
manager = new StandardX509ExtendedTrustManager(extendedTrustManager);
}
@Test
void testCheckClientTrusted() throws CertificateException {
manager.checkClientTrusted(certificates, AUTH_TYPE);
verify(extendedTrustManager).checkClientTrusted(eq(certificates), eq(AUTH_TYPE));
}
@Test
void testCheckClientTrustedSocket() throws CertificateException {
manager.checkClientTrusted(certificates, AUTH_TYPE, socket);
verify(extendedTrustManager).checkClientTrusted(eq(certificates), eq(AUTH_TYPE), eq(socket));
}
@Test
void testCheckClientTrustedEngine() throws CertificateException {
manager.checkClientTrusted(certificates, AUTH_TYPE, sslEngine);
verify(extendedTrustManager).checkClientTrusted(eq(certificates), eq(AUTH_TYPE), eq(sslEngine));
}
@Test
void testCheckServerTrusted() throws CertificateException {
manager.checkServerTrusted(certificates, AUTH_TYPE);
verify(extendedTrustManager).checkServerTrusted(eq(certificates), eq(AUTH_TYPE));
}
@Test
void testCheckServerTrustedSocket() throws CertificateException {
manager.checkServerTrusted(certificates, AUTH_TYPE, socket);
verify(extendedTrustManager).checkServerTrusted(eq(certificates), eq(AUTH_TYPE), eq(socket));
}
@Test
void testCheckServerTrustedEngine() throws CertificateException {
manager.checkServerTrusted(certificates, AUTH_TYPE, sslEngine);
verify(extendedTrustManager).checkServerTrusted(eq(certificates), eq(AUTH_TYPE), eq(sslEngine));
}
@Test
void testGetAcceptedIssuers() {
manager.getAcceptedIssuers();
verify(extendedTrustManager).getAcceptedIssuers();
manager.setTrustManager(updatedTrustManager);
manager.getAcceptedIssuers();
verify(updatedTrustManager).getAcceptedIssuers();
}
}

View File

@ -50,15 +50,6 @@
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-properties</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-property-utils</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@ -17,14 +17,12 @@
package org.apache.nifi.security.util;
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
import org.apache.nifi.util.NiFiProperties;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
/**
* This class serves as a concrete immutable domain object (acting as an internal DTO)
@ -168,61 +166,6 @@ public class StandardTlsConfiguration implements TlsConfiguration {
this.protocol = other.getProtocol();
}
// Static factory method from NiFiProperties
/**
* Returns a {@link org.apache.nifi.security.util.TlsConfiguration} instantiated from the relevant NiFi properties.
*
* @param niFiProperties the NiFi properties
* @return a populated TlsConfiguration container object
*/
public static TlsConfiguration fromNiFiProperties(final NiFiProperties niFiProperties) {
final Properties properties = new Properties();
niFiProperties.getPropertyKeys().forEach(key -> properties.setProperty(key, niFiProperties.getProperty(key)));
return fromNiFiProperties(properties);
}
/**
* Returns a {@link org.apache.nifi.security.util.TlsConfiguration} instantiated from the relevant NiFi properties.
*
* @param niFiProperties the NiFi properties, as a simple java.util.Properties object
* @return a populated TlsConfiguration container object
*/
public static TlsConfiguration fromNiFiProperties(final Properties niFiProperties) {
Objects.requireNonNull(niFiProperties, "Properties required");
return new StandardTlsConfiguration(
niFiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE),
niFiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD),
niFiProperties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD),
niFiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE),
niFiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE),
niFiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD),
niFiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE),
TLS_PROTOCOL_VERSION
);
}
/**
* Returns a {@link org.apache.nifi.security.util.TlsConfiguration} instantiated
* from the relevant {@link NiFiProperties} properties for the truststore
* <em>only</em>. No keystore properties are read or used.
*
* @param niFiProperties the NiFi properties
* @return a populated TlsConfiguration container object
*/
public static StandardTlsConfiguration fromNiFiPropertiesTruststoreOnly(final NiFiProperties niFiProperties) {
Objects.requireNonNull(niFiProperties, "Properties required");
return new StandardTlsConfiguration(
null,
null,
null,
null,
niFiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE),
niFiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD),
niFiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE),
TLS_PROTOCOL_VERSION);
}
@Override
public String getKeystorePath() {
return keystorePath;

View File

@ -16,19 +16,14 @@
*/
package org.apache.nifi.security.util;
import org.apache.nifi.util.NiFiProperties;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class StandardTlsConfigurationTest {
@ -77,55 +72,6 @@ public class StandardTlsConfigurationTest {
assertEquals(new StandardTlsConfiguration().hashCode(), configuration.hashCode());
}
@Test
public void testFromNiFiPropertiesEmptyProperties() {
final NiFiProperties niFiProperties = NiFiProperties.createBasicNiFiProperties(null);
final TlsConfiguration configuration = StandardTlsConfiguration.fromNiFiProperties(niFiProperties);
assertNotNull(configuration);
assertEquals(PROTOCOL, configuration.getProtocol());
}
@Test
public void testFromNiFiProperties() {
final Map<String, String> properties = new HashMap<>();
properties.put(NiFiProperties.SECURITY_KEYSTORE, KEY_STORE_PATH);
properties.put(NiFiProperties.SECURITY_KEYSTORE_PASSWD, KEY_STORE_PASSWORD);
properties.put(NiFiProperties.SECURITY_KEY_PASSWD, KEY_PASSWORD);
properties.put(NiFiProperties.SECURITY_KEYSTORE_TYPE, KEY_STORE_TYPE);
properties.put(NiFiProperties.SECURITY_TRUSTSTORE, TRUST_STORE_PATH);
properties.put(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, TRUST_STORE_PASSWORD);
properties.put(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, TRUST_STORE_TYPE);
final NiFiProperties niFiProperties = NiFiProperties.createBasicNiFiProperties(null, properties);
final TlsConfiguration configuration = StandardTlsConfiguration.fromNiFiProperties(niFiProperties);
assertNotNull(configuration);
assertEquals(PROTOCOL, configuration.getProtocol());
assertEquals(KEY_STORE_PATH, configuration.getKeystorePath());
assertEquals(KEY_STORE_PASSWORD, configuration.getKeystorePassword());
assertEquals(KEY_PASSWORD, configuration.getKeyPassword());
assertEquals(KEY_STORE_TYPE, configuration.getKeystoreType().getType());
assertEquals(TRUST_STORE_PATH, configuration.getTruststorePath());
assertEquals(TRUST_STORE_PASSWORD, configuration.getTruststorePassword());
assertEquals(TRUST_STORE_TYPE, configuration.getTruststoreType().getType());
}
@Test
public void testFromNiFiPropertiesTrustStoreProperties() {
final Map<String, String> properties = new HashMap<>();
properties.put(NiFiProperties.SECURITY_TRUSTSTORE, TRUST_STORE_PATH);
properties.put(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, TRUST_STORE_PASSWORD);
properties.put(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, TRUST_STORE_TYPE);
final NiFiProperties niFiProperties = NiFiProperties.createBasicNiFiProperties(null, properties);
final TlsConfiguration configuration = StandardTlsConfiguration.fromNiFiPropertiesTruststoreOnly(niFiProperties);
assertNotNull(configuration);
assertNull(configuration.getKeystorePath());
assertEquals(PROTOCOL, configuration.getProtocol());
assertEquals(TRUST_STORE_PATH, configuration.getTruststorePath());
assertEquals(TRUST_STORE_PASSWORD, configuration.getTruststorePassword());
assertEquals(TRUST_STORE_TYPE, configuration.getTruststoreType().getType());
}
@Test
public void testFunctionalKeyPasswordFromKeyStorePassword() {
final TlsConfiguration configuration = new StandardTlsConfiguration(

View File

@ -17,9 +17,6 @@
package org.apache.nifi.io.socket;
import javax.net.ssl.SSLContext;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException;
public final class ServerSocketConfiguration {
@ -27,19 +24,19 @@ public final class ServerSocketConfiguration {
private Integer socketTimeout;
private Boolean reuseAddress;
private Integer receiveBufferSize;
private TlsConfiguration tlsConfiguration;
private SSLContext sslContext;
public SSLContext getSslContext() {
return sslContext;
}
public void setSslContext(final SSLContext sslContext) {
this.sslContext = sslContext;
}
public ServerSocketConfiguration() {
}
public SSLContext createSSLContext() throws TlsException {
return SslContextFactory.createSslContext(tlsConfiguration);
}
public void setTlsConfiguration(final TlsConfiguration tlsConfiguration) {
this.tlsConfiguration = tlsConfiguration;
}
public Integer getSocketTimeout() {
return socketTimeout;
}

View File

@ -17,9 +17,6 @@
package org.apache.nifi.io.socket;
import javax.net.ssl.SSLContext;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException;
public final class SocketConfiguration {
@ -31,14 +28,14 @@ public final class SocketConfiguration {
private Boolean oobInline;
private Boolean tcpNoDelay;
private Integer trafficClass;
private TlsConfiguration tlsConfiguration;
private SSLContext sslContext;
public SSLContext createSSLContext() throws TlsException {
return SslContextFactory.createSslContext(tlsConfiguration);
public SSLContext getSslContext() {
return sslContext;
}
public void setTlsConfiguration(final TlsConfiguration tlsConfiguration) {
this.tlsConfiguration = tlsConfiguration;
public void setSslContext(final SSLContext sslContext) {
this.sslContext = sslContext;
}
public Integer getSocketTimeout() {

View File

@ -51,13 +51,7 @@ public final class SocketUtils {
final Socket socket;
final SSLContext sslContext;
try {
sslContext = config.createSSLContext();
} catch (final Exception e) {
throw new IOException("Could not create SSLContext", e);
}
final SSLContext sslContext = config.getSslContext();
if (sslContext == null) {
socket = new Socket(address.getHostName(), address.getPort());
} else {
@ -121,7 +115,7 @@ public final class SocketUtils {
throw new NullPointerException("Configuration may not be null.");
}
final SSLContext sslContext = config.createSSLContext();
final SSLContext sslContext = config.getSslContext();
final ServerSocket serverSocket;
if (sslContext == null) {
serverSocket = new ServerSocket(port);

View File

@ -67,6 +67,10 @@
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>

View File

@ -0,0 +1,52 @@
/*
* 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.framework.configuration;
import org.apache.nifi.authorization.AuthorizerFactoryBean;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.util.NiFiProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Objects;
/**
* Application Configuration for Authorizer
*/
@Configuration
public class AuthorizerConfiguration {
private final NiFiProperties properties;
private final ExtensionManager extensionManager;
public AuthorizerConfiguration(
final NiFiProperties properties,
final ExtensionManager extensionManager
) {
this.properties = Objects.requireNonNull(properties, "Properties required");
this.extensionManager = Objects.requireNonNull(extensionManager, "Extension Manager required");
}
@Bean
public AuthorizerFactoryBean authorizer() {
final AuthorizerFactoryBean authorizerFactoryBean = new AuthorizerFactoryBean();
authorizerFactoryBean.setProperties(properties);
authorizerFactoryBean.setExtensionManager(extensionManager);
return authorizerFactoryBean;
}
}

View File

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<beans default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<!-- user/entity authorizer -->
<bean id="authorizer" class="org.apache.nifi.authorization.AuthorizerFactoryBean">
<property name="properties" ref="nifiProperties"/>
<property name="extensionManager" ref="extensionManager" />
</bean>
</beans>

View File

@ -90,6 +90,10 @@
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jakarta-xmlbind-annotations</artifactId>

View File

@ -1,67 +0,0 @@
/*
* 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.cluster.protocol.spring;
import java.util.concurrent.TimeUnit;
import org.apache.nifi.io.socket.ServerSocketConfiguration;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.util.NiFiProperties;
import org.springframework.beans.factory.FactoryBean;
/**
* Factory bean for creating a singleton ServerSocketConfiguration instance.
*/
public class ServerSocketConfigurationFactoryBean implements FactoryBean<ServerSocketConfiguration> {
private ServerSocketConfiguration configuration;
private NiFiProperties properties;
@Override
public ServerSocketConfiguration getObject() throws Exception {
if (configuration == null) {
configuration = new ServerSocketConfiguration();
configuration.setNeedClientAuth(true);
final int timeout = (int) FormatUtils.getPreciseTimeDuration(properties.getClusterNodeReadTimeout(), TimeUnit.MILLISECONDS);
configuration.setSocketTimeout(timeout);
configuration.setReuseAddress(true);
// If the cluster protocol is marked as secure
if (Boolean.parseBoolean(properties.getProperty(NiFiProperties.CLUSTER_PROTOCOL_IS_SECURE))) {
// Parse the TLS configuration from the properties
configuration.setTlsConfiguration(StandardTlsConfiguration.fromNiFiProperties(properties));
}
}
return configuration;
}
@Override
public Class<ServerSocketConfiguration> getObjectType() {
return ServerSocketConfiguration.class;
}
@Override
public boolean isSingleton() {
return true;
}
public void setProperties(NiFiProperties properties) {
this.properties = properties;
}
}

View File

@ -1,67 +0,0 @@
/*
* 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.cluster.protocol.spring;
import java.util.concurrent.TimeUnit;
import org.apache.nifi.io.socket.SocketConfiguration;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.util.NiFiProperties;
import org.springframework.beans.factory.FactoryBean;
/**
* Factory bean for creating a singleton SocketConfiguration instance.
*/
public class SocketConfigurationFactoryBean implements FactoryBean<SocketConfiguration> {
private SocketConfiguration configuration;
private NiFiProperties properties;
@Override
public SocketConfiguration getObject() throws Exception {
if (configuration == null) {
configuration = new SocketConfiguration();
final int timeout = (int) FormatUtils.getPreciseTimeDuration(properties.getClusterNodeReadTimeout(), TimeUnit.MILLISECONDS);
configuration.setSocketTimeout(timeout);
configuration.setReuseAddress(true);
// If the cluster protocol is marked as secure
if (Boolean.parseBoolean(properties.getProperty(NiFiProperties.CLUSTER_PROTOCOL_IS_SECURE))) {
// Parse the TLS configuration from the properties
configuration.setTlsConfiguration(StandardTlsConfiguration.fromNiFiProperties(properties));
}
}
return configuration;
}
@Override
public Class<SocketConfiguration> getObjectType() {
return SocketConfiguration.class;
}
@Override
public boolean isSingleton() {
return true;
}
public void setProperties(NiFiProperties properties) {
this.properties = properties;
}
}

View File

@ -0,0 +1,149 @@
/*
* 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.framework.configuration;
import org.apache.nifi.cluster.coordination.node.LeaderElectionNodeProtocolSender;
import org.apache.nifi.cluster.protocol.ClusterCoordinationProtocolSender;
import org.apache.nifi.cluster.protocol.NodeProtocolSender;
import org.apache.nifi.cluster.protocol.ProtocolListener;
import org.apache.nifi.cluster.protocol.impl.ClusterCoordinationProtocolSenderListener;
import org.apache.nifi.cluster.protocol.impl.NodeProtocolSenderListener;
import org.apache.nifi.cluster.protocol.impl.SocketProtocolListener;
import org.apache.nifi.cluster.protocol.impl.StandardClusterCoordinationProtocolSender;
import org.apache.nifi.cluster.protocol.jaxb.JaxbProtocolContext;
import org.apache.nifi.cluster.protocol.jaxb.message.JaxbProtocolUtils;
import org.apache.nifi.cluster.protocol.message.ProtocolMessage;
import org.apache.nifi.controller.leader.election.LeaderElectionManager;
import org.apache.nifi.io.socket.ServerSocketConfiguration;
import org.apache.nifi.io.socket.SocketConfiguration;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.util.NiFiProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.net.ssl.SSLContext;
import java.util.concurrent.TimeUnit;
/**
* Framework Cluster Protocol Configuration with components supporting cluster communication
*/
@Configuration
public class FrameworkClusterProtocolConfiguration {
private NiFiProperties properties;
private SSLContext sslContext;
private LeaderElectionManager leaderElectionManager;
@Autowired
public void setProperties(final NiFiProperties properties) {
this.properties = properties;
}
@Autowired(required = false)
public void setSslContext(final SSLContext sslContext) {
this.sslContext = sslContext;
}
@Autowired
public void setLeaderElectionManager(final LeaderElectionManager leaderElectionManager) {
this.leaderElectionManager = leaderElectionManager;
}
@Bean
public JaxbProtocolContext<ProtocolMessage> protocolContext() {
return new JaxbProtocolContext<>(JaxbProtocolUtils.JAXB_CONTEXT);
}
@Bean
public SocketConfiguration protocolSocketConfiguration() {
final SocketConfiguration configuration = new SocketConfiguration();
final int timeout = (int) FormatUtils.getPreciseTimeDuration(properties.getClusterNodeReadTimeout(), TimeUnit.MILLISECONDS);
configuration.setSocketTimeout(timeout);
configuration.setReuseAddress(true);
configuration.setSslContext(sslContext);
return configuration;
}
@Bean
public ServerSocketConfiguration protocolServerSocketConfiguration() {
final ServerSocketConfiguration configuration = new ServerSocketConfiguration();
configuration.setNeedClientAuth(true);
final int timeout = (int) FormatUtils.getPreciseTimeDuration(properties.getClusterNodeReadTimeout(), TimeUnit.MILLISECONDS);
configuration.setSocketTimeout(timeout);
configuration.setReuseAddress(true);
configuration.setSslContext(sslContext);
return configuration;
}
@Bean
public ClusterCoordinationProtocolSender clusterCoordinationProtocolSender() {
final StandardClusterCoordinationProtocolSender sender = new StandardClusterCoordinationProtocolSender(
protocolSocketConfiguration(),
protocolContext(),
properties.getClusterNodeProtocolMaxPoolSize()
);
sender.setHandshakeTimeout(properties.getClusterNodeConnectionTimeout());
return sender;
}
@Bean
public ClusterCoordinationProtocolSenderListener clusterCoordinationProtocolSenderListener() {
return new ClusterCoordinationProtocolSenderListener(
clusterCoordinationProtocolSender(),
protocolListener()
);
}
@Bean
public ProtocolListener protocolListener() {
final Integer protocolPort = properties.getClusterNodeProtocolPort();
final int clusterNodeProtocolPort = protocolPort == null ? 0 : protocolPort;
return new SocketProtocolListener(
properties.getClusterNodeProtocolMaxPoolSize(),
clusterNodeProtocolPort,
protocolServerSocketConfiguration(),
protocolContext()
);
}
@Bean
public NodeProtocolSenderListener nodeProtocolSenderListener() {
return new NodeProtocolSenderListener(
nodeProtocolSender(),
protocolListener()
);
}
@Bean
public NodeProtocolSender nodeProtocolSender() {
return new LeaderElectionNodeProtocolSender(
protocolSocketConfiguration(),
protocolContext(),
leaderElectionManager
);
}
}

View File

@ -1,90 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<!-- marked as lazy so that cluster protocol beans are not created when applications runs in standalone mode -->
<beans default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd">
<!-- protocol context -->
<bean id="protocolContext" class="org.apache.nifi.cluster.protocol.jaxb.JaxbProtocolContext">
<constructor-arg>
<util:constant static-field="org.apache.nifi.cluster.protocol.jaxb.message.JaxbProtocolUtils.JAXB_CONTEXT"/>
</constructor-arg>
</bean>
<!-- socket configuration -->
<bean id="protocolSocketConfiguration" class="org.apache.nifi.cluster.protocol.spring.SocketConfigurationFactoryBean">
<property name="properties" ref="nifiProperties"/>
</bean>
<!-- server socket configuration -->
<bean id="protocolServerSocketConfiguration" class="org.apache.nifi.cluster.protocol.spring.ServerSocketConfigurationFactoryBean">
<property name="properties" ref="nifiProperties"/>
</bean>
<!-- cluster manager protocol sender -->
<bean id="clusterCoordinationProtocolSender" class="org.apache.nifi.cluster.protocol.impl.StandardClusterCoordinationProtocolSender">
<constructor-arg ref="protocolSocketConfiguration"/>
<constructor-arg ref="protocolContext"/>
<constructor-arg>
<bean factory-bean="nifiProperties" factory-method="getClusterNodeProtocolMaxPoolSize"/>
</constructor-arg>
<property name="handshakeTimeout">
<bean factory-bean="nifiProperties" factory-method="getClusterNodeConnectionTimeout"/>
</property>
</bean>
<!-- cluster manager sender/listener -->
<bean id="clusterCoordinationProtocolSenderListener" class="org.apache.nifi.cluster.protocol.impl.ClusterCoordinationProtocolSenderListener">
<constructor-arg ref="clusterCoordinationProtocolSender"/>
<constructor-arg ref="protocolListener"/>
</bean>
<!-- node protocol sender -->
<!--
<bean id="nodeProtocolSender" class="org.apache.nifi.cluster.coordination.node.CuratorNodeProtocolSender">
<constructor-arg ref="protocolSocketConfiguration"/>
<constructor-arg ref="protocolContext"/>
<constructor-arg ref="nifiProperties"/>
</bean>
-->
<bean id="nodeProtocolSender" class="org.apache.nifi.cluster.coordination.node.LeaderElectionNodeProtocolSender">
<constructor-arg ref="protocolSocketConfiguration"/>
<constructor-arg ref="protocolContext"/>
<constructor-arg ref="leaderElectionManager"/>
</bean>
<!-- protocol listener -->
<bean id="protocolListener" class="org.apache.nifi.cluster.protocol.impl.SocketProtocolListener">
<constructor-arg index="0">
<bean factory-bean="nifiProperties" factory-method="getClusterNodeProtocolMaxPoolSize"/>
</constructor-arg>
<constructor-arg index="1">
<bean factory-bean="nifiProperties" factory-method="getClusterNodeProtocolPort"/>
</constructor-arg>
<constructor-arg ref="protocolServerSocketConfiguration" index="2"/>
<constructor-arg ref="protocolContext" index="3"/>
</bean>
<!-- node sender/listener -->
<bean id="nodeProtocolSenderListener" class="org.apache.nifi.cluster.protocol.impl.NodeProtocolSenderListener">
<constructor-arg ref="nodeProtocolSender"/>
<constructor-arg ref="protocolListener"/>
</bean>
</beans>

View File

@ -1,61 +0,0 @@
/*
* 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.cluster.coordination.flow;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.util.NiFiProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;
import java.util.concurrent.TimeUnit;
public class PopularVoteFlowElectionFactoryBean implements FactoryBean<PopularVoteFlowElection> {
private static final Logger logger = LoggerFactory.getLogger(PopularVoteFlowElectionFactoryBean.class);
private NiFiProperties properties;
@Override
public PopularVoteFlowElection getObject() {
final String maxWaitTime = properties.getFlowElectionMaxWaitTime();
long maxWaitMillis;
try {
maxWaitMillis = FormatUtils.getTimeDuration(maxWaitTime, TimeUnit.MILLISECONDS);
} catch (final Exception e) {
logger.warn("Failed to parse value of property '{}' as a valid time period. Value was '{}'. Ignoring this value and using the default value of '{}'",
NiFiProperties.FLOW_ELECTION_MAX_WAIT_TIME, maxWaitTime, NiFiProperties.DEFAULT_FLOW_ELECTION_MAX_WAIT_TIME);
maxWaitMillis = FormatUtils.getTimeDuration(NiFiProperties.DEFAULT_FLOW_ELECTION_MAX_WAIT_TIME, TimeUnit.MILLISECONDS);
}
final Integer maxNodes = properties.getFlowElectionMaxCandidates();
return new PopularVoteFlowElection(maxWaitMillis, TimeUnit.MILLISECONDS, maxNodes);
}
@Override
public Class<?> getObjectType() {
return PopularVoteFlowElection.class;
}
@Override
public boolean isSingleton() {
return true;
}
public void setProperties(final NiFiProperties properties) {
this.properties = properties;
}
}

View File

@ -58,6 +58,8 @@ import org.slf4j.LoggerFactory;
import jakarta.ws.rs.HttpMethod;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import java.io.Closeable;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@ -89,7 +91,7 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ThreadPoolRequestReplicator implements RequestReplicator {
public class ThreadPoolRequestReplicator implements RequestReplicator, Closeable {
private static final Logger logger = LoggerFactory.getLogger(ThreadPoolRequestReplicator.class);
private static final Pattern SNIPPET_URI_PATTERN = Pattern.compile("/nifi-api/snippets/[a-f0-9\\-]{36}");
@ -165,6 +167,11 @@ public class ThreadPoolRequestReplicator implements RequestReplicator {
maintenanceExecutor.scheduleWithFixedDelay(this::purgeExpiredRequests, 1, 1, TimeUnit.SECONDS);
}
@Override
public void close() {
shutdown();
}
@Override
public void shutdown() {
executorService.shutdown();

View File

@ -38,7 +38,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.GZIPInputStream;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import jakarta.ws.rs.HttpMethod;
import jakarta.ws.rs.core.MultivaluedHashMap;
@ -56,9 +56,6 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.cluster.coordination.http.replication.HttpReplicationClient;
import org.apache.nifi.cluster.coordination.http.replication.PreparedRequest;
import org.apache.nifi.remote.protocol.http.HttpHeaders;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.stream.io.GZIPOutputStream;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.util.NiFiProperties;
@ -75,15 +72,22 @@ public class OkHttpReplicationClient implements HttpReplicationClient {
private final ObjectMapper jsonCodec = new ObjectMapper();
private final OkHttpClient okHttpClient;
private boolean tlsConfigured = false;
private final SSLContext sslContext;
private final X509TrustManager trustManager;
public OkHttpReplicationClient(final NiFiProperties properties) {
public OkHttpReplicationClient(
final NiFiProperties properties,
final SSLContext sslContext,
final X509TrustManager trustManager
) {
jsonCodec.setDefaultPropertyInclusion(Value.construct(Include.NON_NULL, Include.ALWAYS));
jsonCodec.setAnnotationIntrospector(new JakartaXmlBindAnnotationIntrospector(jsonCodec.getTypeFactory()));
jsonSerializer = new JsonEntitySerializer(jsonCodec);
xmlSerializer = new XmlEntitySerializer();
this.sslContext = sslContext;
this.trustManager = trustManager;
okHttpClient = createOkHttpClient(properties);
}
@ -142,16 +146,6 @@ public class OkHttpReplicationClient implements HttpReplicationClient {
return response;
}
/**
* Returns {@code true} if the client has TLS enabled and configured. Even clients created without explicit
* keystore and truststore values have a default cipher suite list available, but no keys to use.
*
* @return true if this client can present keys
*/
public boolean isTLSConfigured() {
return tlsConfigured;
}
private MultivaluedMap<String, String> getHeaders(final okhttp3.Response callResponse) {
final Headers headers = callResponse.headers();
final MultivaluedMap<String, String> headerMap = new MultivaluedHashMap<>();
@ -323,17 +317,9 @@ public class OkHttpReplicationClient implements HttpReplicationClient {
okHttpClientBuilder.connectionPool(new ConnectionPool(connectionPoolSize, 5, TimeUnit.MINUTES));
okHttpClientBuilder.eventListener(new RequestReplicationEventListener());
// Apply the TLS configuration, if present
try {
final TlsConfiguration tlsConfiguration = StandardTlsConfiguration.fromNiFiProperties(properties);
final X509TrustManager trustManager = SslContextFactory.getX509TrustManager(tlsConfiguration);
final SSLContext sslContext = SslContextFactory.createSslContext(tlsConfiguration, new TrustManager[]{trustManager});
okHttpClientBuilder.sslSocketFactory(sslContext.getSocketFactory(), trustManager);
tlsConfigured = true;
} catch (Exception e) {
// Legacy expectations around this client are that it does not throw an exception on invalid TLS configuration
// TODO: The only current use of this class is ThreadPoolRequestReplicatorFactoryBean#getObject() which should be evaluated to see if that can change
tlsConfigured = false;
if (sslContext != null) {
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
okHttpClientBuilder.sslSocketFactory(sslSocketFactory, trustManager);
}
return okHttpClientBuilder.build();

View File

@ -63,10 +63,7 @@ import org.apache.nifi.components.state.StateManager;
import org.apache.nifi.components.state.StateManagerProvider;
import org.apache.nifi.components.state.StateMap;
import org.apache.nifi.controller.leader.election.LeaderElectionManager;
import org.apache.nifi.controller.state.manager.StandardStateManagerProvider;
import org.apache.nifi.events.EventReporter;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.parameter.ParameterLookup;
import org.apache.nifi.reporting.Severity;
import org.apache.nifi.services.FlowService;
import org.apache.nifi.util.NiFiProperties;
@ -127,16 +124,17 @@ public class NodeClusterCoordinator implements ClusterCoordinator, ProtocolHandl
private final List<ClusterTopologyEventListener> eventListeners = new CopyOnWriteArrayList<>();
public NodeClusterCoordinator(final ClusterCoordinationProtocolSenderListener senderListener, final EventReporter eventReporter, final LeaderElectionManager leaderElectionManager,
final FlowElection flowElection, final ClusterNodeFirewall firewall, final RevisionManager revisionManager, final NiFiProperties nifiProperties,
final ExtensionManager extensionManager, final NodeProtocolSender nodeProtocolSender) throws IOException {
this(senderListener, eventReporter, leaderElectionManager, flowElection, firewall, revisionManager, nifiProperties, nodeProtocolSender,
StandardStateManagerProvider.create(nifiProperties, extensionManager, ParameterLookup.EMPTY));
}
public NodeClusterCoordinator(final ClusterCoordinationProtocolSenderListener senderListener, final EventReporter eventReporter, final LeaderElectionManager leaderElectionManager,
final FlowElection flowElection, final ClusterNodeFirewall firewall, final RevisionManager revisionManager, final NiFiProperties nifiProperties,
final NodeProtocolSender nodeProtocolSender, final StateManagerProvider stateManagerProvider) throws IOException {
public NodeClusterCoordinator(
final ClusterCoordinationProtocolSenderListener senderListener,
final EventReporter eventReporter,
final LeaderElectionManager leaderElectionManager,
final FlowElection flowElection,
final ClusterNodeFirewall firewall,
final RevisionManager revisionManager,
final NiFiProperties nifiProperties,
final NodeProtocolSender nodeProtocolSender,
final StateManagerProvider stateManagerProvider
) throws IOException {
this.senderListener = senderListener;
this.flowService = null;
this.eventReporter = eventReporter;

View File

@ -1,66 +0,0 @@
/*
* 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.cluster.spring;
import java.io.File;
import org.apache.nifi.cluster.firewall.ClusterNodeFirewall;
import org.apache.nifi.cluster.firewall.impl.FileBasedClusterNodeFirewall;
import org.apache.nifi.util.NiFiProperties;
import org.springframework.beans.factory.FactoryBean;
/**
* Factory bean for creating a singleton FileBasedClusterNodeFirewall instance.
*/
public class FileBasedClusterNodeFirewallFactoryBean implements FactoryBean<ClusterNodeFirewall> {
private ClusterNodeFirewall firewall;
private NiFiProperties properties;
/**
* Get Cluster Node Firewall should return null when firewall file is not configured
*
* @return Cluster Node Firewall or null when file not configured
* @throws Exception Thrown on new FileBasedClusterNodeFirewall()
*/
@Override
public ClusterNodeFirewall getObject() throws Exception {
if (firewall == null) {
final File config = properties.getClusterNodeFirewallFile();
final File restoreDirectory = properties.getRestoreDirectory();
if (config != null) {
firewall = new FileBasedClusterNodeFirewall(config, restoreDirectory);
}
}
return firewall;
}
@Override
public Class<ClusterNodeFirewall> getObjectType() {
return ClusterNodeFirewall.class;
}
@Override
public boolean isSingleton() {
return true;
}
public void setProperties(NiFiProperties properties) {
this.properties = properties;
}
}

View File

@ -1,68 +0,0 @@
/*
* 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.cluster.spring;
import org.apache.nifi.cluster.coordination.ClusterCoordinator;
import org.apache.nifi.cluster.coordination.heartbeat.ClusterProtocolHeartbeatMonitor;
import org.apache.nifi.cluster.coordination.heartbeat.HeartbeatMonitor;
import org.apache.nifi.cluster.protocol.impl.ClusterCoordinationProtocolSenderListener;
import org.apache.nifi.util.NiFiProperties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class HeartbeatMonitorFactoryBean implements FactoryBean<HeartbeatMonitor>, ApplicationContextAware {
private ApplicationContext applicationContext;
private NiFiProperties properties;
private HeartbeatMonitor heartbeatMonitor = null;
@Override
public HeartbeatMonitor getObject() throws Exception {
if (heartbeatMonitor == null && properties.isNode()) {
final ClusterCoordinationProtocolSenderListener protocolSenderListener =
applicationContext.getBean("clusterCoordinationProtocolSenderListener", ClusterCoordinationProtocolSenderListener.class);
final ClusterCoordinator clusterCoordinator = applicationContext.getBean("clusterCoordinator", ClusterCoordinator.class);
heartbeatMonitor = new ClusterProtocolHeartbeatMonitor(clusterCoordinator, protocolSenderListener, properties);
}
return heartbeatMonitor;
}
@Override
public Class<?> getObjectType() {
return HeartbeatMonitor.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void setProperties(NiFiProperties properties) {
this.properties = properties;
}
}

View File

@ -1,84 +0,0 @@
/*
* 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.cluster.spring;
import org.apache.nifi.cluster.coordination.flow.FlowElection;
import org.apache.nifi.cluster.coordination.node.NodeClusterCoordinator;
import org.apache.nifi.cluster.firewall.ClusterNodeFirewall;
import org.apache.nifi.cluster.protocol.NodeProtocolSender;
import org.apache.nifi.cluster.protocol.impl.ClusterCoordinationProtocolSenderListener;
import org.apache.nifi.controller.leader.election.LeaderElectionManager;
import org.apache.nifi.events.EventReporter;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.revision.RevisionManager;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class NodeClusterCoordinatorFactoryBean implements FactoryBean<NodeClusterCoordinator>, ApplicationContextAware {
private ApplicationContext applicationContext;
private NiFiProperties properties;
private ExtensionManager extensionManager;
private NodeClusterCoordinator nodeClusterCoordinator = null;
@Override
public NodeClusterCoordinator getObject() throws Exception {
if (nodeClusterCoordinator == null && properties.isNode()) {
final ClusterCoordinationProtocolSenderListener protocolSenderListener =
applicationContext.getBean("clusterCoordinationProtocolSenderListener", ClusterCoordinationProtocolSenderListener.class);
final EventReporter eventReporter = applicationContext.getBean("eventReporter", EventReporter.class);
final Object clusterFirewallBean = applicationContext.getBean("clusterFirewall");
final ClusterNodeFirewall clusterFirewall = clusterFirewallBean instanceof ClusterNodeFirewall ? (ClusterNodeFirewall) clusterFirewallBean : null;
final RevisionManager revisionManager = applicationContext.getBean("revisionManager", RevisionManager.class);
final LeaderElectionManager electionManager = applicationContext.getBean("leaderElectionManager", LeaderElectionManager.class);
final FlowElection flowElection = applicationContext.getBean("flowElection", FlowElection.class);
final NodeProtocolSender nodeProtocolSender = applicationContext.getBean("nodeProtocolSender", NodeProtocolSender.class);
nodeClusterCoordinator = new NodeClusterCoordinator(protocolSenderListener, eventReporter, electionManager, flowElection, clusterFirewall,
revisionManager, properties, extensionManager, nodeProtocolSender);
}
return nodeClusterCoordinator;
}
@Override
public Class<?> getObjectType() {
return NodeClusterCoordinator.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void setProperties(NiFiProperties properties) {
this.properties = properties;
}
public void setExtensionManager(ExtensionManager extensionManager) {
this.extensionManager = extensionManager;
}
}

View File

@ -1,82 +0,0 @@
/*
* 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.cluster.spring;
import org.apache.nifi.cluster.coordination.ClusterCoordinator;
import org.apache.nifi.cluster.coordination.http.replication.RequestCompletionCallback;
import org.apache.nifi.cluster.coordination.http.replication.ThreadPoolRequestReplicator;
import org.apache.nifi.cluster.coordination.http.replication.okhttp.OkHttpReplicationClient;
import org.apache.nifi.events.EventReporter;
import org.apache.nifi.util.NiFiProperties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class ThreadPoolRequestReplicatorFactoryBean implements FactoryBean<ThreadPoolRequestReplicator>, DisposableBean, ApplicationContextAware {
private ApplicationContext applicationContext;
private NiFiProperties nifiProperties;
private ThreadPoolRequestReplicator replicator = null;
@Override
public ThreadPoolRequestReplicator getObject() throws Exception {
if (replicator == null && nifiProperties.isNode()) {
final EventReporter eventReporter = applicationContext.getBean("eventReporter", EventReporter.class);
final ClusterCoordinator clusterCoordinator = applicationContext.getBean("clusterCoordinator", ClusterCoordinator.class);
final RequestCompletionCallback requestCompletionCallback = applicationContext.getBean("clusterCoordinator", RequestCompletionCallback.class);
final int maxPoolSize = nifiProperties.getClusterNodeProtocolMaxPoolSize();
final int maxConcurrentRequests = nifiProperties.getClusterNodeMaxConcurrentRequests();
final OkHttpReplicationClient replicationClient = new OkHttpReplicationClient(nifiProperties);
replicator = new ThreadPoolRequestReplicator(maxPoolSize, maxConcurrentRequests, replicationClient, clusterCoordinator,
requestCompletionCallback, eventReporter, nifiProperties);
}
return replicator;
}
@Override
public Class<?> getObjectType() {
return ThreadPoolRequestReplicator.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void setProperties(final NiFiProperties properties) {
this.nifiProperties = properties;
}
@Override
public void destroy() {
if (replicator != null) {
replicator.shutdown();
}
}
}

View File

@ -0,0 +1,166 @@
/*
* 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.framework.configuration;
import org.apache.nifi.cluster.coordination.ClusterCoordinator;
import org.apache.nifi.cluster.coordination.flow.FlowElection;
import org.apache.nifi.cluster.coordination.flow.PopularVoteFlowElection;
import org.apache.nifi.cluster.coordination.heartbeat.ClusterProtocolHeartbeatMonitor;
import org.apache.nifi.cluster.coordination.heartbeat.HeartbeatMonitor;
import org.apache.nifi.cluster.coordination.node.NodeClusterCoordinator;
import org.apache.nifi.cluster.firewall.ClusterNodeFirewall;
import org.apache.nifi.cluster.firewall.impl.FileBasedClusterNodeFirewall;
import org.apache.nifi.cluster.protocol.NodeProtocolSender;
import org.apache.nifi.cluster.protocol.impl.ClusterCoordinationProtocolSenderListener;
import org.apache.nifi.components.state.StateManagerProvider;
import org.apache.nifi.controller.leader.election.LeaderElectionManager;
import org.apache.nifi.events.EventReporter;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.revision.RevisionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.concurrent.TimeUnit;
/**
* Cluster Coordinator Configuration
*/
@Configuration
public class ClusterCoordinatorConfiguration {
private NiFiProperties properties;
private ClusterCoordinationProtocolSenderListener protocolListener;
private NodeProtocolSender nodeProtocolSender;
private RevisionManager revisionManager;
private EventReporter eventReporter;
private LeaderElectionManager leaderElectionManager;
private StateManagerProvider stateManagerProvider;
@Autowired
public void setProperties(final NiFiProperties properties) {
this.properties = properties;
}
@Autowired
public void setStateManagerProvider(final StateManagerProvider stateManagerProvider) {
this.stateManagerProvider = stateManagerProvider;
}
@Autowired
public void setLeaderElectionManager(final LeaderElectionManager leaderElectionManager) {
this.leaderElectionManager = leaderElectionManager;
}
@Autowired
public void setEventReporter(final EventReporter eventReporter) {
this.eventReporter = eventReporter;
}
@Autowired
public void setRevisionManager(final RevisionManager revisionManager) {
this.revisionManager = revisionManager;
}
@Qualifier("nodeProtocolSender")
@Autowired(required = false)
public void setNodeProtocolSender(final NodeProtocolSender nodeProtocolSender) {
this.nodeProtocolSender = nodeProtocolSender;
}
@Autowired(required = false)
public void setProtocolListener(final ClusterCoordinationProtocolSenderListener protocolListener) {
this.protocolListener = protocolListener;
}
@Bean
public ClusterCoordinator clusterCoordinator() {
final ClusterCoordinator clusterCoordinator;
if (properties.isClustered()) {
try {
clusterCoordinator = new NodeClusterCoordinator(
protocolListener,
eventReporter,
leaderElectionManager,
flowElection(),
clusterFirewall(),
revisionManager,
properties,
nodeProtocolSender,
stateManagerProvider
);
} catch (final IOException e) {
throw new UncheckedIOException("Failed to load Cluster Coordinator", e);
}
} else {
clusterCoordinator = null;
}
return clusterCoordinator;
}
@Bean
public HeartbeatMonitor heartbeatMonitor() {
final HeartbeatMonitor heartbeatMonitor;
final ClusterCoordinator clusterCoordinator = clusterCoordinator();
if (clusterCoordinator == null) {
heartbeatMonitor = null;
} else {
heartbeatMonitor = new ClusterProtocolHeartbeatMonitor(clusterCoordinator, protocolListener, properties);
}
return heartbeatMonitor;
}
private ClusterNodeFirewall clusterFirewall() {
final ClusterNodeFirewall firewall;
final File config = properties.getClusterNodeFirewallFile();
final File restoreDirectory = properties.getRestoreDirectory();
if (config == null) {
firewall = null;
} else {
try {
firewall = new FileBasedClusterNodeFirewall(config, restoreDirectory);
} catch (final IOException e) {
throw new UncheckedIOException("Failed to load Cluster Firewall configuration", e);
}
}
return firewall;
}
private FlowElection flowElection() {
final String maxWaitTime = properties.getFlowElectionMaxWaitTime();
final long maxWaitMillis = FormatUtils.getTimeDuration(maxWaitTime, TimeUnit.MILLISECONDS);
final Integer maxNodes = properties.getFlowElectionMaxCandidates();
return new PopularVoteFlowElection(maxWaitMillis, TimeUnit.MILLISECONDS, maxNodes);
}
}

View File

@ -0,0 +1,104 @@
/*
* 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.framework.configuration;
import org.apache.nifi.cluster.ClusterDetailsFactory;
import org.apache.nifi.cluster.StandardClusterDetailsFactory;
import org.apache.nifi.cluster.coordination.ClusterCoordinator;
import org.apache.nifi.cluster.coordination.http.replication.RequestCompletionCallback;
import org.apache.nifi.cluster.coordination.http.replication.ThreadPoolRequestReplicator;
import org.apache.nifi.cluster.coordination.http.replication.okhttp.OkHttpReplicationClient;
import org.apache.nifi.cluster.lifecycle.ClusterDecommissionTask;
import org.apache.nifi.controller.FlowController;
import org.apache.nifi.events.EventReporter;
import org.apache.nifi.util.NiFiProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;
/**
* Framework Cluster Configuration with components supporting request replication and cluster details
*/
@Configuration
public class FrameworkClusterConfiguration {
private NiFiProperties properties;
private EventReporter eventReporter;
private FlowController flowController;
private ClusterCoordinator clusterCoordinator;
@Autowired
public void setProperties(final NiFiProperties properties) {
this.properties = properties;
}
@Autowired
public void setFlowController(final FlowController flowController) {
this.flowController = flowController;
}
@Autowired
public void setEventReporter(final EventReporter eventReporter) {
this.eventReporter = eventReporter;
}
@Autowired
public void setClusterCoordinator(final ClusterCoordinator clusterCoordinator) {
this.clusterCoordinator = clusterCoordinator;
}
@Bean
public ThreadPoolRequestReplicator requestReplicator(
@Autowired(required = false) final SSLContext sslContext,
@Autowired(required = false) final X509TrustManager trustManager
) {
final ThreadPoolRequestReplicator replicator;
if (clusterCoordinator == null) {
replicator = null;
} else {
final OkHttpReplicationClient replicationClient = new OkHttpReplicationClient(properties, sslContext, trustManager);
replicator = new ThreadPoolRequestReplicator(
properties.getClusterNodeProtocolMaxPoolSize(),
properties.getClusterNodeMaxConcurrentRequests(),
replicationClient,
clusterCoordinator,
(RequestCompletionCallback) clusterCoordinator,
eventReporter,
properties
);
}
return replicator;
}
@Bean
public ClusterDecommissionTask decommissionTask() {
return new ClusterDecommissionTask(clusterCoordinator, flowController);
}
@Bean
public ClusterDetailsFactory clusterDetailsFactory() {
return new StandardClusterDetailsFactory(clusterCoordinator);
}
}

View File

@ -1,67 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<!-- marked as lazy so that clustering beans are not created when applications runs in non-clustered mode -->
<beans default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
<!-- cluster firewall -->
<bean id="clusterFirewall" class="org.apache.nifi.cluster.spring.FileBasedClusterNodeFirewallFactoryBean">
<property name="properties" ref="nifiProperties"/>
</bean>
<!-- Request Replicator -->
<bean id="requestReplicator" class="org.apache.nifi.cluster.spring.ThreadPoolRequestReplicatorFactoryBean">
<property name="properties" ref="nifiProperties"/>
</bean>
<!-- Leader Election Manager -->
<bean id="leaderElectionManager" class="org.apache.nifi.spring.LeaderElectionManagerFactoryBean">
<property name="properties" ref="nifiProperties" />
<property name="extensionManager" ref="extensionManager" />
</bean>
<bean id="flowElection" class="org.apache.nifi.cluster.coordination.flow.PopularVoteFlowElectionFactoryBean">
<property name="properties" ref="nifiProperties" />
</bean>
<!-- Cluster Coordinator -->
<bean id="clusterCoordinator" class="org.apache.nifi.cluster.spring.NodeClusterCoordinatorFactoryBean">
<property name="properties" ref="nifiProperties"/>
<property name="extensionManager" ref="extensionManager" />
</bean>
<!-- Heartbeat Monitor -->
<bean id="heartbeatMonitor" class="org.apache.nifi.cluster.spring.HeartbeatMonitorFactoryBean">
<property name="properties" ref="nifiProperties"/>
</bean>
<bean id="decommissionTask" class="org.apache.nifi.cluster.lifecycle.ClusterDecommissionTask">
<constructor-arg ref="clusterCoordinator" />
<constructor-arg ref="flowController" />
</bean>
<bean id="clusterDetailsFactory" class="org.apache.nifi.cluster.StandardClusterDetailsFactory">
<constructor-arg ref="clusterCoordinator" />
</bean>
</beans>

View File

@ -1,233 +0,0 @@
/*
* 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.cluster.coordination.http.replication.okhttp;
import org.apache.nifi.cluster.coordination.http.replication.PreparedRequest;
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.util.NiFiProperties;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class OkHttpReplicationClientTest {
private static TlsConfiguration tlsConfiguration;
@BeforeAll
public static void setUpOnce() {
tlsConfiguration = new TemporaryKeyStoreBuilder().build();
}
@Test
public void testShouldReplaceNonZeroContentLengthHeader() {
final Map<String, String> headers = new HashMap<>();
headers.put("Content-Length", "123");
headers.put("Other-Header", "arbitrary value");
// must be case-insensitive
final String[] methods = new String[] {"DELETE", "delete", "DeLeTe"};
final NiFiProperties mockProperties = mockNiFiProperties();
final OkHttpReplicationClient client = new OkHttpReplicationClient(mockProperties);
for (final String method: methods) {
client.prepareRequest(method, headers, null);
assertEquals(2, headers.size());
assertEquals("0", headers.get("Content-Length"));
}
}
@Test
void testShouldNotReplaceContentLengthHeaderWhenZeroOrNull() {
final String method = "DELETE";
final String[] zeroOrNullContentLengths = new String[] {null, "0"};
final NiFiProperties mockProperties = mockNiFiProperties();
final OkHttpReplicationClient client = new OkHttpReplicationClient(mockProperties);
final Map<String, String> headers = new HashMap<>();
for (final String contentLength: zeroOrNullContentLengths) {
headers.put("Content-Length", contentLength);
headers.put("Other-Header", "arbitrary value");
client.prepareRequest(method, headers, null);
assertEquals(2, headers.size());
assertEquals(contentLength, headers.get("Content-Length"));
}
}
@Test
void testShouldNotReplaceNonZeroContentLengthHeaderOnOtherMethod() {
final Map<String, String> headers = new HashMap<>();
headers.put("Content-Length", "123");
headers.put("Other-Header", "arbitrary value");
final NiFiProperties mockProperties = mockNiFiProperties();
final OkHttpReplicationClient client = new OkHttpReplicationClient(mockProperties);
final String[] nonDeleteMethods = new String[] {"POST", "PUT", "GET", "HEAD"};
for (final String method: nonDeleteMethods) {
client.prepareRequest(method, headers, null);
assertEquals(2, headers.size());
assertEquals("123", headers.get("Content-Length"));
}
}
@Test
void testShouldReadCasInsensitiveAcceptEncoding() {
final Map<String, String> headers = new HashMap<>();
headers.put("accept-encoding", "gzip");
headers.put("Other-Header", "arbitrary value");
final NiFiProperties mockProperties = mockNiFiProperties();
final OkHttpReplicationClient client = new OkHttpReplicationClient(mockProperties);
PreparedRequest request = client.prepareRequest("POST", headers, "TEST");
assertEquals(3, request.getHeaders().size());
assertEquals("gzip", request.getHeaders().get("accept-encoding"));
assertEquals("gzip", request.getHeaders().get("Content-Encoding"));
assertEquals("arbitrary value", request.getHeaders().get("Other-Header"));
}
@Test
void testShouldUseKeystorePasswordIfKeyPasswordIsBlank() {
final Map<String, String> propsMap = new HashMap<>();
propsMap.put(NiFiProperties.SECURITY_TRUSTSTORE, tlsConfiguration.getTruststorePath());
propsMap.put(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
propsMap.put(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, tlsConfiguration.getTruststorePassword());
propsMap.put(NiFiProperties.SECURITY_KEYSTORE, tlsConfiguration.getKeystorePath());
propsMap.put(NiFiProperties.SECURITY_KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
propsMap.put(NiFiProperties.SECURITY_KEYSTORE_PASSWD, tlsConfiguration.getKeystorePassword());
propsMap.put(NiFiProperties.SECURITY_KEY_PASSWD, "");
propsMap.put(NiFiProperties.WEB_HTTPS_HOST, "localhost");
propsMap.put(NiFiProperties.WEB_HTTPS_PORT, "51552");
final NiFiProperties mockNiFiProperties = new NiFiProperties(propsMap);
final OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties);
assertTrue(client.isTLSConfigured());
}
@Test
void testShouldUseKeystorePasswordIfKeyPasswordIsNull() {
final Map<String, String> flowfileEncryptionProps = new HashMap<>();
flowfileEncryptionProps.put(NiFiProperties.SECURITY_TRUSTSTORE, tlsConfiguration.getTruststorePath());
flowfileEncryptionProps.put(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
flowfileEncryptionProps.put(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, tlsConfiguration.getTruststorePassword());
flowfileEncryptionProps.put(NiFiProperties.SECURITY_KEYSTORE, tlsConfiguration.getKeystorePath());
flowfileEncryptionProps.put(NiFiProperties.SECURITY_KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
flowfileEncryptionProps.put(NiFiProperties.SECURITY_KEYSTORE_PASSWD, tlsConfiguration.getKeystorePassword());
flowfileEncryptionProps.put(NiFiProperties.WEB_HTTPS_HOST, "localhost");
flowfileEncryptionProps.put(NiFiProperties.WEB_HTTPS_PORT, "51552");
final NiFiProperties mockNiFiProperties = new NiFiProperties(flowfileEncryptionProps);
final OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties);
assertTrue(client.isTLSConfigured());
}
@Test
void testShouldFailIfKeyPasswordIsSetButKeystorePasswordIsBlank() {
final Map<String, String> propsMap = new HashMap<>();
propsMap.put(NiFiProperties.SECURITY_TRUSTSTORE, tlsConfiguration.getTruststorePath());
propsMap.put(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
propsMap.put(NiFiProperties.SECURITY_KEYSTORE, tlsConfiguration.getKeystorePath());
propsMap.put(NiFiProperties.SECURITY_KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
propsMap.put(NiFiProperties.SECURITY_KEYSTORE_PASSWD, "");
propsMap.put(NiFiProperties.WEB_HTTPS_HOST, "localhost");
propsMap.put(NiFiProperties.WEB_HTTPS_PORT, "51552");
final NiFiProperties mockNiFiProperties = new NiFiProperties(propsMap);
final OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties);
assertFalse(client.isTLSConfigured());
}
@Test
void testShouldFailIfKeyPasswordAndKeystorePasswordAreBlank() {
final Map<String, String> propsMap = new HashMap<>();
propsMap.put(NiFiProperties.SECURITY_TRUSTSTORE, tlsConfiguration.getTruststorePath());
propsMap.put(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
propsMap.put(NiFiProperties.SECURITY_KEYSTORE, tlsConfiguration.getKeystorePath());
propsMap.put(NiFiProperties.SECURITY_KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
propsMap.put(NiFiProperties.SECURITY_KEYSTORE_PASSWD, "");
propsMap.put(NiFiProperties.SECURITY_KEY_PASSWD, "");
propsMap.put(NiFiProperties.WEB_HTTPS_HOST, "localhost");
propsMap.put(NiFiProperties.WEB_HTTPS_PORT, "51552");
final NiFiProperties mockNiFiProperties = new NiFiProperties(propsMap);
final OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties);
assertFalse(client.isTLSConfigured());
}
@Test
void testShouldDetermineIfTLSConfigured() {
final Map<String, String> propsMap = new HashMap<>();
propsMap.put(NiFiProperties.WEB_HTTPS_HOST, "localhost");
propsMap.put(NiFiProperties.WEB_HTTPS_PORT, "51552");
final Map<String, String> tlsPropsMap = new HashMap<>(propsMap);
tlsPropsMap.put(NiFiProperties.SECURITY_TRUSTSTORE, tlsConfiguration.getTruststorePath());
tlsPropsMap.put(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
tlsPropsMap.put(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, tlsConfiguration.getTruststorePassword());
tlsPropsMap.put(NiFiProperties.SECURITY_KEYSTORE, tlsConfiguration.getKeystorePath());
tlsPropsMap.put(NiFiProperties.SECURITY_KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
tlsPropsMap.put(NiFiProperties.SECURITY_KEYSTORE_PASSWD, tlsConfiguration.getKeystorePassword());
final Map<String, String> invalidTlsPropsMap = new HashMap<>(tlsPropsMap);
// Remove the keystore password to create an invalid configuration
invalidTlsPropsMap.remove(NiFiProperties.SECURITY_KEYSTORE_PASSWD);
final NiFiProperties mockNiFiProperties = new NiFiProperties(propsMap);
final NiFiProperties mockTLSNiFiProperties = new NiFiProperties(tlsPropsMap);
final NiFiProperties mockInvalidTLSNiFiProperties = new NiFiProperties(invalidTlsPropsMap);
final OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties);
final OkHttpReplicationClient invalidTlsClient = new OkHttpReplicationClient(mockInvalidTLSNiFiProperties);
final OkHttpReplicationClient tlsClient = new OkHttpReplicationClient(mockTLSNiFiProperties);
assertFalse(client.isTLSConfigured());
assertFalse(invalidTlsClient.isTLSConfigured());
assertTrue(tlsClient.isTLSConfigured());
}
private static NiFiProperties mockNiFiProperties() {
return NiFiProperties.createBasicNiFiProperties(null);
}
}

View File

@ -1,74 +0,0 @@
/*
* 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.cluster.firewall.impl;
import org.apache.nifi.cluster.firewall.ClusterNodeFirewall;
import org.apache.nifi.cluster.spring.FileBasedClusterNodeFirewallFactoryBean;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.util.StringUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
public class FileBasedClusterNodeFirewallFactoryBeanTest {
private static final String PROPERTIES_SUFFIX = ".firewall.properties";
private FileBasedClusterNodeFirewallFactoryBean factoryBean;
private NiFiProperties properties;
@BeforeEach
public void setFactoryBean() {
properties = NiFiProperties.createBasicNiFiProperties(StringUtils.EMPTY);
factoryBean = new FileBasedClusterNodeFirewallFactoryBean();
factoryBean.setProperties(properties);
}
@Test
public void testGetObjectType() {
final Class<ClusterNodeFirewall> objectType = factoryBean.getObjectType();
assertEquals(ClusterNodeFirewall.class, objectType);
}
@Test
public void testGetObjectClusterNodeFirewallFileNotConfigured() throws Exception {
final ClusterNodeFirewall clusterNodeFirewall = factoryBean.getObject();
assertNull(clusterNodeFirewall);
}
@Test
public void testGetObjectClusterNodeFirewallFileConfigured() throws Exception {
final File firewallProperties = File.createTempFile(FileBasedClusterNodeFirewallFactoryBeanTest.class.getName(), PROPERTIES_SUFFIX);
firewallProperties.deleteOnExit();
final Map<String, String> beanProperties = new HashMap<>();
beanProperties.put(NiFiProperties.CLUSTER_FIREWALL_FILE, firewallProperties.getAbsolutePath());
properties = NiFiProperties.createBasicNiFiProperties(StringUtils.EMPTY, beanProperties);
factoryBean.setProperties(properties);
final ClusterNodeFirewall clusterNodeFirewall = factoryBean.getObject();
assertNotNull(clusterNodeFirewall);
assertEquals(FileBasedClusterNodeFirewall.class, clusterNodeFirewall.getClass());
}
}

View File

@ -1,15 +0,0 @@
# 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.
org.apache.nifi.cluster.integration.NopStateProvider

View File

@ -67,10 +67,6 @@ import org.apache.nifi.parameter.ParameterParser;
import org.apache.nifi.parameter.ParameterTokenList;
import org.apache.nifi.processor.SimpleProcessLogger;
import org.apache.nifi.processor.StandardValidationContext;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.util.NiFiProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -106,16 +102,18 @@ public class StandardStateManagerProvider implements StateManagerProvider {
return clusterStateProvider;
}
public static synchronized StateManagerProvider create(final NiFiProperties properties, final ExtensionManager extensionManager,
final ParameterLookup parameterLookup) throws ConfigParseException, IOException {
public static synchronized StateManagerProvider create(
final NiFiProperties properties,
final SSLContext sslContext,
final ExtensionManager extensionManager,
final ParameterLookup parameterLookup
) throws ConfigParseException, IOException {
nifiProperties = properties;
if (provider != null) {
return provider;
}
final SSLContext sslContext = createSslContext(properties);
final StateProvider localProvider = createLocalStateProvider(properties, sslContext, extensionManager, parameterLookup);
final StateProvider clusterProvider;
@ -136,15 +134,6 @@ public class StandardStateManagerProvider implements StateManagerProvider {
provider = null;
}
private static SSLContext createSslContext(final NiFiProperties properties) {
final TlsConfiguration standardTlsConfiguration = StandardTlsConfiguration.fromNiFiProperties(properties);
try {
return SslContextFactory.createSslContext(standardTlsConfiguration);
} catch (final TlsException e) {
throw new IllegalStateException("TLS Security Properties not valid for State Manager configuration", e);
}
}
private static StateProvider createLocalStateProvider(
final NiFiProperties properties,
final SSLContext sslContext,

View File

@ -120,7 +120,6 @@ import org.apache.nifi.controller.service.StandardConfigurationContext;
import org.apache.nifi.controller.service.StandardControllerServiceApiLookup;
import org.apache.nifi.controller.service.StandardControllerServiceProvider;
import org.apache.nifi.controller.service.StandardControllerServiceResolver;
import org.apache.nifi.controller.state.manager.StandardStateManagerProvider;
import org.apache.nifi.controller.state.server.ZooKeeperStateServer;
import org.apache.nifi.controller.status.NodeStatus;
import org.apache.nifi.controller.status.StorageStatus;
@ -160,7 +159,6 @@ import org.apache.nifi.nar.NarCloseable;
import org.apache.nifi.nar.NarThreadContextClassLoader;
import org.apache.nifi.nar.PythonBundle;
import org.apache.nifi.parameter.ParameterContextManager;
import org.apache.nifi.parameter.ParameterLookup;
import org.apache.nifi.parameter.ParameterProvider;
import org.apache.nifi.parameter.StandardParameterContextManager;
import org.apache.nifi.processor.Processor;
@ -197,10 +195,6 @@ import org.apache.nifi.reporting.StandardEventAccess;
import org.apache.nifi.reporting.UserAwareEventAccess;
import org.apache.nifi.repository.encryption.configuration.EncryptionProtocol;
import org.apache.nifi.scheduling.SchedulingStrategy;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.services.FlowService;
import org.apache.nifi.stream.io.LimitingInputStream;
import org.apache.nifi.stream.io.StreamUtils;
@ -284,7 +278,6 @@ public class FlowController implements ReportingTaskProvider, FlowAnalysisRulePr
private final long gracefulShutdownSeconds;
private final ExtensionDiscoveringManager extensionManager;
private final NiFiProperties nifiProperties;
private final SSLContext sslContext;
private final Set<RemoteSiteListener> externalSiteListeners = new HashSet<>();
private final AtomicReference<CounterRepository> counterRepositoryRef;
private final AtomicBoolean initialized = new AtomicBoolean(false);
@ -399,6 +392,7 @@ public class FlowController implements ReportingTaskProvider, FlowAnalysisRulePr
public static FlowController createStandaloneInstance(
final FlowFileEventRepository flowFileEventRepo,
final SSLContext sslContext,
final NiFiProperties properties,
final Authorizer authorizer,
final AuditService auditService,
@ -406,10 +400,13 @@ public class FlowController implements ReportingTaskProvider, FlowAnalysisRulePr
final BulletinRepository bulletinRepo,
final ExtensionDiscoveringManager extensionManager,
final StatusHistoryRepository statusHistoryRepository,
final RuleViolationsManager ruleViolationsManager) {
final RuleViolationsManager ruleViolationsManager,
final StateManagerProvider stateManagerProvider
) {
return new FlowController(
flowFileEventRepo,
sslContext,
properties,
authorizer,
auditService,
@ -423,11 +420,14 @@ public class FlowController implements ReportingTaskProvider, FlowAnalysisRulePr
extensionManager,
null,
statusHistoryRepository,
ruleViolationsManager);
ruleViolationsManager,
stateManagerProvider
);
}
public static FlowController createClusteredInstance(
final FlowFileEventRepository flowFileEventRepo,
final SSLContext sslContext,
final NiFiProperties properties,
final Authorizer authorizer,
final AuditService auditService,
@ -440,10 +440,13 @@ public class FlowController implements ReportingTaskProvider, FlowAnalysisRulePr
final ExtensionDiscoveringManager extensionManager,
final RevisionManager revisionManager,
final StatusHistoryRepository statusHistoryRepository,
final RuleViolationsManager ruleViolationsManager) {
final RuleViolationsManager ruleViolationsManager,
final StateManagerProvider stateManagerProvider
) {
final FlowController flowController = new FlowController(
return new FlowController(
flowFileEventRepo,
sslContext,
properties,
authorizer,
auditService,
@ -457,13 +460,14 @@ public class FlowController implements ReportingTaskProvider, FlowAnalysisRulePr
extensionManager,
revisionManager,
statusHistoryRepository,
ruleViolationsManager);
return flowController;
ruleViolationsManager,
stateManagerProvider
);
}
private FlowController(
final FlowFileEventRepository flowFileEventRepo,
final SSLContext sslContext,
final NiFiProperties nifiProperties,
final Authorizer authorizer,
final AuditService auditService,
@ -477,7 +481,9 @@ public class FlowController implements ReportingTaskProvider, FlowAnalysisRulePr
final ExtensionDiscoveringManager extensionManager,
final RevisionManager revisionManager,
final StatusHistoryRepository statusHistoryRepository,
final RuleViolationsManager ruleViolationsManager) {
final RuleViolationsManager ruleViolationsManager,
final StateManagerProvider stateManagerProvider
) {
maxTimerDrivenThreads = new AtomicInteger(10);
@ -492,15 +498,7 @@ public class FlowController implements ReportingTaskProvider, FlowAnalysisRulePr
this.configuredForClustering = configuredForClustering;
this.revisionManager = revisionManager;
this.statusHistoryRepository = statusHistoryRepository;
try {
// Form the container object from the properties
TlsConfiguration tlsConfiguration = StandardTlsConfiguration.fromNiFiProperties(nifiProperties);
this.sslContext = SslContextFactory.createSslContext(tlsConfiguration);
} catch (TlsException e) {
LOG.error("Unable to start the flow controller because the TLS configuration was invalid: {}", e.getLocalizedMessage());
throw new IllegalStateException("Flow controller TLS configuration is invalid", e);
}
this.stateManagerProvider = stateManagerProvider;
timerDrivenEngineRef = new AtomicReference<>(new FlowEngine(maxTimerDrivenThreads.get(), "Timer-Driven Process"));
@ -535,12 +533,6 @@ public class FlowController implements ReportingTaskProvider, FlowAnalysisRulePr
throw new RuntimeException("Unable to create Content Repository", e);
}
try {
this.stateManagerProvider = StandardStateManagerProvider.create(nifiProperties, extensionManager, ParameterLookup.EMPTY);
} catch (final IOException e) {
throw new RuntimeException(e);
}
lifecycleStateManager = new StandardLifecycleStateManager();
processScheduler = new StandardProcessScheduler(timerDrivenEngineRef.get(), this, stateManagerProvider, this.nifiProperties, lifecycleStateManager);

View File

@ -83,9 +83,6 @@ import org.apache.nifi.remote.StandardRemoteProcessGroup;
import org.apache.nifi.remote.TransferDirection;
import org.apache.nifi.reporting.BulletinRepository;
import org.apache.nifi.reporting.ReportingTask;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.util.ReflectionUtils;
@ -395,14 +392,6 @@ public class StandardFlowManager extends AbstractFlowManager implements FlowMana
final LogRepository logRepository = LogRepositoryFactory.getRepository(id);
final ExtensionManager extensionManager = flowController.getExtensionManager();
final SSLContext systemSslContext;
try {
systemSslContext = SslContextFactory.createSslContext(StandardTlsConfiguration.fromNiFiProperties(nifiProperties));
} catch (final TlsException e) {
throw new IllegalStateException("Could not instantiate flow registry client because of TLS issues", e);
}
final FlowRegistryClientNode clientNode = new ExtensionBuilder()
.identifier(id)
.type(type)
@ -415,7 +404,7 @@ public class StandardFlowManager extends AbstractFlowManager implements FlowMana
.reloadComponent(flowController.getReloadComponent())
.addClasspathUrls(additionalUrls)
.kerberosConfig(flowController.createKerberosConfig(nifiProperties))
.systemSslContext(systemSslContext)
.systemSslContext(sslContext)
.extensionManager(extensionManager)
.classloaderIsolationKey(classloaderIsolationKey)
.flowAnalysisAtRegistryCommit(nifiProperties.flowRegistryCheckForRuleViolationsBeforeCommit())

View File

@ -0,0 +1,41 @@
/*
* 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.framework.configuration;
import org.apache.nifi.properties.NiFiPropertiesLoader;
import org.apache.nifi.util.NiFiBootstrapUtils;
import org.apache.nifi.util.NiFiProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Application Properties Configuration class for Spring Application
*/
@Configuration
public class ApplicationPropertiesConfiguration {
/**
* Application Properties
*
* @return NiFi Application Properties
*/
@Bean
public NiFiProperties nifiProperties() {
final NiFiPropertiesLoader loader = new NiFiPropertiesLoader();
final String propertiesPath = NiFiBootstrapUtils.getDefaultApplicationPropertiesFilePath();
return loader.load(propertiesPath);
}
}

View File

@ -0,0 +1,326 @@
/*
* 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.framework.configuration;
import org.apache.nifi.admin.service.AuditService;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.cluster.coordination.ClusterCoordinator;
import org.apache.nifi.cluster.coordination.heartbeat.HeartbeatMonitor;
import org.apache.nifi.cluster.protocol.NodeProtocolSender;
import org.apache.nifi.cluster.protocol.impl.NodeProtocolSenderListener;
import org.apache.nifi.components.state.StateManagerProvider;
import org.apache.nifi.controller.FlowController;
import org.apache.nifi.controller.StandardFlowService;
import org.apache.nifi.controller.leader.election.LeaderElectionManager;
import org.apache.nifi.controller.repository.metrics.RingBufferEventRepository;
import org.apache.nifi.controller.status.history.JsonNodeStatusHistoryDumpFactory;
import org.apache.nifi.controller.status.history.StatusHistoryDumpFactory;
import org.apache.nifi.controller.status.history.StatusHistoryRepository;
import org.apache.nifi.controller.status.history.VolatileComponentStatusRepository;
import org.apache.nifi.diagnostics.DiagnosticsFactory;
import org.apache.nifi.diagnostics.bootstrap.BootstrapDiagnosticsFactory;
import org.apache.nifi.encrypt.PropertyEncryptor;
import org.apache.nifi.encrypt.PropertyEncryptorFactory;
import org.apache.nifi.extension.manifest.parser.ExtensionManifestParser;
import org.apache.nifi.extension.manifest.parser.jaxb.JAXBExtensionManifestParser;
import org.apache.nifi.manifest.RuntimeManifestService;
import org.apache.nifi.manifest.StandardRuntimeManifestService;
import org.apache.nifi.nar.ExtensionDiscoveringManager;
import org.apache.nifi.nar.NarThreadContextClassLoader;
import org.apache.nifi.reporting.BulletinRepository;
import org.apache.nifi.services.FlowService;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.validation.RuleViolationsManager;
import org.apache.nifi.validation.StandardRuleViolationsManager;
import org.apache.nifi.web.revision.RevisionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.net.ssl.SSLContext;
/**
* Framework Flow Controller Configuration class for Spring Application
*/
@Configuration
public class FlowControllerConfiguration {
private NiFiProperties properties;
private ExtensionDiscoveringManager extensionManager;
private AuditService auditService;
private Authorizer authorizer;
private RevisionManager revisionManager;
private LeaderElectionManager leaderElectionManager;
private SSLContext sslContext;
private StateManagerProvider stateManagerProvider;
private BulletinRepository bulletinRepository;
private NodeProtocolSender nodeProtocolSender;
private NodeProtocolSenderListener nodeProtocolSenderListener;
private HeartbeatMonitor heartbeatMonitor;
private ClusterCoordinator clusterCoordinator;
@Autowired
public void setProperties(final NiFiProperties properties) {
this.properties = properties;
}
@Autowired
public void setExtensionManager(final ExtensionDiscoveringManager extensionManager) {
this.extensionManager = extensionManager;
}
@Autowired
public void setAuditService(final AuditService auditService) {
this.auditService = auditService;
}
@Autowired
public void setAuthorizer(final Authorizer authorizer) {
this.authorizer = authorizer;
}
@Autowired
public void setRevisionManager(final RevisionManager revisionManager) {
this.revisionManager = revisionManager;
}
@Autowired
public void setLeaderElectionManager(final LeaderElectionManager leaderElectionManager) {
this.leaderElectionManager = leaderElectionManager;
}
@Autowired
public void setStateManagerProvider(final StateManagerProvider stateManagerProvider) {
this.stateManagerProvider = stateManagerProvider;
}
@Autowired
public void setBulletinRepository(final BulletinRepository bulletinRepository) {
this.bulletinRepository = bulletinRepository;
}
@Autowired(required = false)
public void setSslContext(final SSLContext sslContext) {
this.sslContext = sslContext;
}
@Qualifier("nodeProtocolSender")
@Autowired(required = false)
public void setNodeProtocolSender(final NodeProtocolSender nodeProtocolSender) {
this.nodeProtocolSender = nodeProtocolSender;
}
@Autowired(required = false)
public void setNodeProtocolSenderListener(final NodeProtocolSenderListener nodeProtocolSenderListener) {
this.nodeProtocolSenderListener = nodeProtocolSenderListener;
}
@Autowired(required = false)
public void setHeartbeatMonitor(final HeartbeatMonitor heartbeatMonitor) {
this.heartbeatMonitor = heartbeatMonitor;
}
@Autowired(required = false)
public void setClusterCoordinator(final ClusterCoordinator clusterCoordinator) {
this.clusterCoordinator = clusterCoordinator;
}
/**
* Flow Controller implementation depends on cluster configuration
*
* @return Flow Controller
* @throws Exception Thrown on failures to create Flow Controller
*/
@Bean
public FlowController flowController() throws Exception {
final FlowController flowController;
if (clusterCoordinator == null) {
flowController = FlowController.createStandaloneInstance(
flowFileEventRepository(),
sslContext,
properties,
authorizer,
auditService,
propertyEncryptor(),
bulletinRepository,
extensionManager,
statusHistoryRepository(),
ruleViolationsManager(),
stateManagerProvider
);
} else {
flowController = FlowController.createClusteredInstance(
flowFileEventRepository(),
sslContext,
properties,
authorizer,
auditService,
propertyEncryptor(),
nodeProtocolSender,
bulletinRepository,
clusterCoordinator,
heartbeatMonitor,
leaderElectionManager,
extensionManager,
revisionManager,
statusHistoryRepository(),
ruleViolationsManager(),
stateManagerProvider
);
}
return flowController;
}
/**
* Flow Service implementation depends on cluster configuration
*
* @return Flow Service
* @throws Exception Thrown on failures to create Flow Service
*/
@Bean
public FlowService flowService() throws Exception {
final FlowService flowService;
if (clusterCoordinator == null) {
flowService = StandardFlowService.createStandaloneInstance(
flowController(),
properties,
revisionManager,
authorizer
);
} else {
flowService = StandardFlowService.createClusteredInstance(
flowController(),
properties,
nodeProtocolSenderListener,
clusterCoordinator,
revisionManager,
authorizer
);
}
return flowService;
}
/**
* FlowFile Event Repository using Ring Buffer
*
* @return Ring Buffer Event Repository
*/
@Bean
public RingBufferEventRepository flowFileEventRepository() {
return new RingBufferEventRepository(5);
}
/**
* Rule Violations Manager for Flow Analysis
*
* @return Rule Violations Manager
*/
@Bean
public RuleViolationsManager ruleViolationsManager() {
return new StandardRuleViolationsManager();
}
/**
* Property Encryptor configured using Application Properties
*
* @return Property Encryptor
*/
@Bean
public PropertyEncryptor propertyEncryptor() {
return PropertyEncryptorFactory.getPropertyEncryptor(properties);
}
/**
* Status History Repository configured from NiFi Application Properties
*
* @return Status History Repository
* @throws Exception Thrown on failures to create Status History Repository
*/
@Bean
public StatusHistoryRepository statusHistoryRepository() throws Exception {
final String configuredClassName = properties.getProperty(NiFiProperties.COMPONENT_STATUS_REPOSITORY_IMPLEMENTATION);
final String className = configuredClassName == null ? VolatileComponentStatusRepository.class.getName() : configuredClassName;
final StatusHistoryRepository statusHistoryRepository = NarThreadContextClassLoader.createInstance(extensionManager, className, StatusHistoryRepository.class, properties);
statusHistoryRepository.start();
return statusHistoryRepository;
}
/**
* Status History Dump Factory using JSON implementation
*
* @return Status History Dump Factory
* @throws Exception Thrown on failure to get Status History Repository
*/
@Bean
public StatusHistoryDumpFactory statusHistoryDumpFactory() throws Exception {
final JsonNodeStatusHistoryDumpFactory statusHistoryDumpFactory = new JsonNodeStatusHistoryDumpFactory();
statusHistoryDumpFactory.setStatusHistoryRepository(statusHistoryRepository());
return statusHistoryDumpFactory;
}
/**
* Diagnostics Factory with Bootstrap-based implementation
*
* @return Diagnostics Factory
* @throws Exception Thrown on failures to Diagnostics Factory
*/
@Bean
public DiagnosticsFactory diagnosticsFactory() throws Exception {
final BootstrapDiagnosticsFactory diagnosticsFactory = new BootstrapDiagnosticsFactory();
diagnosticsFactory.setNifiProperties(properties);
diagnosticsFactory.setFlowController(flowController());
return diagnosticsFactory;
}
/**
* Extension Manifest Parser using JAXB implementation
*
* @return Extension Manifest Parser
*/
@Bean
public ExtensionManifestParser extensionManifestParser() {
return new JAXBExtensionManifestParser();
}
/**
* Runtime Manifest Service uses Extension Manager and Manifest Parser
*
* @return Runtime Manifest Service
*/
@Bean
public RuntimeManifestService runtimeManifestService() {
return new StandardRuntimeManifestService(extensionManager, extensionManifestParser());
}
}

View File

@ -0,0 +1,145 @@
/*
* 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.framework.configuration;
import org.apache.nifi.components.state.StateManagerProvider;
import org.apache.nifi.controller.leader.election.LeaderElectionManager;
import org.apache.nifi.controller.leader.election.StandaloneLeaderElectionManager;
import org.apache.nifi.controller.state.manager.StandardStateManagerProvider;
import org.apache.nifi.events.EventReporter;
import org.apache.nifi.events.StandardEventReporter;
import org.apache.nifi.events.VolatileBulletinRepository;
import org.apache.nifi.nar.ExtensionDefinition;
import org.apache.nifi.nar.ExtensionDiscoveringManager;
import org.apache.nifi.nar.ExtensionManagerHolder;
import org.apache.nifi.nar.NarThreadContextClassLoader;
import org.apache.nifi.parameter.ParameterLookup;
import org.apache.nifi.reporting.BulletinRepository;
import org.apache.nifi.util.NiFiProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Optional;
import java.util.Set;
import static org.apache.nifi.util.NiFiProperties.CLUSTER_LEADER_ELECTION_IMPLEMENTATION;
import static org.apache.nifi.util.NiFiProperties.DEFAULT_CLUSTER_LEADER_ELECTION_IMPLEMENTATION;
/**
* Shared Manager Configuration distinct from Flow Controller Configuration to avoid dependency resolution issues
*/
@Configuration
public class ManagerConfiguration {
private NiFiProperties properties;
private SSLContext sslContext;
@Autowired
public void setProperties(final NiFiProperties properties) {
this.properties = properties;
}
@Autowired(required = false)
public void setSslContext(final SSLContext sslContext) {
this.sslContext = sslContext;
}
/**
* Extension Manager for tracking components
*
* @return Extension Discovering Manager
*/
@Bean
public ExtensionDiscoveringManager extensionManager() {
return (ExtensionDiscoveringManager) ExtensionManagerHolder.getExtensionManager();
}
/**
* State Manager Provider based on local and clustered state management configuration
*
* @return State Manager Provider
*/
@Bean
public StateManagerProvider stateManagerProvider() {
try {
return StandardStateManagerProvider.create(properties, sslContext, extensionManager(), ParameterLookup.EMPTY);
} catch (final IOException e) {
throw new UncheckedIOException("Failed to create State Manager Provider", e);
}
}
/**
* Leader Election Manager implementation based on cluster configuration
*
* @return Leader Election Manager
* @throws Exception Thrown on failure to create Leader Election Manager
*/
@Bean
public LeaderElectionManager leaderElectionManager() throws Exception {
final LeaderElectionManager leaderElectionManager;
if (properties.isClustered()) {
final ExtensionDiscoveringManager extensionManager = extensionManager();
final String configuredClassName = properties.getProperty(CLUSTER_LEADER_ELECTION_IMPLEMENTATION, DEFAULT_CLUSTER_LEADER_ELECTION_IMPLEMENTATION);
final Set<ExtensionDefinition> extensions = extensionManager.getExtensions(LeaderElectionManager.class);
final Optional<ExtensionDefinition> extensionFound = extensions.stream()
.filter(extensionDefinition -> {
final String extensionClassName = extensionDefinition.getImplementationClassName();
return extensionClassName.equals(configuredClassName) || extensionClassName.endsWith(configuredClassName);
})
.findFirst();
final ExtensionDefinition extension = extensionFound.orElseThrow(() -> {
final String message = String.format("No Extensions Found for %s", LeaderElectionManager.class.getName());
return new IllegalStateException(message);
});
final String extensionImplementationClass = extension.getImplementationClassName();
leaderElectionManager = NarThreadContextClassLoader.createInstance(extensionManager, extensionImplementationClass, LeaderElectionManager.class, properties);
} else {
leaderElectionManager = new StandaloneLeaderElectionManager();
}
return leaderElectionManager;
}
/**
* Bulletin Repository using volatile memory-based implementation
*
* @return Bulletin Repository
*/
@Bean
public BulletinRepository bulletinRepository() {
return new VolatileBulletinRepository();
}
/**
* Event Reporter requires Bulletin Repository
*
* @return Event Reporter
*/
@Bean
public EventReporter eventReporter() {
return new StandardEventReporter(bulletinRepository());
}
}

View File

@ -0,0 +1,293 @@
/*
* 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.framework.configuration;
import org.apache.nifi.framework.ssl.FrameworkSslContextHolder;
import org.apache.nifi.framework.ssl.SecurityStoreChangedPathListener;
import org.apache.nifi.framework.ssl.WatchServiceMonitorCommand;
import org.apache.nifi.security.ssl.KeyManagerListener;
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
import org.apache.nifi.security.ssl.TrustManagerListener;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.util.NiFiProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchService;
import java.security.KeyStore;
import java.time.Duration;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static org.apache.nifi.util.NiFiProperties.SECURITY_AUTO_RELOAD_ENABLED;
import static org.apache.nifi.util.NiFiProperties.SECURITY_AUTO_RELOAD_INTERVAL;
import static org.apache.nifi.util.NiFiProperties.SECURITY_KEYSTORE;
import static org.apache.nifi.util.NiFiProperties.SECURITY_KEYSTORE_PASSWD;
import static org.apache.nifi.util.NiFiProperties.SECURITY_KEYSTORE_TYPE;
import static org.apache.nifi.util.NiFiProperties.SECURITY_TRUSTSTORE;
import static org.apache.nifi.util.NiFiProperties.SECURITY_TRUSTSTORE_PASSWD;
import static org.apache.nifi.util.NiFiProperties.SECURITY_TRUSTSTORE_TYPE;
/**
* SSL Context Configuration class for Spring Application
*/
@Configuration
public class SslContextConfiguration {
private static final Logger logger = LoggerFactory.getLogger(SslContextConfiguration.class);
private static final String EMPTY = "";
private NiFiProperties properties;
@Autowired
public void setProperties(final NiFiProperties properties) {
this.properties = properties;
}
@Bean
public SSLContext sslContext() {
return FrameworkSslContextHolder.getSslContext();
}
@Bean
public X509ExtendedKeyManager keyManager() {
return FrameworkSslContextHolder.getKeyManager();
}
@Bean
public X509ExtendedTrustManager trustManager() {
return FrameworkSslContextHolder.getTrustManager();
}
@Bean
public WatchServiceMonitorCommand watchServiceMonitorCommand() {
final WatchServiceMonitorCommand command;
if (isReloadEnabled()) {
final String reloadIntervalProperty = properties.getProperty(SECURITY_AUTO_RELOAD_INTERVAL);
final long reloadIntervalSeconds = Math.round(FormatUtils.getPreciseTimeDuration(reloadIntervalProperty, TimeUnit.SECONDS));
final Duration reloadDuration = Duration.ofSeconds(reloadIntervalSeconds);
final X509ExtendedKeyManager keyManager = keyManager();
final X509ExtendedTrustManager trustManager = trustManager();
if (keyManager instanceof KeyManagerListener keyManagerListener && trustManager instanceof TrustManagerListener trustManagerListener) {
final Set<Path> storeFileNames = getStoreFileNames();
final SecurityStoreChangedPathListener changedPathListener = new SecurityStoreChangedPathListener(
storeFileNames,
keyManagerListener,
FrameworkSslContextHolder.getKeyManagerBuilder(),
trustManagerListener,
FrameworkSslContextHolder.getTrustManagerBuilder()
);
final WatchService watchService = storeWatchService();
command = new WatchServiceMonitorCommand(watchService, changedPathListener);
watchServiceScheduler().scheduleAtFixedRate(command, reloadDuration);
logger.info("Scheduled Security Store Monitor with Duration [{}]", reloadDuration);
} else {
throw new IllegalStateException("Key Manager Listener or Trust Manager Listener not found");
}
} else {
command = null;
}
return command;
}
@Bean
public ThreadPoolTaskScheduler watchServiceScheduler() {
final ThreadPoolTaskScheduler scheduler;
if (isReloadEnabled()) {
scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("Security Store Monitor ");
} else {
scheduler = null;
}
return scheduler;
}
@Bean
public WatchService storeWatchService() {
final WatchService watchService;
final String keyStoreProperty = properties.getProperty(SECURITY_KEYSTORE);
if (keyStoreProperty == null || keyStoreProperty.isBlank()) {
watchService = null;
} else if (isReloadEnabled()) {
final Set<Path> storeDirectories = getStoreDirectories();
final FileSystem fileSystem = FileSystems.getDefault();
try {
watchService = fileSystem.newWatchService();
for (final Path storeDirectory : storeDirectories) {
storeDirectory.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY);
}
} catch (final IOException e) {
throw new UncheckedIOException("Store Watch Service creation failed", e);
}
} else {
watchService = null;
}
return watchService;
}
@Bean
public KeyStore keyStore() {
final KeyStore keyStore;
final String keyStorePath = properties.getProperty(SECURITY_KEYSTORE);
if (keyStorePath == null || keyStorePath.isBlank()) {
keyStore = null;
} else {
final char[] keyStorePassword = properties.getProperty(SECURITY_KEYSTORE_PASSWD, EMPTY).toCharArray();
final String keyStoreType = properties.getProperty(SECURITY_KEYSTORE_TYPE);
try (InputStream inputStream = new FileInputStream(keyStorePath)) {
keyStore = new StandardKeyStoreBuilder()
.inputStream(inputStream)
.password(keyStorePassword)
.type(keyStoreType)
.build();
} catch (final IOException e) {
throw new IllegalStateException("Failed to load Key Store [%s] Type [%s]".formatted(keyStorePath, keyStoreType), e);
}
}
return keyStore;
}
@Bean
public KeyStore trustStore() {
final KeyStore trustStore;
final String trustStorePath = properties.getProperty(SECURITY_TRUSTSTORE);
if (trustStorePath == null || trustStorePath.isBlank()) {
trustStore = null;
} else {
final char[] trustStorePassword = properties.getProperty(SECURITY_TRUSTSTORE_PASSWD, EMPTY).toCharArray();
final String trustStoreType = properties.getProperty(SECURITY_TRUSTSTORE_TYPE);
try (InputStream inputStream = new FileInputStream(trustStorePath)) {
trustStore = new StandardKeyStoreBuilder()
.inputStream(inputStream)
.password(trustStorePassword)
.type(trustStoreType)
.build();
} catch (final IOException e) {
throw new IllegalStateException("Failed to load Trust Store [%s] Type [%s]".formatted(trustStorePath, trustStoreType), e);
}
}
return trustStore;
}
private Set<Path> getStoreFileNames() {
final Set<Path> storeFileNames = new HashSet<>();
final Path keyStorePath = getKeyStorePath();
addStoreFileName(keyStorePath, storeFileNames);
final Path trustStorePath = getTrustStorePath();
addStoreFileName(trustStorePath, storeFileNames);
return storeFileNames;
}
private void addStoreFileName(final Path storePath, final Set<Path> storeFileNames) {
storeFileNames.add(storePath.getFileName());
if (Files.isSymbolicLink(storePath)) {
try {
final Path realStorePath = storePath.toRealPath();
storeFileNames.add(realStorePath.getFileName());
} catch (final IOException e) {
throw new UncheckedIOException("Failed to resolve Store Path Link [%s]".formatted(storePath), e);
}
}
}
private Set<Path> getStoreDirectories() {
final Set<Path> storeDirectories = new HashSet<>();
final Path keyStorePath = getKeyStorePath();
addStorePath(keyStorePath, storeDirectories);
final Path trustStorePath = getTrustStorePath();
addStorePath(trustStorePath, storeDirectories);
return storeDirectories;
}
private void addStorePath(final Path storePath, final Set<Path> storeDirectories) {
final Path storeDirectory = storePath.getParent();
storeDirectories.add(storeDirectory);
if (Files.isSymbolicLink(storePath)) {
try {
final Path realStorePath = storePath.toRealPath();
final Path realStoreDirectory = realStorePath.getParent();
storeDirectories.add(realStoreDirectory);
} catch (final IOException e) {
throw new UncheckedIOException("Failed to resolve Store Path Link [%s]".formatted(storePath), e);
}
}
}
private Path getKeyStorePath() {
final String keyStoreProperty = properties.getProperty(SECURITY_KEYSTORE);
if (keyStoreProperty == null || keyStoreProperty.isBlank()) {
throw new IllegalStateException("Security Property [%s] not configured".formatted(SECURITY_KEYSTORE));
}
return Paths.get(keyStoreProperty).toAbsolutePath();
}
private Path getTrustStorePath() {
final String trustStoreProperty = properties.getProperty(SECURITY_TRUSTSTORE);
if (trustStoreProperty == null || trustStoreProperty.isBlank()) {
throw new IllegalStateException("Security Property [%s] not configured".formatted(SECURITY_TRUSTSTORE));
}
return Paths.get(trustStoreProperty).toAbsolutePath();
}
private boolean isReloadEnabled() {
final String reloadEnabledProperty = properties.getProperty(SECURITY_AUTO_RELOAD_ENABLED);
return Boolean.parseBoolean(reloadEnabledProperty);
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.framework.ssl;
import org.apache.nifi.security.ssl.BuilderConfigurationException;
import org.apache.nifi.security.ssl.StandardKeyManagerBuilder;
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
import org.apache.nifi.security.ssl.StandardX509ExtendedKeyManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.util.Objects;
/**
* Framework implementation fo Key Manager Builder capable of reloading a Key Store when building a Key Manager
*/
public class FrameworkKeyManagerBuilder extends StandardKeyManagerBuilder {
private static final Logger logger = LoggerFactory.getLogger(FrameworkKeyManagerBuilder.class);
private final Path keyStorePath;
private final StandardKeyStoreBuilder keyStoreBuilder;
public FrameworkKeyManagerBuilder(
final Path keyStorePath,
final StandardKeyStoreBuilder keyStoreBuilder,
final char[] keyPassword
) {
this.keyStorePath = Objects.requireNonNull(keyStorePath, "Key Store Path required");
this.keyStoreBuilder = Objects.requireNonNull(keyStoreBuilder, "Key Store Builder required");
keyPassword(Objects.requireNonNull(keyPassword, "Key Password required"));
}
/**
* Build X.509 Extended Key Manager after loading Key Store
*
* @return X.509 Extended Key Manager
*/
@Override
public X509ExtendedKeyManager build() {
this.loadKeyStore();
final X509ExtendedKeyManager keyManager = super.build();
logger.info("Key Manager loaded from Key Store [{}]", keyStorePath);
return new StandardX509ExtendedKeyManager(keyManager);
}
private void loadKeyStore() {
try (InputStream inputStream = Files.newInputStream(keyStorePath)) {
final KeyStore loadedKeyStore = keyStoreBuilder.inputStream(inputStream).build();
keyStore(loadedKeyStore);
} catch (final IOException e) {
throw new BuilderConfigurationException("Key Store loading failed", e);
}
}
}

View File

@ -0,0 +1,80 @@
/*
* 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.framework.ssl;
import org.apache.nifi.security.ssl.KeyManagerBuilder;
import org.apache.nifi.security.ssl.TrustManagerBuilder;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.util.Objects;
/**
* Holder class for sharing SSLContext components loaded in Jetty Server with Spring Application Context objects
*/
public class FrameworkSslContextHolder {
private static SSLContext sslContext;
private static X509ExtendedKeyManager keyManager;
private static X509ExtendedTrustManager trustManager;
private static KeyManagerBuilder keyManagerBuilder;
private static TrustManagerBuilder trustManagerBuilder;
public static void setSslContext(final SSLContext sslContext) {
FrameworkSslContextHolder.sslContext = Objects.requireNonNull(sslContext, "SSL Context required");
}
public static SSLContext getSslContext() {
return sslContext;
}
public static void setKeyManager(final X509ExtendedKeyManager keyManager) {
FrameworkSslContextHolder.keyManager = Objects.requireNonNull(keyManager, "Key Manager required");
}
public static X509ExtendedKeyManager getKeyManager() {
return keyManager;
}
public static void setKeyManagerBuilder(final KeyManagerBuilder keyManagerBuilder) {
FrameworkSslContextHolder.keyManagerBuilder = Objects.requireNonNull(keyManagerBuilder, "Key Manager Builder required");
}
public static KeyManagerBuilder getKeyManagerBuilder() {
return keyManagerBuilder;
}
public static void setTrustManager(final X509ExtendedTrustManager trustManager) {
FrameworkSslContextHolder.trustManager = Objects.requireNonNull(trustManager, "Trust Manager required");
}
public static X509ExtendedTrustManager getTrustManager() {
return trustManager;
}
public static void setTrustManagerBuilder(final TrustManagerBuilder trustManagerBuilder) {
FrameworkSslContextHolder.trustManagerBuilder = Objects.requireNonNull(trustManagerBuilder, "Trust Manager Builder required");
}
public static TrustManagerBuilder getTrustManagerBuilder() {
return trustManagerBuilder;
}
}

View File

@ -0,0 +1,137 @@
/*
* 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.framework.ssl;
import org.apache.nifi.security.ssl.KeyManagerBuilder;
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
import org.apache.nifi.security.ssl.TrustManagerBuilder;
import org.apache.nifi.util.NiFiProperties;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.Optional;
import static org.apache.nifi.util.NiFiProperties.SECURITY_KEYSTORE;
import static org.apache.nifi.util.NiFiProperties.SECURITY_KEYSTORE_PASSWD;
import static org.apache.nifi.util.NiFiProperties.SECURITY_KEYSTORE_TYPE;
import static org.apache.nifi.util.NiFiProperties.SECURITY_KEY_PASSWD;
import static org.apache.nifi.util.NiFiProperties.SECURITY_TRUSTSTORE;
import static org.apache.nifi.util.NiFiProperties.SECURITY_TRUSTSTORE_PASSWD;
import static org.apache.nifi.util.NiFiProperties.SECURITY_TRUSTSTORE_TYPE;
/**
* Framework SSL Context Provider handles loading of Security Key Store and Trust Store along with component registration
*/
public class FrameworkSslContextProvider {
private static final String EMPTY = "";
private final NiFiProperties properties;
public FrameworkSslContextProvider(final NiFiProperties properties) {
this.properties = Objects.requireNonNull(properties, "Application Properties required");
}
/**
* Load SSL Context from Application Properties and register components with framework
*
* @return Loaded SSL Context or null when not configured
*/
public Optional<SSLContext> loadSslContext() {
final KeyManagerBuilder keyManagerBuilder = getKeyManagerBuilder();
final TrustManagerBuilder trustManagerBuilder = getTrustManagerBuilder();
final Optional<SSLContext> sslContextHolder;
if (keyManagerBuilder == null || trustManagerBuilder == null) {
sslContextHolder = Optional.empty();
} else {
final X509ExtendedKeyManager keyManager = keyManagerBuilder.build();
final X509ExtendedTrustManager trustManager = trustManagerBuilder.build();
final SSLContext sslContext = new StandardSslContextBuilder().keyManager(keyManager).trustManager(trustManager).build();
sslContextHolder = Optional.of(sslContext);
FrameworkSslContextHolder.setSslContext(sslContext);
FrameworkSslContextHolder.setKeyManager(keyManager);
FrameworkSslContextHolder.setKeyManagerBuilder(keyManagerBuilder);
FrameworkSslContextHolder.setTrustManager(trustManager);
FrameworkSslContextHolder.setTrustManagerBuilder(trustManagerBuilder);
}
return sslContextHolder;
}
private KeyManagerBuilder getKeyManagerBuilder() {
final KeyManagerBuilder keyManagerBuilder;
if (properties.isHTTPSConfigured()) {
final Path keyStorePath = getKeyStorePath();
final String keyStorePassword = properties.getProperty(SECURITY_KEYSTORE_PASSWD, EMPTY);
final char[] keyPassword = properties.getProperty(SECURITY_KEY_PASSWD, keyStorePassword).toCharArray();
final String keyStoreType = properties.getProperty(SECURITY_KEYSTORE_TYPE);
final StandardKeyStoreBuilder keyStoreBuilder = new StandardKeyStoreBuilder()
.password(keyStorePassword.toCharArray())
.type(keyStoreType);
keyManagerBuilder = new FrameworkKeyManagerBuilder(keyStorePath, keyStoreBuilder, keyPassword);
} else {
keyManagerBuilder = null;
}
return keyManagerBuilder;
}
private TrustManagerBuilder getTrustManagerBuilder() {
final TrustManagerBuilder trustManagerBuilder;
if (properties.isHTTPSConfigured()) {
final Path trustStorePath = getTrustStorePath();
final String trustStorePassword = properties.getProperty(SECURITY_TRUSTSTORE_PASSWD, EMPTY);
final String trustStoreType = properties.getProperty(SECURITY_TRUSTSTORE_TYPE);
final StandardKeyStoreBuilder trustStoreBuilder = new StandardKeyStoreBuilder()
.password(trustStorePassword.toCharArray())
.type(trustStoreType);
trustManagerBuilder = new FrameworkTrustManagerBuilder(trustStorePath, trustStoreBuilder);
} else {
trustManagerBuilder = null;
}
return trustManagerBuilder;
}
private Path getKeyStorePath() {
final String keyStoreProperty = properties.getProperty(SECURITY_KEYSTORE);
if (keyStoreProperty == null || keyStoreProperty.isBlank()) {
throw new IllegalStateException("Security Property [%s] not configured".formatted(SECURITY_KEYSTORE));
}
return Paths.get(keyStoreProperty);
}
private Path getTrustStorePath() {
final String trustStoreProperty = properties.getProperty(SECURITY_TRUSTSTORE);
if (trustStoreProperty == null || trustStoreProperty.isBlank()) {
throw new IllegalStateException("Security Property [%s] not configured".formatted(SECURITY_TRUSTSTORE));
}
return Paths.get(trustStoreProperty);
}
}

View File

@ -0,0 +1,66 @@
/*
* 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.framework.ssl;
import org.apache.nifi.security.ssl.BuilderConfigurationException;
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
import org.apache.nifi.security.ssl.StandardTrustManagerBuilder;
import org.apache.nifi.security.ssl.StandardX509ExtendedTrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.util.Objects;
/**
* Framework implementation fo Trust Manager Builder capable of reloading a Trust Store when building a Trust Manager
*/
public class FrameworkTrustManagerBuilder extends StandardTrustManagerBuilder {
private static final Logger logger = LoggerFactory.getLogger(FrameworkTrustManagerBuilder.class);
private final Path trustStorePath;
private final StandardKeyStoreBuilder trustStoreBuilder;
public FrameworkTrustManagerBuilder(final Path trustStorePath, final StandardKeyStoreBuilder trustStoreBuilder) {
this.trustStorePath = Objects.requireNonNull(trustStorePath, "Trust Store Path required");
this.trustStoreBuilder = Objects.requireNonNull(trustStoreBuilder, "Trust Store Builder required");
}
@Override
public X509ExtendedTrustManager build() {
loadTrustStore();
final X509ExtendedTrustManager trustManager = super.build();
logger.info("Trust Manager loaded from Trust Store [{}]", trustStorePath);
return new StandardX509ExtendedTrustManager(trustManager);
}
private void loadTrustStore() {
try (InputStream inputStream = Files.newInputStream(trustStorePath)) {
final KeyStore loadedTrustStore = trustStoreBuilder.inputStream(inputStream).build();
trustStore(loadedTrustStore);
} catch (final IOException e) {
throw new BuilderConfigurationException("Trust Store loading failed", e);
}
}
}

View File

@ -0,0 +1,89 @@
/*
* 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.framework.ssl;
import org.apache.nifi.security.ssl.KeyManagerBuilder;
import org.apache.nifi.security.ssl.KeyManagerListener;
import org.apache.nifi.security.ssl.TrustManagerBuilder;
import org.apache.nifi.security.ssl.TrustManagerListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
/**
* Changed Path Listener loads new Key Manager and Trust Manager implementations from Path resources
*/
public class SecurityStoreChangedPathListener implements WatchServiceMonitorCommand.ChangedPathListener {
private static final Logger logger = LoggerFactory.getLogger(SecurityStoreChangedPathListener.class);
private final Set<Path> storeFileNames;
private final KeyManagerListener keyManagerListener;
private final KeyManagerBuilder keyManagerBuilder;
private final TrustManagerListener trustManagerListener;
private final TrustManagerBuilder trustManagerBuilder;
/**
* Security Store Changed Path Listener reloads Key Manager and Trust Manager when changed paths contain files match store file names
*
* @param storeFileNames Key Store and Trust Store File Names that must be matched
* @param keyManagerListener Key Manager Listener for handling updated Key Manager
* @param keyManagerBuilder Key Manager Builder for creating new Key Manager instances
* @param trustManagerListener Trust Manager Listener for handling updated Trust Manager
* @param trustManagerBuilder Trust Manager Builder for creating new Trust Manager instances
*/
public SecurityStoreChangedPathListener(
final Set<Path> storeFileNames,
final KeyManagerListener keyManagerListener,
final KeyManagerBuilder keyManagerBuilder,
final TrustManagerListener trustManagerListener,
final TrustManagerBuilder trustManagerBuilder
) {
this.storeFileNames = Objects.requireNonNull(storeFileNames, "Store File Names required");
this.keyManagerListener = Objects.requireNonNull(keyManagerListener, "Key Manager Listener required");
this.keyManagerBuilder = Objects.requireNonNull(keyManagerBuilder, "Key Manager Builder required");
this.trustManagerListener = Objects.requireNonNull(trustManagerListener, "Trust Manager Listener required");
this.trustManagerBuilder = Objects.requireNonNull(trustManagerBuilder, "Trust Manager Builder required");
}
@Override
public void onChanged(final List<Path> changedPaths) {
final Optional<Path> storeFileNameFound = changedPaths.stream()
.map(Path::getFileName)
.filter(storeFileNames::contains)
.findFirst();
if (storeFileNameFound.isPresent()) {
final X509ExtendedKeyManager keyManager = keyManagerBuilder.build();
final X509ExtendedTrustManager trustManager = trustManagerBuilder.build();
keyManagerListener.setKeyManager(keyManager);
trustManagerListener.setTrustManager(trustManager);
logger.info("Key Manager and Trust Manager Reloaded from Changed Path [{}]", storeFileNameFound.get().toAbsolutePath());
}
}
}

View File

@ -0,0 +1,106 @@
/*
* 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.framework.ssl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Runnable command to poll a configured File Watch Service and notify registered listeners
*/
public class WatchServiceMonitorCommand implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(WatchServiceMonitorCommand.class);
private final WatchService watchService;
private final ChangedPathListener changedPathListener;
public WatchServiceMonitorCommand(final WatchService watchService, final ChangedPathListener changedPathListener) {
this.watchService = Objects.requireNonNull(watchService, "Watch Service required");
this.changedPathListener = Objects.requireNonNull(changedPathListener, "Changed Path Listener required");
}
/**
* Poll Watch Service and process events
*/
public void run() {
final WatchKey watchKey = watchService.poll();
if (watchKey == null) {
logger.debug("Watch Key not found");
} else {
try {
processWatchKey(watchKey);
} finally {
if (watchKey.reset()) {
logger.debug("Watch Key reset completed");
} else {
logger.warn("Watch Key reset failed: Watch Service no longer valid");
}
}
}
}
private void processWatchKey(final WatchKey watchKey) {
final List<WatchEvent<?>> events = watchKey.pollEvents();
final List<Path> changedPaths = getChangedPaths(events);
if (changedPaths.isEmpty()) {
logger.debug("Changed Paths not found");
} else {
logger.debug("Changed Paths found {}", changedPaths);
changedPathListener.onChanged(changedPaths);
}
}
private List<Path> getChangedPaths(final List<WatchEvent<?>> events) {
final List<Path> changedPaths = new ArrayList<>();
for (final WatchEvent<?> event : events) {
final WatchEvent.Kind<?> kind = event.kind();
if (StandardWatchEventKinds.OVERFLOW == kind) {
continue;
}
final Object context = event.context();
if (context instanceof Path path) {
changedPaths.add(path);
}
}
return changedPaths;
}
/**
* Changed Path Listener for handling file watch events
*/
public interface ChangedPathListener {
/**
* Handle Changed Paths
*
* @param changedPaths Changed Paths
*/
void onChanged(List<Path> changedPaths);
}
}

View File

@ -1,45 +0,0 @@
/*
* 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.spring;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.nar.ExtensionManagerHolder;
import org.apache.nifi.nar.StandardExtensionDiscoveringManager;
import org.springframework.beans.factory.FactoryBean;
/**
* Spring factory bean that returns the ExtensionManager instance from ExtensionManagerHolder.
*
* The ExtensionManagerHolder will be initialized before the Spring context starts.
*/
public class ExtensionManagerFactoryBean implements FactoryBean<ExtensionManager> {
@Override
public ExtensionManager getObject() {
return ExtensionManagerHolder.getExtensionManager();
}
@Override
public Class<?> getObjectType() {
return StandardExtensionDiscoveringManager.class;
}
@Override
public boolean isSingleton() {
return true;
}
}

View File

@ -1,159 +0,0 @@
/*
* 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.spring;
import org.apache.nifi.admin.service.AuditService;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.cluster.coordination.ClusterCoordinator;
import org.apache.nifi.cluster.coordination.heartbeat.HeartbeatMonitor;
import org.apache.nifi.cluster.protocol.NodeProtocolSender;
import org.apache.nifi.controller.FlowController;
import org.apache.nifi.controller.leader.election.LeaderElectionManager;
import org.apache.nifi.controller.repository.FlowFileEventRepository;
import org.apache.nifi.controller.status.history.StatusHistoryRepository;
import org.apache.nifi.encrypt.PropertyEncryptor;
import org.apache.nifi.nar.ExtensionDiscoveringManager;
import org.apache.nifi.reporting.BulletinRepository;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.validation.RuleViolationsManager;
import org.apache.nifi.web.revision.RevisionManager;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* Factory bean for creating a singleton FlowController instance. If the application is configured to act as the cluster manager, then null is always returned as the created instance.
*/
@SuppressWarnings("rawtypes")
public class FlowControllerFactoryBean implements FactoryBean, ApplicationContextAware {
private ApplicationContext applicationContext;
private FlowController flowController;
private NiFiProperties properties;
private Authorizer authorizer;
private AuditService auditService;
private PropertyEncryptor encryptor;
private BulletinRepository bulletinRepository;
private ClusterCoordinator clusterCoordinator;
private LeaderElectionManager leaderElectionManager;
private ExtensionDiscoveringManager extensionManager;
private RevisionManager revisionManager;
private RuleViolationsManager ruleViolationsManager;
private StatusHistoryRepository statusHistoryRepository;
@Override
public Object getObject() throws Exception {
if (flowController == null) {
final FlowFileEventRepository flowFileEventRepository = applicationContext.getBean("flowFileEventRepository", FlowFileEventRepository.class);
if (properties.isNode()) {
final NodeProtocolSender nodeProtocolSender = applicationContext.getBean("nodeProtocolSender", NodeProtocolSender.class);
final HeartbeatMonitor heartbeatMonitor = applicationContext.getBean("heartbeatMonitor", HeartbeatMonitor.class);
flowController = FlowController.createClusteredInstance(
flowFileEventRepository,
properties,
authorizer,
auditService,
encryptor,
nodeProtocolSender,
bulletinRepository,
clusterCoordinator,
heartbeatMonitor,
leaderElectionManager,
extensionManager,
revisionManager,
statusHistoryRepository,
ruleViolationsManager);
} else {
flowController = FlowController.createStandaloneInstance(
flowFileEventRepository,
properties,
authorizer,
auditService,
encryptor,
bulletinRepository,
extensionManager,
statusHistoryRepository,
ruleViolationsManager);
}
}
return flowController;
}
@Override
public Class getObjectType() {
return FlowController.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void setProperties(NiFiProperties properties) {
this.properties = properties;
}
public void setAuthorizer(final Authorizer authorizer) {
this.authorizer = authorizer;
}
public void setEncryptor(final PropertyEncryptor encryptor) {
this.encryptor = encryptor;
}
public void setAuditService(final AuditService auditService) {
this.auditService = auditService;
}
public void setBulletinRepository(final BulletinRepository bulletinRepository) {
this.bulletinRepository = bulletinRepository;
}
public void setClusterCoordinator(final ClusterCoordinator clusterCoordinator) {
this.clusterCoordinator = clusterCoordinator;
}
public void setLeaderElectionManager(final LeaderElectionManager leaderElectionManager) {
this.leaderElectionManager = leaderElectionManager;
}
public void setExtensionManager(ExtensionDiscoveringManager extensionManager) {
this.extensionManager = extensionManager;
}
public void setRevisionManager(final RevisionManager revisionManager) {
this.revisionManager = revisionManager;
}
public void setRuleViolationsManager(final RuleViolationsManager ruleViolationsManager) {
this.ruleViolationsManager = ruleViolationsManager;
}
public void setStatusHistoryRepository(final StatusHistoryRepository statusHistoryRepository) {
this.statusHistoryRepository = statusHistoryRepository;
}
}

View File

@ -1,83 +0,0 @@
/*
* 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.spring;
import java.lang.reflect.InvocationTargetException;
import java.util.Optional;
import java.util.Set;
import org.apache.nifi.controller.leader.election.LeaderElectionManager;
import org.apache.nifi.controller.leader.election.StandaloneLeaderElectionManager;
import org.apache.nifi.nar.ExtensionDefinition;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.nar.NarThreadContextClassLoader;
import org.apache.nifi.util.NiFiProperties;
import org.springframework.beans.factory.FactoryBean;
import static org.apache.nifi.util.NiFiProperties.CLUSTER_LEADER_ELECTION_IMPLEMENTATION;
import static org.apache.nifi.util.NiFiProperties.DEFAULT_CLUSTER_LEADER_ELECTION_IMPLEMENTATION;
public class LeaderElectionManagerFactoryBean implements FactoryBean<LeaderElectionManager> {
private ExtensionManager extensionManager;
private NiFiProperties properties;
@Override
public LeaderElectionManager getObject() throws Exception {
final boolean isNode = properties.isNode();
if (isNode) {
return loadClusterLeaderElectionManager();
} else {
return new StandaloneLeaderElectionManager();
}
}
@Override
public Class<?> getObjectType() {
return LeaderElectionManager.class;
}
@Override
public boolean isSingleton() {
return true;
}
public void setExtensionManager(final ExtensionManager extensionManager) {
this.extensionManager = extensionManager;
}
public void setProperties(final NiFiProperties properties) {
this.properties = properties;
}
private LeaderElectionManager loadClusterLeaderElectionManager() throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
final String leaderElectionImplementation = properties.getProperty(CLUSTER_LEADER_ELECTION_IMPLEMENTATION, DEFAULT_CLUSTER_LEADER_ELECTION_IMPLEMENTATION);
final Set<ExtensionDefinition> extensions = extensionManager.getExtensions(LeaderElectionManager.class);
final Optional<ExtensionDefinition> extensionFound = extensions.stream()
.filter(extensionDefinition -> {
final String extensionClassName = extensionDefinition.getImplementationClassName();
return extensionClassName.equals(leaderElectionImplementation) || extensionClassName.endsWith(leaderElectionImplementation);
})
.findFirst();
final ExtensionDefinition extension = extensionFound.orElseThrow(() -> {
final String message = String.format("No Extensions Found for %s", LeaderElectionManager.class.getName());
return new IllegalStateException(message);
});
final String extensionImplementationClass = extension.getImplementationClassName();
return NarThreadContextClassLoader.createInstance(extensionManager, extensionImplementationClass, LeaderElectionManager.class, properties);
}
}

View File

@ -1,44 +0,0 @@
/*
* 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.spring;
import org.apache.nifi.controller.repository.metrics.RingBufferEventRepository;
import org.springframework.beans.factory.FactoryBean;
public class RingBufferEventRepositoryBean implements FactoryBean<RingBufferEventRepository> {
private RingBufferEventRepository repository;
@Override
public RingBufferEventRepository getObject() throws Exception {
if (repository == null) {
// create the h2 repository
repository = new RingBufferEventRepository(5);
}
return repository;
}
@Override
public Class<?> getObjectType() {
return RingBufferEventRepository.class;
}
@Override
public boolean isSingleton() {
return true;
}
}

View File

@ -1,100 +0,0 @@
/*
* 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.spring;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.cluster.coordination.ClusterCoordinator;
import org.apache.nifi.cluster.protocol.impl.NodeProtocolSenderListener;
import org.apache.nifi.controller.FlowController;
import org.apache.nifi.controller.StandardFlowService;
import org.apache.nifi.encrypt.PropertyEncryptor;
import org.apache.nifi.services.FlowService;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.revision.RevisionManager;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* Factory bean for creating a singleton FlowController instance. If the application is configured to act as the cluster manager, then null is always returned as the created instance.
*/
@SuppressWarnings("rawtypes")
public class StandardFlowServiceFactoryBean implements FactoryBean, ApplicationContextAware {
private ApplicationContext applicationContext;
private FlowService flowService;
private NiFiProperties properties;
private PropertyEncryptor encryptor;
private Authorizer authorizer;
@Override
public Object getObject() throws Exception {
if (flowService == null) {
final FlowController flowController = applicationContext.getBean("flowController", FlowController.class);
final RevisionManager revisionManager = applicationContext.getBean("revisionManager", RevisionManager.class);
if (properties.isNode()) {
final NodeProtocolSenderListener nodeProtocolSenderListener = applicationContext.getBean("nodeProtocolSenderListener", NodeProtocolSenderListener.class);
final ClusterCoordinator clusterCoordinator = applicationContext.getBean("clusterCoordinator", ClusterCoordinator.class);
flowService = StandardFlowService.createClusteredInstance(
flowController,
properties,
nodeProtocolSenderListener,
clusterCoordinator,
revisionManager,
authorizer);
} else {
flowService = StandardFlowService.createStandaloneInstance(
flowController,
properties,
revisionManager,
authorizer);
}
}
return flowService;
}
@Override
public Class getObjectType() {
return FlowService.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void setProperties(NiFiProperties properties) {
this.properties = properties;
}
public void setEncryptor(PropertyEncryptor encryptor) {
this.encryptor = encryptor;
}
public void setAuthorizer(Authorizer authorizer) {
this.authorizer = authorizer;
}
}

View File

@ -1,75 +0,0 @@
/*
* 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.spring;
import org.apache.nifi.controller.status.history.StatusHistoryRepository;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.nar.NarThreadContextClassLoader;
import org.apache.nifi.util.NiFiProperties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* Factory bean for creating a singleton StatusHistoryRepository instance.
*/
public class StatusHistoryRepositoryFactoryBean implements FactoryBean<StatusHistoryRepository>, ApplicationContextAware {
private static final String DEFAULT_COMPONENT_STATUS_REPO_IMPLEMENTATION = "org.apache.nifi.controller.status.history.VolatileComponentStatusRepository";
private ApplicationContext applicationContext;
private NiFiProperties nifiProperties;
private ExtensionManager extensionManager;
private StatusHistoryRepository statusHistoryRepository;
@Override
public StatusHistoryRepository getObject() throws Exception {
final String implementationClassName = nifiProperties.getProperty(NiFiProperties.COMPONENT_STATUS_REPOSITORY_IMPLEMENTATION, DEFAULT_COMPONENT_STATUS_REPO_IMPLEMENTATION);
if (implementationClassName == null) {
throw new BeanCreationException("Cannot create Status History Repository because the NiFi Properties is missing the following property: "
+ NiFiProperties.COMPONENT_STATUS_REPOSITORY_IMPLEMENTATION);
}
try {
statusHistoryRepository = NarThreadContextClassLoader.createInstance(extensionManager, implementationClassName, StatusHistoryRepository.class, nifiProperties);
statusHistoryRepository.start();
return statusHistoryRepository;
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Class<?> getObjectType() {
return StatusHistoryRepository.class;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void setNifiProperties(NiFiProperties nifiProperties) {
this.nifiProperties = nifiProperties;
}
public void setExtensionManager(ExtensionManager extensionManager) {
this.extensionManager = extensionManager;
}
}

View File

@ -1,91 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<beans default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<!-- nifi properties -->
<bean id="nifiProperties" class="org.apache.nifi.properties.NiFiPropertiesLoader" factory-method="load">
</bean>
<!-- flow file event repository -->
<bean id="flowFileEventRepository" class="org.apache.nifi.spring.RingBufferEventRepositoryBean">
</bean>
<bean id="propertyEncryptor" class="org.apache.nifi.encrypt.PropertyEncryptorFactory" factory-method="getPropertyEncryptor">
<constructor-arg ref="nifiProperties" />
</bean>
<!-- extension manager -->
<bean id="extensionManager" class="org.apache.nifi.spring.ExtensionManagerFactoryBean">
</bean>
<bean id="ruleViolationsManager" class="org.apache.nifi.validation.StandardRuleViolationsManager">
</bean>
<!-- flow controller -->
<bean id="flowController" class="org.apache.nifi.spring.FlowControllerFactoryBean">
<property name="properties" ref="nifiProperties"/>
<property name="authorizer" ref="authorizer" />
<property name="auditService" ref="auditService" />
<property name="encryptor" ref="propertyEncryptor" />
<property name="bulletinRepository" ref="bulletinRepository" />
<property name="clusterCoordinator" ref="clusterCoordinator" />
<property name="leaderElectionManager" ref="leaderElectionManager" />
<property name="extensionManager" ref="extensionManager" />
<property name="revisionManager" ref="revisionManager" />
<property name="ruleViolationsManager" ref="ruleViolationsManager" />
<property name="statusHistoryRepository" ref="statusHistoryRepository" />
</bean>
<!-- flow service -->
<bean id="flowService" class="org.apache.nifi.spring.StandardFlowServiceFactoryBean">
<property name="properties" ref="nifiProperties"/>
<property name="encryptor" ref="propertyEncryptor" />
<property name="authorizer" ref="authorizer" />
</bean>
<!-- extension manifest parser -->
<bean id="extensionManifestParser" class="org.apache.nifi.extension.manifest.parser.jaxb.JAXBExtensionManifestParser" />
<!-- runtime manifest generator -->
<bean id="runtimeManifestService" class="org.apache.nifi.manifest.StandardRuntimeManifestService" >
<constructor-arg ref="extensionManager" />
<constructor-arg ref="extensionManifestParser" />
</bean>
<!-- bulletin repository -->
<bean id="bulletinRepository" class="org.apache.nifi.events.VolatileBulletinRepository" />
<bean id="diagnosticsFactory" class="org.apache.nifi.diagnostics.bootstrap.BootstrapDiagnosticsFactory">
<property name="flowController" ref="flowController" />
<property name="nifiProperties" ref="nifiProperties" />
</bean>
<bean id="eventReporter" class="org.apache.nifi.events.StandardEventReporter">
<constructor-arg ref="bulletinRepository" />
</bean>
<bean id="statusHistoryRepository" class="org.apache.nifi.spring.StatusHistoryRepositoryFactoryBean">
<property name="nifiProperties" ref="nifiProperties"/>
<property name="extensionManager" ref="extensionManager" />
</bean>
<bean id="statusHistoryDumpFactory" class="org.apache.nifi.controller.status.history.JsonNodeStatusHistoryDumpFactory">
<property name="statusHistoryRepository" ref="statusHistoryRepository" />
</bean>
</beans>

View File

@ -1,319 +0,0 @@
/*
* 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.controller;
import org.apache.commons.io.IOUtils;
import org.apache.nifi.admin.service.AuditService;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.cluster.protocol.StandardDataFlow;
import org.apache.nifi.controller.flow.VersionedDataflow;
import org.apache.nifi.controller.repository.FlowFileEventRepository;
import org.apache.nifi.controller.serialization.FlowSerializationException;
import org.apache.nifi.controller.serialization.FlowSerializer;
import org.apache.nifi.controller.serialization.ScheduledStateLookup;
import org.apache.nifi.controller.serialization.VersionedFlowSerializer;
import org.apache.nifi.controller.status.history.StatusHistoryRepository;
import org.apache.nifi.encrypt.PropertyEncryptor;
import org.apache.nifi.events.VolatileBulletinRepository;
import org.apache.nifi.nar.ExtensionDiscoveringManager;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.api.dto.ConnectableDTO;
import org.apache.nifi.web.api.dto.ConnectionDTO;
import org.apache.nifi.web.api.dto.FlowSnippetDTO;
import org.apache.nifi.web.api.dto.LabelDTO;
import org.apache.nifi.web.api.dto.PortDTO;
import org.apache.nifi.web.api.dto.ProcessGroupDTO;
import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
import org.apache.nifi.web.api.dto.ProcessorDTO;
import org.apache.nifi.web.revision.RevisionManager;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;
/**
*/
@Disabled
public class StandardFlowServiceTest {
private StandardFlowService flowService;
private FlowController flowController;
private NiFiProperties properties;
private FlowFileEventRepository mockFlowFileEventRepository;
private Authorizer authorizer;
private AuditService mockAuditService;
private PropertyEncryptor mockEncryptor;
private RevisionManager revisionManager;
private ExtensionDiscoveringManager extensionManager;
private StatusHistoryRepository statusHistoryRepository;
@BeforeAll
public static void setupSuite() {
System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, StandardFlowServiceTest.class.getResource("/conf/nifi.properties").getFile());
}
@BeforeEach
public void setup() throws Exception {
properties = NiFiProperties.createBasicNiFiProperties(null);
mockFlowFileEventRepository = mock(FlowFileEventRepository.class);
authorizer = mock(Authorizer.class);
mockAuditService = mock(AuditService.class);
revisionManager = mock(RevisionManager.class);
extensionManager = mock(ExtensionDiscoveringManager.class);
flowController = FlowController.createStandaloneInstance(mockFlowFileEventRepository, properties, authorizer, mockAuditService, mockEncryptor,
new VolatileBulletinRepository(), extensionManager, statusHistoryRepository, null);
flowService = StandardFlowService.createStandaloneInstance(flowController, properties, revisionManager, authorizer);
statusHistoryRepository = mock(StatusHistoryRepository.class);
}
@Test
public void testLoadWithFlow() throws IOException {
byte[] flowBytes = IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow.json"));
flowService.load(new StandardDataFlow(flowBytes, null, null, new HashSet<>()));
final FlowSerializer<VersionedDataflow> serializer = new VersionedFlowSerializer(extensionManager);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
final VersionedDataflow doc = serializer.transform(flowController, ScheduledStateLookup.IDENTITY_LOOKUP);
serializer.serialize(doc, baos);
String expectedFlow = new String(flowBytes).trim();
String actualFlow = new String(baos.toByteArray()).trim();
Assertions.assertEquals(expectedFlow, actualFlow);
}
@Test
public void testLoadWithCorruptFlow() throws IOException {
byte[] flowBytes = IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow-corrupt.xml"));
assertThrows(FlowSerializationException.class, () ->
flowService.load(new StandardDataFlow(flowBytes, null,
null, new HashSet<>())));
}
@Test
public void testLoadExistingFlow() throws IOException {
byte[] flowBytes = IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow.json"));
flowService.load(new StandardDataFlow(flowBytes, null, null, new HashSet<>()));
flowBytes = IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow-inheritable.json"));
flowService.load(new StandardDataFlow(flowBytes, null, null, new HashSet<>()));
final FlowSerializer<VersionedDataflow> serializer = new VersionedFlowSerializer(extensionManager);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
final VersionedDataflow doc = serializer.transform(flowController, ScheduledStateLookup.IDENTITY_LOOKUP);
serializer.serialize(doc, baos);
String expectedFlow = new String(flowBytes).trim();
String actualFlow = new String(baos.toByteArray()).trim();
Assertions.assertEquals(expectedFlow, actualFlow);
}
@Test
public void testLoadExistingFlowWithUninheritableFlow() throws IOException {
byte[] originalBytes = IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow.json"));
flowService.load(new StandardDataFlow(originalBytes, null, null, new HashSet<>()));
try {
byte[] updatedBytes = IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow-uninheritable.json"));
flowService.load(new StandardDataFlow(updatedBytes, null, null, new HashSet<>()));
fail("should have thrown " + UninheritableFlowException.class);
} catch (UninheritableFlowException ufe) {
final FlowSerializer<VersionedDataflow> serializer = new VersionedFlowSerializer(extensionManager);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
final VersionedDataflow doc = serializer.transform(flowController, ScheduledStateLookup.IDENTITY_LOOKUP);
serializer.serialize(doc, baos);
String expectedFlow = new String(originalBytes).trim();
String actualFlow = new String(baos.toByteArray()).trim();
Assertions.assertEquals(expectedFlow, actualFlow);
}
}
@Test
public void testLoadExistingFlowWithCorruptFlow() throws IOException {
byte[] originalBytes = IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow.json"));
flowService.load(new StandardDataFlow(originalBytes, null, null, new HashSet<>()));
try {
byte[] updatedBytes = IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow-corrupt.xml"));
flowService.load(new StandardDataFlow(updatedBytes, null, null, new HashSet<>()));
fail("should have thrown " + FlowSerializationException.class);
} catch (FlowSerializationException ufe) {
final FlowSerializer<VersionedDataflow> serializer = new VersionedFlowSerializer(extensionManager);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
final VersionedDataflow doc = serializer.transform(flowController, ScheduledStateLookup.IDENTITY_LOOKUP);
serializer.serialize(doc, baos);
String expectedFlow = new String(originalBytes).trim();
String actualFlow = new String(baos.toByteArray()).trim();
Assertions.assertEquals(expectedFlow, actualFlow);
}
}
private void assertEquals(ProcessGroupDTO expected, ProcessGroupDTO actual) {
if (expected == null && actual == null) {
return;
}
Assertions.assertEquals(expected.getComments(), actual.getComments());
assertEquals(expected.getContents(), actual.getContents());
}
private void assertEquals(FlowSnippetDTO expected, FlowSnippetDTO actual) {
if (expected == null && actual == null) {
return;
}
// check connections
Assertions.assertEquals(expected.getConnections().size(), actual.getConnections().size());
List<ConnectionDTO> expectedConnections = new ArrayList<>(expected.getConnections());
List<ConnectionDTO> actualConnections = new ArrayList<>(actual.getConnections());
for (int i = 0; i < expectedConnections.size(); i++) {
assertEquals(expectedConnections.get(i), actualConnections.get(i));
}
// check groups
Assertions.assertEquals(expected.getProcessGroups().size(), actual.getProcessGroups().size());
List<ProcessGroupDTO> expectedProcessGroups = new ArrayList<>(expected.getProcessGroups());
List<ProcessGroupDTO> actualProcessGroups = new ArrayList<>(actual.getProcessGroups());
for (int i = 0; i < expectedProcessGroups.size(); i++) {
assertEquals(expectedProcessGroups.get(i), actualProcessGroups.get(i));
}
// check input ports
Assertions.assertEquals(expected.getInputPorts().size(), actual.getInputPorts().size());
List<PortDTO> expectedInputPorts = new ArrayList<>(expected.getInputPorts());
List<PortDTO> actualInputPort = new ArrayList<>(actual.getInputPorts());
for (int i = 0; i < expectedInputPorts.size(); i++) {
assertEquals(expectedInputPorts.get(i), actualInputPort.get(i));
}
// check labels
Assertions.assertEquals(expected.getLabels().size(), actual.getLabels().size());
List<LabelDTO> expectedLabels = new ArrayList<>(expected.getLabels());
List<LabelDTO> actualLabels = new ArrayList<>(actual.getLabels());
for (int i = 0; i < expectedLabels.size(); i++) {
assertEquals(expectedLabels.get(i), actualLabels.get(i));
}
// check output ports
Assertions.assertEquals(expected.getOutputPorts().size(), actual.getOutputPorts().size());
List<PortDTO> expectedOutputPorts = new ArrayList<>(expected.getOutputPorts());
List<PortDTO> actualOutputPort = new ArrayList<>(actual.getOutputPorts());
for (int i = 0; i < expectedOutputPorts.size(); i++) {
assertEquals(expectedOutputPorts.get(i), actualOutputPort.get(i));
}
// check processors
Assertions.assertEquals(expected.getProcessors().size(), actual.getProcessors().size());
List<ProcessorDTO> expectedProcessors = new ArrayList<>(expected.getProcessors());
List<ProcessorDTO> actualProcessors = new ArrayList<>(actual.getProcessors());
for (int i = 0; i < expectedProcessors.size(); i++) {
assertEquals(expectedProcessors.get(i), actualProcessors.get(i));
}
}
private void assertEquals(ConnectionDTO expected, ConnectionDTO actual) {
if (expected == null && actual == null) {
return;
}
Assertions.assertEquals(expected.getAvailableRelationships(), actual.getAvailableRelationships());
assertEquals(expected.getDestination(), actual.getDestination());
Assertions.assertEquals(expected.getId(), actual.getId());
Assertions.assertEquals(expected.getName(), actual.getName());
Assertions.assertEquals(expected.getParentGroupId(), actual.getParentGroupId());
Assertions.assertEquals(expected.getSelectedRelationships(), actual.getSelectedRelationships());
assertEquals(expected.getSource(), actual.getSource());
}
private void assertEquals(ConnectableDTO expected, ConnectableDTO actual) {
if (expected == null && actual == null) {
return;
}
Assertions.assertEquals(expected.getGroupId(), actual.getGroupId());
Assertions.assertEquals(expected.getId(), actual.getId());
Assertions.assertEquals(expected.getName(), actual.getName());
Assertions.assertEquals(expected.getType(), actual.getType());
}
private void assertEquals(PortDTO expected, PortDTO actual) {
if (expected == null && actual == null) {
return;
}
Assertions.assertEquals(expected.getId(), actual.getId());
Assertions.assertEquals(expected.getName(), actual.getName());
Assertions.assertEquals(expected.getParentGroupId(), actual.getParentGroupId());
}
private void assertEquals(LabelDTO expected, LabelDTO actual) {
if (expected == null && actual == null) {
return;
}
Assertions.assertEquals(expected.getId(), actual.getId());
Assertions.assertEquals(expected.getLabel(), actual.getLabel());
Assertions.assertEquals(expected.getParentGroupId(), actual.getParentGroupId());
Assertions.assertEquals(expected.getStyle(), actual.getStyle());
}
private void assertEquals(ProcessorDTO expected, ProcessorDTO actual) {
if (expected == null && actual == null) {
return;
}
Assertions.assertEquals(expected.getId(), actual.getId());
Assertions.assertEquals(expected.getName(), actual.getName());
Assertions.assertEquals(expected.getParentGroupId(), actual.getParentGroupId());
Assertions.assertEquals(expected.getStyle(), actual.getStyle());
Assertions.assertEquals(expected.getType(), actual.getType());
Assertions.assertEquals(expected.getState(), actual.getState());
Assertions.assertEquals(expected.getRelationships(), actual.getRelationships());
Assertions.assertEquals(expected.getValidationErrors(), actual.getValidationErrors());
assertEquals(expected.getConfig(), actual.getConfig());
}
private void assertEquals(ProcessorConfigDTO expected, ProcessorConfigDTO actual) {
if (expected == null && actual == null) {
return;
}
Assertions.assertEquals(expected.getAnnotationData(), actual.getAnnotationData());
Assertions.assertEquals(expected.getComments(), actual.getComments());
Assertions.assertEquals(expected.getConcurrentlySchedulableTaskCount(), actual.getConcurrentlySchedulableTaskCount());
Assertions.assertEquals(expected.getCustomUiUrl(), actual.getCustomUiUrl());
Assertions.assertEquals(expected.getDescriptors(), actual.getDescriptors());
Assertions.assertEquals(expected.getProperties(), actual.getProperties());
Assertions.assertEquals(expected.getSchedulingPeriod(), actual.getSchedulingPeriod());
}
}

View File

@ -1,150 +0,0 @@
/*
* 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.controller.reporting;
import org.apache.commons.io.FileUtils;
import org.apache.nifi.admin.service.AuditService;
import org.apache.nifi.authorization.AbstractPolicyBasedAuthorizer;
import org.apache.nifi.authorization.AccessPolicy;
import org.apache.nifi.authorization.Group;
import org.apache.nifi.authorization.MockPolicyBasedAuthorizer;
import org.apache.nifi.authorization.RequestAction;
import org.apache.nifi.authorization.User;
import org.apache.nifi.bundle.Bundle;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.controller.DummyScheduledReportingTask;
import org.apache.nifi.controller.FlowController;
import org.apache.nifi.controller.ReportingTaskNode;
import org.apache.nifi.controller.repository.FlowFileEventRepository;
import org.apache.nifi.controller.status.history.StatusHistoryRepository;
import org.apache.nifi.encrypt.PropertyEncryptor;
import org.apache.nifi.nar.ExtensionDiscoveringManager;
import org.apache.nifi.nar.StandardExtensionDiscoveringManager;
import org.apache.nifi.nar.SystemBundle;
import org.apache.nifi.provenance.MockProvenanceRepository;
import org.apache.nifi.reporting.BulletinRepository;
import org.apache.nifi.util.NiFiProperties;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestStandardReportingContext {
private FlowController controller;
private AbstractPolicyBasedAuthorizer authorizer;
private FlowFileEventRepository flowFileEventRepo;
private AuditService auditService;
private PropertyEncryptor encryptor;
private NiFiProperties nifiProperties;
private Bundle systemBundle;
private ExtensionDiscoveringManager extensionManager;
private BulletinRepository bulletinRepo;
private StatusHistoryRepository statusHistoryRepository;
private volatile String propsFile = TestStandardReportingContext.class.getResource("/flowcontrollertest.nifi.properties").getFile();
@BeforeEach
public void setup() {
flowFileEventRepo = Mockito.mock(FlowFileEventRepository.class);
auditService = Mockito.mock(AuditService.class);
final Map<String, String> otherProps = new HashMap<>();
otherProps.put(NiFiProperties.PROVENANCE_REPO_IMPLEMENTATION_CLASS, MockProvenanceRepository.class.getName());
otherProps.put("nifi.remote.input.socket.port", "");
otherProps.put("nifi.remote.input.secure", "");
nifiProperties = NiFiProperties.createBasicNiFiProperties(propsFile, otherProps);
encryptor = Mockito.mock(PropertyEncryptor.class);
// use the system bundle
systemBundle = SystemBundle.create(nifiProperties);
extensionManager = new StandardExtensionDiscoveringManager();
extensionManager.discoverExtensions(systemBundle, Collections.emptySet());
statusHistoryRepository = Mockito.mock(StatusHistoryRepository.class);
User user1 = new User.Builder().identifier("user-id-1").identity("user-1").build();
User user2 = new User.Builder().identifier("user-id-2").identity("user-2").build();
Group group1 = new Group.Builder().identifier("group-id-1").name("group-1").addUser(user1.getIdentifier()).build();
Group group2 = new Group.Builder().identifier("group-id-2").name("group-2").build();
AccessPolicy policy1 = new AccessPolicy.Builder()
.identifier("policy-id-1")
.resource("resource1")
.action(RequestAction.READ)
.addUser(user1.getIdentifier())
.addUser(user2.getIdentifier())
.build();
AccessPolicy policy2 = new AccessPolicy.Builder()
.identifier("policy-id-2")
.resource("resource2")
.action(RequestAction.READ)
.addGroup(group1.getIdentifier())
.addGroup(group2.getIdentifier())
.addUser(user1.getIdentifier())
.addUser(user2.getIdentifier())
.build();
Set<Group> groups1 = new LinkedHashSet<>();
groups1.add(group1);
groups1.add(group2);
Set<User> users1 = new LinkedHashSet<>();
users1.add(user1);
users1.add(user2);
Set<AccessPolicy> policies1 = new LinkedHashSet<>();
policies1.add(policy1);
policies1.add(policy2);
authorizer = new MockPolicyBasedAuthorizer(groups1, users1, policies1);
bulletinRepo = Mockito.mock(BulletinRepository.class);
controller = FlowController.createStandaloneInstance(flowFileEventRepo, nifiProperties, authorizer, auditService, encryptor,
bulletinRepo, extensionManager, statusHistoryRepository, null);
}
@AfterEach
public void cleanup() throws Exception {
controller.shutdown(true);
FileUtils.deleteDirectory(new File("./target/flowcontrollertest"));
}
@Test
public void testGetPropertyReportingTask() throws ReportingTaskInstantiationException {
ReportingTaskNode reportingTask = controller.getFlowManager().createReportingTask(DummyScheduledReportingTask.class.getName(), systemBundle.getBundleDetails().getCoordinate());
PropertyDescriptor TEST_WITHOUT_DEFAULT_VALUE = new PropertyDescriptor.Builder().name("Test without default value").build();
PropertyDescriptor TEST_WITH_DEFAULT_VALUE = new PropertyDescriptor.Builder().name("Test with default value").build();
PropertyValue defaultValue = reportingTask.getReportingContext().getProperty(TEST_WITH_DEFAULT_VALUE);
assertEquals("nifi", defaultValue.getValue());
PropertyValue value = reportingTask.getReportingContext().getProperty(TEST_WITHOUT_DEFAULT_VALUE);
assertEquals(null, value.getValue());
}
}

View File

@ -0,0 +1,80 @@
/*
* 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.framework.ssl;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.io.TempDir;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class WatchServiceMonitorCommandTest {
@TempDir
private Path tempDir;
@Test
void testRunChangedPathsNotFound() throws IOException {
final FileSystem fileSystem = FileSystems.getDefault();
try (WatchService watchService = fileSystem.newWatchService()) {
registerTempDir(watchService);
final List<Path> changedPaths = new ArrayList<>();
final WatchServiceMonitorCommand command = new WatchServiceMonitorCommand(watchService, changedPaths::addAll);
command.run();
assertTrue(changedPaths.isEmpty());
}
}
@Timeout(5)
@Test
void testRunChangedPathsFound() throws IOException {
final FileSystem fileSystem = FileSystems.getDefault();
try (WatchService watchService = fileSystem.newWatchService()) {
registerTempDir(watchService);
final List<Path> changedPaths = new ArrayList<>();
final WatchServiceMonitorCommand command = new WatchServiceMonitorCommand(watchService, changedPaths::addAll);
final Path tempFile = Files.createTempFile(tempDir, WatchServiceMonitorCommandTest.class.getSimpleName(), null);
while (changedPaths.isEmpty()) {
command.run();
}
final Path firstChangedPath = changedPaths.getFirst();
assertEquals(tempFile.getFileName(), firstChangedPath.getFileName());
}
}
private void registerTempDir(final WatchService watchService) throws IOException {
tempDir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY);
}
}

View File

@ -1,89 +0,0 @@
/*
* 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.spring;
import org.apache.nifi.bundle.Bundle;
import org.apache.nifi.controller.leader.election.LeaderElectionManager;
import org.apache.nifi.controller.leader.election.StandaloneLeaderElectionManager;
import org.apache.nifi.nar.ExtensionDefinition;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.util.NiFiProperties;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class LeaderElectionManagerFactoryBeanTest {
@Mock
ExtensionManager extensionManager;
@Mock
Bundle bundle;
LeaderElectionManagerFactoryBean bean;
@BeforeEach
void setBean() {
bean = new LeaderElectionManagerFactoryBean();
bean.setExtensionManager(extensionManager);
}
@Test
void testGetObjectStandalone() throws Exception {
final NiFiProperties properties = NiFiProperties.createBasicNiFiProperties(null, Collections.emptyMap());
bean.setProperties(properties);
final LeaderElectionManager leaderElectionManager = bean.getObject();
assertInstanceOf(StandaloneLeaderElectionManager.class, leaderElectionManager);
}
@Test
void testGetObjectCluster() throws Exception {
final Map<String, String> clusterProperties = new LinkedHashMap<>();
clusterProperties.put(NiFiProperties.CLUSTER_IS_NODE, Boolean.TRUE.toString());
clusterProperties.put(NiFiProperties.CLUSTER_LEADER_ELECTION_IMPLEMENTATION, MockLeaderElectionManager.class.getSimpleName());
final NiFiProperties properties = NiFiProperties.createBasicNiFiProperties(null, clusterProperties);
bean.setProperties(properties);
when(bundle.getClassLoader()).thenReturn(Thread.currentThread().getContextClassLoader());
when(extensionManager.getBundles(eq(MockLeaderElectionManager.class.getName()))).thenReturn(Collections.singletonList(bundle));
final ExtensionDefinition extension = new ExtensionDefinition.Builder()
.implementationClassName(MockLeaderElectionManager.class.getName())
.bundle(bundle)
.extensionType(LeaderElectionManager.class)
.build();
when(extensionManager.getExtensions(eq(LeaderElectionManager.class))).thenReturn(Collections.singleton(extension));
final LeaderElectionManager leaderElectionManager = bean.getObject();
assertInstanceOf(MockLeaderElectionManager.class, leaderElectionManager);
}
}

View File

@ -16,35 +16,30 @@
*/
package org.apache.nifi.flow.resource;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.util.NiFiProperties;
import javax.net.ssl.SSLContext;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
public final class PropertyBasedExternalResourceProviderInitializationContext implements ExternalResourceProviderInitializationContext {
private static Set<String> GUARDED_PROPERTIES = new HashSet<>(Arrays.asList("implementation"));
private static Set<String> GUARDED_PROPERTIES = Set.of("implementation");
private final Map<String, String> properties;
private final Predicate<ExternalResourceDescriptor> filter;
private final SSLContext sslContext;
public PropertyBasedExternalResourceProviderInitializationContext(
final NiFiProperties properties,
final String prefix,
final Predicate<ExternalResourceDescriptor> filter
) throws TlsException {
final SSLContext sslContext,
final NiFiProperties properties,
final String prefix,
final Predicate<ExternalResourceDescriptor> filter
) {
this.properties = extractProperties(properties, prefix);
this.filter = filter;
this.sslContext = createNiFiSSLContext(properties);
this.sslContext = sslContext;
}
@Override
@ -62,11 +57,6 @@ public final class PropertyBasedExternalResourceProviderInitializationContext im
return sslContext;
}
private SSLContext createNiFiSSLContext(final NiFiProperties properties) throws TlsException {
final TlsConfiguration tlsConfiguration = StandardTlsConfiguration.fromNiFiProperties(properties);
return SslContextFactory.createSslContext(tlsConfiguration);
}
private Map<String, String> extractProperties(final NiFiProperties properties, final String prefix) {
final Map<String, String> candidates = properties.getPropertiesWithPrefix(prefix);
final Map<String, String> result = new HashMap<>();

View File

@ -1,95 +0,0 @@
/*
* 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.flow.resource;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.util.NiFiProperties;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.HashMap;
import java.util.Map;
@ExtendWith(MockitoExtension.class)
public class PropertyBasedExternalResourceProviderInitializationContextTest {
private static final String PROVIDER_NAME = "external";
private static final String PREFIX = "nifi.test.resources.external.provider." + PROVIDER_NAME + ".";
@Mock
private NiFiProperties properties;
@Test
public void testEmptyProperties() throws TlsException {
final PropertyBasedExternalResourceProviderInitializationContext testSubject = getTestSubject();
final Map<String, String> result = testSubject.getProperties();
Mockito.verify(properties, Mockito.times(1)).getPropertiesWithPrefix(PREFIX);
Assertions.assertTrue(result.isEmpty());
}
@Test
public void testGuardedPropertiesAreNotReturned() throws TlsException {
final Map<String, String> availableProperties = new HashMap<>();
availableProperties.put(PREFIX + "implementation", "value");
Mockito.when(properties.getPropertiesWithPrefix(PREFIX)).thenReturn(availableProperties);
final PropertyBasedExternalResourceProviderInitializationContext testSubject = getTestSubject();
final Map<String, String> result = testSubject.getProperties();
Mockito.verify(properties, Mockito.times(1)).getPropertiesWithPrefix(PREFIX);
Assertions.assertTrue(result.isEmpty());
}
@Test
public void testPropertiesWouldHaveEmptyKeyAreNotReturned() throws TlsException {
final Map<String, String> availableProperties = new HashMap<>();
availableProperties.put(PREFIX, "value");
Mockito.when(properties.getPropertiesWithPrefix(PREFIX)).thenReturn(availableProperties);
final PropertyBasedExternalResourceProviderInitializationContext testSubject = getTestSubject();
final Map<String, String> result = testSubject.getProperties();
Mockito.verify(properties, Mockito.times(1)).getPropertiesWithPrefix(PREFIX);
Assertions.assertTrue(result.isEmpty());
}
@Test
public void testPrefixIsRemoved() throws TlsException {
final Map<String, String> availableProperties = new HashMap<>();
availableProperties.put(PREFIX + "key1", "value1");
availableProperties.put(PREFIX + "key2", "value2");
Mockito.when(properties.getPropertiesWithPrefix(PREFIX)).thenReturn(availableProperties);
final PropertyBasedExternalResourceProviderInitializationContext testSubject = getTestSubject();
final Map<String, String> result = testSubject.getProperties();
Mockito.verify(properties, Mockito.times(1)).getPropertiesWithPrefix(PREFIX);
Assertions.assertEquals(2, result.size());
Assertions.assertTrue(result.containsKey("key1"));
Assertions.assertTrue(result.containsKey("key2"));
Assertions.assertEquals("value1", result.get("key1"));
Assertions.assertEquals("value2", result.get("key2"));
}
private PropertyBasedExternalResourceProviderInitializationContext getTestSubject() throws TlsException {
return new PropertyBasedExternalResourceProviderInitializationContext(properties, PREFIX, (descriptor -> true));
}
}

View File

@ -1,80 +0,0 @@
/*
* 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.nar;
import org.apache.nifi.flow.resource.ExternalResourceProviderInitializationContext;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.util.NiFiProperties;
import javax.net.ssl.SSLContext;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* A facade at front of {@code NiFiProperties} for auto loader extensions. Also limits the scope of the reachable properties.
*/
public class PropertyBasedNarProviderInitializationContext implements ExternalResourceProviderInitializationContext {
private static Set<String> GUARDED_PROPERTIES = new HashSet<>(Arrays.asList("implementation"));
static final String BASIC_PREFIX = "nifi.nar.library.provider.";
private final Map<String, String> properties;
private final SSLContext sslContext;
private final String name;
public PropertyBasedNarProviderInitializationContext(final NiFiProperties properties, final String name) throws TlsException {
this.properties = extractProperties(properties, name);
this.sslContext = createSSLContext(properties);
this.name = name;
}
@Override
public Map<String, String> getProperties() {
return properties;
}
@Override
public SSLContext getSSLContext() {
return sslContext;
}
private Map<String, String> extractProperties(final NiFiProperties properties, final String name) {
final String prefix = BASIC_PREFIX + name + ".";
final Map<String, String> candidates = properties.getPropertiesWithPrefix(prefix);
final Map<String, String> result = new HashMap<>();
for (final Map.Entry<String, String> entry : candidates.entrySet()) {
final String parameterKey = entry.getKey().substring(prefix.length());
if (!parameterKey.isEmpty() && !GUARDED_PROPERTIES.contains(parameterKey)) {
result.put(parameterKey, entry.getValue());
}
}
return result;
}
private SSLContext createSSLContext(final NiFiProperties properties) throws TlsException {
final TlsConfiguration tlsConfiguration = StandardTlsConfiguration.fromNiFiProperties(properties);
return SslContextFactory.createSslContext(tlsConfiguration);
}
}

View File

@ -1,105 +0,0 @@
/*
* 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.nar;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.util.NiFiProperties;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ExtendWith(MockitoExtension.class)
public class TestPropertyBasedNarProviderInitializationContext {
private static final String PROVIDER_NAME = "external";
private static final String PREFIX = PropertyBasedNarProviderInitializationContext.BASIC_PREFIX + PROVIDER_NAME + ".";
@Mock
NiFiProperties properties;
@Test
public void testEmptyProperties() throws TlsException {
// when
final PropertyBasedNarProviderInitializationContext testSubject = new PropertyBasedNarProviderInitializationContext(properties, PROVIDER_NAME);
final Map<String, String> result = testSubject.getProperties();
// then
Mockito.verify(properties, Mockito.times(1)).getPropertiesWithPrefix(PREFIX);
assertTrue(result.isEmpty());
}
@Test
public void testGuardedPropertiesAreNotReturned() throws TlsException {
// given
final Map<String, String> availableProperties = new HashMap<>();
availableProperties.put(PREFIX + "implementation", "value");
Mockito.when(properties.getPropertiesWithPrefix(PREFIX)).thenReturn(availableProperties);
// when
final PropertyBasedNarProviderInitializationContext testSubject = new PropertyBasedNarProviderInitializationContext(properties, PROVIDER_NAME);
final Map<String, String> result = testSubject.getProperties();
// then
Mockito.verify(properties, Mockito.times(1)).getPropertiesWithPrefix(PREFIX);
assertTrue(result.isEmpty());
}
@Test
public void testPropertiesWouldHaveEmptyKeyAreNotReturned() throws TlsException {
// given
final Map<String, String> availableProperties = new HashMap<>();
availableProperties.put(PREFIX, "value");
Mockito.when(properties.getPropertiesWithPrefix(PREFIX)).thenReturn(availableProperties);
// when
final PropertyBasedNarProviderInitializationContext testSubject = new PropertyBasedNarProviderInitializationContext(properties, PROVIDER_NAME);
final Map<String, String> result = testSubject.getProperties();
// then
Mockito.verify(properties, Mockito.times(1)).getPropertiesWithPrefix(PREFIX);
assertTrue(result.isEmpty());
}
@Test
public void testPrefixIsRemoved() throws TlsException {
// given
final Map<String, String> availableProperties = new HashMap<>();
availableProperties.put(PREFIX + "key1", "value1");
availableProperties.put(PREFIX + "key2", "value2");
Mockito.when(properties.getPropertiesWithPrefix(PREFIX)).thenReturn(availableProperties);
// when
final PropertyBasedNarProviderInitializationContext testSubject = new PropertyBasedNarProviderInitializationContext(properties, PROVIDER_NAME);
final Map<String, String> result = testSubject.getProperties();
// then
Mockito.verify(properties, Mockito.times(1)).getPropertiesWithPrefix(PREFIX);
assertEquals(2, result.size());
assertTrue(result.containsKey("key1"));
assertTrue(result.containsKey("key2"));
assertEquals("value1", result.get("key1"));
assertEquals("value2", result.get("key2"));
}
}

View File

@ -29,12 +29,14 @@ import org.apache.nifi.authorization.exception.AuthorizerDestructionException;
import org.apache.nifi.bundle.Bundle;
import org.apache.nifi.cluster.ClusterDetailsFactory;
import org.apache.nifi.cluster.ConnectionState;
import org.apache.nifi.components.state.StateManagerProvider;
import org.apache.nifi.controller.DecommissionTask;
import org.apache.nifi.controller.FlowController;
import org.apache.nifi.controller.StandardFlowService;
import org.apache.nifi.controller.flow.FlowManager;
import org.apache.nifi.controller.repository.FlowFileEventRepository;
import org.apache.nifi.controller.repository.metrics.RingBufferEventRepository;
import org.apache.nifi.controller.state.manager.StandardStateManagerProvider;
import org.apache.nifi.controller.status.history.StatusHistoryDumpFactory;
import org.apache.nifi.controller.status.history.StatusHistoryRepository;
import org.apache.nifi.diagnostics.DiagnosticsDump;
@ -45,32 +47,35 @@ import org.apache.nifi.diagnostics.bootstrap.BootstrapDiagnosticsFactory;
import org.apache.nifi.encrypt.PropertyEncryptor;
import org.apache.nifi.encrypt.PropertyEncryptorBuilder;
import org.apache.nifi.events.VolatileBulletinRepository;
import org.apache.nifi.framework.configuration.SslContextConfiguration;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.nar.ExtensionManagerHolder;
import org.apache.nifi.nar.ExtensionMapping;
import org.apache.nifi.nar.NarAutoLoader;
import org.apache.nifi.nar.NarClassLoadersHolder;
import org.apache.nifi.nar.NarLoader;
import org.apache.nifi.nar.NarThreadContextClassLoader;
import org.apache.nifi.nar.NarUnpackMode;
import org.apache.nifi.nar.StandardExtensionDiscoveringManager;
import org.apache.nifi.nar.StandardNarLoader;
import org.apache.nifi.parameter.ParameterLookup;
import org.apache.nifi.reporting.BulletinRepository;
import org.apache.nifi.services.FlowService;
import org.apache.nifi.spring.StatusHistoryRepositoryFactoryBean;
import org.apache.nifi.util.FlowParser;
import org.apache.nifi.util.NiFiProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.util.List;
import java.util.Set;
/**
*
*/
public class HeadlessNiFiServer implements NiFiServer {
private static final String DEFAULT_COMPONENT_STATUS_REPO_IMPLEMENTATION = "org.apache.nifi.controller.status.history.VolatileComponentStatusRepository";
private static final Logger logger = LoggerFactory.getLogger(HeadlessNiFiServer.class);
private NiFiProperties props;
private Bundle systemBundle;
@ -128,15 +133,17 @@ public class HeadlessNiFiServer implements NiFiServer {
final String propertiesKey = props.getProperty(NiFiProperties.SENSITIVE_PROPS_KEY);
final String propertiesAlgorithm = props.getProperty(NiFiProperties.SENSITIVE_PROPS_ALGORITHM);
final PropertyEncryptor encryptor = new PropertyEncryptorBuilder(propertiesKey).setAlgorithm(propertiesAlgorithm).build();
BulletinRepository bulletinRepository = new VolatileBulletinRepository();
final BulletinRepository bulletinRepository = new VolatileBulletinRepository();
final StatusHistoryRepository statusHistoryRepository = getStatusHistoryRepository(extensionManager);
final StatusHistoryRepositoryFactoryBean statusHistoryRepositoryFactoryBean = new StatusHistoryRepositoryFactoryBean();
statusHistoryRepositoryFactoryBean.setNifiProperties(props);
statusHistoryRepositoryFactoryBean.setExtensionManager(extensionManager);
StatusHistoryRepository statusHistoryRepository = statusHistoryRepositoryFactoryBean.getObject();
final SslContextConfiguration sslContextConfiguration = new SslContextConfiguration();
sslContextConfiguration.setProperties(props);
final SSLContext sslContext = sslContextConfiguration.sslContext();
final StateManagerProvider stateManagerProvider = StandardStateManagerProvider.create(props, sslContext, extensionManager, ParameterLookup.EMPTY);
flowController = FlowController.createStandaloneInstance(
flowFileEventRepository,
sslContext,
props,
authorizer,
auditService,
@ -144,7 +151,9 @@ public class HeadlessNiFiServer implements NiFiServer {
bulletinRepository,
extensionManager,
statusHistoryRepository,
null);
null,
stateManagerProvider
);
flowService = StandardFlowService.createStandaloneInstance(
flowController,
@ -193,6 +202,18 @@ public class HeadlessNiFiServer implements NiFiServer {
System.exit(1);
}
private StatusHistoryRepository getStatusHistoryRepository(final ExtensionManager extensionManager) {
final String implementationClassName = props.getProperty(NiFiProperties.COMPONENT_STATUS_REPOSITORY_IMPLEMENTATION, DEFAULT_COMPONENT_STATUS_REPO_IMPLEMENTATION);
try {
final StatusHistoryRepository repository = NarThreadContextClassLoader.createInstance(extensionManager, implementationClassName, StatusHistoryRepository.class, props);
repository.start();
return repository;
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void initialize(NiFiProperties properties, Bundle systemBundle, Set<Bundle> bundles, ExtensionMapping extensionMapping) {
this.props = properties;

View File

@ -74,6 +74,7 @@ import org.apache.nifi.flow.resource.ExternalResourceProviderInitializationConte
import org.apache.nifi.flow.resource.ExternalResourceProviderService;
import org.apache.nifi.flow.resource.ExternalResourceProviderServiceBuilder;
import org.apache.nifi.flow.resource.PropertyBasedExternalResourceProviderInitializationContext;
import org.apache.nifi.framework.ssl.FrameworkSslContextProvider;
import org.apache.nifi.lifecycle.LifeCycleStartException;
import org.apache.nifi.nar.ExtensionDefinition;
import org.apache.nifi.nar.ExtensionManager;
@ -88,7 +89,6 @@ import org.apache.nifi.nar.NarUnpackMode;
import org.apache.nifi.nar.StandardExtensionDiscoveringManager;
import org.apache.nifi.nar.StandardNarLoader;
import org.apache.nifi.processor.Processor;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.services.FlowService;
import org.apache.nifi.ui.extension.UiExtension;
import org.apache.nifi.ui.extension.UiExtensionMapping;
@ -128,6 +128,8 @@ import org.springframework.context.ApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.net.ssl.SSLContext;
import static org.apache.nifi.nar.ExtensionDefinition.ExtensionRuntime.PYTHON;
/**
@ -169,6 +171,7 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
private Server server;
private NiFiProperties props;
private SSLContext sslContext;
private Bundle systemBundle;
private Set<Bundle> bundles;
@ -201,6 +204,9 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
final QueuedThreadPool threadPool = new QueuedThreadPool(props.getWebThreads());
threadPool.setName("NiFi Web Server");
this.server = new Server(threadPool);
final FrameworkSslContextProvider sslContextProvider = new FrameworkSslContextProvider(props);
this.sslContext = sslContextProvider.loadSslContext().orElse(null);
configureConnectors(server);
final ContextHandlerCollection handlerCollection = new ContextHandlerCollection();
@ -676,6 +682,10 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
private void configureConnectors(final Server server) {
try {
final FrameworkServerConnectorFactory serverConnectorFactory = new FrameworkServerConnectorFactory(server, props);
if (props.isHTTPSConfigured()) {
serverConnectorFactory.setSslContext(sslContext);
}
final Map<String, String> interfaces = props.isHTTPSConfigured() ? props.getHttpsNetworkInterfaces() : props.getHttpNetworkInterfaces();
final Set<String> interfaceNames = interfaces.values().stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet());
// Add Server Connectors based on configured Network Interface Names
@ -744,7 +754,7 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
// Additionally loaded NARs and collected flow resources must be in place before starting the flows
narProviderService = new ExternalResourceProviderServiceBuilder("NAR Auto-Loader Provider", extensionManager)
.providers(buildExternalResourceProviders(extensionManager, NAR_PROVIDER_PREFIX, descriptor -> descriptor.getLocation().toLowerCase().endsWith(".nar")))
.providers(buildExternalResourceProviders(sslContext, extensionManager, NAR_PROVIDER_PREFIX, descriptor -> descriptor.getLocation().toLowerCase().endsWith(".nar")))
.targetDirectory(new File(props.getProperty(NiFiProperties.NAR_LIBRARY_AUTOLOAD_DIRECTORY, NiFiProperties.DEFAULT_NAR_LIBRARY_AUTOLOAD_DIR)))
.conflictResolutionStrategy(props.getProperty(NAR_PROVIDER_CONFLICT_RESOLUTION, DEFAULT_NAR_PROVIDER_CONFLICT_RESOLUTION))
.pollInterval(props.getProperty(NAR_PROVIDER_POLL_INTERVAL_PROPERTY, DEFAULT_NAR_PROVIDER_POLL_INTERVAL))
@ -882,9 +892,13 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
}
}
public Map<String, ExternalResourceProvider> buildExternalResourceProviders(final ExtensionManager extensionManager, final String providerPropertyPrefix,
final Predicate<ExternalResourceDescriptor> filter)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, TlsException, InvocationTargetException, NoSuchMethodException {
private Map<String, ExternalResourceProvider> buildExternalResourceProviders(
final SSLContext sslContext,
final ExtensionManager extensionManager,
final String providerPropertyPrefix,
final Predicate<ExternalResourceDescriptor> filter
)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
final Map<String, ExternalResourceProvider> result = new HashMap<>();
final Set<String> externalSourceNames = props.getDirectSubsequentTokens(providerPropertyPrefix);
@ -896,7 +910,7 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
final String providerId = UUID.randomUUID().toString();
final ExternalResourceProviderInitializationContext context
= new PropertyBasedExternalResourceProviderInitializationContext(props, providerPropertyPrefix + externalSourceName + ".", filter);
= new PropertyBasedExternalResourceProviderInitializationContext(sslContext, props, providerPropertyPrefix + externalSourceName + ".", filter);
result.put(providerId, createProviderInstance(extensionManager, providerClass, providerId, context));
}

View File

@ -21,27 +21,19 @@ import org.apache.commons.lang3.ObjectUtils;
import org.apache.nifi.jetty.configuration.connector.ApplicationLayerProtocol;
import org.apache.nifi.jetty.configuration.connector.StandardServerConnectorFactory;
import org.apache.nifi.processor.DataUnit;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.security.util.TlsPlatform;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.server.util.StoreScanner;
import org.eclipse.jetty.server.HostHeaderCustomizer;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import javax.net.ssl.SSLContext;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.apache.nifi.security.util.SslContextFactory.createSslContext;
/**
* Framework extension of Server Connector Factory configures additional settings based on application properties
*/
@ -56,14 +48,10 @@ public class FrameworkServerConnectorFactory extends StandardServerConnectorFact
private final int idleTimeout;
private final Integer storeScanInterval;
private final String includeCipherSuites;
private final String excludeCipherSuites;
private TlsConfiguration tlsConfiguration;
private SslContextFactory.Server sslContextFactory;
/**
@ -81,34 +69,16 @@ public class FrameworkServerConnectorFactory extends StandardServerConnectorFact
idleTimeout = getIdleTimeout();
if (properties.isHTTPSConfigured()) {
tlsConfiguration = StandardTlsConfiguration.fromNiFiProperties(properties);
try {
final SSLContext sslContext = createSslContext(tlsConfiguration);
setSslContext(sslContext);
} catch (final TlsException e) {
throw new IllegalStateException("Invalid nifi.web.https configuration in nifi.properties", e);
}
if (properties.isClientAuthRequiredForRestApi()) {
setNeedClientAuth(true);
} else {
setWantClientAuth(true);
}
if (properties.isSecurityAutoReloadEnabled()) {
final String securityAutoReloadInterval = properties.getSecurityAutoReloadInterval();
final double reloadIntervalSeconds = FormatUtils.getPreciseTimeDuration(securityAutoReloadInterval, TimeUnit.SECONDS);
storeScanInterval = (int) reloadIntervalSeconds;
} else {
storeScanInterval = null;
}
setApplicationLayerProtocols(properties);
// Set Transport Layer Security Protocols based on platform configuration
setIncludeSecurityProtocols(TlsPlatform.getPreferredProtocols().toArray(new String[0]));
} else {
storeScanInterval = null;
}
}
@ -149,18 +119,6 @@ public class FrameworkServerConnectorFactory extends StandardServerConnectorFact
final String[] cipherSuites = getCipherSuites(excludeCipherSuites);
sslContextFactory.setExcludeCipherSuites(cipherSuites);
}
if (storeScanInterval != null) {
sslContextFactory.setKeyStorePath(tlsConfiguration.getKeystorePath());
final StoreScanner keyStoreScanner = new StoreScanner(sslContextFactory, tlsConfiguration, sslContextFactory.getKeyStoreResource());
keyStoreScanner.setScanInterval(storeScanInterval);
getServer().addBean(keyStoreScanner);
sslContextFactory.setTrustStorePath(tlsConfiguration.getTruststorePath());
final StoreScanner trustStoreScanner = new StoreScanner(sslContextFactory, tlsConfiguration, sslContextFactory.getTrustStoreResource());
trustStoreScanner.setScanInterval(storeScanInterval);
getServer().addBean(trustStoreScanner);
}
}
return sslContextFactory;

View File

@ -1,150 +0,0 @@
/*
* 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.web.server.util;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedOperation;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.util.Collections;
import static org.apache.nifi.security.util.SslContextFactory.createSslContext;
/**
* File Scanner for Keystore or Truststore reloading using provided TLS Configuration
*/
public class StoreScanner extends ContainerLifeCycle implements Scanner.DiscreteListener {
private static final Logger LOG = LoggerFactory.getLogger(StoreScanner.class);
private final SslContextFactory sslContextFactory;
private final TlsConfiguration tlsConfiguration;
private final File file;
private final Scanner scanner;
private final String resourceName;
public StoreScanner(final SslContextFactory sslContextFactory,
final TlsConfiguration tlsConfiguration,
final Resource resource) {
this.sslContextFactory = sslContextFactory;
this.tlsConfiguration = tlsConfiguration;
this.resourceName = resource.getName();
File monitoredFile = resource.getPath().toFile();
if (!monitoredFile.exists()) {
throw new IllegalArgumentException(String.format("%s file does not exist", resourceName));
}
if (monitoredFile.isDirectory()) {
throw new IllegalArgumentException(String.format("expected %s file not directory", resourceName));
}
file = monitoredFile;
if (LOG.isDebugEnabled()) {
LOG.debug("File monitoring started {} [{}]", resourceName, monitoredFile);
}
File parentFile = file.getParentFile();
if (!parentFile.exists() || !parentFile.isDirectory()) {
throw new IllegalArgumentException(String.format("error obtaining %s dir", resourceName));
}
scanner = new Scanner(null, false);
scanner.setScanDirs(Collections.singletonList(parentFile.toPath()));
scanner.setScanInterval(1);
scanner.setReportDirs(false);
scanner.setReportExistingFilesOnStartup(false);
scanner.setScanDepth(1);
scanner.addListener(this);
addBean(scanner);
}
@Override
public void fileAdded(final String filename) {
LOG.debug("Resource [{}] File [{}] added", resourceName, filename);
reloadMatched(filename);
}
@Override
public void fileChanged(final String filename) {
LOG.debug("Resource [{}] File [{}] changed", resourceName, filename);
reloadMatched(filename);
}
@Override
public void fileRemoved(final String filename) {
LOG.debug("Resource [{}] File [{}] removed", resourceName, filename);
reloadMatched(filename);
}
@ManagedOperation(
value = "Scan for changes in the SSL Keystore/Truststore",
impact = "ACTION"
)
public void scan() {
LOG.debug("Resource [{}] scanning started", resourceName);
this.scanner.scan(new Callback.Completable());
this.scanner.scan(new Callback.Completable());
}
@ManagedOperation(
value = "Reload the SSL Keystore/Truststore",
impact = "ACTION"
)
public void reload() {
LOG.debug("File [{}] reload started", file);
try {
this.sslContextFactory.reload(contextFactory -> contextFactory.setSslContext(createContext()));
LOG.info("File [{}] reload completed", file);
} catch (final Throwable t) {
LOG.warn("File [{}] reload failed", file, t);
}
}
@ManagedAttribute("scanning interval to detect changes which need reloaded")
public int getScanInterval() {
return this.scanner.getScanInterval();
}
public void setScanInterval(int scanInterval) {
this.scanner.setScanInterval(scanInterval);
}
private void reloadMatched(final String filename) {
if (file.toPath().toString().equals(filename)) {
reload();
}
}
private SSLContext createContext() {
try {
return createSslContext(tlsConfiguration);
} catch (final TlsException e) {
throw new IllegalArgumentException("Failed to create SSL context with the TLS configuration", e);
}
}
}

View File

@ -18,8 +18,8 @@ package org.apache.nifi.web.server.connector;
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.server.util.StoreScanner;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.server.HttpConnectionFactory;
@ -30,7 +30,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.Collection;
import javax.net.ssl.SSLContext;
import java.util.Properties;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -114,7 +114,6 @@ class FrameworkServerConnectorFactoryTest {
assertFalse(sslContextFactory.getWantClientAuth());
assertCipherSuitesConfigured(sslContextFactory);
assertAutoReloadEnabled(serverConnector);
final HTTP2ServerConnectionFactory http2ServerConnectionFactory = serverConnector.getConnectionFactory(HTTP2ServerConnectionFactory.class);
assertNotNull(http2ServerConnectionFactory);
@ -154,7 +153,14 @@ class FrameworkServerConnectorFactoryTest {
private FrameworkServerConnectorFactory getHttpsConnectorFactory(final Properties serverProperties) {
final NiFiProperties properties = getProperties(serverProperties);
final Server server = new Server();
return new FrameworkServerConnectorFactory(server, properties);
final FrameworkServerConnectorFactory factory = new FrameworkServerConnectorFactory(server, properties);
try {
final SSLContext sslContext = org.apache.nifi.security.util.SslContextFactory.createSslContext(tlsConfiguration);
factory.setSslContext(sslContext);
} catch (final TlsException e) {
throw new IllegalStateException("Failed to create SSL Context", e);
}
return factory;
}
private SslConnectionFactory assertSslConnectionFactoryFound(final ServerConnector serverConnector) {
@ -179,12 +185,6 @@ class FrameworkServerConnectorFactoryTest {
assertEquals(INCLUDED_CIPHER_SUITE_PATTERN, includedCipherSuites[0]);
}
private void assertAutoReloadEnabled(final ServerConnector serverConnector) {
final Server server = serverConnector.getServer();
final Collection<StoreScanner> scanners = server.getBeans(StoreScanner.class);
assertEquals(2, scanners.size());
}
private NiFiProperties getProperties(final Properties serverProperties) {
return NiFiProperties.createBasicNiFiProperties(PROPERTIES_FILE_PATH, serverProperties);
}

View File

@ -1,142 +0,0 @@
/*
* 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.web.server.util;
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
import org.apache.nifi.security.util.TlsConfiguration;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Captor;
import org.mockito.junit.jupiter.MockitoExtension;
import javax.net.ssl.SSLContext;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class StoreScannerTest {
private SslContextFactory sslContextFactory;
private static TlsConfiguration tlsConfiguration;
private static Path keyStoreFile;
private static Path trustStoreFile;
private static Map<Path, StoreScanner> filesToScannerMap;
@Captor
private ArgumentCaptor<Consumer<SslContextFactory>> consumerArgumentCaptor;
@BeforeAll
public static void initClass() {
tlsConfiguration = new TemporaryKeyStoreBuilder().build();
keyStoreFile = Paths.get(tlsConfiguration.getKeystorePath());
trustStoreFile = Paths.get(tlsConfiguration.getTruststorePath());
}
@BeforeEach
public void init() {
sslContextFactory = mock(SslContextFactory.class);
Resource keyStoreResource = mock(Resource.class);
when(keyStoreResource.getPath()).thenReturn(keyStoreFile);
when(sslContextFactory.getKeyStoreResource()).thenReturn(keyStoreResource);
Resource trustStoreResource = mock(Resource.class);
when(trustStoreResource.getPath()).thenReturn(trustStoreFile);
when(sslContextFactory.getTrustStoreResource()).thenReturn(trustStoreResource);
final StoreScanner keyStoreScanner = new StoreScanner(sslContextFactory, tlsConfiguration, sslContextFactory.getKeyStoreResource());
final StoreScanner trustStoreScanner = new StoreScanner(sslContextFactory, tlsConfiguration, sslContextFactory.getTrustStoreResource());
filesToScannerMap = new HashMap<>();
filesToScannerMap.put(keyStoreFile, keyStoreScanner);
filesToScannerMap.put(trustStoreFile, trustStoreScanner);
}
@Test
public void testFileAdded() throws Exception {
for (final Map.Entry<Path, StoreScanner> entry : filesToScannerMap.entrySet()) {
final Path file = entry.getKey();
final StoreScanner scanner = entry.getValue();
scanner.fileAdded(file.toAbsolutePath().toString());
verify(sslContextFactory).reload(consumerArgumentCaptor.capture());
final Consumer<SslContextFactory> consumer = consumerArgumentCaptor.getValue();
consumer.accept(sslContextFactory);
verify(sslContextFactory).setSslContext(ArgumentMatchers.any(SSLContext.class));
clearInvocations(sslContextFactory);
}
}
@Test
public void testFileChanged() throws Exception {
for (final Map.Entry<Path, StoreScanner> entry : filesToScannerMap.entrySet()) {
final Path file = entry.getKey();
final StoreScanner scanner = entry.getValue();
scanner.fileChanged(file.toAbsolutePath().toString());
verify(sslContextFactory).reload(consumerArgumentCaptor.capture());
final Consumer<SslContextFactory> consumer = consumerArgumentCaptor.getValue();
consumer.accept(sslContextFactory);
verify(sslContextFactory).setSslContext(ArgumentMatchers.any(SSLContext.class));
clearInvocations(sslContextFactory);
}
}
@Test
public void testFileRemoved() throws Exception {
for (final Map.Entry<Path, StoreScanner> entry : filesToScannerMap.entrySet()) {
final Path file = entry.getKey();
final StoreScanner scanner = entry.getValue();
scanner.fileRemoved(file.toAbsolutePath().toString());
verify(sslContextFactory).reload(consumerArgumentCaptor.capture());
final Consumer<SslContextFactory> consumer = consumerArgumentCaptor.getValue();
consumer.accept(sslContextFactory);
verify(sslContextFactory).setSslContext(ArgumentMatchers.any(SSLContext.class));
clearInvocations(sslContextFactory);
}
}
@Test
public void testReload() throws Exception {
for (final Map.Entry<Path, StoreScanner> entry : filesToScannerMap.entrySet()) {
final StoreScanner scanner = entry.getValue();
scanner.reload();
verify(sslContextFactory).reload(consumerArgumentCaptor.capture());
final Consumer<SslContextFactory> consumer = consumerArgumentCaptor.getValue();
consumer.accept(sslContextFactory);
verify(sslContextFactory).setSslContext(ArgumentMatchers.any(SSLContext.class));
clearInvocations(sslContextFactory);
}
}
}

View File

@ -23,6 +23,7 @@ import org.apache.nifi.web.configuration.AuthenticationConfiguration;
import org.apache.nifi.web.security.configuration.WebSecurityConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
@ -36,14 +37,13 @@ import java.net.URISyntaxException;
* Web Application Spring Configuration
*/
@Configuration
@ComponentScan(basePackages = "org.apache.nifi.framework.configuration")
@Import({
WebSecurityConfiguration.class
})
@ImportResource({"classpath:nifi-context.xml",
"classpath:nifi-authorizer-context.xml",
"classpath:nifi-cluster-manager-context.xml",
"classpath:nifi-cluster-protocol-context.xml",
"classpath:nifi-web-api-context.xml"})
@ImportResource({
"classpath:nifi-web-api-context.xml"
})
public class NiFiWebApiConfiguration {
private static final URI OAUTH2_AUTHORIZATION_URI = getPathUri("/nifi-api/oauth2/authorization/consumer");

View File

@ -1,60 +0,0 @@
/*
* 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.web.spring;
import org.apache.nifi.components.state.StateManagerProvider;
import org.apache.nifi.controller.FlowController;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
*
*/
public class StateManagerProviderFactoryBean implements FactoryBean<StateManagerProvider>, ApplicationContextAware {
private ApplicationContext context;
private StateManagerProvider stateManagerProvider;
@Override
public StateManagerProvider getObject() throws Exception {
if (stateManagerProvider == null) {
final FlowController flowController = context.getBean("flowController", FlowController.class);
if (flowController != null) {
stateManagerProvider = flowController.getStateManagerProvider();
}
}
return stateManagerProvider;
}
@Override
public Class<StateManagerProvider> getObjectType() {
return StateManagerProvider.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
}

View File

@ -27,9 +27,6 @@
<bean id="reportingTaskProvider" class="org.apache.nifi.web.spring.ReportingTaskProviderFactoryBean" depends-on="flowController" />
<bean id="flowAnalysisRuleProvider" class="org.apache.nifi.web.spring.FlowAnalysisRuleProviderFactoryBean" depends-on="flowController" />
<!-- component state -->
<bean id="stateManagerProvider" class="org.apache.nifi.web.spring.StateManagerProviderFactoryBean" depends-on="flowController" />
<!-- revision manager -->
<bean id="revisionManager" class="org.apache.nifi.web.revision.NaiveRevisionManager">
</bean>

View File

@ -16,13 +16,8 @@
*/
package org.apache.nifi.web.security.configuration;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.oidc.OidcConfigurationException;
import org.apache.nifi.web.security.oidc.registration.ClientRegistrationProvider;
import org.apache.nifi.web.security.oidc.registration.DisabledClientRegistrationRepository;
import org.apache.nifi.web.security.oidc.registration.StandardClientRegistrationProvider;
@ -61,9 +56,14 @@ public class ClientRegistrationConfiguration {
private final NiFiProperties properties;
@Autowired
public ClientRegistrationConfiguration(final NiFiProperties properties) {
private final SSLContext sslContext;
public ClientRegistrationConfiguration(
@Autowired final NiFiProperties properties,
@Autowired(required = false) final SSLContext sslContext
) {
this.properties = Objects.requireNonNull(properties, "Application properties required");
this.sslContext = sslContext;
}
/**
@ -124,7 +124,7 @@ public class ClientRegistrationConfiguration {
final HttpClient.Builder builder = HttpClient.newBuilder().connectTimeout(connectTimeout);
if (NIFI_TRUSTSTORE_STRATEGY.equals(properties.getOidcClientTruststoreStrategy())) {
setSslSocketFactory(builder);
builder.sslContext(sslContext);
}
return builder.build();
@ -139,15 +139,4 @@ public class ClientRegistrationConfiguration {
return DEFAULT_SOCKET_TIMEOUT;
}
}
private void setSslSocketFactory(final HttpClient.Builder builder) {
final TlsConfiguration tlsConfiguration = StandardTlsConfiguration.fromNiFiProperties(properties);
try {
final SSLContext sslContext = Objects.requireNonNull(SslContextFactory.createSslContext(tlsConfiguration), "SSLContext required");
builder.sslContext(sslContext);
} catch (final TlsException e) {
throw new OidcConfigurationException("OpenID Connect HTTP TLS configuration failed", e);
}
}
}

View File

@ -73,6 +73,9 @@ import org.springframework.security.saml2.provider.service.web.authentication.lo
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@ -92,15 +95,26 @@ public class SamlAuthenticationSecurityConfiguration {
private final LogoutRequestManager logoutRequestManager;
@Autowired
private final SSLContext sslContext;
private final X509ExtendedKeyManager keyManager;
private final X509ExtendedTrustManager trustManager;
public SamlAuthenticationSecurityConfiguration(
final NiFiProperties properties,
final BearerTokenProvider bearerTokenProvider,
final LogoutRequestManager logoutRequestManager
@Autowired final NiFiProperties properties,
@Autowired final BearerTokenProvider bearerTokenProvider,
@Autowired final LogoutRequestManager logoutRequestManager,
@Autowired(required = false) final SSLContext sslContext,
@Autowired(required = false) final X509ExtendedKeyManager keyManager,
@Autowired(required = false) final X509ExtendedTrustManager trustManager
) {
this.properties = Objects.requireNonNull(properties, "Properties required");
this.bearerTokenProvider = Objects.requireNonNull(bearerTokenProvider, "Bearer Token Provider required");
this.logoutRequestManager = Objects.requireNonNull(logoutRequestManager, "Logout Request Manager required");
this.sslContext = sslContext;
this.keyManager = keyManager;
this.trustManager = trustManager;
}
/**
@ -305,7 +319,9 @@ public class SamlAuthenticationSecurityConfiguration {
*/
@Bean
public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {
return properties.isSamlEnabled() ? new StandardRelyingPartyRegistrationRepository(properties) : getDisabledRelyingPartyRegistrationRepository();
return properties.isSamlEnabled()
? new StandardRelyingPartyRegistrationRepository(properties, sslContext, keyManager, trustManager)
: getDisabledRelyingPartyRegistrationRepository();
}
/**

View File

@ -21,10 +21,6 @@ import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.saml2.SamlConfigurationException;
@ -35,7 +31,6 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.InputStream;
@ -55,8 +50,18 @@ class StandardRegistrationBuilderProvider implements RegistrationBuilderProvider
private final NiFiProperties properties;
public StandardRegistrationBuilderProvider(final NiFiProperties properties) {
private final SSLContext sslContext;
private final X509TrustManager trustManager;
public StandardRegistrationBuilderProvider(
final NiFiProperties properties,
final SSLContext sslContext,
final X509TrustManager trustManager
) {
this.properties = Objects.requireNonNull(properties, "Properties required");
this.sslContext = sslContext;
this.trustManager = trustManager;
}
/**
@ -117,23 +122,10 @@ class StandardRegistrationBuilderProvider implements RegistrationBuilderProvider
.readTimeout(readTimeout);
if (NIFI_TRUST_STORE_STRATEGY.equals(properties.getSamlHttpClientTruststoreStrategy())) {
setSslSocketFactory(builder);
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
builder.sslSocketFactory(sslSocketFactory, trustManager);
}
return builder.build();
}
private void setSslSocketFactory(final OkHttpClient.Builder builder) {
final TlsConfiguration tlsConfiguration = StandardTlsConfiguration.fromNiFiProperties(properties);
try {
final X509TrustManager trustManager = Objects.requireNonNull(SslContextFactory.getX509TrustManager(tlsConfiguration), "TrustManager required");
final TrustManager[] trustManagers = new TrustManager[] {trustManager};
final SSLContext sslContext = Objects.requireNonNull(SslContextFactory.createSslContext(tlsConfiguration, trustManagers), "SSLContext required");
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
builder.sslSocketFactory(sslSocketFactory, trustManager);
} catch (final TlsException e) {
throw new SamlConfigurationException("SAML Metadata HTTP TLS configuration failed", e);
}
}
}

View File

@ -16,26 +16,24 @@
*/
package org.apache.nifi.web.security.saml2.registration;
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.saml2.SamlUrlPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.saml2.Saml2Exception;
import org.springframework.security.saml2.core.Saml2X509Credential;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
* Standard implementation of Relying Party Registration Repository based on NiFi Properties
*/
@ -46,23 +44,40 @@ public class StandardRelyingPartyRegistrationRepository implements RelyingPartyR
static final String SINGLE_LOGOUT_RESPONSE_SERVICE_LOCATION = String.format(BASE_URL_FORMAT, SamlUrlPath.SINGLE_LOGOUT_RESPONSE.getPath());
private static final char[] BLANK_PASSWORD = new char[0];
private static final String RSA_PUBLIC_KEY_ALGORITHM = "RSA";
private static final Principal[] UNFILTERED_ISSUERS = {};
private static final Logger logger = LoggerFactory.getLogger(StandardRelyingPartyRegistrationRepository.class);
private final Saml2CredentialProvider saml2CredentialProvider = new StandardSaml2CredentialProvider();
private final NiFiProperties properties;
private final SSLContext sslContext;
private final X509ExtendedTrustManager trustManager;
private final X509ExtendedKeyManager keyManager;
private final RelyingPartyRegistration relyingPartyRegistration;
/**
* Standard implementation builds a Registration based on NiFi Properties and returns the same instance for all queries
*
* @param sslContext SSL Context loaded from properties
* @param keyManager Key Manager loaded from properties
* @param trustManager Trust manager loaded from properties
* @param properties NiFi Application Properties
*/
public StandardRelyingPartyRegistrationRepository(final NiFiProperties properties) {
public StandardRelyingPartyRegistrationRepository(
final NiFiProperties properties,
final SSLContext sslContext,
final X509ExtendedKeyManager keyManager,
final X509ExtendedTrustManager trustManager
) {
this.properties = properties;
this.sslContext = sslContext;
this.keyManager = keyManager;
this.trustManager = trustManager;
this.relyingPartyRegistration = getRelyingPartyRegistration();
}
@ -72,7 +87,7 @@ public class StandardRelyingPartyRegistrationRepository implements RelyingPartyR
}
private RelyingPartyRegistration getRelyingPartyRegistration() {
final RegistrationBuilderProvider registrationBuilderProvider = new StandardRegistrationBuilderProvider(properties);
final RegistrationBuilderProvider registrationBuilderProvider = new StandardRegistrationBuilderProvider(properties, sslContext, trustManager);
final RelyingPartyRegistration.Builder builder = registrationBuilderProvider.getRegistrationBuilder();
builder.registrationId(Saml2RegistrationProperty.REGISTRATION_ID.getProperty());
@ -111,50 +126,46 @@ public class StandardRelyingPartyRegistrationRepository implements RelyingPartyR
}
private Collection<Saml2X509Credential> getCredentials() {
final TlsConfiguration tlsConfiguration = StandardTlsConfiguration.fromNiFiProperties(properties);
final List<Saml2X509Credential> credentials = new ArrayList<>();
if (tlsConfiguration.isKeystorePopulated()) {
final KeyStore keyStore = getKeyStore(tlsConfiguration);
final char[] keyPassword = tlsConfiguration.getKeyPassword() == null
? tlsConfiguration.getKeystorePassword().toCharArray()
: tlsConfiguration.getKeyPassword().toCharArray();
final Collection<Saml2X509Credential> keyStoreCredentials = saml2CredentialProvider.getCredentials(keyStore, keyPassword);
credentials.addAll(keyStoreCredentials);
if (keyManager != null) {
final List<String> keyAliases = getKeyAliases();
for (final String alias : keyAliases) {
final PrivateKey privateKey = keyManager.getPrivateKey(alias);
final X509Certificate[] certificateChain = keyManager.getCertificateChain(alias);
final X509Certificate certificate = certificateChain[0];
final Saml2X509Credential credential = new Saml2X509Credential(
privateKey,
certificate,
Saml2X509Credential.Saml2X509CredentialType.SIGNING,
Saml2X509Credential.Saml2X509CredentialType.DECRYPTION
);
credentials.add(credential);
}
}
if (tlsConfiguration.isTruststorePopulated()) {
final KeyStore trustStore = getTrustStore(tlsConfiguration);
final Collection<Saml2X509Credential> trustStoreCredentials = saml2CredentialProvider.getCredentials(trustStore, BLANK_PASSWORD);
credentials.addAll(trustStoreCredentials);
if (trustManager != null) {
for (final X509Certificate certificate : trustManager.getAcceptedIssuers()) {
final Saml2X509Credential credential = new Saml2X509Credential(
certificate,
Saml2X509Credential.Saml2X509CredentialType.ENCRYPTION,
Saml2X509Credential.Saml2X509CredentialType.VERIFICATION
);
credentials.add(credential);
}
}
return credentials;
}
private KeyStore getTrustStore(final TlsConfiguration tlsConfiguration) {
try (InputStream inputStream = new FileInputStream(tlsConfiguration.getTruststorePath())) {
return new StandardKeyStoreBuilder()
.inputStream(inputStream)
.type(tlsConfiguration.getTruststoreType().getType())
.password(tlsConfiguration.getTruststorePassword().toCharArray())
.build();
} catch (final IOException e) {
throw new Saml2Exception("Trust Store loading failed", e);
}
}
private List<String> getKeyAliases() {
final List<String> keyAliases = new ArrayList<>();
private KeyStore getKeyStore(final TlsConfiguration tlsConfiguration) {
try (InputStream inputStream = new FileInputStream(tlsConfiguration.getKeystorePath())) {
return new StandardKeyStoreBuilder()
.inputStream(inputStream)
.type(tlsConfiguration.getKeystoreType().getType())
.password(tlsConfiguration.getKeystorePassword().toCharArray())
.build();
} catch (final IOException e) {
throw new Saml2Exception("Key Store loading failed", e);
final String[] serverAliases = keyManager.getServerAliases(RSA_PUBLIC_KEY_ALGORITHM, UNFILTERED_ISSUERS);
if (serverAliases != null) {
keyAliases.addAll(Arrays.asList(serverAliases));
}
return keyAliases;
}
}

View File

@ -1,116 +0,0 @@
/*
* 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.web.security.saml2.registration;
import org.springframework.security.saml2.Saml2Exception;
import org.springframework.security.saml2.core.Saml2X509Credential;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
/**
* Standard implementation of SAML2 Credential Provider capable of reading Key Store and Trust Store entries
*/
public class StandardSaml2CredentialProvider implements Saml2CredentialProvider {
/**
* Get Credentials from Key Store
*
* @param keyStore Key Store containing credentials
* @param keyPassword Optional key password for loading Private Keys
* @return Collection of SAML2 X.509 Credentials
*/
@Override
public Collection<Saml2X509Credential> getCredentials(final KeyStore keyStore, final char[] keyPassword) {
Objects.requireNonNull(keyStore, "Key Store required");
final List<Saml2X509Credential> credentials = new ArrayList<>();
try {
final Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
final String alias = aliases.nextElement();
if (keyStore.isKeyEntry(alias)) {
processKeyEntry(keyStore, alias, keyPassword, credentials);
} else if (keyStore.isCertificateEntry(alias)) {
processCertificateEntry(keyStore, alias, credentials);
}
}
} catch (final KeyStoreException e) {
throw new Saml2Exception("Loading SAML Credentials failed", e);
}
return credentials;
}
private Key getKey(final KeyStore keyStore, final String alias, final char[] keyPassword) {
try {
return keyStore.getKey(alias, keyPassword);
} catch (final GeneralSecurityException e) {
throw new Saml2Exception(String.format("Loading Key [%s] failed", alias), e);
}
}
private void processKeyEntry(
final KeyStore keyStore,
final String alias,
final char[] keyPassword,
final List<Saml2X509Credential> credentials
) throws KeyStoreException {
final Key key = getKey(keyStore, alias, keyPassword);
if (key instanceof PrivateKey) {
final PrivateKey privateKey = (PrivateKey) key;
final Certificate certificateEntry = keyStore.getCertificate(alias);
if (certificateEntry instanceof X509Certificate) {
final X509Certificate certificate = (X509Certificate) certificateEntry;
final Saml2X509Credential credential = new Saml2X509Credential(
privateKey,
certificate,
Saml2X509Credential.Saml2X509CredentialType.SIGNING,
Saml2X509Credential.Saml2X509CredentialType.DECRYPTION
);
credentials.add(credential);
}
}
}
private void processCertificateEntry(
final KeyStore keyStore,
final String alias,
final List<Saml2X509Credential> credentials
) throws KeyStoreException {
final Certificate certificateEntry = keyStore.getCertificate(alias);
if (certificateEntry instanceof X509Certificate) {
final X509Certificate certificate = (X509Certificate) certificateEntry;
final Saml2X509Credential credential = new Saml2X509Credential(
certificate,
Saml2X509Credential.Saml2X509CredentialType.VERIFICATION,
Saml2X509Credential.Saml2X509CredentialType.ENCRYPTION
);
credentials.add(credential);
}
}
}

View File

@ -41,9 +41,6 @@ import jakarta.ws.rs.core.Response;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.x509.ocsp.OcspStatus.ValidationStatus;
@ -78,7 +75,6 @@ public class OcspCertificateValidator {
private static final Logger logger = LoggerFactory.getLogger(OcspCertificateValidator.class);
private static final String HTTPS = "https";
private static final String OCSP_REQUEST_CONTENT_TYPE = "application/ocsp-request";
private static final int CONNECT_TIMEOUT = 10000;
@ -105,12 +101,7 @@ public class OcspCertificateValidator {
clientConfig.property(ClientProperties.CONNECT_TIMEOUT, CONNECT_TIMEOUT);
// initialize the client
if (HTTPS.equalsIgnoreCase(validationAuthorityURI.getScheme())) {
TlsConfiguration tlsConfiguration = StandardTlsConfiguration.fromNiFiProperties(properties);
client = WebUtils.createClient(clientConfig, SslContextFactory.createSslContext(tlsConfiguration));
} else {
client = WebUtils.createClient(clientConfig);
}
client = WebUtils.createClient(clientConfig);
// get the trusted CAs
trustedCAs = getTrustedCAs(properties);
@ -397,7 +388,7 @@ public class OcspCertificateValidator {
} catch (final OCSPException | IOException | ProcessingException | OperatorCreationException e) {
logger.error(e.getMessage(), e);
} catch (CertificateException e) {
e.printStackTrace();
logger.error("Certificate processing failed", e);
}
return ocspStatus;

View File

@ -32,7 +32,9 @@ import org.junit.jupiter.api.Test;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
@ -70,7 +72,7 @@ class StandardRegistrationBuilderProviderTest {
void testGetRegistrationBuilderFileUrl() {
final NiFiProperties properties = getProperties(getFileMetadataUrl());
assertRegistrationFound(properties);
assertRegistrationFound(properties, null, null);
}
@Test
@ -82,7 +84,7 @@ class StandardRegistrationBuilderProviderTest {
final NiFiProperties properties = getProperties(metadataUrl);
assertRegistrationFound(properties);
assertRegistrationFound(properties, null, null);
}
@Test
@ -93,7 +95,7 @@ class StandardRegistrationBuilderProviderTest {
final NiFiProperties properties = getProperties(metadataUrl);
final StandardRegistrationBuilderProvider provider = new StandardRegistrationBuilderProvider(properties);
final StandardRegistrationBuilderProvider provider = new StandardRegistrationBuilderProvider(properties, null, null);
final SamlConfigurationException exception = assertThrows(SamlConfigurationException.class, provider::getRegistrationBuilder);
assertTrue(exception.getMessage().contains(Integer.toString(HTTP_NOT_FOUND)));
@ -102,7 +104,10 @@ class StandardRegistrationBuilderProviderTest {
@Test
void testGetRegistrationBuilderHttpsUrl() throws IOException, TlsException {
final TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build();
final SSLSocketFactory sslSocketFactory = Objects.requireNonNull(SslContextFactory.createSSLSocketFactory(tlsConfiguration));
final SSLContext sslContext = Objects.requireNonNull(SslContextFactory.createSslContext(tlsConfiguration));
final X509TrustManager trustManager = SslContextFactory.getX509TrustManager(tlsConfiguration);
final SSLSocketFactory sslSocketFactory = Objects.requireNonNull(sslContext.getSocketFactory());
mockWebServer.useHttps(sslSocketFactory, PROXY_DISABLED);
final String metadata = getMetadata();
@ -112,7 +117,7 @@ class StandardRegistrationBuilderProviderTest {
final NiFiProperties properties = getProperties(metadataUrl, tlsConfiguration);
assertRegistrationFound(properties);
assertRegistrationFound(properties, sslContext, trustManager);
}
private String getMetadataUrl() {
@ -120,8 +125,8 @@ class StandardRegistrationBuilderProviderTest {
return url.toString();
}
private void assertRegistrationFound(final NiFiProperties properties) {
final StandardRegistrationBuilderProvider provider = new StandardRegistrationBuilderProvider(properties);
private void assertRegistrationFound(final NiFiProperties properties, final SSLContext sslContext, final X509TrustManager trustManager) {
final StandardRegistrationBuilderProvider provider = new StandardRegistrationBuilderProvider(properties, sslContext, trustManager);
final RelyingPartyRegistration.Builder builder = provider.getRegistrationBuilder();
final RelyingPartyRegistration registration = builder.build();

View File

@ -16,6 +16,10 @@
*/
package org.apache.nifi.web.security.saml2.registration;
import org.apache.nifi.security.ssl.StandardKeyManagerBuilder;
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
import org.apache.nifi.security.ssl.StandardTrustManagerBuilder;
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.util.NiFiProperties;
@ -24,8 +28,15 @@ import org.opensaml.xmlsec.signature.support.SignatureConstants;
import org.springframework.security.saml2.core.Saml2X509Credential;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.security.auth.x500.X500Principal;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Objects;
@ -48,7 +59,7 @@ class StandardRelyingPartyRegistrationRepositoryTest {
@Test
void testFindByRegistrationId() {
final NiFiProperties properties = getProperties();
final StandardRelyingPartyRegistrationRepository repository = new StandardRelyingPartyRegistrationRepository(properties);
final StandardRelyingPartyRegistrationRepository repository = new StandardRelyingPartyRegistrationRepository(properties, null, null, null);
final RelyingPartyRegistration registration = repository.findByRegistrationId(Saml2RegistrationProperty.REGISTRATION_ID.getProperty());
@ -66,11 +77,14 @@ class StandardRelyingPartyRegistrationRepositoryTest {
}
@Test
void testFindByRegistrationIdSingleLogoutEnabled() {
void testFindByRegistrationIdSingleLogoutEnabled() throws IOException {
final TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build();
final X509ExtendedKeyManager keyManager = getKeyManager(tlsConfiguration);
final X509ExtendedTrustManager trustManager = getTrustManager(tlsConfiguration);
final SSLContext sslContext = new StandardSslContextBuilder().keyManager(keyManager).trustManager(trustManager).build();
final NiFiProperties properties = getSingleLogoutProperties(tlsConfiguration);
final StandardRelyingPartyRegistrationRepository repository = new StandardRelyingPartyRegistrationRepository(properties);
final StandardRelyingPartyRegistrationRepository repository = new StandardRelyingPartyRegistrationRepository(properties, sslContext, keyManager, trustManager);
final RelyingPartyRegistration registration = repository.findByRegistrationId(Saml2RegistrationProperty.REGISTRATION_ID.getProperty());
@ -145,4 +159,31 @@ class StandardRelyingPartyRegistrationRepositoryTest {
final URL resource = Objects.requireNonNull(getClass().getResource(METADATA_PATH));
return resource.toString();
}
private X509ExtendedKeyManager getKeyManager(final TlsConfiguration tlsConfiguration) throws IOException {
try (InputStream inputStream = new FileInputStream(tlsConfiguration.getKeystorePath())) {
final KeyStore keyStore = new StandardKeyStoreBuilder()
.inputStream(inputStream)
.password(tlsConfiguration.getKeystorePassword().toCharArray())
.type(tlsConfiguration.getKeystoreType().getType())
.build();
return new StandardKeyManagerBuilder()
.keyStore(keyStore)
.keyPassword(tlsConfiguration.getFunctionalKeyPassword().toCharArray())
.build();
}
}
private X509ExtendedTrustManager getTrustManager(final TlsConfiguration tlsConfiguration) throws IOException {
try (InputStream inputStream = new FileInputStream(tlsConfiguration.getTruststorePath())) {
final KeyStore trustStore = new StandardKeyStoreBuilder()
.inputStream(inputStream)
.password(tlsConfiguration.getTruststorePassword().toCharArray())
.type(tlsConfiguration.getTruststoreType().getType())
.build();
return new StandardTrustManagerBuilder().trustStore(trustStore).build();
}
}
}