mirror of
https://github.com/apache/nifi.git
synced 2025-02-06 18:18:27 +00:00
NIFI-8931 Removed OTP Authentication
- Removed download-token and ui-extension-token REST resources - Removed getAccessToken functions from JavaScript components Signed-off-by: Nathan Gough <thenatog@gmail.com> This closes #5235.
This commit is contained in:
parent
d4a560c59a
commit
0ba9f0dc21
@ -25,15 +25,11 @@ import org.apache.nifi.web.security.jwt.NiFiBearerTokenResolver;
|
|||||||
import org.apache.nifi.web.security.knox.KnoxAuthenticationFilter;
|
import org.apache.nifi.web.security.knox.KnoxAuthenticationFilter;
|
||||||
import org.apache.nifi.web.security.knox.KnoxAuthenticationProvider;
|
import org.apache.nifi.web.security.knox.KnoxAuthenticationProvider;
|
||||||
import org.apache.nifi.web.security.oidc.OIDCEndpoints;
|
import org.apache.nifi.web.security.oidc.OIDCEndpoints;
|
||||||
import org.apache.nifi.web.security.otp.OtpAuthenticationFilter;
|
|
||||||
import org.apache.nifi.web.security.otp.OtpAuthenticationProvider;
|
|
||||||
import org.apache.nifi.web.security.saml.SAMLEndpoints;
|
import org.apache.nifi.web.security.saml.SAMLEndpoints;
|
||||||
import org.apache.nifi.web.security.x509.X509AuthenticationFilter;
|
import org.apache.nifi.web.security.x509.X509AuthenticationFilter;
|
||||||
import org.apache.nifi.web.security.x509.X509AuthenticationProvider;
|
import org.apache.nifi.web.security.x509.X509AuthenticationProvider;
|
||||||
import org.apache.nifi.web.security.x509.X509CertificateExtractor;
|
import org.apache.nifi.web.security.x509.X509CertificateExtractor;
|
||||||
import org.apache.nifi.web.security.x509.X509IdentityProvider;
|
import org.apache.nifi.web.security.x509.X509IdentityProvider;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@ -63,8 +59,6 @@ import java.util.Arrays;
|
|||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||||
public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapter {
|
public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(NiFiWebApiSecurityConfiguration.class);
|
|
||||||
|
|
||||||
private NiFiProperties properties;
|
private NiFiProperties properties;
|
||||||
|
|
||||||
private X509AuthenticationFilter x509AuthenticationFilter;
|
private X509AuthenticationFilter x509AuthenticationFilter;
|
||||||
@ -76,9 +70,6 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
|
|||||||
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||||
private JwtAuthenticationProvider jwtAuthenticationProvider;
|
private JwtAuthenticationProvider jwtAuthenticationProvider;
|
||||||
|
|
||||||
private OtpAuthenticationFilter otpAuthenticationFilter;
|
|
||||||
private OtpAuthenticationProvider otpAuthenticationProvider;
|
|
||||||
|
|
||||||
private KnoxAuthenticationFilter knoxAuthenticationFilter;
|
private KnoxAuthenticationFilter knoxAuthenticationFilter;
|
||||||
private KnoxAuthenticationProvider knoxAuthenticationProvider;
|
private KnoxAuthenticationProvider knoxAuthenticationProvider;
|
||||||
|
|
||||||
@ -89,11 +80,13 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
|
|||||||
super(true); // disable defaults
|
super(true); // disable defaults
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure Web Security with ignoring matchers for authentication requests
|
||||||
|
*
|
||||||
|
* @param webSecurity Spring Web Security Configuration
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void configure(WebSecurity webSecurity) throws Exception {
|
public void configure(final WebSecurity webSecurity) {
|
||||||
// ignore the access endpoints for obtaining the access config, the access token
|
|
||||||
// granting, and access status for a given user (note: we are not ignoring the
|
|
||||||
// the /access/download-token and /access/ui-extension-token endpoints
|
|
||||||
webSecurity
|
webSecurity
|
||||||
.ignoring()
|
.ignoring()
|
||||||
.antMatchers(
|
.antMatchers(
|
||||||
@ -138,9 +131,6 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
|
|||||||
// jwt
|
// jwt
|
||||||
http.addFilterBefore(jwtFilterBean(), AnonymousAuthenticationFilter.class);
|
http.addFilterBefore(jwtFilterBean(), AnonymousAuthenticationFilter.class);
|
||||||
|
|
||||||
// otp
|
|
||||||
http.addFilterBefore(otpFilterBean(), AnonymousAuthenticationFilter.class);
|
|
||||||
|
|
||||||
// knox
|
// knox
|
||||||
http.addFilterBefore(knoxFilterBean(), AnonymousAuthenticationFilter.class);
|
http.addFilterBefore(knoxFilterBean(), AnonymousAuthenticationFilter.class);
|
||||||
|
|
||||||
@ -173,7 +163,6 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
|
|||||||
auth
|
auth
|
||||||
.authenticationProvider(x509AuthenticationProvider)
|
.authenticationProvider(x509AuthenticationProvider)
|
||||||
.authenticationProvider(jwtAuthenticationProvider)
|
.authenticationProvider(jwtAuthenticationProvider)
|
||||||
.authenticationProvider(otpAuthenticationProvider)
|
|
||||||
.authenticationProvider(knoxAuthenticationProvider)
|
.authenticationProvider(knoxAuthenticationProvider)
|
||||||
.authenticationProvider(anonymousAuthenticationProvider);
|
.authenticationProvider(anonymousAuthenticationProvider);
|
||||||
}
|
}
|
||||||
@ -188,16 +177,6 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
|
|||||||
return jwtAuthenticationFilter;
|
return jwtAuthenticationFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
public OtpAuthenticationFilter otpFilterBean() throws Exception {
|
|
||||||
if (otpAuthenticationFilter == null) {
|
|
||||||
otpAuthenticationFilter = new OtpAuthenticationFilter();
|
|
||||||
otpAuthenticationFilter.setProperties(properties);
|
|
||||||
otpAuthenticationFilter.setAuthenticationManager(authenticationManager());
|
|
||||||
}
|
|
||||||
return otpAuthenticationFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public KnoxAuthenticationFilter knoxFilterBean() throws Exception {
|
public KnoxAuthenticationFilter knoxFilterBean() throws Exception {
|
||||||
if (knoxAuthenticationFilter == null) {
|
if (knoxAuthenticationFilter == null) {
|
||||||
@ -240,11 +219,6 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
|
|||||||
this.jwtAuthenticationProvider = jwtAuthenticationProvider;
|
this.jwtAuthenticationProvider = jwtAuthenticationProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public void setOtpAuthenticationProvider(OtpAuthenticationProvider otpAuthenticationProvider) {
|
|
||||||
this.otpAuthenticationProvider = otpAuthenticationProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public void setKnoxAuthenticationProvider(KnoxAuthenticationProvider knoxAuthenticationProvider) {
|
public void setKnoxAuthenticationProvider(KnoxAuthenticationProvider knoxAuthenticationProvider) {
|
||||||
this.knoxAuthenticationProvider = knoxAuthenticationProvider;
|
this.knoxAuthenticationProvider = knoxAuthenticationProvider;
|
||||||
|
@ -51,10 +51,8 @@ import org.apache.nifi.web.security.kerberos.KerberosService;
|
|||||||
import org.apache.nifi.web.security.knox.KnoxService;
|
import org.apache.nifi.web.security.knox.KnoxService;
|
||||||
import org.apache.nifi.web.security.logout.LogoutRequest;
|
import org.apache.nifi.web.security.logout.LogoutRequest;
|
||||||
import org.apache.nifi.web.security.logout.LogoutRequestManager;
|
import org.apache.nifi.web.security.logout.LogoutRequestManager;
|
||||||
import org.apache.nifi.web.security.otp.OtpService;
|
|
||||||
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
|
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
|
||||||
import org.apache.nifi.web.security.token.NiFiAuthenticationToken;
|
import org.apache.nifi.web.security.token.NiFiAuthenticationToken;
|
||||||
import org.apache.nifi.web.security.token.OtpAuthenticationToken;
|
|
||||||
import org.apache.nifi.web.security.x509.X509AuthenticationProvider;
|
import org.apache.nifi.web.security.x509.X509AuthenticationProvider;
|
||||||
import org.apache.nifi.web.security.x509.X509AuthenticationRequestToken;
|
import org.apache.nifi.web.security.x509.X509AuthenticationRequestToken;
|
||||||
import org.apache.nifi.web.security.x509.X509CertificateExtractor;
|
import org.apache.nifi.web.security.x509.X509CertificateExtractor;
|
||||||
@ -106,7 +104,6 @@ public class AccessResource extends ApplicationResource {
|
|||||||
private LoginIdentityProvider loginIdentityProvider;
|
private LoginIdentityProvider loginIdentityProvider;
|
||||||
private JwtAuthenticationProvider jwtAuthenticationProvider;
|
private JwtAuthenticationProvider jwtAuthenticationProvider;
|
||||||
private JwtService jwtService;
|
private JwtService jwtService;
|
||||||
private OtpService otpService;
|
|
||||||
private KnoxService knoxService;
|
private KnoxService knoxService;
|
||||||
private KerberosService kerberosService;
|
private KerberosService kerberosService;
|
||||||
protected LogoutRequestManager logoutRequestManager;
|
protected LogoutRequestManager logoutRequestManager;
|
||||||
@ -310,96 +307,6 @@ public class AccessResource extends ApplicationResource {
|
|||||||
return generateOkResponse(entity).build();
|
return generateOkResponse(entity).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a single use access token for downloading FlowFile content.
|
|
||||||
*
|
|
||||||
* @param httpServletRequest the servlet request
|
|
||||||
* @return A token (string)
|
|
||||||
*/
|
|
||||||
@POST
|
|
||||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
@Path("/download-token")
|
|
||||||
@ApiOperation(
|
|
||||||
value = "Creates a single use access token for downloading FlowFile content.",
|
|
||||||
notes = "The token returned is a base64 encoded string. It is valid for a single request up to five minutes from being issued. " +
|
|
||||||
"It is used as a query parameter name 'access_token'.",
|
|
||||||
response = String.class
|
|
||||||
)
|
|
||||||
@ApiResponses(
|
|
||||||
value = {
|
|
||||||
@ApiResponse(code = 403, message = "Client is not authorized to make this request."),
|
|
||||||
@ApiResponse(code = 409, message = "Unable to create the download token because NiFi is not in the appropriate state. " +
|
|
||||||
"(i.e. may not have any tokens to grant or be configured to support username/password login)"),
|
|
||||||
@ApiResponse(code = 500, message = "Unable to create download token because an unexpected error occurred.")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
public Response createDownloadToken(@Context HttpServletRequest httpServletRequest) {
|
|
||||||
// only support access tokens when communicating over HTTPS
|
|
||||||
if (!httpServletRequest.isSecure()) {
|
|
||||||
throw new IllegalStateException("Download tokens are only issued over HTTPS.");
|
|
||||||
}
|
|
||||||
|
|
||||||
final NiFiUser user = NiFiUserUtils.getNiFiUser();
|
|
||||||
if (user == null) {
|
|
||||||
throw new AccessDeniedException("No user authenticated in the request.");
|
|
||||||
}
|
|
||||||
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(user.getIdentity());
|
|
||||||
|
|
||||||
// generate otp for response
|
|
||||||
final String token = otpService.generateDownloadToken(authenticationToken);
|
|
||||||
|
|
||||||
// build the response
|
|
||||||
final URI uri = URI.create(generateResourceUri("access", "download-token"));
|
|
||||||
return generateCreatedResponse(uri, token).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a single use access token for accessing a NiFi UI extension.
|
|
||||||
*
|
|
||||||
* @param httpServletRequest the servlet request
|
|
||||||
* @return A token (string)
|
|
||||||
*/
|
|
||||||
@POST
|
|
||||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
@Path("/ui-extension-token")
|
|
||||||
@ApiOperation(
|
|
||||||
value = "Creates a single use access token for accessing a NiFi UI extension.",
|
|
||||||
notes = "The token returned is a base64 encoded string. It is valid for a single request up to five minutes from being issued. " +
|
|
||||||
"It is used as a query parameter name 'access_token'.",
|
|
||||||
response = String.class
|
|
||||||
)
|
|
||||||
@ApiResponses(
|
|
||||||
value = {
|
|
||||||
@ApiResponse(code = 403, message = "Client is not authorized to make this request."),
|
|
||||||
@ApiResponse(code = 409, message = "Unable to create the download token because NiFi is not in the appropriate state. " +
|
|
||||||
"(i.e. may not have any tokens to grant or be configured to support username/password login)"),
|
|
||||||
@ApiResponse(code = 500, message = "Unable to create download token because an unexpected error occurred.")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
public Response createUiExtensionToken(@Context HttpServletRequest httpServletRequest) {
|
|
||||||
// only support access tokens when communicating over HTTPS
|
|
||||||
if (!httpServletRequest.isSecure()) {
|
|
||||||
throw new AuthenticationNotSupportedException("UI extension access tokens are only issued over HTTPS.");
|
|
||||||
}
|
|
||||||
|
|
||||||
final NiFiUser user = NiFiUserUtils.getNiFiUser();
|
|
||||||
if (user == null) {
|
|
||||||
throw new AccessDeniedException("No user authenticated in the request.");
|
|
||||||
}
|
|
||||||
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(user.getIdentity());
|
|
||||||
|
|
||||||
// generate otp for response
|
|
||||||
final String token = otpService.generateUiExtensionToken(authenticationToken);
|
|
||||||
|
|
||||||
// build the response
|
|
||||||
final URI uri = URI.create(generateResourceUri("access", "ui-extension-token"));
|
|
||||||
return generateCreatedResponse(uri, token).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a token for accessing the REST API via Kerberos ticket exchange / SPNEGO negotiation.
|
* Creates a token for accessing the REST API via Kerberos ticket exchange / SPNEGO negotiation.
|
||||||
*
|
*
|
||||||
@ -705,10 +612,6 @@ public class AccessResource extends ApplicationResource {
|
|||||||
this.certificateExtractor = certificateExtractor;
|
this.certificateExtractor = certificateExtractor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOtpService(OtpService otpService) {
|
|
||||||
this.otpService = otpService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKnoxService(KnoxService knoxService) {
|
public void setKnoxService(KnoxService knoxService) {
|
||||||
this.knoxService = knoxService;
|
this.knoxService = knoxService;
|
||||||
}
|
}
|
||||||
|
@ -583,7 +583,6 @@
|
|||||||
<property name="principalExtractor" ref="principalExtractor"/>
|
<property name="principalExtractor" ref="principalExtractor"/>
|
||||||
<property name="jwtAuthenticationProvider" ref="jwtAuthenticationProvider"/>
|
<property name="jwtAuthenticationProvider" ref="jwtAuthenticationProvider"/>
|
||||||
<property name="jwtService" ref="jwtService"/>
|
<property name="jwtService" ref="jwtService"/>
|
||||||
<property name="otpService" ref="otpService"/>
|
|
||||||
<property name="kerberosService" ref="kerberosService"/>
|
<property name="kerberosService" ref="kerberosService"/>
|
||||||
<property name="properties" ref="nifiProperties"/>
|
<property name="properties" ref="nifiProperties"/>
|
||||||
<property name="clusterCoordinator" ref="clusterCoordinator"/>
|
<property name="clusterCoordinator" ref="clusterCoordinator"/>
|
||||||
|
@ -107,38 +107,12 @@
|
|||||||
|
|
||||||
// if the selection has changesd, reload the page
|
// if the selection has changesd, reload the page
|
||||||
if (currentLocation !== option.value) {
|
if (currentLocation !== option.value) {
|
||||||
// get an access token if necessary
|
|
||||||
var getAccessToken = $$.Deferred(function (deferred) {
|
|
||||||
if (nf.Storage.hasItem('jwt')) {
|
|
||||||
$$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
url: '../nifi-api/access/ui-extension-token'
|
|
||||||
}).done(function (token) {
|
|
||||||
deferred.resolve(token);
|
|
||||||
}).fail(function () {
|
|
||||||
$$('#content-viewer-message').text('Unable to generate a token to view the content.');
|
|
||||||
$$('#content-viewer-message-dialog').modal('show');
|
|
||||||
deferred.reject();
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
deferred.resolve('');
|
|
||||||
}
|
|
||||||
}).promise();
|
|
||||||
|
|
||||||
// reload as appropriate
|
|
||||||
getAccessToken.done(function (uiExtensionToken) {
|
|
||||||
var contentParameter = {
|
var contentParameter = {
|
||||||
mode: option.value
|
mode: option.value
|
||||||
};
|
};
|
||||||
|
|
||||||
// include the download token if applicable
|
|
||||||
if (typeof uiExtensionToken !== 'undefined' && uiExtensionToken !== null && $$.trim(uiExtensionToken) !== '') {
|
|
||||||
contentParameter['access_token'] = uiExtensionToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
var url = window.location.origin + window.location.pathname;
|
var url = window.location.origin + window.location.pathname;
|
||||||
window.location.href = url + '?' + $$.param($$.extend(contentParameter, params));
|
window.location.href = url + '?' + $$.param($$.extend(contentParameter, params));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -42,7 +42,7 @@ public class OidcService {
|
|||||||
private Cache<CacheKey, String> jwtLookupForCompletedRequests; // identifier from cookie -> jwt or identity (and generate jwt on retrieval)
|
private Cache<CacheKey, String> jwtLookupForCompletedRequests; // identifier from cookie -> jwt or identity (and generate jwt on retrieval)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new OtpService with an expiration of 1 minute.
|
* Creates a new OIDC with an expiration of 1 minute.
|
||||||
*
|
*
|
||||||
* @param identityProvider The identity provider
|
* @param identityProvider The identity provider
|
||||||
*/
|
*/
|
||||||
@ -51,7 +51,7 @@ public class OidcService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new OtpService.
|
* Creates a new OIDC Service.
|
||||||
*
|
*
|
||||||
* @param identityProvider The identity provider
|
* @param identityProvider The identity provider
|
||||||
* @param duration The expiration duration
|
* @param duration The expiration duration
|
||||||
|
@ -1,34 +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.otp;
|
|
||||||
|
|
||||||
import org.springframework.security.core.AuthenticationException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown if a one time use token fails authentication.
|
|
||||||
*/
|
|
||||||
public class OtpAuthenticationException extends AuthenticationException {
|
|
||||||
|
|
||||||
public OtpAuthenticationException(String msg) {
|
|
||||||
super(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public OtpAuthenticationException(String msg, Throwable t) {
|
|
||||||
super(msg, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,81 +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.otp;
|
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import org.apache.nifi.web.security.NiFiAuthenticationFilter;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This filter is used to capture one time passwords (OTP) from requests made to download files through the browser.
|
|
||||||
* It's required because when we initiate a download in the browser, it must be opened in a new tab. The new tab
|
|
||||||
* cannot be initialized with authentication headers, so we must add a token as a query parameter instead. As
|
|
||||||
* tokens in URL strings are visible in various places, this must only be used once - hence our OTP.
|
|
||||||
*/
|
|
||||||
public class OtpAuthenticationFilter extends NiFiAuthenticationFilter {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(OtpAuthenticationFilter.class);
|
|
||||||
|
|
||||||
private static final Pattern PROVENANCE_DOWNLOAD_PATTERN =
|
|
||||||
Pattern.compile("/provenance-events/([0-9]+)/content/((?:input)|(?:output))");
|
|
||||||
private static final Pattern QUEUE_DOWNLOAD_PATTERN =
|
|
||||||
Pattern.compile("/flowfile-queues/([a-f0-9\\-]{36})/flowfiles/([a-f0-9\\-]{36})/content");
|
|
||||||
private static final Pattern TEMPLATE_DOWNLOAD_PATTERN =
|
|
||||||
Pattern.compile("/templates/[a-f0-9\\-]{36}/download");
|
|
||||||
private static final Pattern FLOW_DOWNLOAD_PATTERN =
|
|
||||||
Pattern.compile("/process-groups/[a-f0-9\\-]{36}/download");
|
|
||||||
|
|
||||||
protected static final String ACCESS_TOKEN = "access_token";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Authentication attemptAuthentication(final HttpServletRequest request) {
|
|
||||||
// only support otp login when running securely
|
|
||||||
if (!request.isSecure()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the accessToken out of the query string
|
|
||||||
final String accessToken = request.getParameter(ACCESS_TOKEN);
|
|
||||||
|
|
||||||
// if there is no authorization header, we don't know the user
|
|
||||||
if (accessToken == null) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
if (request.getContextPath().equals("/nifi-api")) {
|
|
||||||
if (isDownloadRequest(request.getPathInfo())) {
|
|
||||||
// handle download requests
|
|
||||||
return new OtpAuthenticationRequestToken(accessToken, true, request.getRemoteAddr());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// handle requests to other context paths (other UI extensions)
|
|
||||||
return new OtpAuthenticationRequestToken(accessToken, false, request.getRemoteAddr());
|
|
||||||
}
|
|
||||||
|
|
||||||
// the path is a support path for otp tokens
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isDownloadRequest(final String pathInfo) {
|
|
||||||
return PROVENANCE_DOWNLOAD_PATTERN.matcher(pathInfo).matches() || QUEUE_DOWNLOAD_PATTERN.matcher(pathInfo).matches()
|
|
||||||
|| TEMPLATE_DOWNLOAD_PATTERN.matcher(pathInfo).matches() || FLOW_DOWNLOAD_PATTERN.matcher(pathInfo).matches();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,87 +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.otp;
|
|
||||||
|
|
||||||
import org.apache.nifi.admin.service.IdpUserGroupService;
|
|
||||||
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.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.core.AuthenticationException;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This provider will be used when the request is attempting to authenticate with a download or ui extension OTP/token.
|
|
||||||
*/
|
|
||||||
public class OtpAuthenticationProvider extends NiFiAuthenticationProvider {
|
|
||||||
|
|
||||||
private OtpService otpService;
|
|
||||||
private final IdpUserGroupService idpUserGroupService;
|
|
||||||
|
|
||||||
public OtpAuthenticationProvider(OtpService otpService, NiFiProperties nifiProperties, Authorizer authorizer, IdpUserGroupService idpUserGroupService) {
|
|
||||||
super(nifiProperties, authorizer);
|
|
||||||
this.otpService = otpService;
|
|
||||||
this.idpUserGroupService = idpUserGroupService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
|
||||||
final OtpAuthenticationRequestToken request = (OtpAuthenticationRequestToken) authentication;
|
|
||||||
|
|
||||||
try {
|
|
||||||
final String otpPrincipal;
|
|
||||||
if (request.isDownloadToken()) {
|
|
||||||
otpPrincipal = otpService.getAuthenticationFromDownloadToken(request.getToken());
|
|
||||||
} else {
|
|
||||||
otpPrincipal = otpService.getAuthenticationFromUiExtensionToken(request.getToken());
|
|
||||||
}
|
|
||||||
final String mappedIdentity = mapIdentity(otpPrincipal);
|
|
||||||
final Set<String> userGroupProviderGroups = getUserGroups(mappedIdentity);
|
|
||||||
final Set<String> idpUserGroups = getIdpUserGroups(mappedIdentity);
|
|
||||||
|
|
||||||
final NiFiUser user = new Builder()
|
|
||||||
.identity(mappedIdentity)
|
|
||||||
.groups(userGroupProviderGroups)
|
|
||||||
.identityProviderGroups(idpUserGroups)
|
|
||||||
.clientAddress(request.getClientAddress())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return new NiFiAuthenticationToken(new NiFiUserDetails(user));
|
|
||||||
} catch (OtpAuthenticationException e) {
|
|
||||||
throw new InvalidAuthenticationException(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supports(Class<?> authentication) {
|
|
||||||
return OtpAuthenticationRequestToken.class.isAssignableFrom(authentication);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<String> getIdpUserGroups(final String mappedIdentity) {
|
|
||||||
return idpUserGroupService.getUserGroups(mappedIdentity).stream()
|
|
||||||
.map(ug -> ug.getGroupName())
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright ownership.
|
|
||||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
||||||
* (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.apache.nifi.web.security.otp;
|
|
||||||
|
|
||||||
import org.apache.nifi.web.security.NiFiAuthenticationRequestToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is an authentication request with a given OTP token.
|
|
||||||
*/
|
|
||||||
public class OtpAuthenticationRequestToken extends NiFiAuthenticationRequestToken {
|
|
||||||
|
|
||||||
private final String token;
|
|
||||||
private final boolean isDownloadToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a representation of the otp authentication request for a user.
|
|
||||||
*
|
|
||||||
* @param token The unique token for this user
|
|
||||||
* @param isDownloadToken Whether or not this represents a download token
|
|
||||||
* @param clientAddress the address of the client making the request
|
|
||||||
*/
|
|
||||||
public OtpAuthenticationRequestToken(final String token, final boolean isDownloadToken, final String clientAddress) {
|
|
||||||
super(clientAddress);
|
|
||||||
setAuthenticated(false);
|
|
||||||
this.token = token;
|
|
||||||
this.isDownloadToken = isDownloadToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getCredentials() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getPrincipal() {
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getToken() {
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDownloadToken() {
|
|
||||||
return isDownloadToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "<OTP token>";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,190 +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.otp;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import javax.crypto.Mac;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
import org.apache.nifi.web.security.token.OtpAuthenticationToken;
|
|
||||||
import org.apache.nifi.web.security.util.CacheKey;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OtpService is a service for generating and verifying one time password tokens.
|
|
||||||
*/
|
|
||||||
public class OtpService {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(OtpService.class);
|
|
||||||
|
|
||||||
private static final String HMAC_SHA256 = "HmacSHA256";
|
|
||||||
|
|
||||||
// protected for testing purposes
|
|
||||||
protected static final int MAX_CACHE_SOFT_LIMIT = 100;
|
|
||||||
|
|
||||||
private final TokenCache downloadTokens;
|
|
||||||
private final TokenCache uiExtensionTokens;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new OtpService with an expiration of 5 minutes.
|
|
||||||
*/
|
|
||||||
public OtpService() {
|
|
||||||
this(5, TimeUnit.MINUTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new OtpService.
|
|
||||||
*
|
|
||||||
* @param duration The expiration duration
|
|
||||||
* @param units The expiration units
|
|
||||||
* @throws NullPointerException If units is null
|
|
||||||
* @throws IllegalArgumentException If duration is negative
|
|
||||||
*/
|
|
||||||
public OtpService(final int duration, final TimeUnit units) {
|
|
||||||
downloadTokens = new TokenCache("download tokens", duration, units);
|
|
||||||
uiExtensionTokens = new TokenCache("UI extension tokens", duration, units);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a download token for the specified authentication.
|
|
||||||
*
|
|
||||||
* @param authenticationToken The authentication
|
|
||||||
* @return The one time use download token
|
|
||||||
*/
|
|
||||||
public String generateDownloadToken(final OtpAuthenticationToken authenticationToken) {
|
|
||||||
return generateToken(downloadTokens, authenticationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the authenticated identity from the specified one time use download token. This method will not return null.
|
|
||||||
*
|
|
||||||
* @param token The one time use download token
|
|
||||||
* @return The authenticated identity
|
|
||||||
* @throws OtpAuthenticationException When the specified token does not correspond to an authenticated identity
|
|
||||||
*/
|
|
||||||
public String getAuthenticationFromDownloadToken(final String token) throws OtpAuthenticationException {
|
|
||||||
return getAuthenticationFromToken(downloadTokens, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a UI extension token for the specified authentication.
|
|
||||||
*
|
|
||||||
* @param authenticationToken The authentication
|
|
||||||
* @return The one time use UI extension token
|
|
||||||
*/
|
|
||||||
public String generateUiExtensionToken(final OtpAuthenticationToken authenticationToken) {
|
|
||||||
return generateToken(uiExtensionTokens, authenticationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the authenticated identity from the specified one time use UI extension token. This method will not return null.
|
|
||||||
*
|
|
||||||
* @param token The one time use UI extension token
|
|
||||||
* @return The authenticated identity
|
|
||||||
* @throws OtpAuthenticationException When the specified token does not correspond to an authenticated identity
|
|
||||||
*/
|
|
||||||
public String getAuthenticationFromUiExtensionToken(final String token) throws OtpAuthenticationException {
|
|
||||||
return getAuthenticationFromToken(uiExtensionTokens, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a token and stores it in the specified cache.
|
|
||||||
*
|
|
||||||
* @param tokenCache A cache that maps tokens to users
|
|
||||||
* @param authenticationToken The authentication
|
|
||||||
* @return The one time use token
|
|
||||||
*/
|
|
||||||
private String generateToken(final TokenCache tokenCache, final OtpAuthenticationToken authenticationToken) {
|
|
||||||
final String userId = (String) authenticationToken.getPrincipal();
|
|
||||||
|
|
||||||
// If the user has a token already, return it
|
|
||||||
if(tokenCache.containsValue(userId)) {
|
|
||||||
return (tokenCache.getKeyForValue(userId)).getKey();
|
|
||||||
} else {
|
|
||||||
// Otherwise, generate a token
|
|
||||||
if (tokenCache.size() >= MAX_CACHE_SOFT_LIMIT) {
|
|
||||||
throw new IllegalStateException("The maximum number of single use tokens have been issued.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash the authentication and build a cache key
|
|
||||||
final CacheKey cacheKey = new CacheKey(hash(authenticationToken));
|
|
||||||
|
|
||||||
// Store the token and user in the cache
|
|
||||||
tokenCache.put(cacheKey, userId);
|
|
||||||
|
|
||||||
// Return the token
|
|
||||||
return cacheKey.getKey();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the corresponding authentication for the specified one time use token. The specified token will be removed
|
|
||||||
* from the token cache.
|
|
||||||
*
|
|
||||||
* @param tokenCache A cache that maps tokens to users
|
|
||||||
* @param token The one time use token
|
|
||||||
* @return The authenticated identity
|
|
||||||
*/
|
|
||||||
private String getAuthenticationFromToken(final TokenCache tokenCache, final String token) throws OtpAuthenticationException {
|
|
||||||
final CacheKey cacheKey = new CacheKey(token);
|
|
||||||
final String authenticatedUser = (String) tokenCache.getIfPresent(cacheKey);
|
|
||||||
|
|
||||||
if (authenticatedUser == null) {
|
|
||||||
throw new OtpAuthenticationException("Unable to validate the access token.");
|
|
||||||
} else {
|
|
||||||
tokenCache.invalidate(cacheKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
return authenticatedUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hashes the specified authentication token. The resulting value will be used as the one time use token.
|
|
||||||
*
|
|
||||||
* @param authenticationToken the authentication token
|
|
||||||
* @return the one time use token
|
|
||||||
*/
|
|
||||||
private String hash(final OtpAuthenticationToken authenticationToken) {
|
|
||||||
try {
|
|
||||||
// input is the user identity and timestamp
|
|
||||||
final String input = authenticationToken.getName() + "-" + System.nanoTime();
|
|
||||||
|
|
||||||
// create the secret using secure random
|
|
||||||
final SecureRandom secureRandom = new SecureRandom();
|
|
||||||
final byte[] randomBytes = new byte[32];
|
|
||||||
secureRandom.nextBytes(randomBytes);
|
|
||||||
final SecretKeySpec secret = new SecretKeySpec(randomBytes, HMAC_SHA256); // 256 bit
|
|
||||||
|
|
||||||
// hash the input
|
|
||||||
final Mac hmacSha256 = Mac.getInstance(HMAC_SHA256);
|
|
||||||
hmacSha256.init(secret);
|
|
||||||
final byte[] output = hmacSha256.doFinal(input.getBytes(StandardCharsets.UTF_8));
|
|
||||||
|
|
||||||
// return the result as a base 64 string
|
|
||||||
return Base64.encodeBase64URLSafeString(output);
|
|
||||||
} catch (final NoSuchAlgorithmException | InvalidKeyException e) {
|
|
||||||
final String errorMessage = "There was an error generating the OTP";
|
|
||||||
logger.error(errorMessage, e);
|
|
||||||
throw new IllegalStateException("Unable to generate single use token.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,144 +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.otp;
|
|
||||||
|
|
||||||
import com.google.common.cache.AbstractCache;
|
|
||||||
import com.google.common.cache.Cache;
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
|
||||||
import com.google.common.cache.CacheStats;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import org.apache.nifi.web.security.util.CacheKey;
|
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class provides a specific wrapper implementation based on the Guava {@link Cache} but with
|
|
||||||
* reverse-index capability because of the special use case (a user [the cache value] can only have
|
|
||||||
* one active token [the cache key] at a time). This allows reverse lookup semantics.
|
|
||||||
*/
|
|
||||||
public class TokenCache extends AbstractCache<CacheKey, String> {
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(TokenCache.class);
|
|
||||||
|
|
||||||
private final String contentsDescription;
|
|
||||||
private final Cache<CacheKey, String> internalCache;
|
|
||||||
|
|
||||||
public TokenCache(String contentsDescription, final int duration, final TimeUnit units) {
|
|
||||||
this.contentsDescription = contentsDescription;
|
|
||||||
internalCache = CacheBuilder.newBuilder().expireAfterWrite(duration, units).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value associated with {@code key} in this cache, or {@code null} if there is no
|
|
||||||
* cached value for {@code key}.
|
|
||||||
*
|
|
||||||
* @param key the (wrapped) {@code token}
|
|
||||||
* @since 11.0
|
|
||||||
* @return the retrieved value ({@code user})
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public @Nullable String getIfPresent(Object key) {
|
|
||||||
return internalCache.getIfPresent(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Puts the provided value ({@code user}) in the cache at the provided key (wrapped {@code token}).
|
|
||||||
*
|
|
||||||
* @param key the cache key
|
|
||||||
* @param value the value to insert
|
|
||||||
* @since 11.0
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void put(CacheKey key, String value) {
|
|
||||||
internalCache.put(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns {@code true} if the cache contains the provided value.
|
|
||||||
*
|
|
||||||
* @param value the value ({@code user}) to look for
|
|
||||||
* @return true if the user exists in the cache
|
|
||||||
*/
|
|
||||||
public boolean containsValue(String value) {
|
|
||||||
return internalCache.asMap().containsValue(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the {@link CacheKey} representing the key ({@code token}) associated with the provided value ({@code user}).
|
|
||||||
*
|
|
||||||
* @param value the value ({@code user}) to look for
|
|
||||||
* @return the CacheKey ({@code token}) associated with this user, or {@code null} if the user has no tokens in this cache
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public CacheKey getKeyForValue(String value) {
|
|
||||||
if (containsValue(value)) {
|
|
||||||
Map<CacheKey, String> cacheMap = internalCache.asMap();
|
|
||||||
for (Map.Entry<CacheKey, String> e : cacheMap.entrySet()) {
|
|
||||||
if (e.getValue().equals(value)) {
|
|
||||||
return e.getKey();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalStateException("The value existed in the cache but expired during retrieval");
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override the unsupported abstract methods from the parent
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void invalidate(Object key) {
|
|
||||||
internalCache.invalidate(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void invalidateAll() {
|
|
||||||
internalCache.invalidateAll(internalCache.asMap().keySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long size() {
|
|
||||||
return internalCache.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CacheStats stats() {
|
|
||||||
return internalCache.stats();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConcurrentMap<CacheKey, String> asMap() {
|
|
||||||
return internalCache.asMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation of the cache.
|
|
||||||
*
|
|
||||||
* @return a string representation of the cache
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return new StringBuilder("TokenCache for ")
|
|
||||||
.append(contentsDescription)
|
|
||||||
.append(" with ")
|
|
||||||
.append(internalCache.size())
|
|
||||||
.append(" elements")
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -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.token;
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is an Authentication Token for logging in. Once a user is authenticated, they can be issued an ID token.
|
|
||||||
*/
|
|
||||||
public class OtpAuthenticationToken extends AbstractAuthenticationToken {
|
|
||||||
|
|
||||||
private final String identity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a representation of the otp authentication token for a user.
|
|
||||||
*
|
|
||||||
* @param identity The unique identifier for this user
|
|
||||||
*/
|
|
||||||
public OtpAuthenticationToken(final String identity) {
|
|
||||||
super(null);
|
|
||||||
setAuthenticated(true);
|
|
||||||
this.identity = identity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getCredentials() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getPrincipal() {
|
|
||||||
return identity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return new StringBuilder("OtpAuthenticationToken for ")
|
|
||||||
.append(getName())
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -39,7 +39,7 @@
|
|||||||
<property name="certificateValidator" ref="certificateValidator"/>
|
<property name="certificateValidator" ref="certificateValidator"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<!-- otp authentication provider -->
|
<!-- x509 authentication provider -->
|
||||||
<bean id="x509AuthenticationProvider" class="org.apache.nifi.web.security.x509.X509AuthenticationProvider">
|
<bean id="x509AuthenticationProvider" class="org.apache.nifi.web.security.x509.X509AuthenticationProvider">
|
||||||
<constructor-arg ref="certificateIdentityProvider" index="0"/>
|
<constructor-arg ref="certificateIdentityProvider" index="0"/>
|
||||||
<constructor-arg ref="authorizer" index="1"/>
|
<constructor-arg ref="authorizer" index="1"/>
|
||||||
@ -59,17 +59,6 @@
|
|||||||
<constructor-arg ref="idpUserGroupService" index="3"/>
|
<constructor-arg ref="idpUserGroupService" index="3"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<!-- otp service -->
|
|
||||||
<bean id="otpService" class="org.apache.nifi.web.security.otp.OtpService"/>
|
|
||||||
|
|
||||||
<!-- otp authentication provider -->
|
|
||||||
<bean id="otpAuthenticationProvider" class="org.apache.nifi.web.security.otp.OtpAuthenticationProvider">
|
|
||||||
<constructor-arg ref="otpService" index="0"/>
|
|
||||||
<constructor-arg ref="nifiProperties" index="1"/>
|
|
||||||
<constructor-arg ref="authorizer" index="2"/>
|
|
||||||
<constructor-arg ref="idpUserGroupService" index="3"/>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<!-- knox service -->
|
<!-- knox service -->
|
||||||
<bean id="knoxService" class="org.apache.nifi.web.security.knox.KnoxServiceFactoryBean">
|
<bean id="knoxService" class="org.apache.nifi.web.security.knox.KnoxServiceFactoryBean">
|
||||||
<property name="properties" ref="nifiProperties"/>
|
<property name="properties" ref="nifiProperties"/>
|
||||||
|
@ -1,249 +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.otp
|
|
||||||
|
|
||||||
|
|
||||||
import org.apache.nifi.web.security.token.OtpAuthenticationToken
|
|
||||||
import org.apache.nifi.web.security.util.CacheKey
|
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.BeforeClass
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.junit.runners.JUnit4
|
|
||||||
import org.slf4j.Logger
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
|
|
||||||
import java.security.Security
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
class TokenCacheTest extends GroovyTestCase {
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(TokenCache.class)
|
|
||||||
|
|
||||||
private static final String andy = "alopresto"
|
|
||||||
private static final String nathan = "ngough"
|
|
||||||
private static final String matt = "mgilman"
|
|
||||||
|
|
||||||
private static final int LONG_CACHE_EXPIRATION = 10
|
|
||||||
private static final int SHORT_CACHE_EXPIRATION = 1
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
static void setUpOnce() throws Exception {
|
|
||||||
Security.addProvider(new BouncyCastleProvider())
|
|
||||||
|
|
||||||
logger.metaClass.methodMissing = { String name, args ->
|
|
||||||
logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
void setUp() throws Exception {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
void tearDown() throws Exception {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a simple "hash" of the provided principal (for test purposes, simply reverses the String).
|
|
||||||
*
|
|
||||||
* @param principal the token principal
|
|
||||||
* @return the hashed token output
|
|
||||||
*/
|
|
||||||
private static String hash(def principal) {
|
|
||||||
principal.toString().reverse()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the {@link CacheKey} constructed from the provided token.
|
|
||||||
*
|
|
||||||
* @param token the authentication token
|
|
||||||
* @return the cache key
|
|
||||||
*/
|
|
||||||
private static CacheKey buildCacheKey(OtpAuthenticationToken token) {
|
|
||||||
new CacheKey(hash(token.principal))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testShouldCheckIfContainsValue() throws Exception {
|
|
||||||
// Arrange
|
|
||||||
TokenCache tokenCache = new TokenCache("test tokens", LONG_CACHE_EXPIRATION, TimeUnit.SECONDS)
|
|
||||||
|
|
||||||
OtpAuthenticationToken andyToken = new OtpAuthenticationToken(andy)
|
|
||||||
OtpAuthenticationToken nathanToken = new OtpAuthenticationToken(nathan)
|
|
||||||
|
|
||||||
tokenCache.put(buildCacheKey(andyToken), andy)
|
|
||||||
tokenCache.put(buildCacheKey(nathanToken), nathan)
|
|
||||||
|
|
||||||
logger.info(tokenCache.toString())
|
|
||||||
|
|
||||||
// Act
|
|
||||||
boolean containsAndyToken = tokenCache.containsValue(andy)
|
|
||||||
boolean containsNathanToken = tokenCache.containsValue(nathan)
|
|
||||||
boolean containsMattToken = tokenCache.containsValue(matt)
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assert containsAndyToken
|
|
||||||
assert containsNathanToken
|
|
||||||
assert !containsMattToken
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testShouldGetKeyByValue() throws Exception {
|
|
||||||
// Arrange
|
|
||||||
TokenCache tokenCache = new TokenCache("test tokens", LONG_CACHE_EXPIRATION, TimeUnit.SECONDS)
|
|
||||||
|
|
||||||
OtpAuthenticationToken andyToken = new OtpAuthenticationToken(andy)
|
|
||||||
OtpAuthenticationToken nathanToken = new OtpAuthenticationToken(nathan)
|
|
||||||
|
|
||||||
tokenCache.put(buildCacheKey(andyToken), andy)
|
|
||||||
tokenCache.put(buildCacheKey(nathanToken), nathan)
|
|
||||||
|
|
||||||
logger.info(tokenCache.toString())
|
|
||||||
|
|
||||||
// Act
|
|
||||||
CacheKey keyForAndyToken = tokenCache.getKeyForValue(andy)
|
|
||||||
CacheKey keyForNathanToken = tokenCache.getKeyForValue(nathan)
|
|
||||||
CacheKey keyForMattToken = tokenCache.getKeyForValue(matt)
|
|
||||||
|
|
||||||
def tokens = [keyForAndyToken, keyForNathanToken, keyForMattToken]
|
|
||||||
logger.info("Retrieved tokens: ${tokens}")
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assert keyForAndyToken.getKey() == hash(andyToken.principal)
|
|
||||||
assert keyForNathanToken.getKey() == hash(nathanToken.principal)
|
|
||||||
assert !keyForMattToken
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testShouldNotGetKeyByValueAfterExpiration() throws Exception {
|
|
||||||
// Arrange
|
|
||||||
TokenCache tokenCache = new TokenCache("test tokens", SHORT_CACHE_EXPIRATION, TimeUnit.SECONDS)
|
|
||||||
|
|
||||||
OtpAuthenticationToken andyToken = new OtpAuthenticationToken(andy)
|
|
||||||
OtpAuthenticationToken nathanToken = new OtpAuthenticationToken(nathan)
|
|
||||||
|
|
||||||
tokenCache.put(buildCacheKey(andyToken), andy)
|
|
||||||
tokenCache.put(buildCacheKey(nathanToken), nathan)
|
|
||||||
|
|
||||||
logger.info(tokenCache.toString())
|
|
||||||
|
|
||||||
// Sleep to allow the cache entries to expire (was failing on Windows JDK 8 when only sleeping for 1 second)
|
|
||||||
sleep(SHORT_CACHE_EXPIRATION * 2 * 1000)
|
|
||||||
|
|
||||||
// Act
|
|
||||||
CacheKey keyForAndyToken = tokenCache.getKeyForValue(andy)
|
|
||||||
CacheKey keyForNathanToken = tokenCache.getKeyForValue(nathan)
|
|
||||||
CacheKey keyForMattToken = tokenCache.getKeyForValue(matt)
|
|
||||||
|
|
||||||
def tokens = [keyForAndyToken, keyForNathanToken, keyForMattToken]
|
|
||||||
logger.info("Retrieved tokens: ${tokens}")
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assert !keyForAndyToken
|
|
||||||
assert !keyForNathanToken
|
|
||||||
assert !keyForMattToken
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testShouldInvalidateSingleKey() throws Exception {
|
|
||||||
// Arrange
|
|
||||||
TokenCache tokenCache = new TokenCache("test tokens", LONG_CACHE_EXPIRATION, TimeUnit.SECONDS)
|
|
||||||
|
|
||||||
OtpAuthenticationToken andyToken = new OtpAuthenticationToken(andy)
|
|
||||||
OtpAuthenticationToken nathanToken = new OtpAuthenticationToken(nathan)
|
|
||||||
OtpAuthenticationToken mattToken = new OtpAuthenticationToken(matt)
|
|
||||||
|
|
||||||
CacheKey andyKey = buildCacheKey(andyToken)
|
|
||||||
CacheKey nathanKey = buildCacheKey(nathanToken)
|
|
||||||
CacheKey mattKey = buildCacheKey(mattToken)
|
|
||||||
|
|
||||||
tokenCache.put(andyKey, andy)
|
|
||||||
tokenCache.put(nathanKey, nathan)
|
|
||||||
tokenCache.put(mattKey, matt)
|
|
||||||
|
|
||||||
logger.info(tokenCache.toString())
|
|
||||||
|
|
||||||
// Act
|
|
||||||
tokenCache.invalidate(andyKey)
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assert !tokenCache.containsValue(andy)
|
|
||||||
assert tokenCache.containsValue(nathan)
|
|
||||||
assert tokenCache.containsValue(matt)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testShouldInvalidateMultipleKeys() throws Exception {
|
|
||||||
// Arrange
|
|
||||||
TokenCache tokenCache = new TokenCache("test tokens", LONG_CACHE_EXPIRATION, TimeUnit.SECONDS)
|
|
||||||
|
|
||||||
OtpAuthenticationToken andyToken = new OtpAuthenticationToken(andy)
|
|
||||||
OtpAuthenticationToken nathanToken = new OtpAuthenticationToken(nathan)
|
|
||||||
OtpAuthenticationToken mattToken = new OtpAuthenticationToken(matt)
|
|
||||||
|
|
||||||
CacheKey andyKey = buildCacheKey(andyToken)
|
|
||||||
CacheKey nathanKey = buildCacheKey(nathanToken)
|
|
||||||
CacheKey mattKey = buildCacheKey(mattToken)
|
|
||||||
|
|
||||||
tokenCache.put(andyKey, andy)
|
|
||||||
tokenCache.put(nathanKey, nathan)
|
|
||||||
tokenCache.put(mattKey, matt)
|
|
||||||
|
|
||||||
logger.info(tokenCache.toString())
|
|
||||||
|
|
||||||
// Act
|
|
||||||
tokenCache.invalidateAll([andyKey, nathanKey])
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assert !tokenCache.containsValue(andy)
|
|
||||||
assert !tokenCache.containsValue(nathan)
|
|
||||||
assert tokenCache.containsValue(matt)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testShouldInvalidateAll() throws Exception {
|
|
||||||
// Arrange
|
|
||||||
TokenCache tokenCache = new TokenCache("test tokens", LONG_CACHE_EXPIRATION, TimeUnit.SECONDS)
|
|
||||||
|
|
||||||
OtpAuthenticationToken andyToken = new OtpAuthenticationToken(andy)
|
|
||||||
OtpAuthenticationToken nathanToken = new OtpAuthenticationToken(nathan)
|
|
||||||
OtpAuthenticationToken mattToken = new OtpAuthenticationToken(matt)
|
|
||||||
|
|
||||||
CacheKey andyKey = buildCacheKey(andyToken)
|
|
||||||
CacheKey nathanKey = buildCacheKey(nathanToken)
|
|
||||||
CacheKey mattKey = buildCacheKey(mattToken)
|
|
||||||
|
|
||||||
tokenCache.put(andyKey, andy)
|
|
||||||
tokenCache.put(nathanKey, nathan)
|
|
||||||
tokenCache.put(mattKey, matt)
|
|
||||||
|
|
||||||
logger.info(tokenCache.toString())
|
|
||||||
|
|
||||||
// Act
|
|
||||||
tokenCache.invalidateAll()
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assert !tokenCache.containsValue(andy)
|
|
||||||
assert !tokenCache.containsValue(nathan)
|
|
||||||
assert !tokenCache.containsValue(matt)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,141 +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.otp;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
public class OtpAuthenticationFilterTest {
|
|
||||||
|
|
||||||
private final static String UI_EXTENSION_AUTHENTICATED_USER = "ui-extension-token-authenticated-user";
|
|
||||||
private final static String UI_EXTENSION_TOKEN = "ui-extension-token";
|
|
||||||
|
|
||||||
private final static String DOWNLOAD_AUTHENTICATED_USER = "download-token-authenticated-user";
|
|
||||||
private final static String DOWNLOAD_TOKEN = "download-token";
|
|
||||||
|
|
||||||
private OtpAuthenticationFilter otpAuthenticationFilter;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
otpAuthenticationFilter = new OtpAuthenticationFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInsecureHttp() throws Exception {
|
|
||||||
final HttpServletRequest request = mock(HttpServletRequest.class);
|
|
||||||
when(request.isSecure()).thenReturn(false);
|
|
||||||
assertNull(otpAuthenticationFilter.attemptAuthentication(request));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNoAccessToken() throws Exception {
|
|
||||||
final HttpServletRequest request = mock(HttpServletRequest.class);
|
|
||||||
when(request.isSecure()).thenReturn(true);
|
|
||||||
when(request.getParameter(OtpAuthenticationFilter.ACCESS_TOKEN)).thenReturn(null);
|
|
||||||
assertNull(otpAuthenticationFilter.attemptAuthentication(request));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUnsupportedDownloadPath() throws Exception {
|
|
||||||
final HttpServletRequest request = mock(HttpServletRequest.class);
|
|
||||||
when(request.isSecure()).thenReturn(true);
|
|
||||||
when(request.getParameter(OtpAuthenticationFilter.ACCESS_TOKEN)).thenReturn("my-access-token");
|
|
||||||
when(request.getContextPath()).thenReturn("/nifi-api");
|
|
||||||
when(request.getPathInfo()).thenReturn("/flow/cluster/summary");
|
|
||||||
|
|
||||||
assertNull(otpAuthenticationFilter.attemptAuthentication(request));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUiExtensionPath() throws Exception {
|
|
||||||
final HttpServletRequest request = mock(HttpServletRequest.class);
|
|
||||||
when(request.isSecure()).thenReturn(true);
|
|
||||||
when(request.getParameter(OtpAuthenticationFilter.ACCESS_TOKEN)).thenReturn(UI_EXTENSION_TOKEN);
|
|
||||||
when(request.getContextPath()).thenReturn("/nifi-update-attribute-ui");
|
|
||||||
|
|
||||||
final OtpAuthenticationRequestToken result = (OtpAuthenticationRequestToken) otpAuthenticationFilter.attemptAuthentication(request);
|
|
||||||
assertEquals(UI_EXTENSION_TOKEN, result.getToken());
|
|
||||||
assertFalse(result.isDownloadToken());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testProvenanceInputContentDownload() throws Exception {
|
|
||||||
final HttpServletRequest request = mock(HttpServletRequest.class);
|
|
||||||
when(request.isSecure()).thenReturn(true);
|
|
||||||
when(request.getParameter(OtpAuthenticationFilter.ACCESS_TOKEN)).thenReturn(DOWNLOAD_TOKEN);
|
|
||||||
when(request.getContextPath()).thenReturn("/nifi-api");
|
|
||||||
when(request.getPathInfo()).thenReturn("/provenance-events/0/content/input");
|
|
||||||
|
|
||||||
final OtpAuthenticationRequestToken result = (OtpAuthenticationRequestToken) otpAuthenticationFilter.attemptAuthentication(request);
|
|
||||||
assertEquals(DOWNLOAD_TOKEN, result.getToken());
|
|
||||||
assertTrue(result.isDownloadToken());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testProvenanceOutputContentDownload() throws Exception {
|
|
||||||
final HttpServletRequest request = mock(HttpServletRequest.class);
|
|
||||||
when(request.isSecure()).thenReturn(true);
|
|
||||||
when(request.getParameter(OtpAuthenticationFilter.ACCESS_TOKEN)).thenReturn(DOWNLOAD_TOKEN);
|
|
||||||
when(request.getContextPath()).thenReturn("/nifi-api");
|
|
||||||
when(request.getPathInfo()).thenReturn("/provenance-events/0/content/output");
|
|
||||||
|
|
||||||
final OtpAuthenticationRequestToken result = (OtpAuthenticationRequestToken) otpAuthenticationFilter.attemptAuthentication(request);
|
|
||||||
assertEquals(DOWNLOAD_TOKEN, result.getToken());
|
|
||||||
assertTrue(result.isDownloadToken());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFlowFileContentDownload() throws Exception {
|
|
||||||
final String uuid = UUID.randomUUID().toString();
|
|
||||||
|
|
||||||
final HttpServletRequest request = mock(HttpServletRequest.class);
|
|
||||||
when(request.isSecure()).thenReturn(true);
|
|
||||||
when(request.getParameter(OtpAuthenticationFilter.ACCESS_TOKEN)).thenReturn(DOWNLOAD_TOKEN);
|
|
||||||
when(request.getContextPath()).thenReturn("/nifi-api");
|
|
||||||
when(request.getPathInfo()).thenReturn(String.format("/flowfile-queues/%s/flowfiles/%s/content", uuid, uuid));
|
|
||||||
|
|
||||||
final OtpAuthenticationRequestToken result = (OtpAuthenticationRequestToken) otpAuthenticationFilter.attemptAuthentication(request);
|
|
||||||
assertEquals(DOWNLOAD_TOKEN, result.getToken());
|
|
||||||
assertTrue(result.isDownloadToken());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTemplateDownload() throws Exception {
|
|
||||||
final String uuid = UUID.randomUUID().toString();
|
|
||||||
|
|
||||||
final HttpServletRequest request = mock(HttpServletRequest.class);
|
|
||||||
when(request.isSecure()).thenReturn(true);
|
|
||||||
when(request.getParameter(OtpAuthenticationFilter.ACCESS_TOKEN)).thenReturn(DOWNLOAD_TOKEN);
|
|
||||||
when(request.getContextPath()).thenReturn("/nifi-api");
|
|
||||||
when(request.getPathInfo()).thenReturn(String.format("/templates/%s/download", uuid));
|
|
||||||
|
|
||||||
final OtpAuthenticationRequestToken result = (OtpAuthenticationRequestToken) otpAuthenticationFilter.attemptAuthentication(request);
|
|
||||||
assertEquals(DOWNLOAD_TOKEN, result.getToken());
|
|
||||||
assertTrue(result.isDownloadToken());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,261 +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.otp;
|
|
||||||
|
|
||||||
import org.apache.nifi.admin.service.IdpUserGroupService;
|
|
||||||
import org.apache.nifi.authorization.AccessPolicyProvider;
|
|
||||||
import org.apache.nifi.authorization.Authorizer;
|
|
||||||
import org.apache.nifi.authorization.Group;
|
|
||||||
import org.apache.nifi.authorization.ManagedAuthorizer;
|
|
||||||
import org.apache.nifi.authorization.User;
|
|
||||||
import org.apache.nifi.authorization.UserAndGroups;
|
|
||||||
import org.apache.nifi.authorization.UserGroupProvider;
|
|
||||||
import org.apache.nifi.authorization.user.NiFiUser;
|
|
||||||
import org.apache.nifi.authorization.user.NiFiUserDetails;
|
|
||||||
import org.apache.nifi.idp.IdpType;
|
|
||||||
import org.apache.nifi.idp.IdpUserGroup;
|
|
||||||
import org.apache.nifi.util.NiFiProperties;
|
|
||||||
import org.apache.nifi.web.security.token.NiFiAuthenticationToken;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
|
||||||
import static org.mockito.Mockito.doAnswer;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
public class OtpAuthenticationProviderTest {
|
|
||||||
|
|
||||||
private final static String UI_EXTENSION_AUTHENTICATED_USER = "ui-extension-token-authenticated-user";
|
|
||||||
private final static String UI_EXTENSION_TOKEN = "ui-extension-token";
|
|
||||||
|
|
||||||
private final static String DOWNLOAD_AUTHENTICATED_USER = "download-token-authenticated-user";
|
|
||||||
private final static String DOWNLOAD_TOKEN = "download-token";
|
|
||||||
|
|
||||||
private OtpService otpService;
|
|
||||||
private OtpAuthenticationProvider otpAuthenticationProvider;
|
|
||||||
private NiFiProperties nifiProperties;
|
|
||||||
private IdpUserGroupService idpUserGroupService;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
otpService = mock(OtpService.class);
|
|
||||||
doAnswer(new Answer<String>() {
|
|
||||||
@Override
|
|
||||||
public String answer(InvocationOnMock invocation) throws Throwable {
|
|
||||||
Object[] args = invocation.getArguments();
|
|
||||||
String downloadToken = (String) args[0];
|
|
||||||
|
|
||||||
if (DOWNLOAD_TOKEN.equals(downloadToken)) {
|
|
||||||
return DOWNLOAD_AUTHENTICATED_USER;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new OtpAuthenticationException("Invalid token");
|
|
||||||
}
|
|
||||||
}).when(otpService).getAuthenticationFromDownloadToken(anyString());
|
|
||||||
doAnswer(new Answer<String>() {
|
|
||||||
@Override
|
|
||||||
public String answer(InvocationOnMock invocation) throws Throwable {
|
|
||||||
Object[] args = invocation.getArguments();
|
|
||||||
String uiExtensionToken = (String) args[0];
|
|
||||||
|
|
||||||
if (UI_EXTENSION_TOKEN.equals(uiExtensionToken)) {
|
|
||||||
return UI_EXTENSION_AUTHENTICATED_USER;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new OtpAuthenticationException("Invalid token");
|
|
||||||
}
|
|
||||||
}).when(otpService).getAuthenticationFromUiExtensionToken(anyString());
|
|
||||||
|
|
||||||
idpUserGroupService = mock(IdpUserGroupService.class);
|
|
||||||
|
|
||||||
otpAuthenticationProvider = new OtpAuthenticationProvider(
|
|
||||||
otpService, mock(NiFiProperties.class), mock(Authorizer.class), idpUserGroupService);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUiExtensionPath() throws Exception {
|
|
||||||
when(idpUserGroupService.getUserGroups(anyString())).thenReturn(Collections.emptyList());
|
|
||||||
|
|
||||||
final OtpAuthenticationRequestToken request = new OtpAuthenticationRequestToken(UI_EXTENSION_TOKEN, false, null);
|
|
||||||
|
|
||||||
final NiFiAuthenticationToken result = (NiFiAuthenticationToken) otpAuthenticationProvider.authenticate(request);
|
|
||||||
final NiFiUserDetails details = (NiFiUserDetails) result.getPrincipal();
|
|
||||||
assertEquals(UI_EXTENSION_AUTHENTICATED_USER, details.getUsername());
|
|
||||||
assertNotNull(details.getNiFiUser());
|
|
||||||
|
|
||||||
assertNull(details.getNiFiUser().getGroups());
|
|
||||||
|
|
||||||
assertNotNull(details.getNiFiUser().getIdentityProviderGroups());
|
|
||||||
assertEquals(0, details.getNiFiUser().getIdentityProviderGroups().size());
|
|
||||||
|
|
||||||
assertNotNull(details.getNiFiUser().getAllGroups());
|
|
||||||
assertEquals(0, details.getNiFiUser().getAllGroups().size());
|
|
||||||
|
|
||||||
verify(otpService, times(1)).getAuthenticationFromUiExtensionToken(UI_EXTENSION_TOKEN);
|
|
||||||
verify(otpService, never()).getAuthenticationFromDownloadToken(anyString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDownload() throws Exception {
|
|
||||||
when(idpUserGroupService.getUserGroups(anyString())).thenReturn(Collections.emptyList());
|
|
||||||
|
|
||||||
final OtpAuthenticationRequestToken request = new OtpAuthenticationRequestToken(DOWNLOAD_TOKEN, true, null);
|
|
||||||
|
|
||||||
final NiFiAuthenticationToken result = (NiFiAuthenticationToken) otpAuthenticationProvider.authenticate(request);
|
|
||||||
final NiFiUserDetails details = (NiFiUserDetails) result.getPrincipal();
|
|
||||||
assertEquals(DOWNLOAD_AUTHENTICATED_USER, details.getUsername());
|
|
||||||
assertNotNull(details.getNiFiUser());
|
|
||||||
|
|
||||||
assertNull(details.getNiFiUser().getGroups());
|
|
||||||
|
|
||||||
assertNotNull(details.getNiFiUser().getIdentityProviderGroups());
|
|
||||||
assertEquals(0, details.getNiFiUser().getIdentityProviderGroups().size());
|
|
||||||
|
|
||||||
assertNotNull(details.getNiFiUser().getAllGroups());
|
|
||||||
assertEquals(0, details.getNiFiUser().getAllGroups().size());
|
|
||||||
|
|
||||||
verify(otpService, never()).getAuthenticationFromUiExtensionToken(anyString());
|
|
||||||
verify(otpService, times(1)).getAuthenticationFromDownloadToken(DOWNLOAD_TOKEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWhenIdpUserGroupsArePresent() {
|
|
||||||
final String groupName1 = "group1";
|
|
||||||
final IdpUserGroup idpUserGroup1 = createIdpUserGroup(1, DOWNLOAD_AUTHENTICATED_USER, groupName1, IdpType.SAML);
|
|
||||||
|
|
||||||
final String groupName2 = "group2";
|
|
||||||
final IdpUserGroup idpUserGroup2 = createIdpUserGroup(2, DOWNLOAD_AUTHENTICATED_USER, groupName2, IdpType.SAML);
|
|
||||||
|
|
||||||
when(idpUserGroupService.getUserGroups(DOWNLOAD_AUTHENTICATED_USER)).thenReturn(Arrays.asList(idpUserGroup1, idpUserGroup2));
|
|
||||||
|
|
||||||
final OtpAuthenticationRequestToken request = new OtpAuthenticationRequestToken(DOWNLOAD_TOKEN, true, null);
|
|
||||||
|
|
||||||
final NiFiAuthenticationToken result = (NiFiAuthenticationToken) otpAuthenticationProvider.authenticate(request);
|
|
||||||
final NiFiUserDetails details = (NiFiUserDetails) result.getPrincipal();
|
|
||||||
assertEquals(DOWNLOAD_AUTHENTICATED_USER, details.getUsername());
|
|
||||||
|
|
||||||
final NiFiUser returnedUser = details.getNiFiUser();
|
|
||||||
assertNotNull(returnedUser);
|
|
||||||
|
|
||||||
// user-group-provider groups are null
|
|
||||||
assertNull(returnedUser.getGroups());
|
|
||||||
|
|
||||||
// identity-provider group contain the two groups
|
|
||||||
assertNotNull(returnedUser.getIdentityProviderGroups());
|
|
||||||
assertEquals(2, returnedUser.getIdentityProviderGroups().size());
|
|
||||||
assertTrue(returnedUser.getIdentityProviderGroups().contains(groupName1));
|
|
||||||
assertTrue(returnedUser.getIdentityProviderGroups().contains(groupName1));
|
|
||||||
|
|
||||||
verify(otpService, never()).getAuthenticationFromUiExtensionToken(anyString());
|
|
||||||
verify(otpService, times(1)).getAuthenticationFromDownloadToken(DOWNLOAD_TOKEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWhenUserGroupProviderGroupsAndIdpUserGroupsArePresent() {
|
|
||||||
// setup idp user group service...
|
|
||||||
|
|
||||||
final String groupName1 = "group1";
|
|
||||||
final IdpUserGroup idpUserGroup1 = createIdpUserGroup(1, DOWNLOAD_AUTHENTICATED_USER, groupName1, IdpType.SAML);
|
|
||||||
|
|
||||||
final String groupName2 = "group2";
|
|
||||||
final IdpUserGroup idpUserGroup2 = createIdpUserGroup(2, DOWNLOAD_AUTHENTICATED_USER, groupName2, IdpType.SAML);
|
|
||||||
|
|
||||||
when(idpUserGroupService.getUserGroups(DOWNLOAD_AUTHENTICATED_USER)).thenReturn(Arrays.asList(idpUserGroup1, idpUserGroup2));
|
|
||||||
|
|
||||||
// setup managed authorizer...
|
|
||||||
|
|
||||||
final String groupName3 = "group3";
|
|
||||||
final Group group3 = new Group.Builder().identifierGenerateRandom().name(groupName3).build();
|
|
||||||
|
|
||||||
final UserGroupProvider userGroupProvider = mock(UserGroupProvider.class);
|
|
||||||
when(userGroupProvider.getUserAndGroups(DOWNLOAD_AUTHENTICATED_USER)).thenReturn(new UserAndGroups() {
|
|
||||||
@Override
|
|
||||||
public User getUser() {
|
|
||||||
return new User.Builder().identifier(DOWNLOAD_AUTHENTICATED_USER).identity(DOWNLOAD_AUTHENTICATED_USER).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<Group> getGroups() {
|
|
||||||
return Collections.singleton(group3);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final AccessPolicyProvider accessPolicyProvider = mock(AccessPolicyProvider.class);
|
|
||||||
when(accessPolicyProvider.getUserGroupProvider()).thenReturn(userGroupProvider);
|
|
||||||
|
|
||||||
final ManagedAuthorizer managedAuthorizer = mock(ManagedAuthorizer.class);
|
|
||||||
when(managedAuthorizer.getAccessPolicyProvider()).thenReturn(accessPolicyProvider);
|
|
||||||
|
|
||||||
// create OTP auth provider using mocks from above
|
|
||||||
|
|
||||||
otpAuthenticationProvider = new OtpAuthenticationProvider(
|
|
||||||
otpService, mock(NiFiProperties.class), managedAuthorizer, idpUserGroupService);
|
|
||||||
|
|
||||||
// test...
|
|
||||||
|
|
||||||
final OtpAuthenticationRequestToken request = new OtpAuthenticationRequestToken(DOWNLOAD_TOKEN, true, null);
|
|
||||||
|
|
||||||
final NiFiAuthenticationToken result = (NiFiAuthenticationToken) otpAuthenticationProvider.authenticate(request);
|
|
||||||
final NiFiUserDetails details = (NiFiUserDetails) result.getPrincipal();
|
|
||||||
assertEquals(DOWNLOAD_AUTHENTICATED_USER, details.getUsername());
|
|
||||||
|
|
||||||
final NiFiUser returnedUser = details.getNiFiUser();
|
|
||||||
assertNotNull(returnedUser);
|
|
||||||
|
|
||||||
// Assert user-group-provider groups are correct
|
|
||||||
assertEquals(1, returnedUser.getGroups().size());
|
|
||||||
assertTrue(returnedUser.getGroups().contains(groupName3));
|
|
||||||
|
|
||||||
// Assert identity-provider groups are correct
|
|
||||||
assertEquals(2, returnedUser.getIdentityProviderGroups().size());
|
|
||||||
assertTrue(returnedUser.getIdentityProviderGroups().contains(groupName1));
|
|
||||||
assertTrue(returnedUser.getIdentityProviderGroups().contains(groupName2));
|
|
||||||
|
|
||||||
// Assert combined groups are correct
|
|
||||||
assertEquals(3, returnedUser.getAllGroups().size());
|
|
||||||
assertTrue(returnedUser.getAllGroups().contains(groupName1));
|
|
||||||
assertTrue(returnedUser.getAllGroups().contains(groupName2));
|
|
||||||
assertTrue(returnedUser.getAllGroups().contains(groupName3));
|
|
||||||
|
|
||||||
verify(otpService, never()).getAuthenticationFromUiExtensionToken(anyString());
|
|
||||||
verify(otpService, times(1)).getAuthenticationFromDownloadToken(DOWNLOAD_TOKEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IdpUserGroup createIdpUserGroup(int id, String identity, String groupName, IdpType idpType) {
|
|
||||||
final IdpUserGroup userGroup = new IdpUserGroup();
|
|
||||||
userGroup.setId(id);
|
|
||||||
userGroup.setIdentity(identity);
|
|
||||||
userGroup.setGroupName(groupName);
|
|
||||||
userGroup.setType(idpType);
|
|
||||||
return userGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,291 +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.otp;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNotEquals;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import org.apache.nifi.web.security.token.OtpAuthenticationToken;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class OtpServiceTest {
|
|
||||||
|
|
||||||
private final static String USER_1 = "user-identity-1";
|
|
||||||
private final static int CACHE_EXPIRY_TIME = 1;
|
|
||||||
private final static int WAIT_TIME = 2000;
|
|
||||||
|
|
||||||
private OtpService otpService;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
otpService = new OtpService();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetAuthenticationForValidDownloadToken() throws Exception {
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(USER_1);
|
|
||||||
final String downloadToken = otpService.generateDownloadToken(authenticationToken);
|
|
||||||
final String authenticatedUser = otpService.getAuthenticationFromDownloadToken(downloadToken);
|
|
||||||
|
|
||||||
assertNotNull(authenticatedUser);
|
|
||||||
assertEquals(USER_1, authenticatedUser);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// ensure the token is no longer valid
|
|
||||||
otpService.getAuthenticationFromDownloadToken(downloadToken);
|
|
||||||
fail();
|
|
||||||
} catch (final OtpAuthenticationException oae) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetAuthenticationForValidUiExtensionToken() throws Exception {
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(USER_1);
|
|
||||||
final String uiExtensionToken = otpService.generateUiExtensionToken(authenticationToken);
|
|
||||||
final String authenticatedUser = otpService.getAuthenticationFromUiExtensionToken(uiExtensionToken);
|
|
||||||
|
|
||||||
assertNotNull(authenticatedUser);
|
|
||||||
assertEquals(USER_1, authenticatedUser);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// ensure the token is no longer valid
|
|
||||||
otpService.getAuthenticationFromUiExtensionToken(uiExtensionToken);
|
|
||||||
fail();
|
|
||||||
} catch (final OtpAuthenticationException oae) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = OtpAuthenticationException.class)
|
|
||||||
public void testGetNonExistentDownloadToken() throws Exception {
|
|
||||||
otpService.getAuthenticationFromDownloadToken("Not a real download token");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = OtpAuthenticationException.class)
|
|
||||||
public void testGetNonExistentUiExtensionToken() throws Exception {
|
|
||||||
otpService.getAuthenticationFromUiExtensionToken("Not a real ui extension token");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalStateException.class)
|
|
||||||
public void testMaxDownloadTokenLimit() throws Exception {
|
|
||||||
// ensure we'll try to loop past the limit
|
|
||||||
for (int i = 1; i < OtpService.MAX_CACHE_SOFT_LIMIT + 10; i++) {
|
|
||||||
try {
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken("user-identity-" + i);
|
|
||||||
otpService.generateDownloadToken(authenticationToken);
|
|
||||||
} catch (final IllegalStateException iae) {
|
|
||||||
// ensure we failed when we've passed the limit
|
|
||||||
assertEquals(OtpService.MAX_CACHE_SOFT_LIMIT + 1, i);
|
|
||||||
throw iae;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalStateException.class)
|
|
||||||
public void testMaxUiExtensionTokenLimit() throws Exception {
|
|
||||||
// ensure we'll try to loop past the limit
|
|
||||||
for (int i = 1; i < OtpService.MAX_CACHE_SOFT_LIMIT + 10; i++) {
|
|
||||||
try {
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken("user-identity-" + i);
|
|
||||||
otpService.generateUiExtensionToken(authenticationToken);
|
|
||||||
} catch (final IllegalStateException iae) {
|
|
||||||
// ensure we failed when we've passed the limit
|
|
||||||
assertEquals(OtpService.MAX_CACHE_SOFT_LIMIT + 1, i);
|
|
||||||
throw iae;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = NullPointerException.class)
|
|
||||||
public void testNullTimeUnits() throws Exception {
|
|
||||||
new OtpService(0, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void testNegativeExpiration() throws Exception {
|
|
||||||
new OtpService(-1, TimeUnit.MINUTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = OtpAuthenticationException.class)
|
|
||||||
public void testUiExtensionTokenExpiration() throws Exception {
|
|
||||||
final OtpService otpServiceWithTightExpiration = new OtpService(CACHE_EXPIRY_TIME, TimeUnit.SECONDS);
|
|
||||||
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(USER_1);
|
|
||||||
final String downloadToken = otpServiceWithTightExpiration.generateUiExtensionToken(authenticationToken);
|
|
||||||
|
|
||||||
// sleep for 2 seconds which should sufficiently expire the valid token
|
|
||||||
Thread.sleep(WAIT_TIME);
|
|
||||||
|
|
||||||
// attempt to get the token now that it's expired
|
|
||||||
otpServiceWithTightExpiration.getAuthenticationFromUiExtensionToken(downloadToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = OtpAuthenticationException.class)
|
|
||||||
public void testDownloadTokenExpiration() throws Exception {
|
|
||||||
final OtpService otpServiceWithTightExpiration = new OtpService(CACHE_EXPIRY_TIME, TimeUnit.SECONDS);
|
|
||||||
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(USER_1);
|
|
||||||
final String downloadToken = otpServiceWithTightExpiration.generateDownloadToken(authenticationToken);
|
|
||||||
|
|
||||||
// sleep for 2 seconds which should sufficiently expire the valid token
|
|
||||||
Thread.sleep(WAIT_TIME);
|
|
||||||
|
|
||||||
// attempt to get the token now that it's expired
|
|
||||||
otpServiceWithTightExpiration.getAuthenticationFromDownloadToken(downloadToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDownloadTokenIsTheSameForSubsequentRequests() {
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(USER_1);
|
|
||||||
final String downloadToken = otpService.generateDownloadToken(authenticationToken);
|
|
||||||
final String secondDownloadToken = otpService.generateDownloadToken(authenticationToken);
|
|
||||||
|
|
||||||
assertEquals(downloadToken, secondDownloadToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDownloadTokenIsTheSameForSubsequentRequestsUntilUsed() {
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(USER_1);
|
|
||||||
|
|
||||||
// generate two tokens
|
|
||||||
final String downloadToken = otpService.generateDownloadToken(authenticationToken);
|
|
||||||
final String secondDownloadToken = otpService.generateDownloadToken(authenticationToken);
|
|
||||||
|
|
||||||
assertEquals(downloadToken, secondDownloadToken);
|
|
||||||
|
|
||||||
// use the token
|
|
||||||
otpService.getAuthenticationFromDownloadToken(downloadToken);
|
|
||||||
|
|
||||||
// make sure the next token is now different
|
|
||||||
final String thirdDownloadToken = otpService.generateDownloadToken(authenticationToken);
|
|
||||||
assertNotEquals(downloadToken, thirdDownloadToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDownloadTokenIsValidForSubsequentGenerateAndUse() {
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(USER_1);
|
|
||||||
|
|
||||||
// generate a token
|
|
||||||
final String downloadToken = otpService.generateDownloadToken(authenticationToken);
|
|
||||||
|
|
||||||
// use the token
|
|
||||||
final String auth = otpService.getAuthenticationFromDownloadToken(downloadToken);
|
|
||||||
assertEquals(USER_1, auth);
|
|
||||||
|
|
||||||
// generate a new token, make sure it's different, then authenticate with it
|
|
||||||
final String secondDownloadToken = otpService.generateDownloadToken(authenticationToken);
|
|
||||||
assertNotEquals(downloadToken, secondDownloadToken);
|
|
||||||
final String secondAuth = otpService.getAuthenticationFromDownloadToken(secondDownloadToken);
|
|
||||||
assertEquals(USER_1, secondAuth);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSingleUserCannotGenerateTooManyUIExtensionTokens() throws Exception {
|
|
||||||
// ensure we'll try to loop past the limit
|
|
||||||
for (int i = 1; i < OtpService.MAX_CACHE_SOFT_LIMIT + 10; i++) {
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken("user-identity-1");
|
|
||||||
otpService.generateUiExtensionToken(authenticationToken);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure other users can still generate tokens
|
|
||||||
final OtpAuthenticationToken anotherAuthenticationToken = new OtpAuthenticationToken("user-identity-2");
|
|
||||||
final String auth = otpService.generateUiExtensionToken(anotherAuthenticationToken);
|
|
||||||
assertNotNull(auth);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSingleUserCannotGenerateTooManyDownloadTokens() throws Exception {
|
|
||||||
// ensure we'll try to loop past the limit
|
|
||||||
for (int i = 1; i < OtpService.MAX_CACHE_SOFT_LIMIT + 10; i++) {
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken("user-identity-1");
|
|
||||||
otpService.generateDownloadToken(authenticationToken);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure other users can still generate tokens
|
|
||||||
final OtpAuthenticationToken anotherAuthenticationToken = new OtpAuthenticationToken("user-identity-2");
|
|
||||||
final String auth = otpService.generateDownloadToken(anotherAuthenticationToken);
|
|
||||||
assertNotNull(auth);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = OtpAuthenticationException.class)
|
|
||||||
public void testDownloadTokenNotValidAfterUse() throws Exception {
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(USER_1);
|
|
||||||
final String downloadToken = otpService.generateDownloadToken(authenticationToken);
|
|
||||||
|
|
||||||
// use the token
|
|
||||||
final String authenticatedUser = otpService.getAuthenticationFromDownloadToken(downloadToken);
|
|
||||||
|
|
||||||
// check we authenticated successfully
|
|
||||||
assertNotNull(authenticatedUser);
|
|
||||||
assertEquals(USER_1, authenticatedUser);
|
|
||||||
|
|
||||||
// check authentication fails with the used token
|
|
||||||
final String failedAuthentication = otpService.getAuthenticationFromDownloadToken(downloadToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = OtpAuthenticationException.class)
|
|
||||||
public void testUIExtensionTokenNotValidAfterUse() throws Exception {
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(USER_1);
|
|
||||||
final String downloadToken = otpService.generateDownloadToken(authenticationToken);
|
|
||||||
|
|
||||||
// use the token
|
|
||||||
final String authenticatedUser = otpService.getAuthenticationFromUiExtensionToken(downloadToken);
|
|
||||||
|
|
||||||
// check we authenticated successfully
|
|
||||||
assertNotNull(authenticatedUser);
|
|
||||||
assertEquals(USER_1, authenticatedUser);
|
|
||||||
|
|
||||||
// check authentication fails with the used token
|
|
||||||
final String failedAuthentication = otpService.getAuthenticationFromUiExtensionToken(downloadToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShouldGenerateNewDownloadTokenAfterExpiration() throws Exception {
|
|
||||||
final OtpService otpServiceWithTightExpiration = new OtpService(CACHE_EXPIRY_TIME, TimeUnit.SECONDS);
|
|
||||||
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(USER_1);
|
|
||||||
final String downloadToken = otpServiceWithTightExpiration.generateDownloadToken(authenticationToken);
|
|
||||||
|
|
||||||
// sleep for 2 seconds which should sufficiently expire the valid token
|
|
||||||
Thread.sleep(WAIT_TIME);
|
|
||||||
|
|
||||||
// get a new token and make sure the previous one had expired
|
|
||||||
final String secondDownloadToken = otpServiceWithTightExpiration.generateDownloadToken(authenticationToken);
|
|
||||||
assertNotEquals(downloadToken, secondDownloadToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDownloadTokenRemainsTheSameBeforeExpirationButNotAfter() throws Exception {
|
|
||||||
final OtpService otpServiceWithTightExpiration = new OtpService(CACHE_EXPIRY_TIME, TimeUnit.SECONDS);
|
|
||||||
|
|
||||||
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(USER_1);
|
|
||||||
final String downloadToken = otpServiceWithTightExpiration.generateDownloadToken(authenticationToken);
|
|
||||||
final String secondDownloadToken = otpServiceWithTightExpiration.generateDownloadToken(authenticationToken);
|
|
||||||
|
|
||||||
assertEquals(downloadToken, secondDownloadToken);
|
|
||||||
|
|
||||||
// sleep for 2 seconds which should sufficiently expire the valid token
|
|
||||||
Thread.sleep(WAIT_TIME);
|
|
||||||
|
|
||||||
// get a new token and make sure the previous one had expired
|
|
||||||
final String thirdDownloadToken = otpServiceWithTightExpiration.generateDownloadToken(authenticationToken);
|
|
||||||
assertNotEquals(downloadToken, thirdDownloadToken);
|
|
||||||
}
|
|
||||||
}
|
|
@ -151,7 +151,6 @@
|
|||||||
api: '../nifi-api',
|
api: '../nifi-api',
|
||||||
controller: '../nifi-api/controller',
|
controller: '../nifi-api/controller',
|
||||||
parameterContexts: '../nifi-api/parameter-contexts',
|
parameterContexts: '../nifi-api/parameter-contexts',
|
||||||
downloadToken: '../nifi-api/access/download-token'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1648,26 +1647,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (processGroupId !== null) {
|
if (processGroupId !== null) {
|
||||||
nfCommon.getAccessToken(config.urls.downloadToken).done(function (downloadToken) {
|
|
||||||
var parameters = {};
|
var parameters = {};
|
||||||
|
|
||||||
// conditionally include the download token
|
|
||||||
if (!nfCommon.isBlank(downloadToken)) {
|
|
||||||
parameters['access_token'] = downloadToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
// open the url
|
// open the url
|
||||||
var uri = '../nifi-api/process-groups/' + encodeURIComponent(processGroupId) + '/download';
|
var uri = '../nifi-api/process-groups/' + encodeURIComponent(processGroupId) + '/download';
|
||||||
if (!$.isEmptyObject(parameters)) {
|
|
||||||
uri += ('?' + $.param(parameters));
|
|
||||||
}
|
|
||||||
window.open(uri);
|
window.open(uri);
|
||||||
}).fail(function () {
|
|
||||||
nfDialog.showOkDialog({
|
|
||||||
headerText: 'Download Flow Definition',
|
|
||||||
dialogContent: 'Unable to generate access token for downloading content.'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -55,7 +55,6 @@
|
|||||||
*/
|
*/
|
||||||
showCustomUi: function (entity, uri, editable) {
|
showCustomUi: function (entity, uri, editable) {
|
||||||
return $.Deferred(function (deferred) {
|
return $.Deferred(function (deferred) {
|
||||||
nfCommon.getAccessToken('../nifi-api/access/ui-extension-token').done(function (uiExtensionToken) {
|
|
||||||
// record the processor id
|
// record the processor id
|
||||||
$('#shell-close-button');
|
$('#shell-close-button');
|
||||||
|
|
||||||
@ -70,22 +69,10 @@
|
|||||||
'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged()
|
'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged()
|
||||||
};
|
};
|
||||||
|
|
||||||
// conditionally include the ui extension token
|
|
||||||
if (!nfCommon.isBlank(uiExtensionToken)) {
|
|
||||||
customUiParams['access_token'] = uiExtensionToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
// show the shell
|
// show the shell
|
||||||
nfShell.showPage('..' + uri + '?' + $.param(customUiParams), false).done(function () {
|
nfShell.showPage('..' + uri + '?' + $.param(customUiParams), false).done(function () {
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
});
|
});
|
||||||
}).fail(function () {
|
|
||||||
nfDialog.showOkDialog({
|
|
||||||
headerText: 'Advanced Configuration',
|
|
||||||
dialogContent: 'Unable to generate access token for accessing the advanced configuration dialog.'
|
|
||||||
});
|
|
||||||
deferred.resolve();
|
|
||||||
});
|
|
||||||
}).promise();
|
}).promise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,8 +67,6 @@
|
|||||||
*/
|
*/
|
||||||
var config = {
|
var config = {
|
||||||
urls: {
|
urls: {
|
||||||
uiExtensionToken: '../nifi-api/access/ui-extension-token',
|
|
||||||
downloadToken: '../nifi-api/access/download-token'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -247,15 +245,8 @@
|
|||||||
var downloadContent = function (flowFileSummary) {
|
var downloadContent = function (flowFileSummary) {
|
||||||
var dataUri = ((nfCommon.isDefinedAndNotNull(flowFileSummary.uri))?flowFileSummary.uri:$('#flowfile-uri').text())+ '/content';
|
var dataUri = ((nfCommon.isDefinedAndNotNull(flowFileSummary.uri))?flowFileSummary.uri:$('#flowfile-uri').text())+ '/content';
|
||||||
|
|
||||||
// perform the request once we've received a token
|
|
||||||
nfCommon.getAccessToken(config.urls.downloadToken).done(function (downloadToken) {
|
|
||||||
var parameters = {};
|
var parameters = {};
|
||||||
|
|
||||||
// conditionally include the ui extension token
|
|
||||||
if (!nfCommon.isBlank(downloadToken)) {
|
|
||||||
parameters['access_token'] = downloadToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
// conditionally include the cluster node id
|
// conditionally include the cluster node id
|
||||||
var clusterNodeId = (nfCommon.isDefinedAndNotNull(flowFileSummary.clusterNodeId))?flowFileSummary.clusterNodeId:$('#flowfile-cluster-node-id').text();
|
var clusterNodeId = (nfCommon.isDefinedAndNotNull(flowFileSummary.clusterNodeId))?flowFileSummary.clusterNodeId:$('#flowfile-cluster-node-id').text();
|
||||||
if (!nfCommon.isBlank(clusterNodeId)) {
|
if (!nfCommon.isBlank(clusterNodeId)) {
|
||||||
@ -268,12 +259,6 @@
|
|||||||
} else {
|
} else {
|
||||||
window.open(dataUri + '?' + $.param(parameters));
|
window.open(dataUri + '?' + $.param(parameters));
|
||||||
}
|
}
|
||||||
}).fail(function () {
|
|
||||||
nfDialog.showOkDialog({
|
|
||||||
headerText: 'Queue Listing',
|
|
||||||
dialogContent: 'Unable to generate access token for downloading content.'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -285,38 +270,6 @@
|
|||||||
|
|
||||||
var dataUri = ((nfCommon.isDefinedAndNotNull(flowFileSummary.uri))?flowFileSummary.uri:$('#flowfile-uri').text())+ '/content';
|
var dataUri = ((nfCommon.isDefinedAndNotNull(flowFileSummary.uri))?flowFileSummary.uri:$('#flowfile-uri').text())+ '/content';
|
||||||
|
|
||||||
// generate tokens as necessary
|
|
||||||
var getAccessTokens = $.Deferred(function (deferred) {
|
|
||||||
if (nfStorage.hasItem('jwt')) {
|
|
||||||
// generate a token for the ui extension and another for the callback
|
|
||||||
var uiExtensionToken = $.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
url: config.urls.uiExtensionToken
|
|
||||||
});
|
|
||||||
var downloadToken = $.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
url: config.urls.downloadToken
|
|
||||||
});
|
|
||||||
|
|
||||||
// wait for each token
|
|
||||||
$.when(uiExtensionToken, downloadToken).done(function (uiExtensionTokenResult, downloadTokenResult) {
|
|
||||||
var uiExtensionToken = uiExtensionTokenResult[0];
|
|
||||||
var downloadToken = downloadTokenResult[0];
|
|
||||||
deferred.resolve(uiExtensionToken, downloadToken);
|
|
||||||
}).fail(function () {
|
|
||||||
nfDialog.showOkDialog({
|
|
||||||
headerText: 'Queue Listing',
|
|
||||||
dialogContent: 'Unable to generate access token for viewing content.'
|
|
||||||
});
|
|
||||||
deferred.reject();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
deferred.resolve('', '');
|
|
||||||
}
|
|
||||||
}).promise();
|
|
||||||
|
|
||||||
// perform the request after we've received the tokens
|
|
||||||
getAccessTokens.done(function (uiExtensionToken, downloadToken) {
|
|
||||||
var dataUriParameters = {};
|
var dataUriParameters = {};
|
||||||
|
|
||||||
// conditionally include the cluster node id
|
// conditionally include the cluster node id
|
||||||
@ -325,11 +278,6 @@
|
|||||||
dataUriParameters['clusterNodeId'] = clusterNodeId;
|
dataUriParameters['clusterNodeId'] = clusterNodeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// include the download token if applicable
|
|
||||||
if (!nfCommon.isBlank(downloadToken)) {
|
|
||||||
dataUriParameters['access_token'] = downloadToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
// include parameters if necessary
|
// include parameters if necessary
|
||||||
if ($.isEmptyObject(dataUriParameters) === false) {
|
if ($.isEmptyObject(dataUriParameters) === false) {
|
||||||
dataUri = dataUri + '?' + $.param(dataUriParameters);
|
dataUri = dataUri + '?' + $.param(dataUriParameters);
|
||||||
@ -351,14 +299,8 @@
|
|||||||
'ref': dataUri
|
'ref': dataUri
|
||||||
};
|
};
|
||||||
|
|
||||||
// include the download token if applicable
|
|
||||||
if (!nfCommon.isBlank(uiExtensionToken)) {
|
|
||||||
contentViewerParameters['access_token'] = uiExtensionToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
// open the content viewer
|
// open the content viewer
|
||||||
window.open(contentViewerUrl + $.param(contentViewerParameters));
|
window.open(contentViewerUrl + $.param(contentViewerParameters));
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1195,29 +1195,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an access token from the specified url.
|
|
||||||
*
|
|
||||||
* @param accessTokenUrl The access token
|
|
||||||
* @returns the access token as a deferred
|
|
||||||
*/
|
|
||||||
getAccessToken: function (accessTokenUrl) {
|
|
||||||
return $.Deferred(function (deferred) {
|
|
||||||
if (nfStorage.hasItem('jwt')) {
|
|
||||||
$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
url: accessTokenUrl
|
|
||||||
}).done(function (token) {
|
|
||||||
deferred.resolve(token);
|
|
||||||
}).fail(function () {
|
|
||||||
deferred.reject();
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
deferred.resolve('');
|
|
||||||
}
|
|
||||||
}).promise();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constants for time duration formatting.
|
* Constants for time duration formatting.
|
||||||
*/
|
*/
|
||||||
|
@ -70,9 +70,7 @@
|
|||||||
provenanceEvents: '../nifi-api/provenance-events/',
|
provenanceEvents: '../nifi-api/provenance-events/',
|
||||||
clusterSearch: '../nifi-api/flow/cluster/search-results',
|
clusterSearch: '../nifi-api/flow/cluster/search-results',
|
||||||
d3Script: 'js/d3/build/d3.min.js',
|
d3Script: 'js/d3/build/d3.min.js',
|
||||||
lineageScript: 'js/nf/provenance/nf-provenance-lineage.js',
|
lineageScript: 'js/nf/provenance/nf-provenance-lineage.js'
|
||||||
uiExtensionToken: '../nifi-api/access/ui-extension-token',
|
|
||||||
downloadToken: '../nifi-api/access/download-token'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -92,15 +90,8 @@
|
|||||||
// build the url
|
// build the url
|
||||||
var dataUri = config.urls.provenanceEvents + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction);
|
var dataUri = config.urls.provenanceEvents + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction);
|
||||||
|
|
||||||
// perform the request once we've received a token
|
|
||||||
nfCommon.getAccessToken(config.urls.downloadToken).done(function (downloadToken) {
|
|
||||||
var parameters = {};
|
var parameters = {};
|
||||||
|
|
||||||
// conditionally include the ui extension token
|
|
||||||
if (!nfCommon.isBlank(downloadToken)) {
|
|
||||||
parameters['access_token'] = downloadToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
// conditionally include the cluster node id
|
// conditionally include the cluster node id
|
||||||
var clusterNodeId = $('#provenance-event-cluster-node-id').text();
|
var clusterNodeId = $('#provenance-event-cluster-node-id').text();
|
||||||
if (!nfCommon.isBlank(clusterNodeId)) {
|
if (!nfCommon.isBlank(clusterNodeId)) {
|
||||||
@ -113,12 +104,6 @@
|
|||||||
} else {
|
} else {
|
||||||
window.open(dataUri + '?' + $.param(parameters));
|
window.open(dataUri + '?' + $.param(parameters));
|
||||||
}
|
}
|
||||||
}).fail(function () {
|
|
||||||
nfDialog.showOkDialog({
|
|
||||||
headerText: 'Provenance',
|
|
||||||
dialogContent: 'Unable to generate access token for downloading content.'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,38 +118,6 @@
|
|||||||
// build the uri to the data
|
// build the uri to the data
|
||||||
var dataUri = controllerUri + 'provenance-events/' + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction);
|
var dataUri = controllerUri + 'provenance-events/' + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction);
|
||||||
|
|
||||||
// generate tokens as necessary
|
|
||||||
var getAccessTokens = $.Deferred(function (deferred) {
|
|
||||||
if (nfStorage.hasItem('jwt')) {
|
|
||||||
// generate a token for the ui extension and another for the callback
|
|
||||||
var uiExtensionToken = $.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
url: config.urls.uiExtensionToken
|
|
||||||
});
|
|
||||||
var downloadToken = $.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
url: config.urls.downloadToken
|
|
||||||
});
|
|
||||||
|
|
||||||
// wait for each token
|
|
||||||
$.when(uiExtensionToken, downloadToken).done(function (uiExtensionTokenResult, downloadTokenResult) {
|
|
||||||
var uiExtensionToken = uiExtensionTokenResult[0];
|
|
||||||
var downloadToken = downloadTokenResult[0];
|
|
||||||
deferred.resolve(uiExtensionToken, downloadToken);
|
|
||||||
}).fail(function () {
|
|
||||||
nfDialog.showOkDialog({
|
|
||||||
headerText: 'Provenance',
|
|
||||||
dialogContent: 'Unable to generate access token for viewing content.'
|
|
||||||
});
|
|
||||||
deferred.reject();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
deferred.resolve('', '');
|
|
||||||
}
|
|
||||||
}).promise();
|
|
||||||
|
|
||||||
// perform the request after we've received the tokens
|
|
||||||
getAccessTokens.done(function (uiExtensionToken, downloadToken) {
|
|
||||||
var dataUriParameters = {};
|
var dataUriParameters = {};
|
||||||
|
|
||||||
// conditionally include the cluster node id
|
// conditionally include the cluster node id
|
||||||
@ -173,11 +126,6 @@
|
|||||||
dataUriParameters['clusterNodeId'] = clusterNodeId;
|
dataUriParameters['clusterNodeId'] = clusterNodeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// include the download token if applicable
|
|
||||||
if (!nfCommon.isBlank(downloadToken)) {
|
|
||||||
dataUriParameters['access_token'] = downloadToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
// include parameters if necessary
|
// include parameters if necessary
|
||||||
if ($.isEmptyObject(dataUriParameters) === false) {
|
if ($.isEmptyObject(dataUriParameters) === false) {
|
||||||
dataUri = dataUri + '?' + $.param(dataUriParameters);
|
dataUri = dataUri + '?' + $.param(dataUriParameters);
|
||||||
@ -199,14 +147,8 @@
|
|||||||
'ref': dataUri
|
'ref': dataUri
|
||||||
};
|
};
|
||||||
|
|
||||||
// include the download token if applicable
|
|
||||||
if (!nfCommon.isBlank(uiExtensionToken)) {
|
|
||||||
contentViewerParameters['access_token'] = uiExtensionToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
// open the content viewer
|
// open the content viewer
|
||||||
window.open(contentViewerUrl + $.param(contentViewerParameters));
|
window.open(contentViewerUrl + $.param(contentViewerParameters));
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,8 +51,7 @@
|
|||||||
*/
|
*/
|
||||||
var config = {
|
var config = {
|
||||||
urls: {
|
urls: {
|
||||||
templates: '../nifi-api/flow/templates',
|
templates: '../nifi-api/flow/templates'
|
||||||
downloadToken: '../nifi-api/access/download-token'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -204,26 +203,7 @@
|
|||||||
* @param {object} templateEntity The template
|
* @param {object} templateEntity The template
|
||||||
*/
|
*/
|
||||||
var downloadTemplate = function (templateEntity) {
|
var downloadTemplate = function (templateEntity) {
|
||||||
nfCommon.getAccessToken(config.urls.downloadToken).done(function (downloadToken) {
|
|
||||||
var parameters = {};
|
|
||||||
|
|
||||||
// conditionally include the download token
|
|
||||||
if (!nfCommon.isBlank(downloadToken)) {
|
|
||||||
parameters['access_token'] = downloadToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
// open the url
|
|
||||||
if ($.isEmptyObject(parameters)) {
|
|
||||||
window.open(templateEntity.template.uri + '/download');
|
window.open(templateEntity.template.uri + '/download');
|
||||||
} else {
|
|
||||||
window.open(templateEntity.template.uri + '/download' + '?' + $.param(parameters));
|
|
||||||
}
|
|
||||||
}).fail(function () {
|
|
||||||
nfDialog.showOkDialog({
|
|
||||||
headerText: 'Download Template',
|
|
||||||
dialogContent: 'Unable to generate access token for downloading content.'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -45,7 +45,7 @@ public class OidcService {
|
|||||||
private Cache<CacheKey, String> jwtLookupForCompletedRequests; // identifier from cookie -> jwt or identity (and generate jwt on retrieval)
|
private Cache<CacheKey, String> jwtLookupForCompletedRequests; // identifier from cookie -> jwt or identity (and generate jwt on retrieval)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new OtpService with an expiration of 1 minute.
|
* Creates a new OIDC Service with an expiration of 1 minute.
|
||||||
*
|
*
|
||||||
* @param identityProvider The identity provider
|
* @param identityProvider The identity provider
|
||||||
*/
|
*/
|
||||||
@ -55,7 +55,7 @@ public class OidcService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new OtpService.
|
* Creates a new OIDC Service.
|
||||||
*
|
*
|
||||||
* @param identityProvider The identity provider
|
* @param identityProvider The identity provider
|
||||||
* @param duration The expiration duration
|
* @param duration The expiration duration
|
||||||
|
Loading…
x
Reference in New Issue
Block a user