mirror of https://github.com/apache/nifi.git
NIFI-7333 Added OIDC trust store strategy property
This closes #5753 Signed-off-by: David Handermann <exceptionfactory@apache.org>
This commit is contained in:
parent
a8fd5993eb
commit
7ef2fd2986
|
@ -188,6 +188,7 @@ public class NiFiProperties extends ApplicationProperties {
|
|||
public static final String SECURITY_USER_OIDC_READ_TIMEOUT = "nifi.security.user.oidc.read.timeout";
|
||||
public static final String SECURITY_USER_OIDC_CLIENT_ID = "nifi.security.user.oidc.client.id";
|
||||
public static final String SECURITY_USER_OIDC_CLIENT_SECRET = "nifi.security.user.oidc.client.secret";
|
||||
public static final String SECURITY_USER_OIDC_TRUSTSTORE_STRATEGY = "nifi.security.user.oidc.truststore.strategy";
|
||||
public static final String SECURITY_USER_OIDC_PREFERRED_JWSALGORITHM = "nifi.security.user.oidc.preferred.jwsalgorithm";
|
||||
public static final String SECURITY_USER_OIDC_ADDITIONAL_SCOPES = "nifi.security.user.oidc.additional.scopes";
|
||||
public static final String SECURITY_USER_OIDC_CLAIM_IDENTIFYING_USER = "nifi.security.user.oidc.claim.identifying.user";
|
||||
|
@ -369,6 +370,7 @@ public class NiFiProperties extends ApplicationProperties {
|
|||
public static final String DEFAULT_FLOW_CONFIGURATION_ARCHIVE_MAX_STORAGE = "500 MB";
|
||||
public static final String DEFAULT_SECURITY_USER_OIDC_CONNECT_TIMEOUT = "5 secs";
|
||||
public static final String DEFAULT_SECURITY_USER_OIDC_READ_TIMEOUT = "5 secs";
|
||||
public static final String DEFAULT_SECURITY_USER_OIDC_TRUSTSTORE_STRATEGY = "JDK";
|
||||
public static final String DEFAULT_SECURITY_USER_SAML_METADATA_SIGNING_ENABLED = "false";
|
||||
public static final String DEFAULT_SECURITY_USER_SAML_REQUEST_SIGNING_ENABLED = "false";
|
||||
public static final String DEFAULT_SECURITY_USER_SAML_WANT_ASSERTIONS_SIGNED = "true";
|
||||
|
@ -1121,6 +1123,10 @@ public class NiFiProperties extends ApplicationProperties {
|
|||
}
|
||||
}
|
||||
|
||||
public String getOidcClientTruststoreStrategy() {
|
||||
return getProperty(SECURITY_USER_OIDC_TRUSTSTORE_STRATEGY, DEFAULT_SECURITY_USER_OIDC_TRUSTSTORE_STRATEGY);
|
||||
}
|
||||
|
||||
public boolean shouldSendServerVersion() {
|
||||
return Boolean.parseBoolean(getProperty(WEB_SHOULD_SEND_SERVER_VERSION, DEFAULT_WEB_SHOULD_SEND_SERVER_VERSION));
|
||||
}
|
||||
|
|
|
@ -448,6 +448,7 @@ JSON Web Key (JWK) provided through the jwks_uri in the metadata found at the di
|
|||
|`nifi.security.user.oidc.additional.scopes` | Comma separated scopes that are sent to OpenId Connect Provider in addition to `openid` and `email`.
|
||||
|`nifi.security.user.oidc.claim.identifying.user` | Claim that identifies the user to be logged in; default is `email`. May need to be requested via the `nifi.security.user.oidc.additional.scopes` before usage.
|
||||
|`nifi.security.user.oidc.fallback.claims.identifying.user` | Comma separated possible fallback claims used to identify the user in case `nifi.security.user.oidc.claim.identifying.user` claim is not present for the login user.
|
||||
|`nifi.security.user.oidc.truststore.strategy` | If value is `NIFI`, use the NiFi truststore when connecting to the OIDC service, otherwise if value is `JDK` use Java's default `cacerts` truststore. The default value is `JDK`.
|
||||
|==================================================================================================================================================
|
||||
|
||||
[[saml]]
|
||||
|
|
|
@ -169,6 +169,7 @@
|
|||
<nifi.security.user.oidc.additional.scopes />
|
||||
<nifi.security.user.oidc.claim.identifying.user />
|
||||
<nifi.security.user.oidc.fallback.claims.identifying.user />
|
||||
<nifi.security.user.oidc.truststore.strategy>JDK</nifi.security.user.oidc.truststore.strategy>
|
||||
|
||||
<!-- nifi.properties: apache knox -->
|
||||
<nifi.security.user.knox.url />
|
||||
|
|
|
@ -200,6 +200,7 @@ nifi.security.user.oidc.preferred.jwsalgorithm=${nifi.security.user.oidc.preferr
|
|||
nifi.security.user.oidc.additional.scopes=${nifi.security.user.oidc.additional.scopes}
|
||||
nifi.security.user.oidc.claim.identifying.user=${nifi.security.user.oidc.claim.identifying.user}
|
||||
nifi.security.user.oidc.fallback.claims.identifying.user=${nifi.security.user.oidc.fallback.claims.identifying.user}
|
||||
nifi.security.user.oidc.truststore.strategy=${nifi.security.user.oidc.truststore.strategy}
|
||||
|
||||
# Apache Knox SSO Properties #
|
||||
nifi.security.user.knox.url=${nifi.security.user.knox.url}
|
||||
|
|
|
@ -38,16 +38,21 @@ import org.apache.http.impl.client.HttpClientBuilder;
|
|||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.nifi.authentication.exception.AuthenticationNotSupportedException;
|
||||
import org.apache.nifi.authorization.user.NiFiUserUtils;
|
||||
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.apache.nifi.web.api.cookie.ApplicationCookieName;
|
||||
import org.apache.nifi.web.security.jwt.provider.BearerTokenProvider;
|
||||
import org.apache.nifi.web.security.oidc.OIDCEndpoints;
|
||||
import org.apache.nifi.web.security.oidc.OidcService;
|
||||
import org.apache.nifi.web.security.oidc.TruststoreStrategy;
|
||||
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.Consumes;
|
||||
|
@ -89,19 +94,9 @@ public class OIDCAccessResource extends ApplicationResource {
|
|||
|
||||
private OidcService oidcService;
|
||||
private BearerTokenProvider bearerTokenProvider;
|
||||
private final CloseableHttpClient httpClient;
|
||||
|
||||
public OIDCAccessResource() {
|
||||
RequestConfig config = RequestConfig.custom()
|
||||
.setConnectTimeout(msTimeout)
|
||||
.setConnectionRequestTimeout(msTimeout)
|
||||
.setSocketTimeout(msTimeout)
|
||||
.build();
|
||||
|
||||
httpClient = HttpClientBuilder
|
||||
.create()
|
||||
.setDefaultRequestConfig(config)
|
||||
.build();
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -437,7 +432,7 @@ public class OIDCAccessResource extends ApplicationResource {
|
|||
* @throws IOException exceptional case for communication error with the OpenId Connect Provider
|
||||
*/
|
||||
private void revokeEndpointRequest(@Context HttpServletResponse httpServletResponse, String accessToken, URI revokeEndpoint) throws IOException {
|
||||
|
||||
final CloseableHttpClient httpClient = getHttpClient();
|
||||
HttpPost httpPost = new HttpPost(revokeEndpoint);
|
||||
|
||||
List<NameValuePair> params = new ArrayList<>();
|
||||
|
@ -455,12 +450,27 @@ public class OIDCAccessResource extends ApplicationResource {
|
|||
logger.error("There was an error logging out of the OpenId Connect Provider. " +
|
||||
"Response status: " + response.getStatusLine().getStatusCode());
|
||||
}
|
||||
} finally {
|
||||
httpClient.close();
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void closeClient() throws IOException {
|
||||
httpClient.close();
|
||||
private CloseableHttpClient getHttpClient() {
|
||||
RequestConfig config = RequestConfig.custom()
|
||||
.setConnectTimeout(msTimeout)
|
||||
.setConnectionRequestTimeout(msTimeout)
|
||||
.setSocketTimeout(msTimeout)
|
||||
.build();
|
||||
|
||||
HttpClientBuilder builder = HttpClientBuilder
|
||||
.create()
|
||||
.setDefaultRequestConfig(config);
|
||||
|
||||
if (TruststoreStrategy.NIFI.name().equals(properties.getOidcClientTruststoreStrategy())) {
|
||||
builder.setSSLContext(getSslContext());
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private AuthenticationResponse parseOidcResponse(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, boolean isLogin) throws Exception {
|
||||
|
@ -515,6 +525,15 @@ public class OIDCAccessResource extends ApplicationResource {
|
|||
}
|
||||
}
|
||||
|
||||
private SSLContext getSslContext() {
|
||||
TlsConfiguration tlsConfiguration = StandardTlsConfiguration.fromNiFiProperties(properties);
|
||||
try {
|
||||
return SslContextFactory.createSslContext(tlsConfiguration);
|
||||
} catch (TlsException e) {
|
||||
throw new RuntimeException("Unable to establish an SSL context for OIDC access resource from nifi.properties", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getForwardPageTitle(boolean isLogin) {
|
||||
return isLogin ? ApplicationResource.LOGIN_ERROR_TITLE : ApplicationResource.LOGOUT_ERROR_TITLE;
|
||||
}
|
||||
|
|
|
@ -53,6 +53,20 @@ import com.nimbusds.openid.connect.sdk.token.OIDCTokens;
|
|||
import com.nimbusds.openid.connect.sdk.validators.AccessTokenValidator;
|
||||
import com.nimbusds.openid.connect.sdk.validators.IDTokenValidator;
|
||||
import com.nimbusds.openid.connect.sdk.validators.InvalidHashException;
|
||||
import net.minidev.json.JSONObject;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.authentication.exception.IdentityAccessException;
|
||||
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.token.LoginAuthenticationToken;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
@ -63,14 +77,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minidev.json.JSONObject;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.authentication.exception.IdentityAccessException;
|
||||
import org.apache.nifi.util.FormatUtils;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -88,6 +94,7 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
|
|||
private IDTokenValidator tokenValidator;
|
||||
private ClientID clientId;
|
||||
private Secret clientSecret;
|
||||
private SSLContext sslContext;
|
||||
|
||||
/**
|
||||
* Creates a new StandardOidcIdentityProvider.
|
||||
|
@ -110,6 +117,11 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
// Set up trust store SSLContext
|
||||
if (TruststoreStrategy.NIFI.name().equals(properties.getOidcClientTruststoreStrategy())) {
|
||||
setSslContext();
|
||||
}
|
||||
|
||||
validateOIDCConfiguration();
|
||||
|
||||
try {
|
||||
|
@ -122,6 +134,15 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
|
|||
validateOIDCProviderMetadata();
|
||||
}
|
||||
|
||||
private void setSslContext() {
|
||||
TlsConfiguration tlsConfiguration = StandardTlsConfiguration.fromNiFiProperties(properties);
|
||||
try {
|
||||
this.sslContext = SslContextFactory.createSslContext(tlsConfiguration);
|
||||
} catch (TlsException e) {
|
||||
throw new RuntimeException("Unable to establish an SSL context for OIDC identity provider from nifi.properties", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the retrieved OIDC provider metadata.
|
||||
*/
|
||||
|
@ -166,7 +187,7 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
|
|||
} else if (JWSAlgorithm.HS256.equals(preferredJwsAlgorithm) || JWSAlgorithm.HS384.equals(preferredJwsAlgorithm) || JWSAlgorithm.HS512.equals(preferredJwsAlgorithm)) {
|
||||
tokenValidator = new IDTokenValidator(oidcProviderMetadata.getIssuer(), clientId, preferredJwsAlgorithm, clientSecret);
|
||||
} else {
|
||||
final ResourceRetriever retriever = new DefaultResourceRetriever(oidcConnectTimeout, oidcReadTimeout);
|
||||
final ResourceRetriever retriever = getResourceRetriever();
|
||||
tokenValidator = new IDTokenValidator(oidcProviderMetadata.getIssuer(), clientId, preferredJwsAlgorithm, oidcProviderMetadata.getJWKSetURI().toURL(), retriever);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
|
@ -245,9 +266,7 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
|
|||
private OIDCProviderMetadata retrieveOidcProviderMetadata(final String discoveryUri) throws IOException, ParseException {
|
||||
final URL url = new URL(discoveryUri);
|
||||
final HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.GET, url);
|
||||
httpRequest.setConnectTimeout(oidcConnectTimeout);
|
||||
httpRequest.setReadTimeout(oidcReadTimeout);
|
||||
|
||||
setHttpRequestProperties(httpRequest);
|
||||
final HTTPResponse httpResponse = httpRequest.send();
|
||||
|
||||
if (httpResponse.getStatusCode() != 200) {
|
||||
|
@ -485,12 +504,26 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
|
|||
}
|
||||
|
||||
private HTTPRequest formHTTPRequest(Request request) {
|
||||
final HTTPRequest httpRequest = request.toHTTPRequest();
|
||||
return setHttpRequestProperties(request.toHTTPRequest());
|
||||
}
|
||||
|
||||
private HTTPRequest setHttpRequestProperties(final HTTPRequest httpRequest) {
|
||||
httpRequest.setConnectTimeout(oidcConnectTimeout);
|
||||
httpRequest.setReadTimeout(oidcReadTimeout);
|
||||
if (TruststoreStrategy.NIFI.name().equals(properties.getOidcClientTruststoreStrategy())) {
|
||||
httpRequest.setSSLSocketFactory(sslContext.getSocketFactory());
|
||||
}
|
||||
return httpRequest;
|
||||
}
|
||||
|
||||
private ResourceRetriever getResourceRetriever() {
|
||||
if (TruststoreStrategy.NIFI.name().equals(properties.getOidcClientTruststoreStrategy())) {
|
||||
return new DefaultResourceRetriever(oidcConnectTimeout, oidcReadTimeout, 0, true, sslContext.getSocketFactory());
|
||||
} else {
|
||||
return new DefaultResourceRetriever(oidcConnectTimeout, oidcReadTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
private ClientAuthentication createClientAuthentication() {
|
||||
final ClientAuthentication clientAuthentication;
|
||||
List<ClientAuthenticationMethod> authMethods = oidcProviderMetadata.getTokenEndpointAuthMethods();
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.web.security.oidc;
|
||||
|
||||
/**
|
||||
* Indicates which truststore should be used when creating an HttpClient for an https URL.
|
||||
*/
|
||||
public enum TruststoreStrategy {
|
||||
|
||||
/**
|
||||
* Use the JDK truststore.
|
||||
*/
|
||||
JDK,
|
||||
|
||||
/**
|
||||
* Use NiFi's truststore specified in nifi.properties.
|
||||
*/
|
||||
NIFI;
|
||||
}
|
Loading…
Reference in New Issue