mirror of https://github.com/apache/nifi.git
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:
parent
48e8457a8a
commit
42c0aa6aa7
|
@ -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'
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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());
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<>();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue