NIFI-13295 Removed Apache Knox SSO Authentication

This closes #8876

Signed-off-by: Joseph Witt <joewitt@apache.org>
This commit is contained in:
exceptionfactory 2024-05-24 13:44:31 -05:00 committed by Joseph Witt
parent 43cc2b4aaa
commit fa8dc4f1a0
No known key found for this signature in database
GPG Key ID: 9093BF854F811A1A
26 changed files with 1 additions and 1206 deletions

View File

@ -39,7 +39,6 @@ import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* The NiFiProperties class holds all properties which are needed for various
@ -181,12 +180,6 @@ public class NiFiProperties extends ApplicationProperties {
public static final String SECURITY_USER_OIDC_FALLBACK_CLAIMS_IDENTIFYING_USER = "nifi.security.user.oidc.fallback.claims.identifying.user";
public static final String SECURITY_USER_OIDC_TOKEN_REFRESH_WINDOW = "nifi.security.user.oidc.token.refresh.window";
// apache knox
public static final String SECURITY_USER_KNOX_URL = "nifi.security.user.knox.url";
public static final String SECURITY_USER_KNOX_PUBLIC_KEY = "nifi.security.user.knox.publicKey";
public static final String SECURITY_USER_KNOX_COOKIE_NAME = "nifi.security.user.knox.cookieName";
public static final String SECURITY_USER_KNOX_AUDIENCES = "nifi.security.user.knox.audiences";
// saml
public static final String SECURITY_USER_SAML_IDP_METADATA_URL = "nifi.security.user.saml.idp.metadata.url";
public static final String SECURITY_USER_SAML_SP_ENTITY_ID = "nifi.security.user.saml.sp.entity.id";
@ -1149,57 +1142,6 @@ public class NiFiProperties extends ApplicationProperties {
return getProperty(DIAGNOSTICS_ON_SHUTDOWN_MAX_DIRECTORY_SIZE, DEFAULT_DIAGNOSTICS_ON_SHUTDOWN_MAX_DIRECTORY_SIZE);
}
/**
* Returns whether Knox SSO is enabled.
*
* @return whether Knox SSO is enabled
*/
public boolean isKnoxSsoEnabled() {
return !StringUtils.isBlank(getKnoxUrl());
}
/**
* Returns the Knox URL.
*
* @return Knox URL
*/
public String getKnoxUrl() {
return getProperty(SECURITY_USER_KNOX_URL);
}
/**
* Gets the configured Knox Audiences.
*
* @return Knox audiences
*/
public Set<String> getKnoxAudiences() {
final String rawAudiences = getProperty(SECURITY_USER_KNOX_AUDIENCES);
if (StringUtils.isBlank(rawAudiences)) {
return null;
} else {
final String[] audienceTokens = rawAudiences.split(",");
return Stream.of(audienceTokens).map(String::trim).filter(aud -> !StringUtils.isEmpty(aud)).collect(Collectors.toSet());
}
}
/**
* Returns the path to the Knox public key.
*
* @return path to the Knox public key
*/
public Path getKnoxPublicKeyPath() {
return Paths.get(getProperty(SECURITY_USER_KNOX_PUBLIC_KEY));
}
/**
* Returns the name of the Knox cookie.
*
* @return name of the Knox cookie
*/
public String getKnoxCookieName() {
return getProperty(SECURITY_USER_KNOX_COOKIE_NAME);
}
/**
* Returns whether SAML is enabled.
*
@ -1354,7 +1296,6 @@ public class NiFiProperties extends ApplicationProperties {
* - login identity provider is not populated
* - Kerberos service support is not enabled
* - openid connect is not enabled
* - knox sso is not enabled
* - anonymous authentication is not enabled
* </p>
*
@ -1363,7 +1304,6 @@ public class NiFiProperties extends ApplicationProperties {
public boolean isClientAuthRequiredForRestApi() {
return !isLoginIdentityProviderEnabled()
&& !isOidcEnabled()
&& !isKnoxSsoEnabled()
&& !isSamlEnabled()
&& !isAnonymousAuthenticationAllowed();
}

View File

@ -347,9 +347,6 @@ used. The default value of this property is `single-user-provider` supporting au
For Single sign-on authentication, NiFi will redirect users to the Identity Provider before returning to NiFi. NiFi will then
process responses and convert attributes to application token information.
During Apache Knox authentication, NiFi will redirect users to login with Apache Knox before returning to NiFi. NiFi will verify the Apache Knox
token during authentication.
NOTE: NiFi cannot be configured for multiple authentication strategies simultaneously.
NiFi will require client certificates for authenticating users over HTTPS if no other strategies have been configured.
@ -638,21 +635,6 @@ SAML authentication enables the following REST API resources for integration wit
| /nifi-api/access/saml/single-logout/request | Complete SAML 2.0 Single Logout processing initiating a request to the Asserting Party. Requires Single Logout to be enabled.
|======================================
[[apache_knox]]
=== Apache Knox
To enable authentication via Apache Knox the following properties must be configured in _nifi.properties_.
[options="header"]
|==================================================================================================================================================
| Property Name | Description
|`nifi.security.user.knox.url` | The URL for the Apache Knox login page.
|`nifi.security.user.knox.publicKey` | The path to the Apache Knox public key that will be used to verify the signatures of the authentication tokens in the HTTP Cookie.
|`nifi.security.user.knox.cookieName` | The name of the HTTP Cookie that Apache Knox will generate after successful login. The default value is `hadoop-jwt`.
|`nifi.security.user.knox.audiences` | Optional. A comma separate listed of allowed audiences. If set, the audience in the token must be present in
this listing. The audience that is populated in the token can be configured in Knox.
|==================================================================================================================================================
[[json_web_token]]
=== JSON Web Tokens

View File

@ -250,11 +250,6 @@ public class ThreadPoolRequestReplicator implements RequestReplicator {
// remove the access token if present, since the user is already authenticated... authorization
// will happen when the request is replicated using the proxy chain above
removeHeader(headers, SecurityHeader.AUTHORIZATION.getHeader());
// if knox sso cookie name is set, remove any authentication cookie since this user is already authenticated
// and will be included in the proxied entities chain above... authorization will happen when the
// request is replicated
removeCookie(headers, nifiProperties.getKnoxCookieName());
removeCookie(headers, SecurityCookieName.AUTHORIZATION_BEARER.getName());
removeCookie(headers, SecurityCookieName.REQUEST_TOKEN.getName());

View File

@ -174,12 +174,6 @@
<nifi.security.user.oidc.truststore.strategy>JDK</nifi.security.user.oidc.truststore.strategy>
<nifi.security.user.oidc.token.refresh.window>60 secs</nifi.security.user.oidc.token.refresh.window>
<!-- nifi.properties: apache knox -->
<nifi.security.user.knox.url />
<nifi.security.user.knox.publicKey />
<nifi.security.user.knox.cookieName>hadoop-jwt</nifi.security.user.knox.cookieName>
<nifi.security.user.knox.audiences />
<!-- nifi.properties: saml -->
<nifi.security.user.saml.idp.metadata.url />
<nifi.security.user.saml.sp.entity.id />

View File

@ -219,12 +219,6 @@ nifi.security.user.oidc.claim.groups=${nifi.security.user.oidc.claim.groups}
nifi.security.user.oidc.truststore.strategy=${nifi.security.user.oidc.truststore.strategy}
nifi.security.user.oidc.token.refresh.window=${nifi.security.user.oidc.token.refresh.window}
# Apache Knox SSO Properties #
nifi.security.user.knox.url=${nifi.security.user.knox.url}
nifi.security.user.knox.publicKey=${nifi.security.user.knox.publicKey}
nifi.security.user.knox.cookieName=${nifi.security.user.knox.cookieName}
nifi.security.user.knox.audiences=${nifi.security.user.knox.audiences}
# SAML Properties #
nifi.security.user.saml.idp.metadata.url=${nifi.security.user.saml.idp.metadata.url}
nifi.security.user.saml.sp.entity.id=${nifi.security.user.saml.sp.entity.id}

View File

@ -284,7 +284,6 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
// load the web ui app
final WebAppContext webUiContext = loadWar(webUiWar, CONTEXT_PATH_NIFI, frameworkClassLoader);
webUiContext.getInitParams().put("oidc-supported", String.valueOf(props.isOidcEnabled()));
webUiContext.getInitParams().put("knox-supported", String.valueOf(props.isKnoxSsoEnabled()));
webUiContext.getInitParams().put("saml-supported", String.valueOf(props.isSamlEnabled()));
webUiContext.getInitParams().put("saml-single-logout-supported", String.valueOf(props.isSamlSingleLogoutEnabled()));
webAppContextHandlers.addHandler(webUiContext);
@ -311,7 +310,6 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
if (webNewUiWar != null) {
final WebAppContext newUiContext = loadWar(webNewUiWar, CONTEXT_PATH_NF, frameworkClassLoader);
newUiContext.getInitParams().put("oidc-supported", String.valueOf(props.isOidcEnabled()));
newUiContext.getInitParams().put("knox-supported", String.valueOf(props.isKnoxSsoEnabled()));
newUiContext.getInitParams().put("saml-supported", String.valueOf(props.isSamlEnabled()));
newUiContext.getInitParams().put("saml-single-logout-supported", String.valueOf(props.isSamlSingleLogoutEnabled()));
webAppContextHandlers.addHandler(newUiContext);

View File

@ -16,7 +16,6 @@
*/
package org.apache.nifi.web.api;
import java.net.HttpURLConnection;
import java.net.URI;
import java.security.cert.X509Certificate;
import java.time.Instant;
@ -42,7 +41,6 @@ import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriBuilder;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.admin.service.AdministrationException;
import org.apache.nifi.authentication.AuthenticationResponse;
@ -69,7 +67,6 @@ import org.apache.nifi.web.security.UntrustedProxyException;
import org.apache.nifi.web.security.cookie.ApplicationCookieName;
import org.apache.nifi.web.security.jwt.provider.BearerTokenProvider;
import org.apache.nifi.web.security.jwt.revocation.JwtLogoutListener;
import org.apache.nifi.web.security.knox.KnoxService;
import org.apache.nifi.web.security.logout.LogoutRequest;
import org.apache.nifi.web.security.logout.LogoutRequestManager;
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
@ -108,7 +105,6 @@ public class AccessResource extends ApplicationResource {
private JwtDecoder jwtDecoder;
private BearerTokenProvider bearerTokenProvider;
private BearerTokenResolver bearerTokenResolver;
private KnoxService knoxService;
private LogoutRequestManager logoutRequestManager;
/**
@ -140,74 +136,6 @@ public class AccessResource extends ApplicationResource {
return generateOkResponse(entity).build();
}
@GET
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.WILDCARD)
@Path("knox/request")
@Operation(
summary = "Initiates a request to authenticate through Apache Knox."
)
public void knoxRequest(@Context HttpServletRequest httpServletRequest, @Context HttpServletResponse httpServletResponse) throws Exception {
// only consider user specific access over https
if (!httpServletRequest.isSecure()) {
httpServletResponse.sendError(HttpURLConnection.HTTP_NOT_ACCEPTABLE, AUTHENTICATION_NOT_ENABLED_MSG);
return;
}
// ensure knox is enabled
if (!knoxService.isKnoxEnabled()) {
httpServletResponse.sendError(HttpURLConnection.HTTP_NOT_ACCEPTABLE, "Apache Knox SSO support is not configured.");
return;
}
// build the originalUri, and direct back to the ui
final String originalUri = generateResourceUri("access", "knox", "callback");
// build the authorization uri
final URI authorizationUri = UriBuilder.fromUri(knoxService.getKnoxUrl())
.queryParam("originalUrl", originalUri)
.build();
// generate the response
httpServletResponse.sendRedirect(authorizationUri.toString());
}
@GET
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.WILDCARD)
@Path("knox/callback")
@Operation(
summary = "Redirect/callback URI for processing the result of the Apache Knox login sequence."
)
public void knoxCallback(@Context HttpServletRequest httpServletRequest, @Context HttpServletResponse httpServletResponse) throws Exception {
// only consider user specific access over https
if (!httpServletRequest.isSecure()) {
httpServletResponse.sendError(HttpURLConnection.HTTP_NOT_ACCEPTABLE, AUTHENTICATION_NOT_ENABLED_MSG);
return;
}
// ensure knox is enabled
if (!knoxService.isKnoxEnabled()) {
httpServletResponse.sendError(HttpURLConnection.HTTP_NOT_ACCEPTABLE, "Apache Knox SSO support is not configured.");
return;
}
httpServletResponse.sendRedirect(getNiFiUri());
}
@GET
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.WILDCARD)
@Path("knox/logout")
@Operation(
summary = "Performs a logout in the Apache Knox.",
description = NON_GUARANTEED_ENDPOINT
)
public void knoxLogout(@Context HttpServletResponse httpServletResponse) throws Exception {
String redirectPath = generateResourceUri("..", "nifi", "login");
httpServletResponse.sendRedirect(redirectPath);
}
/**
* Gets the status the client's access.
*
@ -542,10 +470,6 @@ public class AccessResource extends ApplicationResource {
this.certificateExtractor = certificateExtractor;
}
public void setKnoxService(KnoxService knoxService) {
this.knoxService = knoxService;
}
public void setLogoutRequestManager(LogoutRequestManager logoutRequestManager) {
this.logoutRequestManager = logoutRequestManager;
}

View File

@ -607,7 +607,6 @@
<bean id="accessResource" class="org.apache.nifi.web.api.AccessResource" scope="singleton">
<property name="logoutRequestManager" ref="logoutRequestManager" />
<property name="loginIdentityProvider" ref="loginIdentityProvider"/>
<property name="knoxService" ref="knoxService"/>
<property name="x509AuthenticationProvider" ref="x509AuthenticationProvider"/>
<property name="certificateExtractor" ref="certificateExtractor"/>
<property name="principalExtractor" ref="principalExtractor"/>

View File

@ -37,7 +37,6 @@ import org.springframework.security.authentication.AuthenticationManager;
ClientRegistrationConfiguration.class,
JwtAuthenticationSecurityConfiguration.class,
JwtDecoderConfiguration.class,
KnoxAuthenticationSecurityConfiguration.class,
OidcSecurityConfiguration.class,
SamlAuthenticationSecurityConfiguration.class,
X509AuthenticationSecurityConfiguration.class

View File

@ -1,67 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.web.security.configuration;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.knox.KnoxAuthenticationFilter;
import org.apache.nifi.web.security.knox.KnoxAuthenticationProvider;
import org.apache.nifi.web.security.knox.KnoxService;
import org.apache.nifi.web.security.knox.KnoxServiceFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
/**
* Knox Configuration for Authentication Security
*/
@Configuration
public class KnoxAuthenticationSecurityConfiguration {
private final NiFiProperties niFiProperties;
private final Authorizer authorizer;
@Autowired
public KnoxAuthenticationSecurityConfiguration(
final NiFiProperties niFiProperties,
final Authorizer authorizer
) {
this.niFiProperties = niFiProperties;
this.authorizer = authorizer;
}
@Bean
public KnoxAuthenticationFilter knoxAuthenticationFilter(final AuthenticationManager authenticationManager) {
final KnoxAuthenticationFilter knoxAuthenticationFilter = new KnoxAuthenticationFilter();
knoxAuthenticationFilter.setAuthenticationManager(authenticationManager);
knoxAuthenticationFilter.setProperties(niFiProperties);
return knoxAuthenticationFilter;
}
@Bean
public KnoxAuthenticationProvider knoxAuthenticationProvider() {
return new KnoxAuthenticationProvider(knoxService(), niFiProperties, authorizer);
}
@Bean
public KnoxService knoxService() {
final KnoxServiceFactoryBean knoxServiceFactoryBean = new KnoxServiceFactoryBean();
knoxServiceFactoryBean.setProperties(niFiProperties);
return knoxServiceFactoryBean.getObject();
}
}

View File

@ -24,7 +24,6 @@ import org.apache.nifi.web.security.csrf.CsrfCookieRequestMatcher;
import org.apache.nifi.web.security.csrf.SkipReplicatedCsrfFilter;
import org.apache.nifi.web.security.csrf.StandardCookieCsrfTokenRepository;
import org.apache.nifi.web.security.csrf.StandardCsrfTokenRequestAttributeHandler;
import org.apache.nifi.web.security.knox.KnoxAuthenticationFilter;
import org.apache.nifi.web.security.log.AuthenticationUserFilter;
import org.apache.nifi.web.security.oidc.client.web.OidcBearerTokenRefreshFilter;
import org.apache.nifi.web.security.oidc.logout.OidcLogoutFilter;
@ -87,7 +86,6 @@ public class WebSecurityConfiguration {
final StandardAuthenticationEntryPoint authenticationEntryPoint,
final X509AuthenticationFilter x509AuthenticationFilter,
final BearerTokenAuthenticationFilter bearerTokenAuthenticationFilter,
final KnoxAuthenticationFilter knoxAuthenticationFilter,
final NiFiAnonymousAuthenticationFilter anonymousAuthenticationFilter,
final OAuth2LoginAuthenticationFilter oAuth2LoginAuthenticationFilter,
final OAuth2AuthorizationCodeGrantFilter oAuth2AuthorizationCodeGrantFilter,
@ -115,8 +113,6 @@ public class WebSecurityConfiguration {
"/access",
"/access/config",
"/access/token",
"/access/knox/callback",
"/access/knox/request",
"/access/logout/complete",
"/authentication/configuration"
).permitAll()
@ -140,10 +136,6 @@ public class WebSecurityConfiguration {
.addFilterBefore(bearerTokenAuthenticationFilter, AnonymousAuthenticationFilter.class)
.addFilterBefore(new AuthenticationUserFilter(), ExceptionTranslationFilter.class);
if (properties.isKnoxSsoEnabled()) {
http.addFilterBefore(knoxAuthenticationFilter, AnonymousAuthenticationFilter.class);
}
if (properties.isAnonymousAuthenticationAllowed() || properties.isHttpEnabled()) {
http.addFilterAfter(anonymousAuthenticationFilter, AnonymousAuthenticationFilter.class);
}

View File

@ -1,71 +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.knox;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.NiFiAuthenticationFilter;
import org.springframework.security.core.Authentication;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
/**
*/
public class KnoxAuthenticationFilter extends NiFiAuthenticationFilter {
@Override
public Authentication attemptAuthentication(final HttpServletRequest request) {
// only support knox login when running securely
if (!request.isSecure()) {
return null;
}
// ensure knox sso support is enabled
final NiFiProperties properties = getProperties();
if (!properties.isKnoxSsoEnabled()) {
return null;
}
// get the principal out of the user token
final String knoxJwt = getJwtFromCookie(request, properties.getKnoxCookieName());
// if there is no cookie, return null to attempt another authentication
if (knoxJwt == null) {
return null;
} else {
// otherwise create the authentication request token
return new KnoxAuthenticationRequestToken(knoxJwt, request.getRemoteAddr());
}
}
public String getJwtFromCookie(final HttpServletRequest request, final String cookieName) {
String jwt = null;
final Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookieName.equals(cookie.getName())) {
jwt = cookie.getValue();
break;
}
}
}
return jwt;
}
}

View File

@ -1,68 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.web.security.knox;
import com.nimbusds.jose.JOSEException;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.user.NiFiUser;
import org.apache.nifi.authorization.user.NiFiUserDetails;
import org.apache.nifi.authorization.user.StandardNiFiUser.Builder;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.InvalidAuthenticationException;
import org.apache.nifi.web.security.NiFiAuthenticationProvider;
import org.apache.nifi.web.security.token.NiFiAuthenticationToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import java.text.ParseException;
/**
*
*/
public class KnoxAuthenticationProvider extends NiFiAuthenticationProvider {
private static final Logger logger = LoggerFactory.getLogger(KnoxAuthenticationProvider.class);
private final KnoxService knoxService;
public KnoxAuthenticationProvider(KnoxService knoxService, NiFiProperties nifiProperties, Authorizer authorizer) {
super(nifiProperties, authorizer);
this.knoxService = knoxService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
final KnoxAuthenticationRequestToken request = (KnoxAuthenticationRequestToken) authentication;
try {
final String jwtPrincipal = knoxService.getAuthenticationFromToken(request.getToken());
final String mappedIdentity = mapIdentity(jwtPrincipal);
final NiFiUser user = new Builder().identity(mappedIdentity).groups(getUserGroups(mappedIdentity)).clientAddress(request.getClientAddress()).build();
return new NiFiAuthenticationToken(new NiFiUserDetails(user));
} catch (ParseException | JOSEException e) {
logger.info("Unable to validate the access token: " + e.getMessage(), e);
throw new InvalidAuthenticationException("Unable to validate the access token.", e);
}
}
@Override
public boolean supports(Class<?> authentication) {
return KnoxAuthenticationRequestToken.class.isAssignableFrom(authentication);
}
}

View File

@ -1,59 +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.knox;
import org.apache.nifi.web.security.NiFiAuthenticationRequestToken;
/**
* This is an authentication request with a given JWT token.
*/
public class KnoxAuthenticationRequestToken extends NiFiAuthenticationRequestToken {
private final String token;
/**
* Creates a representation of the jwt authentication request for a user.
*
* @param token The unique token for this user
* @param clientAddress the address of the client making the request
*/
public KnoxAuthenticationRequestToken(final String token, final String clientAddress) {
super(clientAddress);
setAuthenticated(false);
this.token = token;
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return token;
}
public String getToken() {
return token;
}
@Override
public String toString() {
return "<Knox JWT token>";
}
}

View File

@ -1,33 +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.knox;
import java.security.interfaces.RSAPublicKey;
import java.util.Set;
public interface KnoxConfiguration {
boolean isKnoxEnabled();
String getKnoxUrl();
Set<String> getAudiences();
String getKnoxCookieName();
RSAPublicKey getKnoxPublicKey();
}

View File

@ -1,244 +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.knox;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.web.security.InvalidAuthenticationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Set;
/**
* KnoxService is a service for managing the Apache Knox SSO.
*/
public class KnoxService {
private static final Logger logger = LoggerFactory.getLogger(KnoxService.class);
private KnoxConfiguration configuration;
private JWSVerifier verifier;
private String knoxUrl;
private Set<String> audiences;
/**
* Creates a new KnoxService.
*
* @param configuration knox configuration
*/
public KnoxService(final KnoxConfiguration configuration) {
this.configuration = configuration;
// if knox sso support is enabled, validate the configuration
if (configuration.isKnoxEnabled()) {
// ensure the url is provided
knoxUrl = configuration.getKnoxUrl();
if (StringUtils.isBlank(knoxUrl)) {
throw new RuntimeException("Knox URL is required when Apache Knox SSO support is enabled.");
}
// ensure the cookie name is set
if (StringUtils.isBlank(configuration.getKnoxCookieName())) {
throw new RuntimeException("Knox Cookie Name is required when Apache Knox SSO support is enabled.");
}
// create the verifier
verifier = new RSASSAVerifier(configuration.getKnoxPublicKey());
// get the audience
audiences = configuration.getAudiences();
}
}
/**
* Returns whether Knox support is enabled.
*
* @return whether Knox support is enabled
*/
public boolean isKnoxEnabled() {
return configuration.isKnoxEnabled();
}
/**
* Returns the Knox Url.
*
* @return knox url
*/
public String getKnoxUrl() {
if (!configuration.isKnoxEnabled()) {
throw new IllegalStateException("Apache Knox SSO is not enabled.");
}
return knoxUrl;
}
/**
* Extracts the authentication from the token and verify it.
*
* @param jwt signed jwt string
* @return the user authentication
* @throws ParseException if the payload of the jwt doesn't represent a valid json object and a jwt claims set
* @throws JOSEException if the JWS object couldn't be verified
*/
public String getAuthenticationFromToken(final String jwt) throws ParseException, JOSEException {
if (!configuration.isKnoxEnabled()) {
throw new IllegalStateException("Apache Knox SSO is not enabled.");
}
// attempt to parse the signed jwt
final SignedJWT signedJwt = SignedJWT.parse(jwt);
// validate the token
if (validateToken(signedJwt)) {
final JWTClaimsSet claimsSet = signedJwt.getJWTClaimsSet();
if (claimsSet == null) {
logger.info("Claims set is missing from Knox JWT.");
throw new InvalidAuthenticationException("The Knox JWT token is not valid.");
}
// extract the user identity from the token
return claimsSet.getSubject();
} else {
throw new InvalidAuthenticationException("The Knox JWT token is not valid.");
}
}
/**
* Validate the specified jwt.
*
* @param jwtToken knox jwt
* @return whether this jwt is valid
* @throws JOSEException if the jws object couldn't be verified
* @throws ParseException if the payload of the jwt doesn't represent a valid json object and a jwt claims set
*/
private boolean validateToken(final SignedJWT jwtToken) throws JOSEException, ParseException {
final boolean validSignature = validateSignature(jwtToken);
final boolean validAudience = validateAudience(jwtToken);
final boolean notExpired = validateExpiration(jwtToken);
return validSignature && validAudience && notExpired;
}
/**
* Validate the jwt signature.
*
* @param jwtToken knox jwt
* @return whether this jwt signature is valid
* @throws JOSEException if the jws object couldn't be verified
*/
private boolean validateSignature(final SignedJWT jwtToken) throws JOSEException {
boolean valid = false;
// ensure the token is signed
if (JWSObject.State.SIGNED.equals(jwtToken.getState())) {
// ensure the signature is present
if (jwtToken.getSignature() != null) {
// verify the token
valid = jwtToken.verify(verifier);
}
}
if (!valid) {
logger.error("The Knox JWT has an invalid signature.");
}
return valid;
}
/**
* Validate the jwt audience.
*
* @param jwtToken knox jwt
* @return whether this jwt audience is valid
* @throws ParseException if the payload of the jwt doesn't represent a valid json object and a jwt claims set
*/
private boolean validateAudience(final SignedJWT jwtToken) throws ParseException {
if (audiences == null) {
return true;
}
final JWTClaimsSet claimsSet = jwtToken.getJWTClaimsSet();
if (claimsSet == null) {
logger.error("Claims set is missing from Knox JWT.");
return false;
}
final List<String> tokenAudiences = claimsSet.getAudience();
if (tokenAudiences == null) {
logger.error("Audience is missing from the Knox JWT.");
return false;
}
boolean valid = false;
for (final String tokenAudience : tokenAudiences) {
// ensure one of the audiences is matched
if (audiences.contains(tokenAudience)) {
valid = true;
break;
}
}
if (!valid) {
logger.error(String.format("The Knox JWT does not have the required audience(s). Required one of [%s]. Present in JWT [%s].",
StringUtils.join(audiences, ", "), StringUtils.join(tokenAudiences, ", ")));
}
return valid;
}
/**
* Validate the jwt expiration.
*
* @param jwtToken knox jwt
* @return whether this jwt is not expired
* @throws ParseException if the payload of the jwt doesn't represent a valid json object and a jwt claims set
*/
private boolean validateExpiration(final SignedJWT jwtToken) throws ParseException {
boolean valid = false;
final JWTClaimsSet claimsSet = jwtToken.getJWTClaimsSet();
if (claimsSet == null) {
logger.error("Claims set is missing from Knox JWT.");
return false;
}
final Date now = new Date();
final Date expiration = claimsSet.getExpirationTime();
// the token is not expired if the expiration isn't present or the expiration is after now
if (expiration == null || now.before(expiration)) {
valid = true;
}
if (!valid) {
logger.error("The Knox JWT is expired.");
}
return valid;
}
}

View File

@ -1,56 +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.knox;
import org.apache.nifi.util.NiFiProperties;
import org.springframework.beans.factory.FactoryBean;
public class KnoxServiceFactoryBean implements FactoryBean<KnoxService> {
private KnoxService knoxService = null;
private NiFiProperties properties = null;
@Override
public KnoxService getObject() {
if (knoxService == null) {
// ensure we only allow knox if login and oidc are disabled
if (properties.isKnoxSsoEnabled() && (properties.isLoginIdentityProviderEnabled() || properties.isOidcEnabled() || properties.isSamlEnabled())) {
throw new RuntimeException("Apache Knox SSO support cannot be enabled if the Login Identity Provider or OpenId Connect or SAML is configured.");
}
final KnoxConfiguration configuration = new StandardKnoxConfiguration(properties);
knoxService = new KnoxService(configuration);
}
return knoxService;
}
@Override
public Class<?> getObjectType() {
return KnoxService.class;
}
@Override
public boolean isSingleton() {
return true;
}
public void setProperties(NiFiProperties properties) {
this.properties = properties;
}
}

View File

@ -1,73 +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.knox;
import org.apache.nifi.util.NiFiProperties;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.Set;
public class StandardKnoxConfiguration implements KnoxConfiguration {
private final NiFiProperties properties;
public StandardKnoxConfiguration(NiFiProperties properties) {
this.properties = properties;
}
public boolean isKnoxEnabled() {
return properties.isKnoxSsoEnabled();
}
public String getKnoxUrl() {
return properties.getKnoxUrl();
}
@Override
public Set<String> getAudiences() {
return properties.getKnoxAudiences();
}
public String getKnoxCookieName() {
return properties.getKnoxCookieName();
}
public RSAPublicKey getKnoxPublicKey() {
// get the path to the public key
final Path knoxPublicKeyPath = properties.getKnoxPublicKeyPath();
// ensure the file exists
if (Files.isRegularFile(knoxPublicKeyPath) && Files.exists(knoxPublicKeyPath)) {
try (final InputStream publicKeyStream = Files.newInputStream(knoxPublicKeyPath)) {
final CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
final X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate(publicKeyStream);
return (RSAPublicKey) certificate.getPublicKey();
} catch (final IOException | CertificateException e) {
throw new RuntimeException(e.getMessage(), e);
}
} else {
throw new RuntimeException(String.format("The specified Knox public key path does not exist '%s'", knoxPublicKeyPath.toString()));
}
}
}

View File

@ -1,104 +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.knox;
import org.apache.nifi.util.NiFiProperties;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class KnoxAuthenticationFilterTest {
private static final String COOKIE_NAME = "hadoop-jwt";
private KnoxAuthenticationFilter knoxAuthenticationFilter;
@BeforeEach
public void setUp() throws Exception {
final NiFiProperties nifiProperties = Mockito.mock(NiFiProperties.class);
when(nifiProperties.isKnoxSsoEnabled()).thenReturn(true);
when(nifiProperties.getKnoxCookieName()).thenReturn(COOKIE_NAME);
knoxAuthenticationFilter = new KnoxAuthenticationFilter();
knoxAuthenticationFilter.setProperties(nifiProperties);
}
@Test
public void testInsecureHttp() {
final HttpServletRequest request = mock(HttpServletRequest.class);
when(request.isSecure()).thenReturn(false);
assertNull(knoxAuthenticationFilter.attemptAuthentication(request));
}
@Test
public void testNullCookies() {
final HttpServletRequest request = mock(HttpServletRequest.class);
when(request.isSecure()).thenReturn(true);
when(request.getCookies()).thenReturn(null);
assertNull(knoxAuthenticationFilter.attemptAuthentication(request));
}
@Test
public void testNoCookies() {
final HttpServletRequest request = mock(HttpServletRequest.class);
when(request.isSecure()).thenReturn(true);
when(request.getCookies()).thenReturn(new Cookie[] {});
assertNull(knoxAuthenticationFilter.attemptAuthentication(request));
}
@Test
public void testWrongCookieName() {
final String jwt = "my-jwt";
final Cookie knoxCookie = mock(Cookie.class);
when(knoxCookie.getName()).thenReturn("not-hadoop-jwt");
when(knoxCookie.getValue()).thenReturn(jwt);
final HttpServletRequest request = mock(HttpServletRequest.class);
when(request.isSecure()).thenReturn(true);
when(request.getCookies()).thenReturn(new Cookie[] {knoxCookie});
final KnoxAuthenticationRequestToken authRequest = (KnoxAuthenticationRequestToken) knoxAuthenticationFilter.attemptAuthentication(request);
assertNull(authRequest);
}
@Test
public void testKnoxCookie() {
final String jwt = "my-jwt";
final Cookie knoxCookie = mock(Cookie.class);
when(knoxCookie.getName()).thenReturn(COOKIE_NAME);
when(knoxCookie.getValue()).thenReturn(jwt);
final HttpServletRequest request = mock(HttpServletRequest.class);
when(request.isSecure()).thenReturn(true);
when(request.getCookies()).thenReturn(new Cookie[] {knoxCookie});
final KnoxAuthenticationRequestToken authRequest = (KnoxAuthenticationRequestToken) knoxAuthenticationFilter.attemptAuthentication(request);
assertNotNull(authRequest);
assertEquals(jwt, authRequest.getToken());
}
}

View File

@ -1,212 +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.knox;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.PlainJWT;
import com.nimbusds.oauth2.sdk.auth.JWTAuthenticationClaimsSet;
import com.nimbusds.oauth2.sdk.auth.PrivateKeyJWT;
import com.nimbusds.oauth2.sdk.id.Audience;
import com.nimbusds.oauth2.sdk.id.ClientID;
import com.nimbusds.oauth2.sdk.id.JWTID;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPublicKey;
import java.text.ParseException;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.nifi.web.security.InvalidAuthenticationException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@DisabledOnOs({OS.WINDOWS})
public class KnoxServiceTest {
private static final String AUDIENCE = "https://apache-knox/token";
private static final String AUDIENCE_2 = "https://apache-knox-2/token";
@Test
public void testKnoxSsoNotEnabledGetKnoxUrl() {
final KnoxConfiguration configuration = mock(KnoxConfiguration.class);
when(configuration.isKnoxEnabled()).thenReturn(false);
final KnoxService service = new KnoxService(configuration);
assertFalse(service.isKnoxEnabled());
assertThrows(IllegalStateException.class, service::getKnoxUrl);
}
@Test
public void testKnoxSsoNotEnabledGetAuthenticatedFromToken() {
final KnoxConfiguration configuration = mock(KnoxConfiguration.class);
when(configuration.isKnoxEnabled()).thenReturn(false);
final KnoxService service = new KnoxService(configuration);
assertFalse(service.isKnoxEnabled());
assertThrows(IllegalStateException.class, () -> service.getAuthenticationFromToken("jwt-token-value"));
}
private JWTAuthenticationClaimsSet getAuthenticationClaimsSet(final String subject, final String audience, final Date expiration) {
return new JWTAuthenticationClaimsSet(
new ClientID(subject),
new Audience(audience).toSingleAudienceList(),
expiration,
null,
null,
new JWTID());
}
@Test
public void testSignedJwt() throws Exception {
final String subject = "user-1";
final Date expiration = new Date(System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS));
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
final KeyPair pair = keyGen.generateKeyPair();
final RSAPublicKey publicKey = (RSAPublicKey) pair.getPublic();
final JWTAuthenticationClaimsSet claimsSet = getAuthenticationClaimsSet(subject, AUDIENCE, expiration);
final PrivateKeyJWT privateKeyJWT = new PrivateKeyJWT(claimsSet, JWSAlgorithm.RS256, pair.getPrivate(), null, null);
final KnoxConfiguration configuration = getConfiguration(publicKey);
final KnoxService service = new KnoxService(configuration);
assertEquals(subject, service.getAuthenticationFromToken(privateKeyJWT.getClientAssertion().serialize()));
}
@Test
public void testBadSignedJwt() throws Exception {
final String subject = "user-1";
final Date expiration = new Date(System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS));
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
final KeyPair pair1 = keyGen.generateKeyPair();
final KeyPair pair2 = keyGen.generateKeyPair();
final RSAPublicKey publicKey2 = (RSAPublicKey) pair2.getPublic();
// sign the jwt with pair 1
final JWTAuthenticationClaimsSet claimsSet = getAuthenticationClaimsSet(subject, AUDIENCE, expiration);
final PrivateKeyJWT privateKeyJWT = new PrivateKeyJWT(claimsSet, JWSAlgorithm.RS256, pair1.getPrivate(), null, null);
// attempt to verify it with pair 2
final KnoxConfiguration configuration = getConfiguration(publicKey2);
final KnoxService service = new KnoxService(configuration);
assertThrows(InvalidAuthenticationException.class, () -> service.getAuthenticationFromToken(privateKeyJWT.getClientAssertion().serialize()));
}
@Test
public void testPlainJwt() throws Exception {
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
final KeyPair pair = keyGen.generateKeyPair();
final RSAPublicKey publicKey = (RSAPublicKey) pair.getPublic();
final Date expiration = new Date(System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS));
final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.subject("user-1")
.expirationTime(expiration)
.build();
final PlainJWT plainJWT = new PlainJWT(claimsSet);
final KnoxConfiguration configuration = getConfiguration(publicKey);
final KnoxService service = new KnoxService(configuration);
assertThrows(ParseException.class, () -> service.getAuthenticationFromToken(plainJWT.serialize()));
}
@Test
public void testExpiredJwt() throws Exception {
final String subject = "user-1";
// token expires in 1 sec
final Date expiration = new Date(System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS));
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
final KeyPair pair = keyGen.generateKeyPair();
final RSAPublicKey publicKey = (RSAPublicKey) pair.getPublic();
// wait 2 sec
Thread.sleep(TimeUnit.MILLISECONDS.convert(2, TimeUnit.SECONDS));
final JWTAuthenticationClaimsSet claimsSet = getAuthenticationClaimsSet(subject, AUDIENCE, expiration);
final PrivateKeyJWT privateKeyJWT = new PrivateKeyJWT(claimsSet, JWSAlgorithm.RS256, pair.getPrivate(), null, null);
final KnoxConfiguration configuration = getConfiguration(publicKey);
final KnoxService service = new KnoxService(configuration);
assertThrows(InvalidAuthenticationException.class, () -> service.getAuthenticationFromToken(privateKeyJWT.getClientAssertion().serialize()));
}
@Test
public void testRequiredAudience() throws Exception {
final String subject = "user-1";
final Date expiration = new Date(System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS));
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
final KeyPair pair = keyGen.generateKeyPair();
final RSAPublicKey publicKey = (RSAPublicKey) pair.getPublic();
final JWTAuthenticationClaimsSet claimsSet = getAuthenticationClaimsSet(subject, AUDIENCE, expiration);
final PrivateKeyJWT privateKeyJWT = new PrivateKeyJWT(claimsSet, JWSAlgorithm.RS256, pair.getPrivate(), null, null);
final KnoxConfiguration configuration = getConfiguration(publicKey);
when(configuration.getAudiences()).thenReturn(null);
final KnoxService service = new KnoxService(configuration);
assertEquals(subject, service.getAuthenticationFromToken(privateKeyJWT.getClientAssertion().serialize()));
}
@Test
public void testInvalidAudience() throws Exception {
final String subject = "user-1";
final Date expiration = new Date(System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS));
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
final KeyPair pair = keyGen.generateKeyPair();
final RSAPublicKey publicKey = (RSAPublicKey) pair.getPublic();
final JWTAuthenticationClaimsSet claimsSet = getAuthenticationClaimsSet(subject, "incorrect-audience", expiration);
final PrivateKeyJWT privateKeyJWT = new PrivateKeyJWT(claimsSet, JWSAlgorithm.RS256, pair.getPrivate(), null, null);
final KnoxConfiguration configuration = getConfiguration(publicKey);
final KnoxService service = new KnoxService(configuration);
assertThrows(InvalidAuthenticationException.class, () -> service.getAuthenticationFromToken(privateKeyJWT.getClientAssertion().serialize()));
}
private KnoxConfiguration getConfiguration(final RSAPublicKey publicKey) {
final KnoxConfiguration configuration = mock(KnoxConfiguration.class);
when(configuration.isKnoxEnabled()).thenReturn(true);
when(configuration.getKnoxUrl()).thenReturn("knox-sso-url");
when(configuration.getKnoxCookieName()).thenReturn("knox-cookie-name");
when(configuration.getAudiences()).thenReturn(Stream.of(AUDIENCE, AUDIENCE_2).collect(Collectors.toSet()));
when(configuration.getKnoxPublicKey()).thenReturn(publicKey);
return configuration;
}
}

View File

@ -38,8 +38,6 @@ public class LoginFilter implements Filter {
private static final String SAML2_AUTHENTICATE_FILTER_PATH = "/nifi-api/saml2/authenticate/consumer";
private static final String KNOX_REQUEST_PATH = "/nifi-api/access/knox/request";
private ServletContext servletContext;
@Override
@ -50,16 +48,12 @@ public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
final boolean supportsOidc = Boolean.parseBoolean(servletContext.getInitParameter("oidc-supported"));
final boolean supportsKnoxSso = Boolean.parseBoolean(servletContext.getInitParameter("knox-supported"));
final boolean supportsSAML = Boolean.parseBoolean(servletContext.getInitParameter("saml-supported"));
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
final RequestUriBuilder requestUriBuilder = RequestUriBuilder.fromHttpServletRequest(httpServletRequest);
if (supportsKnoxSso) {
final URI redirectUri = requestUriBuilder.path(KNOX_REQUEST_PATH).build();
sendRedirect(response, redirectUri);
} else if (supportsOidc) {
if (supportsOidc) {
final URI redirectUri = requestUriBuilder.path(OAUTH2_AUTHORIZATION_PATH).build();
// Redirect to authorization URL defined in Spring Security OAuth2AuthorizationRequestRedirectFilter
sendRedirect(response, redirectUri);

View File

@ -41,8 +41,6 @@ public class LogoutFilter implements Filter {
private static final String SAML_SINGLE_LOGOUT_URL = "/nifi-api/access/saml/single-logout/request";
private static final String KNOX_LOGOUT_URL = "/nifi-api/access/knox/logout";
private static final String LOGOUT_COMPLETE_URL = "/nifi-api/access/logout/complete";
private ServletContext servletContext;
@ -55,7 +53,6 @@ public class LogoutFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException {
final boolean supportsOidc = Boolean.parseBoolean(servletContext.getInitParameter("oidc-supported"));
final boolean supportsKnoxSso = Boolean.parseBoolean(servletContext.getInitParameter("knox-supported"));
final boolean supportsSaml = Boolean.parseBoolean(servletContext.getInitParameter("saml-supported"));
final boolean supportsSamlSingleLogout = Boolean.parseBoolean(servletContext.getInitParameter("saml-single-logout-supported"));
@ -68,8 +65,6 @@ public class LogoutFilter implements Filter {
if (supportsOidc) {
sendRedirect(OIDC_LOGOUT_URL, request, response);
} else if (supportsKnoxSso) {
sendRedirect(KNOX_LOGOUT_URL, request, response);
} else if (supportsSaml) {
final String logoutUrl = supportsSamlSingleLogout ? SAML_SINGLE_LOGOUT_URL : SAML_LOCAL_LOGOUT_URL;
sendRedirect(logoutUrl, request, response);

View File

@ -175,12 +175,6 @@ nifi.security.user.oidc.client.id=
nifi.security.user.oidc.client.secret=
nifi.security.user.oidc.preferred.jwsalgorithm=
# Apache Knox SSO Properties #
nifi.security.user.knox.url=
nifi.security.user.knox.publicKey=
nifi.security.user.knox.cookieName=hadoop-jwt
nifi.security.user.knox.audiences=
# Identity Mapping Properties #
# These properties allow normalizing user identities such that identities coming from different identity providers
# (certificates, LDAP, Kerberos) can be treated the same internally in NiFi. The following example demonstrates normalizing

View File

@ -175,12 +175,6 @@ nifi.security.user.oidc.client.id=
nifi.security.user.oidc.client.secret=
nifi.security.user.oidc.preferred.jwsalgorithm=
# Apache Knox SSO Properties #
nifi.security.user.knox.url=
nifi.security.user.knox.publicKey=
nifi.security.user.knox.cookieName=hadoop-jwt
nifi.security.user.knox.audiences=
# Identity Mapping Properties #
# These properties allow normalizing user identities such that identities coming from different identity providers
# (certificates, LDAP, Kerberos) can be treated the same internally in NiFi. The following example demonstrates normalizing

View File

@ -176,12 +176,6 @@ nifi.security.user.oidc.client.id=
nifi.security.user.oidc.client.secret=
nifi.security.user.oidc.preferred.jwsalgorithm=
# Apache Knox SSO Properties #
nifi.security.user.knox.url=
nifi.security.user.knox.publicKey=
nifi.security.user.knox.cookieName=hadoop-jwt
nifi.security.user.knox.audiences=
# Identity Mapping Properties #
# These properties allow normalizing user identities such that identities coming from different identity providers
# (certificates, LDAP, Kerberos) can be treated the same internally in NiFi. The following example demonstrates normalizing

View File

@ -180,12 +180,6 @@ nifi.security.user.oidc.client.id=
nifi.security.user.oidc.client.secret=
nifi.security.user.oidc.preferred.jwsalgorithm=
# Apache Knox SSO Properties #
nifi.security.user.knox.url=
nifi.security.user.knox.publicKey=
nifi.security.user.knox.cookieName=hadoop-jwt
nifi.security.user.knox.audiences=
# Identity Mapping Properties #
# These properties allow normalizing user identities such that identities coming from different identity providers
# (certificates, LDAP, Kerberos) can be treated the same internally in NiFi. The following example demonstrates normalizing