diff --git a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml index 05affc9d8f..92f4994bff 100644 --- a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml +++ b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml @@ -299,8 +299,14 @@ test - com.squareup.okhttp3 - okhttp + org.apache.nifi + nifi-web-client-api + 2.0.0-SNAPSHOT + + + org.apache.nifi + nifi-web-client + 2.0.0-SNAPSHOT org.glassfish.jaxb diff --git a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/SamlAuthenticationSecurityConfiguration.java b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/SamlAuthenticationSecurityConfiguration.java index d3b9c95bbb..27a8095480 100644 --- a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/SamlAuthenticationSecurityConfiguration.java +++ b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/SamlAuthenticationSecurityConfiguration.java @@ -73,7 +73,6 @@ 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; @@ -95,8 +94,6 @@ public class SamlAuthenticationSecurityConfiguration { private final LogoutRequestManager logoutRequestManager; - private final SSLContext sslContext; - private final X509ExtendedKeyManager keyManager; private final X509ExtendedTrustManager trustManager; @@ -105,14 +102,12 @@ public class SamlAuthenticationSecurityConfiguration { @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; } @@ -320,7 +315,7 @@ public class SamlAuthenticationSecurityConfiguration { @Bean public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() { return properties.isSamlEnabled() - ? new StandardRelyingPartyRegistrationRepository(properties, sslContext, keyManager, trustManager) + ? new StandardRelyingPartyRegistrationRepository(properties, keyManager, trustManager) : getDisabledRelyingPartyRegistrationRepository(); } diff --git a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/saml2/registration/StandardRegistrationBuilderProvider.java b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/saml2/registration/StandardRegistrationBuilderProvider.java index b8717dfe70..bd3ba41530 100644 --- a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/saml2/registration/StandardRegistrationBuilderProvider.java +++ b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/saml2/registration/StandardRegistrationBuilderProvider.java @@ -16,26 +16,27 @@ */ package org.apache.nifi.web.security.saml2.registration; -import okhttp3.Call; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.ResponseBody; import org.apache.nifi.util.FormatUtils; import org.apache.nifi.util.NiFiProperties; +import org.apache.nifi.web.client.StandardWebClientService; +import org.apache.nifi.web.client.api.HttpResponseEntity; +import org.apache.nifi.web.client.api.HttpResponseStatus; +import org.apache.nifi.web.client.api.WebClientService; +import org.apache.nifi.web.client.ssl.TlsContext; import org.apache.nifi.web.security.saml2.SamlConfigurationException; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.ResourceLoader; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.time.Duration; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.TimeUnit; /** @@ -46,21 +47,23 @@ class StandardRegistrationBuilderProvider implements RegistrationBuilderProvider private static final String HTTP_SCHEME_PREFIX = "http"; + private static final String TLS_PROTOCOL = "TLS"; + private static final ResourceLoader resourceLoader = new DefaultResourceLoader(); private final NiFiProperties properties; - private final SSLContext sslContext; + private final X509KeyManager keyManager; private final X509TrustManager trustManager; public StandardRegistrationBuilderProvider( final NiFiProperties properties, - final SSLContext sslContext, + final X509KeyManager keyManager, final X509TrustManager trustManager ) { this.properties = Objects.requireNonNull(properties, "Properties required"); - this.sslContext = sslContext; + this.keyManager = keyManager; this.trustManager = trustManager; } @@ -91,25 +94,26 @@ class StandardRegistrationBuilderProvider implements RegistrationBuilderProvider } private InputStream getRemoteInputStream(final String metadataUrl) { - final OkHttpClient client = getHttpClient(); + final WebClientService webClientService = getWebClientService(); + + final URI uri = URI.create(metadataUrl); - final Request request = new Request.Builder().get().url(metadataUrl).build(); - final Call call = client.newCall(request); try { - final Response response = call.execute(); - if (response.isSuccessful()) { - final ResponseBody body = Objects.requireNonNull(response.body(), "SAML Metadata response not found"); - return body.byteStream(); + final HttpResponseEntity responseEntity = webClientService.get().uri(uri).retrieve(); + final int statusCode = responseEntity.statusCode(); + + if (HttpResponseStatus.OK.getCode() == statusCode) { + return responseEntity.body(); } else { - response.close(); - throw new SamlConfigurationException(String.format("SAML Metadata retrieval failed [%s] HTTP %d", metadataUrl, response.code())); + responseEntity.close(); + throw new SamlConfigurationException(String.format("SAML Metadata retrieval failed [%s] HTTP %d", metadataUrl, statusCode)); } } catch (final IOException e) { throw new SamlConfigurationException(String.format("SAML Metadata retrieval failed [%s]", metadataUrl), e); } } - private OkHttpClient getHttpClient() { + private WebClientService getWebClientService() { final Duration connectTimeout = Duration.ofMillis( (long) FormatUtils.getPreciseTimeDuration(properties.getSamlHttpClientConnectTimeout(), TimeUnit.MILLISECONDS) ); @@ -117,15 +121,29 @@ class StandardRegistrationBuilderProvider implements RegistrationBuilderProvider (long) FormatUtils.getPreciseTimeDuration(properties.getSamlHttpClientReadTimeout(), TimeUnit.MILLISECONDS) ); - final OkHttpClient.Builder builder = new OkHttpClient.Builder() - .connectTimeout(connectTimeout) - .readTimeout(readTimeout); + final StandardWebClientService webClientService = new StandardWebClientService(); + webClientService.setConnectTimeout(connectTimeout); + webClientService.setReadTimeout(readTimeout); if (NIFI_TRUST_STORE_STRATEGY.equals(properties.getSamlHttpClientTruststoreStrategy())) { - final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); - builder.sslSocketFactory(sslSocketFactory, trustManager); + webClientService.setTlsContext(new TlsContext() { + @Override + public String getProtocol() { + return TLS_PROTOCOL; + } + + @Override + public X509TrustManager getTrustManager() { + return trustManager; + } + + @Override + public Optional getKeyManager() { + return Optional.ofNullable(keyManager); + } + }); } - return builder.build(); + return webClientService; } } diff --git a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/saml2/registration/StandardRelyingPartyRegistrationRepository.java b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/saml2/registration/StandardRelyingPartyRegistrationRepository.java index 83c1fd435d..f5a62aea31 100644 --- a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/saml2/registration/StandardRelyingPartyRegistrationRepository.java +++ b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/saml2/registration/StandardRelyingPartyRegistrationRepository.java @@ -24,7 +24,6 @@ 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 javax.net.ssl.SSLContext; import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509ExtendedTrustManager; import java.security.Principal; @@ -34,6 +33,7 @@ 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 */ @@ -52,8 +52,6 @@ public class StandardRelyingPartyRegistrationRepository implements RelyingPartyR private final NiFiProperties properties; - private final SSLContext sslContext; - private final X509ExtendedTrustManager trustManager; private final X509ExtendedKeyManager keyManager; @@ -63,19 +61,16 @@ public class StandardRelyingPartyRegistrationRepository implements RelyingPartyR /** * 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, - final SSLContext sslContext, final X509ExtendedKeyManager keyManager, final X509ExtendedTrustManager trustManager ) { this.properties = properties; - this.sslContext = sslContext; this.keyManager = keyManager; this.trustManager = trustManager; this.relyingPartyRegistration = getRelyingPartyRegistration(); @@ -87,7 +82,7 @@ public class StandardRelyingPartyRegistrationRepository implements RelyingPartyR } private RelyingPartyRegistration getRelyingPartyRegistration() { - final RegistrationBuilderProvider registrationBuilderProvider = new StandardRegistrationBuilderProvider(properties, sslContext, trustManager); + final RegistrationBuilderProvider registrationBuilderProvider = new StandardRegistrationBuilderProvider(properties, keyManager, trustManager); final RelyingPartyRegistration.Builder builder = registrationBuilderProvider.getRegistrationBuilder(); builder.registrationId(Saml2RegistrationProperty.REGISTRATION_ID.getProperty()); diff --git a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/saml2/registration/StandardRegistrationBuilderProviderTest.java b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/saml2/registration/StandardRegistrationBuilderProviderTest.java index 6344413824..f3602e275d 100644 --- a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/saml2/registration/StandardRegistrationBuilderProviderTest.java +++ b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/saml2/registration/StandardRegistrationBuilderProviderTest.java @@ -20,10 +20,12 @@ import okhttp3.HttpUrl; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.apache.commons.io.IOUtils; -import org.apache.nifi.security.util.SslContextFactory; +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.security.util.TlsException; import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.web.security.saml2.SamlConfigurationException; import org.junit.jupiter.api.AfterEach; @@ -34,11 +36,16 @@ import org.springframework.security.saml2.provider.service.registration.Saml2Mes import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509ExtendedKeyManager; +import javax.net.ssl.X509ExtendedTrustManager; +import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.security.KeyStore; import java.util.Objects; import java.util.Properties; @@ -102,10 +109,15 @@ class StandardRegistrationBuilderProviderTest { } @Test - void testGetRegistrationBuilderHttpsUrl() throws IOException, TlsException { + void testGetRegistrationBuilderHttpsUrl() throws IOException { final TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build(); - final SSLContext sslContext = Objects.requireNonNull(SslContextFactory.createSslContext(tlsConfiguration)); - final X509TrustManager trustManager = SslContextFactory.getX509TrustManager(tlsConfiguration); + final X509KeyManager keyManager = getKeyManager(tlsConfiguration); + final X509TrustManager trustManager = getTrustManager(tlsConfiguration); + final SSLContext sslContext = new StandardSslContextBuilder() + .keyManager(keyManager) + .keyPassword(tlsConfiguration.getKeyPassword().toCharArray()) + .trustManager(trustManager) + .build(); final SSLSocketFactory sslSocketFactory = Objects.requireNonNull(sslContext.getSocketFactory()); mockWebServer.useHttps(sslSocketFactory, PROXY_DISABLED); @@ -117,7 +129,7 @@ class StandardRegistrationBuilderProviderTest { final NiFiProperties properties = getProperties(metadataUrl, tlsConfiguration); - assertRegistrationFound(properties, sslContext, trustManager); + assertRegistrationFound(properties, keyManager, trustManager); } private String getMetadataUrl() { @@ -125,14 +137,41 @@ class StandardRegistrationBuilderProviderTest { return url.toString(); } - private void assertRegistrationFound(final NiFiProperties properties, final SSLContext sslContext, final X509TrustManager trustManager) { - final StandardRegistrationBuilderProvider provider = new StandardRegistrationBuilderProvider(properties, sslContext, trustManager); + private void assertRegistrationFound(final NiFiProperties properties, final X509KeyManager keyManager, final X509TrustManager trustManager) { + final StandardRegistrationBuilderProvider provider = new StandardRegistrationBuilderProvider(properties, keyManager, trustManager); final RelyingPartyRegistration.Builder builder = provider.getRegistrationBuilder(); final RelyingPartyRegistration registration = builder.build(); assertEquals(Saml2MessageBinding.POST, registration.getAssertionConsumerServiceBinding()); } + 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(); + } + } + private NiFiProperties getProperties(final String metadataUrl) { final Properties properties = new Properties(); properties.setProperty(NiFiProperties.SECURITY_USER_SAML_IDP_METADATA_URL, metadataUrl); diff --git a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/saml2/registration/StandardRelyingPartyRegistrationRepositoryTest.java b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/saml2/registration/StandardRelyingPartyRegistrationRepositoryTest.java index 47c603e4c0..3bc3cb8f1f 100644 --- a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/saml2/registration/StandardRelyingPartyRegistrationRepositoryTest.java +++ b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/saml2/registration/StandardRelyingPartyRegistrationRepositoryTest.java @@ -18,7 +18,6 @@ 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; @@ -28,7 +27,6 @@ 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; @@ -59,7 +57,7 @@ class StandardRelyingPartyRegistrationRepositoryTest { @Test void testFindByRegistrationId() { final NiFiProperties properties = getProperties(); - final StandardRelyingPartyRegistrationRepository repository = new StandardRelyingPartyRegistrationRepository(properties, null, null, null); + final StandardRelyingPartyRegistrationRepository repository = new StandardRelyingPartyRegistrationRepository(properties, null, null); final RelyingPartyRegistration registration = repository.findByRegistrationId(Saml2RegistrationProperty.REGISTRATION_ID.getProperty()); @@ -81,10 +79,9 @@ class StandardRelyingPartyRegistrationRepositoryTest { 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, sslContext, keyManager, trustManager); + final StandardRelyingPartyRegistrationRepository repository = new StandardRelyingPartyRegistrationRepository(properties, keyManager, trustManager); final RelyingPartyRegistration registration = repository.findByRegistrationId(Saml2RegistrationProperty.REGISTRATION_ID.getProperty());