mirror of https://github.com/apache/nifi.git
NIFI-655:
- Starting to implement the JWT service. - Parsing JWT on client side in order to render who the user currently is when logged in.
This commit is contained in:
parent
93aa09dace
commit
b6d09b86b6
22
LICENSE
22
LICENSE
|
@ -374,6 +374,28 @@ For details see http://jqueryui.com
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
This product bundles 'jquery.base64.js' which is available under an MIT style license.
|
||||||
|
|
||||||
|
Copyright (c) 2013 Yannick Albert (http://yckart.com/)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
This product bundles 'SlickGrid v2.2' which is available under an MIT style license.
|
This product bundles 'SlickGrid v2.2' which is available under an MIT style license.
|
||||||
|
|
||||||
Copyright (c) 2010 Michael Leibman, http://github.com/mleibman/slickgrid
|
Copyright (c) 2010 Michael Leibman, http://github.com/mleibman/slickgrid
|
||||||
|
|
|
@ -374,6 +374,28 @@ For details see http://jqueryui.com
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
This product bundles 'jquery.base64.js' which is available under an MIT style license.
|
||||||
|
|
||||||
|
Copyright (c) 2013 Yannick Albert (http://yckart.com/)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
This product bundles 'SlickGrid v2.2' which is available under an MIT style license.
|
This product bundles 'SlickGrid v2.2' which is available under an MIT style license.
|
||||||
|
|
||||||
Copyright (c) 2010 Michael Leibman, http://github.com/mleibman/slickgrid
|
Copyright (c) 2010 Michael Leibman, http://github.com/mleibman/slickgrid
|
||||||
|
|
|
@ -77,6 +77,11 @@
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-core</artifactId>
|
<artifactId>nifi-framework-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt</artifactId>
|
||||||
|
<version>0.6.0</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcprov-jdk16</artifactId>
|
<artifactId>bcprov-jdk16</artifactId>
|
||||||
|
|
|
@ -98,7 +98,7 @@ public class RegistrationStatusFilter extends AbstractAuthenticationProcessingFi
|
||||||
return new RegistrationStatusAuthenticationToken(tokenCredentials);
|
return new RegistrationStatusAuthenticationToken(tokenCredentials);
|
||||||
} else {
|
} else {
|
||||||
// we have a certificate so let's consider a proxy chain
|
// we have a certificate so let's consider a proxy chain
|
||||||
final String principal = extractPrincipal(certificate);
|
final String principal = principalExtractor.extractPrincipal(certificate).toString();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// validate the certificate
|
// validate the certificate
|
||||||
|
@ -144,12 +144,6 @@ public class RegistrationStatusFilter extends AbstractAuthenticationProcessingFi
|
||||||
userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
|
userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String extractPrincipal(final X509Certificate certificate) {
|
|
||||||
// extract the principal
|
|
||||||
final Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
|
|
||||||
return ProxiedEntitiesUtils.formatProxyDn(certificatePrincipal.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain, final Authentication authentication)
|
protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain, final Authentication authentication)
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
|
|
|
@ -44,7 +44,6 @@ import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException;
|
|
||||||
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
|
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,16 +87,16 @@ public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingF
|
||||||
// if there is no certificate, look for an existing token
|
// if there is no certificate, look for an existing token
|
||||||
if (certificate == null) {
|
if (certificate == null) {
|
||||||
final String principal = jwtService.getAuthentication(request);
|
final String principal = jwtService.getAuthentication(request);
|
||||||
|
|
||||||
if (principal == null) {
|
if (principal == null) {
|
||||||
throw new AuthenticationCredentialsNotFoundException("Unable to issue token as issue token as no credentials were found in the request.");
|
throw new AuthenticationCredentialsNotFoundException("Unable to issue token as issue token as no credentials were found in the request.");
|
||||||
}
|
}
|
||||||
|
|
||||||
final LoginCredentials tokenCredentials = new LoginCredentials(principal, null);
|
final LoginCredentials tokenCredentials = new LoginCredentials(principal, null);
|
||||||
return new LoginAuthenticationToken(tokenCredentials);
|
return new LoginAuthenticationToken(tokenCredentials);
|
||||||
} else {
|
} else {
|
||||||
// extract the principal
|
// extract the principal
|
||||||
final String principal = extractPrincipal(certificate);
|
final String principal = principalExtractor.extractPrincipal(certificate).toString();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
certificateValidator.validateClientCertificate(request, certificate);
|
certificateValidator.validateClientCertificate(request, certificate);
|
||||||
|
@ -151,16 +150,14 @@ public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingF
|
||||||
} catch (final UsernameNotFoundException unfe) {
|
} catch (final UsernameNotFoundException unfe) {
|
||||||
// if a username not found exception was thrown, the proxies were authorized and now
|
// if a username not found exception was thrown, the proxies were authorized and now
|
||||||
// we can issue a new ID token to the end user
|
// we can issue a new ID token to the end user
|
||||||
|
} catch (final Exception e) {
|
||||||
|
// any other issue we're going to treat as an authentication exception which will return 401
|
||||||
|
throw new AuthenticationException(e.getMessage(), e) {
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String extractPrincipal(final X509Certificate certificate) {
|
|
||||||
// extract the principal
|
|
||||||
final Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
|
|
||||||
return ProxiedEntitiesUtils.formatProxyDn(certificatePrincipal.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private LoginCredentials getLoginCredentials(HttpServletRequest request) {
|
private LoginCredentials getLoginCredentials(HttpServletRequest request) {
|
||||||
final String username = request.getParameter("username");
|
final String username = request.getParameter("username");
|
||||||
final String password = request.getParameter("password");
|
final String password = request.getParameter("password");
|
||||||
|
@ -178,20 +175,15 @@ public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingF
|
||||||
|
|
||||||
// generate JWT for response
|
// generate JWT for response
|
||||||
jwtService.addToken(response, authentication);
|
jwtService.addToken(response, authentication);
|
||||||
|
|
||||||
// mark as successful
|
|
||||||
response.setStatus(HttpServletResponse.SC_CREATED);
|
|
||||||
response.setContentType("text/plain");
|
|
||||||
response.setContentLength(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void unsuccessfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException failed) throws IOException, ServletException {
|
protected void unsuccessfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException failed) throws IOException, ServletException {
|
||||||
response.setContentType("text/plain");
|
response.setContentType("text/plain");
|
||||||
|
|
||||||
final PrintWriter out = response.getWriter();
|
final PrintWriter out = response.getWriter();
|
||||||
out.println(failed.getMessage());
|
out.println(failed.getMessage());
|
||||||
|
|
||||||
if (failed instanceof BadCredentialsException || failed instanceof AuthenticationCredentialsNotFoundException) {
|
if (failed instanceof BadCredentialsException || failed instanceof AuthenticationCredentialsNotFoundException) {
|
||||||
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,9 +16,22 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.nifi.web.security.jwt;
|
package org.apache.nifi.web.security.jwt;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.ExpiredJwtException;
|
||||||
|
import io.jsonwebtoken.Jws;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.MalformedJwtException;
|
||||||
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
|
import io.jsonwebtoken.SignatureException;
|
||||||
|
import io.jsonwebtoken.UnsupportedJwtException;
|
||||||
|
import io.jsonwebtoken.impl.TextCodec;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.Calendar;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.nifi.util.NiFiProperties;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,6 +41,16 @@ public class JwtService {
|
||||||
|
|
||||||
private final static String AUTHORIZATION = "Authorization";
|
private final static String AUTHORIZATION = "Authorization";
|
||||||
|
|
||||||
|
private final String key;
|
||||||
|
private final Integer expires;
|
||||||
|
|
||||||
|
public JwtService(final NiFiProperties properties) {
|
||||||
|
// TODO - load key (and algo/provider?) and expiration from properties
|
||||||
|
|
||||||
|
key = TextCodec.BASE64.encode("nififtw!");
|
||||||
|
expires = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the Authentication by extracting a JWT token from the specified request.
|
* Gets the Authentication by extracting a JWT token from the specified request.
|
||||||
*
|
*
|
||||||
|
@ -35,12 +58,16 @@ public class JwtService {
|
||||||
* @return The user identifier from the token
|
* @return The user identifier from the token
|
||||||
*/
|
*/
|
||||||
public String getAuthentication(final HttpServletRequest request) {
|
public String getAuthentication(final HttpServletRequest request) {
|
||||||
// TODO : actually extract/verify token
|
|
||||||
|
|
||||||
// extract/verify token from incoming request
|
// extract/verify token from incoming request
|
||||||
final String authorization = request.getHeader(AUTHORIZATION);
|
final String authorization = request.getHeader(AUTHORIZATION);
|
||||||
final String username = StringUtils.substringAfterLast(authorization, " ");
|
final String token = StringUtils.substringAfterLast(authorization, " ");
|
||||||
return username;
|
|
||||||
|
try {
|
||||||
|
final Jws<Claims> jwt = Jwts.parser().setSigningKey(key).parseClaimsJws(token);
|
||||||
|
return jwt.getBody().getSubject();
|
||||||
|
} catch (final MalformedJwtException | UnsupportedJwtException | SignatureException | ExpiredJwtException | IllegalArgumentException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,14 +75,25 @@ public class JwtService {
|
||||||
*
|
*
|
||||||
* @param response The response to add the token to
|
* @param response The response to add the token to
|
||||||
* @param authentication The authentication to generate a token for
|
* @param authentication The authentication to generate a token for
|
||||||
|
* @throws java.io.IOException if an io exception occurs
|
||||||
*/
|
*/
|
||||||
public void addToken(final HttpServletResponse response, final Authentication authentication) {
|
public void addToken(final HttpServletResponse response, final Authentication authentication) throws IOException {
|
||||||
// TODO : actually create real token... in header or response body?
|
// set expiration to one day from now
|
||||||
|
final Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.add(Calendar.DATE, expires);
|
||||||
|
|
||||||
// create a token the specified authentication
|
// create a token the specified authentication
|
||||||
String token = authentication.getName();
|
final String identity = authentication.getPrincipal().toString();
|
||||||
|
final String username = authentication.getName();
|
||||||
|
final String token = Jwts.builder().setSubject(identity).claim("preferred_username", username).setExpiration(calendar.getTime()).signWith(SignatureAlgorithm.HS512, key).compact();
|
||||||
|
|
||||||
// add the token as a response header
|
// add the token as a response header
|
||||||
response.setHeader(AUTHORIZATION, "Bearer " + token);
|
final PrintWriter out = response.getWriter();
|
||||||
|
out.print(token);
|
||||||
|
|
||||||
|
// mark the response as successful
|
||||||
|
response.setStatus(HttpServletResponse.SC_CREATED);
|
||||||
|
response.setContentType("text/plain");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.apache.nifi.web.security.token;
|
package org.apache.nifi.web.security.token;
|
||||||
|
|
||||||
import org.apache.nifi.authentication.LoginCredentials;
|
import org.apache.nifi.authentication.LoginCredentials;
|
||||||
|
import org.apache.nifi.security.util.CertificateUtils;
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,4 +46,11 @@ public class LoginAuthenticationToken extends AbstractAuthenticationToken {
|
||||||
public Object getPrincipal() {
|
public Object getPrincipal() {
|
||||||
return credentials.getUsername();
|
return credentials.getUsername();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
// if the username is a DN this will extract the username or CN... if not will return what was passed
|
||||||
|
return CertificateUtils.extractUsername(credentials.getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,9 @@
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<!-- jwt service -->
|
<!-- jwt service -->
|
||||||
<bean id="jwtService" class="org.apache.nifi.web.security.jwt.JwtService"></bean>
|
<bean id="jwtService" class="org.apache.nifi.web.security.jwt.JwtService">
|
||||||
|
<constructor-arg ref="nifiProperties"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<!-- login identity provider -->
|
<!-- login identity provider -->
|
||||||
<bean id="loginIdentityProvider" class="org.apache.nifi.web.security.spring.LoginIdentityProviderFactoryBean">
|
<bean id="loginIdentityProvider" class="org.apache.nifi.web.security.spring.LoginIdentityProviderFactoryBean">
|
||||||
|
|
|
@ -351,6 +351,28 @@ For details see http://jqueryui.com
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
This product bundles 'jquery.base64.js' which is available under an MIT style license.
|
||||||
|
|
||||||
|
Copyright (c) 2013 Yannick Albert (http://yckart.com/)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
This product bundles 'SlickGrid v2.2' which is available under an MIT style license.
|
This product bundles 'SlickGrid v2.2' which is available under an MIT style license.
|
||||||
|
|
||||||
Copyright (c) 2010 Michael Leibman, http://github.com/mleibman/slickgrid
|
Copyright (c) 2010 Michael Leibman, http://github.com/mleibman/slickgrid
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
<link rel="stylesheet" href="js/jquery/qtip2/jquery.qtip.min.css?" type="text/css" />
|
<link rel="stylesheet" href="js/jquery/qtip2/jquery.qtip.min.css?" type="text/css" />
|
||||||
<link rel="stylesheet" href="js/jquery/ui-smoothness/jquery-ui-1.10.4.min.css" type="text/css" />
|
<link rel="stylesheet" href="js/jquery/ui-smoothness/jquery-ui-1.10.4.min.css" type="text/css" />
|
||||||
<script type="text/javascript" src="js/jquery/jquery-2.1.1.min.js"></script>
|
<script type="text/javascript" src="js/jquery/jquery-2.1.1.min.js"></script>
|
||||||
|
<script type="text/javascript" src="js/jquery/jquery.base64.js"></script>
|
||||||
<script type="text/javascript" src="js/jquery/jquery.count.js"></script>
|
<script type="text/javascript" src="js/jquery/jquery.count.js"></script>
|
||||||
<script type="text/javascript" src="js/jquery/jquery.center.js"></script>
|
<script type="text/javascript" src="js/jquery/jquery.center.js"></script>
|
||||||
<script type="text/javascript" src="js/jquery/modal/jquery.modal.js?${project.version}"></script>
|
<script type="text/javascript" src="js/jquery/modal/jquery.modal.js?${project.version}"></script>
|
||||||
|
|
|
@ -45,7 +45,9 @@
|
||||||
<div id="header-links-container">
|
<div id="header-links-container">
|
||||||
<ul>
|
<ul>
|
||||||
<li id="current-user-container">
|
<li id="current-user-container">
|
||||||
<span id="current-user"></span>
|
<div id="anonymous-user-alert"></div>
|
||||||
|
<div id="current-user"></div>
|
||||||
|
<div class="clear"></div>
|
||||||
</li>
|
</li>
|
||||||
<li id="login-link-container">
|
<li id="login-link-container">
|
||||||
<span id="login-link" class="link">login</span>
|
<span id="login-link" class="link">login</span>
|
||||||
|
|
|
@ -16,7 +16,17 @@
|
||||||
--%>
|
--%>
|
||||||
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
|
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
|
||||||
<div id="nifi-registration-container" class="hidden">
|
<div id="nifi-registration-container" class="hidden">
|
||||||
<div id="nifi-registration-title" class="login-title">Submit Justification</div>
|
<div id="nifi-registration-title" class="login-title nifi-submit-justification">Submit Justification</div>
|
||||||
|
<div id="nifi-user-submit-justification-container" class="nifi-submit-justification">
|
||||||
|
<div class="setting">
|
||||||
|
<div class="setting-name">User</div>
|
||||||
|
<div class="setting-field">
|
||||||
|
<div id="nifi-user-submit-justification"></div>
|
||||||
|
<span id="nifi-user-submit-justification-logout" class="link hidden">logout</span>
|
||||||
|
<div class="clear"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-name">Justification</div>
|
<div class="setting-name">Justification</div>
|
||||||
<div class="setting-field">
|
<div class="setting-field">
|
||||||
|
|
|
@ -506,7 +506,17 @@ div.search-glass-pane {
|
||||||
|
|
||||||
/* styles for the status link */
|
/* styles for the status link */
|
||||||
|
|
||||||
|
#anonymous-user-alert {
|
||||||
|
float: left;
|
||||||
|
margin-top: -2px;
|
||||||
|
margin-right: 6px;
|
||||||
|
width: 18px;
|
||||||
|
height: 16px;
|
||||||
|
background-image: url(../images/iconAlert.png);
|
||||||
|
}
|
||||||
|
|
||||||
#current-user {
|
#current-user {
|
||||||
|
float: left;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,10 +73,24 @@ body.login-body input, body.login-body textarea {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#nifi-registration-container {
|
#nifi-registration-container {
|
||||||
margin-top: 10px;
|
|
||||||
width: 412px;
|
width: 412px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#nifi-user-submit-justification-container {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nifi-user-submit-justification {
|
||||||
|
float: left;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nifi-user-submit-justification-logout {
|
||||||
|
margin-left: 10px;
|
||||||
|
float: left;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
#nifi-registration-justification {
|
#nifi-registration-justification {
|
||||||
height: 200px;
|
height: 200px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*!
|
||||||
|
* jquery.base64.js 0.0.3 - https://github.com/yckart/jquery.base64.js
|
||||||
|
* Makes Base64 en & -decoding simpler as it is.
|
||||||
|
*
|
||||||
|
* Based upon: https://gist.github.com/Yaffle/1284012
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012 Yannick Albert (http://yckart.com)
|
||||||
|
* Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php).
|
||||||
|
* 2013/02/10
|
||||||
|
**/
|
||||||
|
;(function($) {
|
||||||
|
|
||||||
|
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
|
||||||
|
a256 = '',
|
||||||
|
r64 = [256],
|
||||||
|
r256 = [256],
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
var UTF8 = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode multi-byte Unicode string into utf-8 multiple single-byte characters
|
||||||
|
* (BMP / basic multilingual plane only)
|
||||||
|
*
|
||||||
|
* Chars in range U+0080 - U+07FF are encoded in 2 chars, U+0800 - U+FFFF in 3 chars
|
||||||
|
*
|
||||||
|
* @param {String} strUni Unicode string to be encoded as UTF-8
|
||||||
|
* @returns {String} encoded string
|
||||||
|
*/
|
||||||
|
encode: function(strUni) {
|
||||||
|
// use regular expressions & String.replace callback function for better efficiency
|
||||||
|
// than procedural approaches
|
||||||
|
var strUtf = strUni.replace(/[\u0080-\u07ff]/g, // U+0080 - U+07FF => 2 bytes 110yyyyy, 10zzzzzz
|
||||||
|
function(c) {
|
||||||
|
var cc = c.charCodeAt(0);
|
||||||
|
return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f);
|
||||||
|
})
|
||||||
|
.replace(/[\u0800-\uffff]/g, // U+0800 - U+FFFF => 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz
|
||||||
|
function(c) {
|
||||||
|
var cc = c.charCodeAt(0);
|
||||||
|
return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3F, 0x80 | cc & 0x3f);
|
||||||
|
});
|
||||||
|
return strUtf;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode utf-8 encoded string back into multi-byte Unicode characters
|
||||||
|
*
|
||||||
|
* @param {String} strUtf UTF-8 string to be decoded back to Unicode
|
||||||
|
* @returns {String} decoded string
|
||||||
|
*/
|
||||||
|
decode: function(strUtf) {
|
||||||
|
// note: decode 3-byte chars first as decoded 2-byte strings could appear to be 3-byte char!
|
||||||
|
var strUni = strUtf.replace(/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars
|
||||||
|
function(c) { // (note parentheses for precence)
|
||||||
|
var cc = ((c.charCodeAt(0) & 0x0f) << 12) | ((c.charCodeAt(1) & 0x3f) << 6) | (c.charCodeAt(2) & 0x3f);
|
||||||
|
return String.fromCharCode(cc);
|
||||||
|
})
|
||||||
|
.replace(/[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars
|
||||||
|
function(c) { // (note parentheses for precence)
|
||||||
|
var cc = (c.charCodeAt(0) & 0x1f) << 6 | c.charCodeAt(1) & 0x3f;
|
||||||
|
return String.fromCharCode(cc);
|
||||||
|
});
|
||||||
|
return strUni;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
while(i < 256) {
|
||||||
|
var c = String.fromCharCode(i);
|
||||||
|
a256 += c;
|
||||||
|
r256[i] = i;
|
||||||
|
r64[i] = b64.indexOf(c);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
function code(s, discard, alpha, beta, w1, w2) {
|
||||||
|
s = String(s);
|
||||||
|
var buffer = 0,
|
||||||
|
i = 0,
|
||||||
|
length = s.length,
|
||||||
|
result = '',
|
||||||
|
bitsInBuffer = 0;
|
||||||
|
|
||||||
|
while(i < length) {
|
||||||
|
var c = s.charCodeAt(i);
|
||||||
|
c = c < 256 ? alpha[c] : -1;
|
||||||
|
|
||||||
|
buffer = (buffer << w1) + c;
|
||||||
|
bitsInBuffer += w1;
|
||||||
|
|
||||||
|
while(bitsInBuffer >= w2) {
|
||||||
|
bitsInBuffer -= w2;
|
||||||
|
var tmp = buffer >> bitsInBuffer;
|
||||||
|
result += beta.charAt(tmp);
|
||||||
|
buffer ^= tmp << bitsInBuffer;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
if(!discard && bitsInBuffer > 0) result += beta.charAt(buffer << (w2 - bitsInBuffer));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var Plugin = $.base64 = function(dir, input, encode) {
|
||||||
|
return input ? Plugin[dir](input, encode) : dir ? null : this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Plugin.btoa = Plugin.encode = function(plain, utf8encode) {
|
||||||
|
plain = Plugin.raw === false || Plugin.utf8encode || utf8encode ? UTF8.encode(plain) : plain;
|
||||||
|
plain = code(plain, false, r256, b64, 8, 6);
|
||||||
|
return plain + '===='.slice((plain.length % 4) || 4);
|
||||||
|
};
|
||||||
|
|
||||||
|
Plugin.atob = Plugin.decode = function(coded, utf8decode) {
|
||||||
|
coded = coded.replace(/[^A-Za-z0-9\+\/\=]/g, "");
|
||||||
|
coded = String(coded).split('=');
|
||||||
|
var i = coded.length;
|
||||||
|
do {--i;
|
||||||
|
coded[i] = code(coded[i], true, r64, a256, 6, 8);
|
||||||
|
} while (i > 0);
|
||||||
|
coded = coded.join('');
|
||||||
|
return Plugin.raw === false || Plugin.utf8decode || utf8decode ? UTF8.decode(coded) : coded;
|
||||||
|
};
|
||||||
|
}(jQuery));
|
|
@ -64,7 +64,6 @@ nf.Canvas = (function () {
|
||||||
bulletinBoard: '../nifi-api/controller/bulletin-board',
|
bulletinBoard: '../nifi-api/controller/bulletin-board',
|
||||||
banners: '../nifi-api/controller/banners',
|
banners: '../nifi-api/controller/banners',
|
||||||
controller: '../nifi-api/controller',
|
controller: '../nifi-api/controller',
|
||||||
token: '../nifi-api/token',
|
|
||||||
controllerConfig: '../nifi-api/controller/config',
|
controllerConfig: '../nifi-api/controller/config',
|
||||||
loginConfig: '../nifi-api/controller/login/config',
|
loginConfig: '../nifi-api/controller/login/config',
|
||||||
cluster: '../nifi-api/cluster',
|
cluster: '../nifi-api/cluster',
|
||||||
|
@ -191,7 +190,7 @@ nf.Canvas = (function () {
|
||||||
if (!refreshContainer.is(':visible')) {
|
if (!refreshContainer.is(':visible')) {
|
||||||
$('#stats-last-refreshed').addClass('alert');
|
$('#stats-last-refreshed').addClass('alert');
|
||||||
var refreshMessage = "This flow has been modified by '" + revision.lastModifier + "'. Please refresh.";
|
var refreshMessage = "This flow has been modified by '" + revision.lastModifier + "'. Please refresh.";
|
||||||
|
|
||||||
// update the tooltip
|
// update the tooltip
|
||||||
var refreshRequiredIcon = $('#refresh-required-icon');
|
var refreshRequiredIcon = $('#refresh-required-icon');
|
||||||
if (refreshRequiredIcon.data('qtip')) {
|
if (refreshRequiredIcon.data('qtip')) {
|
||||||
|
@ -201,10 +200,10 @@ nf.Canvas = (function () {
|
||||||
content: refreshMessage
|
content: refreshMessage
|
||||||
}, nf.CanvasUtils.config.systemTooltipConfig));
|
}, nf.CanvasUtils.config.systemTooltipConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshContainer.show();
|
refreshContainer.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert the refresh needed text in the settings - if necessary
|
// insert the refresh needed text in the settings - if necessary
|
||||||
if (!settingsRefreshIcon.is(':visible')) {
|
if (!settingsRefreshIcon.is(':visible')) {
|
||||||
$('#settings-last-refreshed').addClass('alert');
|
$('#settings-last-refreshed').addClass('alert');
|
||||||
|
@ -336,7 +335,7 @@ nf.Canvas = (function () {
|
||||||
'offset': '100%',
|
'offset': '100%',
|
||||||
'stop-color': '#ffffff'
|
'stop-color': '#ffffff'
|
||||||
});
|
});
|
||||||
|
|
||||||
// define the gradient for the expiration icon
|
// define the gradient for the expiration icon
|
||||||
var expirationBackground = defs.append('linearGradient')
|
var expirationBackground = defs.append('linearGradient')
|
||||||
.attr({
|
.attr({
|
||||||
|
@ -346,7 +345,7 @@ nf.Canvas = (function () {
|
||||||
'x2': '0%',
|
'x2': '0%',
|
||||||
'y2': '100%'
|
'y2': '100%'
|
||||||
});
|
});
|
||||||
|
|
||||||
expirationBackground.append('stop')
|
expirationBackground.append('stop')
|
||||||
.attr({
|
.attr({
|
||||||
'offset': '0%',
|
'offset': '0%',
|
||||||
|
@ -400,106 +399,106 @@ nf.Canvas = (function () {
|
||||||
// prevent further propagation (to parents and others handlers
|
// prevent further propagation (to parents and others handlers
|
||||||
// on the same element to prevent zoom behavior)
|
// on the same element to prevent zoom behavior)
|
||||||
d3.event.stopImmediatePropagation();
|
d3.event.stopImmediatePropagation();
|
||||||
|
|
||||||
// prevents the browser from changing to a text selection cursor
|
// prevents the browser from changing to a text selection cursor
|
||||||
d3.event.preventDefault();
|
d3.event.preventDefault();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on('mousemove.selection', function () {
|
.on('mousemove.selection', function () {
|
||||||
// update selection box if shift is held down
|
// update selection box if shift is held down
|
||||||
if (d3.event.shiftKey) {
|
if (d3.event.shiftKey) {
|
||||||
// get the selection box
|
// get the selection box
|
||||||
var selectionBox = d3.select('rect.selection');
|
var selectionBox = d3.select('rect.selection');
|
||||||
if (!selectionBox.empty()) {
|
if (!selectionBox.empty()) {
|
||||||
// get the original position
|
// get the original position
|
||||||
var originalPosition = selectionBox.datum();
|
var originalPosition = selectionBox.datum();
|
||||||
var position = d3.mouse(canvas.node());
|
var position = d3.mouse(canvas.node());
|
||||||
|
|
||||||
var d = {};
|
var d = {};
|
||||||
if (originalPosition[0] < position[0]) {
|
if (originalPosition[0] < position[0]) {
|
||||||
d.x = originalPosition[0];
|
d.x = originalPosition[0];
|
||||||
d.width = position[0] - originalPosition[0];
|
d.width = position[0] - originalPosition[0];
|
||||||
} else {
|
} else {
|
||||||
d.x = position[0];
|
d.x = position[0];
|
||||||
d.width = originalPosition[0] - position[0];
|
d.width = originalPosition[0] - position[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (originalPosition[1] < position[1]) {
|
||||||
|
d.y = originalPosition[1];
|
||||||
|
d.height = position[1] - originalPosition[1];
|
||||||
|
} else {
|
||||||
|
d.y = position[1];
|
||||||
|
d.height = originalPosition[1] - position[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the selection box
|
||||||
|
selectionBox.attr(d);
|
||||||
|
|
||||||
|
// prevent further propagation (to parents)
|
||||||
|
d3.event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on('mouseup.selection', function () {
|
||||||
|
// ensure this originated from clicking the canvas, not a component.
|
||||||
|
// when clicking on a component, the event propagation is stopped so
|
||||||
|
// it never reaches the canvas. we cannot do this however on up events
|
||||||
|
// since the drag events break down
|
||||||
|
if (canvasClicked === false) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (originalPosition[1] < position[1]) {
|
// reset the canvas click flag
|
||||||
d.y = originalPosition[1];
|
canvasClicked = false;
|
||||||
d.height = position[1] - originalPosition[1];
|
|
||||||
} else {
|
// get the selection box
|
||||||
d.y = position[1];
|
var selectionBox = d3.select('rect.selection');
|
||||||
d.height = originalPosition[1] - position[1];
|
if (!selectionBox.empty()) {
|
||||||
|
var selectionBoundingBox = {
|
||||||
|
x: parseInt(selectionBox.attr('x'), 10),
|
||||||
|
y: parseInt(selectionBox.attr('y'), 10),
|
||||||
|
width: parseInt(selectionBox.attr('width'), 10),
|
||||||
|
height: parseInt(selectionBox.attr('height'), 10)
|
||||||
|
};
|
||||||
|
|
||||||
|
// see if a component should be selected or not
|
||||||
|
d3.selectAll('g.component').classed('selected', function (d) {
|
||||||
|
// consider it selected if its already selected or enclosed in the bounding box
|
||||||
|
return d3.select(this).classed('selected') ||
|
||||||
|
d.component.position.x >= selectionBoundingBox.x && (d.component.position.x + d.dimensions.width) <= (selectionBoundingBox.x + selectionBoundingBox.width) &&
|
||||||
|
d.component.position.y >= selectionBoundingBox.y && (d.component.position.y + d.dimensions.height) <= (selectionBoundingBox.y + selectionBoundingBox.height);
|
||||||
|
});
|
||||||
|
|
||||||
|
// see if a connection should be selected or not
|
||||||
|
d3.selectAll('g.connection').classed('selected', function (d) {
|
||||||
|
// consider all points
|
||||||
|
var points = [d.start].concat(d.bends, [d.end]);
|
||||||
|
|
||||||
|
// determine the bounding box
|
||||||
|
var x = d3.extent(points, function (pt) {
|
||||||
|
return pt.x;
|
||||||
|
});
|
||||||
|
var y = d3.extent(points, function (pt) {
|
||||||
|
return pt.y;
|
||||||
|
});
|
||||||
|
|
||||||
|
// consider it selected if its already selected or enclosed in the bounding box
|
||||||
|
return d3.select(this).classed('selected') ||
|
||||||
|
x[0] >= selectionBoundingBox.x && x[1] <= (selectionBoundingBox.x + selectionBoundingBox.width) &&
|
||||||
|
y[0] >= selectionBoundingBox.y && y[1] <= (selectionBoundingBox.y + selectionBoundingBox.height);
|
||||||
|
});
|
||||||
|
|
||||||
|
// remove the selection box
|
||||||
|
selectionBox.remove();
|
||||||
|
} else if (panning === false) {
|
||||||
|
// deselect as necessary if we are not panning
|
||||||
|
nf.CanvasUtils.getSelection().classed('selected', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the selection box
|
// update the toolbar
|
||||||
selectionBox.attr(d);
|
nf.CanvasToolbar.refresh();
|
||||||
|
|
||||||
// prevent further propagation (to parents)
|
|
||||||
d3.event.stopPropagation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on('mouseup.selection', function () {
|
|
||||||
// ensure this originated from clicking the canvas, not a component.
|
|
||||||
// when clicking on a component, the event propagation is stopped so
|
|
||||||
// it never reaches the canvas. we cannot do this however on up events
|
|
||||||
// since the drag events break down
|
|
||||||
if (canvasClicked === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset the canvas click flag
|
|
||||||
canvasClicked = false;
|
|
||||||
|
|
||||||
// get the selection box
|
|
||||||
var selectionBox = d3.select('rect.selection');
|
|
||||||
if (!selectionBox.empty()) {
|
|
||||||
var selectionBoundingBox = {
|
|
||||||
x: parseInt(selectionBox.attr('x'), 10),
|
|
||||||
y: parseInt(selectionBox.attr('y'), 10),
|
|
||||||
width: parseInt(selectionBox.attr('width'), 10),
|
|
||||||
height: parseInt(selectionBox.attr('height'), 10)
|
|
||||||
};
|
|
||||||
|
|
||||||
// see if a component should be selected or not
|
|
||||||
d3.selectAll('g.component').classed('selected', function (d) {
|
|
||||||
// consider it selected if its already selected or enclosed in the bounding box
|
|
||||||
return d3.select(this).classed('selected') ||
|
|
||||||
d.component.position.x >= selectionBoundingBox.x && (d.component.position.x + d.dimensions.width) <= (selectionBoundingBox.x + selectionBoundingBox.width) &&
|
|
||||||
d.component.position.y >= selectionBoundingBox.y && (d.component.position.y + d.dimensions.height) <= (selectionBoundingBox.y + selectionBoundingBox.height);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// see if a connection should be selected or not
|
|
||||||
d3.selectAll('g.connection').classed('selected', function (d) {
|
|
||||||
// consider all points
|
|
||||||
var points = [d.start].concat(d.bends, [d.end]);
|
|
||||||
|
|
||||||
// determine the bounding box
|
|
||||||
var x = d3.extent(points, function (pt) {
|
|
||||||
return pt.x;
|
|
||||||
});
|
|
||||||
var y = d3.extent(points, function (pt) {
|
|
||||||
return pt.y;
|
|
||||||
});
|
|
||||||
|
|
||||||
// consider it selected if its already selected or enclosed in the bounding box
|
|
||||||
return d3.select(this).classed('selected') ||
|
|
||||||
x[0] >= selectionBoundingBox.x && x[1] <= (selectionBoundingBox.x + selectionBoundingBox.width) &&
|
|
||||||
y[0] >= selectionBoundingBox.y && y[1] <= (selectionBoundingBox.y + selectionBoundingBox.height);
|
|
||||||
});
|
|
||||||
|
|
||||||
// remove the selection box
|
|
||||||
selectionBox.remove();
|
|
||||||
} else if (panning === false) {
|
|
||||||
// deselect as necessary if we are not panning
|
|
||||||
nf.CanvasUtils.getSelection().classed('selected', false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the toolbar
|
|
||||||
nf.CanvasToolbar.refresh();
|
|
||||||
});
|
|
||||||
|
|
||||||
// define a function for update the graph dimensions
|
// define a function for update the graph dimensions
|
||||||
var updateGraphSize = function () {
|
var updateGraphSize = function () {
|
||||||
// get the location of the bottom of the graph
|
// get the location of the bottom of the graph
|
||||||
|
@ -513,7 +512,7 @@ nf.Canvas = (function () {
|
||||||
var top = parseInt(canvasContainer.css('top'), 10);
|
var top = parseInt(canvasContainer.css('top'), 10);
|
||||||
var windowHeight = $(window).height();
|
var windowHeight = $(window).height();
|
||||||
var canvasHeight = (windowHeight - (bottom + top));
|
var canvasHeight = (windowHeight - (bottom + top));
|
||||||
|
|
||||||
// canvas/svg
|
// canvas/svg
|
||||||
canvasContainer.css({
|
canvasContainer.css({
|
||||||
'height': canvasHeight + 'px',
|
'height': canvasHeight + 'px',
|
||||||
|
@ -539,7 +538,7 @@ nf.Canvas = (function () {
|
||||||
}
|
}
|
||||||
}).on('keydown', function (evt) {
|
}).on('keydown', function (evt) {
|
||||||
var isCtrl = evt.ctrlKey || evt.metaKey;
|
var isCtrl = evt.ctrlKey || evt.metaKey;
|
||||||
|
|
||||||
// consider escape, before checking dialogs
|
// consider escape, before checking dialogs
|
||||||
if (!isCtrl && evt.keyCode === 27) {
|
if (!isCtrl && evt.keyCode === 27) {
|
||||||
// esc
|
// esc
|
||||||
|
@ -555,7 +554,7 @@ nf.Canvas = (function () {
|
||||||
// first consider read only property detail dialog
|
// first consider read only property detail dialog
|
||||||
if ($('div.property-detail').is(':visible')) {
|
if ($('div.property-detail').is(':visible')) {
|
||||||
nf.Common.removeAllPropertyDetailDialogs();
|
nf.Common.removeAllPropertyDetailDialogs();
|
||||||
|
|
||||||
// prevent further bubbling as we're already handled it
|
// prevent further bubbling as we're already handled it
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
@ -573,7 +572,7 @@ nf.Canvas = (function () {
|
||||||
var dialogMax = null;
|
var dialogMax = null;
|
||||||
|
|
||||||
// identify the top most cancellable
|
// identify the top most cancellable
|
||||||
$.each(cancellables, function(_, cancellable) {
|
$.each(cancellables, function (_, cancellable) {
|
||||||
var dialog = $(cancellable);
|
var dialog = $(cancellable);
|
||||||
var zIndex = dialog.css('zIndex');
|
var zIndex = dialog.css('zIndex');
|
||||||
|
|
||||||
|
@ -618,10 +617,10 @@ nf.Canvas = (function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if a dialog is open, disable canvas shortcuts
|
// if a dialog is open, disable canvas shortcuts
|
||||||
if ($('.dialog').is(':visible')) {
|
if ($('.dialog').is(':visible')) {
|
||||||
return;
|
return;
|
||||||
|
@ -836,7 +835,7 @@ nf.Canvas = (function () {
|
||||||
bulletinIcon.show();
|
bulletinIcon.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// update controller service and reporting task bulletins
|
// update controller service and reporting task bulletins
|
||||||
nf.Settings.setBulletins(controllerStatus.controllerServiceBulletins, controllerStatus.reportingTaskBulletins);
|
nf.Settings.setBulletins(controllerStatus.controllerServiceBulletins, controllerStatus.reportingTaskBulletins);
|
||||||
|
|
||||||
|
@ -937,22 +936,18 @@ nf.Canvas = (function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
||||||
ANONYMOUS_USER_TEXT: 'Anonymous user',
|
ANONYMOUS_USER_TEXT: 'Anonymous user',
|
||||||
CANVAS_OFFSET: 0,
|
CANVAS_OFFSET: 0,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if the current broswer supports SVG.
|
* Determines if the current broswer supports SVG.
|
||||||
*/
|
*/
|
||||||
SUPPORTS_SVG: !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect,
|
SUPPORTS_SVG: !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides the splash that is displayed while the application is loading.
|
* Hides the splash that is displayed while the application is loading.
|
||||||
*/
|
*/
|
||||||
hideSplash: function () {
|
hideSplash: function () {
|
||||||
$('#splash').fadeOut();
|
$('#splash').fadeOut();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop polling for revision.
|
* Stop polling for revision.
|
||||||
*/
|
*/
|
||||||
|
@ -960,7 +955,6 @@ nf.Canvas = (function () {
|
||||||
// set polling flag
|
// set polling flag
|
||||||
revisionPolling = false;
|
revisionPolling = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the status poller.
|
* Remove the status poller.
|
||||||
*/
|
*/
|
||||||
|
@ -968,7 +962,6 @@ nf.Canvas = (function () {
|
||||||
// set polling flag
|
// set polling flag
|
||||||
statusPolling = false;
|
statusPolling = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reloads the flow from the server based on the currently specified group id.
|
* Reloads the flow from the server based on the currently specified group id.
|
||||||
* To load another group, update nf.Canvas.setGroupId and call nf.Canvas.reload.
|
* To load another group, update nf.Canvas.setGroupId and call nf.Canvas.reload.
|
||||||
|
@ -977,7 +970,7 @@ nf.Canvas = (function () {
|
||||||
return $.Deferred(function (deferred) {
|
return $.Deferred(function (deferred) {
|
||||||
// hide the context menu
|
// hide the context menu
|
||||||
nf.ContextMenu.hide();
|
nf.ContextMenu.hide();
|
||||||
|
|
||||||
// get the process group to refresh everything
|
// get the process group to refresh everything
|
||||||
var processGroupXhr = reloadProcessGroup(nf.Canvas.getGroupId());
|
var processGroupXhr = reloadProcessGroup(nf.Canvas.getGroupId());
|
||||||
var statusXhr = reloadFlowStatus();
|
var statusXhr = reloadFlowStatus();
|
||||||
|
@ -1011,7 +1004,6 @@ nf.Canvas = (function () {
|
||||||
});
|
});
|
||||||
}).promise();
|
}).promise();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reloads the status.
|
* Reloads the status.
|
||||||
*/
|
*/
|
||||||
|
@ -1025,7 +1017,6 @@ nf.Canvas = (function () {
|
||||||
});
|
});
|
||||||
}).promise();
|
}).promise();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize NiFi.
|
* Initialize NiFi.
|
||||||
*/
|
*/
|
||||||
|
@ -1036,16 +1027,16 @@ nf.Canvas = (function () {
|
||||||
url: config.urls.identity,
|
url: config.urls.identity,
|
||||||
dataType: 'json'
|
dataType: 'json'
|
||||||
});
|
});
|
||||||
|
|
||||||
// get the current user's authorities
|
// get the current user's authorities
|
||||||
var authoritiesXhr = $.ajax({
|
var authoritiesXhr = $.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
url: config.urls.authorities,
|
url: config.urls.authorities,
|
||||||
dataType: 'json'
|
dataType: 'json'
|
||||||
});
|
});
|
||||||
|
|
||||||
// load the identity and authorities for the current user
|
// load the identity and authorities for the current user
|
||||||
var userXhr = $.Deferred(function(deferred) {
|
var userXhr = $.Deferred(function (deferred) {
|
||||||
$.when(authoritiesXhr, identityXhr).done(function (authoritiesResult, identityResult) {
|
$.when(authoritiesXhr, identityXhr).done(function (authoritiesResult, identityResult) {
|
||||||
var authoritiesResponse = authoritiesResult[0];
|
var authoritiesResponse = authoritiesResult[0];
|
||||||
var identityResponse = identityResult[0];
|
var identityResponse = identityResult[0];
|
||||||
|
@ -1057,6 +1048,7 @@ nf.Canvas = (function () {
|
||||||
|
|
||||||
// if the user is logged, we want to determine if they were logged in using a certificate
|
// if the user is logged, we want to determine if they were logged in using a certificate
|
||||||
if (identityResponse.identity !== 'anonymous') {
|
if (identityResponse.identity !== 'anonymous') {
|
||||||
|
// rendner the users name
|
||||||
$('#current-user').text(identityResponse.identity).show();
|
$('#current-user').text(identityResponse.identity).show();
|
||||||
|
|
||||||
// render the logout button if there is a token locally
|
// render the logout button if there is a token locally
|
||||||
|
@ -1064,6 +1056,16 @@ nf.Canvas = (function () {
|
||||||
$('#logout-link-container').show();
|
$('#logout-link-container').show();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// alert user's of anonymous access
|
||||||
|
$('#anonymous-user-alert').show().qtip($.extend({}, nf.Common.config.tooltipConfig, {
|
||||||
|
content: 'You are accessing with limited authority. Log in or request an account to access with additional authority granted to you by an administrator.',
|
||||||
|
position: {
|
||||||
|
my: 'top right',
|
||||||
|
at: 'bottom left'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// render the anonymous user text
|
||||||
$('#current-user').text(nf.Canvas.ANONYMOUS_USER_TEXT).show();
|
$('#current-user').text(nf.Canvas.ANONYMOUS_USER_TEXT).show();
|
||||||
}
|
}
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
|
@ -1076,7 +1078,7 @@ nf.Canvas = (function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}).promise();
|
}).promise();
|
||||||
|
|
||||||
userXhr.done(function () {
|
userXhr.done(function () {
|
||||||
// get the controller config to register the status poller
|
// get the controller config to register the status poller
|
||||||
var configXhr = $.ajax({
|
var configXhr = $.ajax({
|
||||||
|
@ -1109,7 +1111,7 @@ nf.Canvas = (function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}).promise();
|
}).promise();
|
||||||
|
|
||||||
// ensure the config requests are loaded
|
// ensure the config requests are loaded
|
||||||
$.when(configXhr, loginXhr, userXhr).done(function (configResult, loginResult) {
|
$.when(configXhr, loginXhr, userXhr).done(function (configResult, loginResult) {
|
||||||
var configResponse = configResult[0];
|
var configResponse = configResult[0];
|
||||||
|
@ -1193,7 +1195,6 @@ nf.Canvas = (function () {
|
||||||
}).fail(nf.Common.handleAjaxError);
|
}).fail(nf.Common.handleAjaxError);
|
||||||
}).fail(nf.Common.handleAjaxError);
|
}).fail(nf.Common.handleAjaxError);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the gradient colors used to render processors.
|
* Defines the gradient colors used to render processors.
|
||||||
*
|
*
|
||||||
|
@ -1202,7 +1203,6 @@ nf.Canvas = (function () {
|
||||||
defineProcessorColors: function (colors) {
|
defineProcessorColors: function (colors) {
|
||||||
setColors(colors, 'processor');
|
setColors(colors, 'processor');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the gradient colors used to render label.
|
* Defines the gradient colors used to render label.
|
||||||
*
|
*
|
||||||
|
@ -1211,7 +1211,6 @@ nf.Canvas = (function () {
|
||||||
defineLabelColors: function (colors) {
|
defineLabelColors: function (colors) {
|
||||||
setColors(colors, 'label');
|
setColors(colors, 'label');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether this instance of NiFi is clustered.
|
* Return whether this instance of NiFi is clustered.
|
||||||
*
|
*
|
||||||
|
@ -1220,14 +1219,12 @@ nf.Canvas = (function () {
|
||||||
isClustered: function () {
|
isClustered: function () {
|
||||||
return clustered === true;
|
return clustered === true;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether site to site communications is secure.
|
* Returns whether site to site communications is secure.
|
||||||
*/
|
*/
|
||||||
isSecureSiteToSite: function () {
|
isSecureSiteToSite: function () {
|
||||||
return secureSiteToSite;
|
return secureSiteToSite;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the group id.
|
* Set the group id.
|
||||||
*
|
*
|
||||||
|
@ -1236,14 +1233,12 @@ nf.Canvas = (function () {
|
||||||
setGroupId: function (gi) {
|
setGroupId: function (gi) {
|
||||||
groupId = gi;
|
groupId = gi;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the group id.
|
* Get the group id.
|
||||||
*/
|
*/
|
||||||
getGroupId: function () {
|
getGroupId: function () {
|
||||||
return groupId;
|
return groupId;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the group name.
|
* Set the group name.
|
||||||
*
|
*
|
||||||
|
@ -1252,14 +1247,12 @@ nf.Canvas = (function () {
|
||||||
setGroupName: function (gn) {
|
setGroupName: function (gn) {
|
||||||
groupName = gn;
|
groupName = gn;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the group name.
|
* Get the group name.
|
||||||
*/
|
*/
|
||||||
getGroupName: function () {
|
getGroupName: function () {
|
||||||
return groupName;
|
return groupName;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the parent group id.
|
* Set the parent group id.
|
||||||
*
|
*
|
||||||
|
@ -1268,16 +1261,14 @@ nf.Canvas = (function () {
|
||||||
setParentGroupId: function (pgi) {
|
setParentGroupId: function (pgi) {
|
||||||
parentGroupId = pgi;
|
parentGroupId = pgi;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the parent group id.
|
* Get the parent group id.
|
||||||
*/
|
*/
|
||||||
getParentGroupId: function () {
|
getParentGroupId: function () {
|
||||||
return parentGroupId;
|
return parentGroupId;
|
||||||
},
|
},
|
||||||
|
|
||||||
View: (function () {
|
View: (function () {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates component visibility based on their proximity to the screen's viewport.
|
* Updates component visibility based on their proximity to the screen's viewport.
|
||||||
*/
|
*/
|
||||||
|
@ -1344,8 +1335,8 @@ nf.Canvas = (function () {
|
||||||
.classed('entering', function () {
|
.classed('entering', function () {
|
||||||
return visible && !wasVisible;
|
return visible && !wasVisible;
|
||||||
}).classed('leaving', function () {
|
}).classed('leaving', function () {
|
||||||
return !visible && wasVisible;
|
return !visible && wasVisible;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// get the all components
|
// get the all components
|
||||||
|
@ -1432,7 +1423,6 @@ nf.Canvas = (function () {
|
||||||
// add the behavior to the canvas and disable dbl click zoom
|
// add the behavior to the canvas and disable dbl click zoom
|
||||||
svg.call(behavior).on('dblclick.zoom', null);
|
svg.call(behavior).on('dblclick.zoom', null);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not a component should be rendered based solely on the current scale.
|
* Whether or not a component should be rendered based solely on the current scale.
|
||||||
*
|
*
|
||||||
|
@ -1441,7 +1431,6 @@ nf.Canvas = (function () {
|
||||||
shouldRenderPerScale: function () {
|
shouldRenderPerScale: function () {
|
||||||
return nf.Canvas.View.scale() >= MIN_SCALE_TO_RENDER;
|
return nf.Canvas.View.scale() >= MIN_SCALE_TO_RENDER;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates component visibility based on the current translation/scale.
|
* Updates component visibility based on the current translation/scale.
|
||||||
*/
|
*/
|
||||||
|
@ -1449,7 +1438,6 @@ nf.Canvas = (function () {
|
||||||
updateComponentVisibility();
|
updateComponentVisibility();
|
||||||
nf.Graph.pan();
|
nf.Graph.pan();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets/gets the current translation.
|
* Sets/gets the current translation.
|
||||||
*
|
*
|
||||||
|
@ -1462,7 +1450,6 @@ nf.Canvas = (function () {
|
||||||
behavior.translate(translate);
|
behavior.translate(translate);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets/gets the current scale.
|
* Sets/gets the current scale.
|
||||||
*
|
*
|
||||||
|
@ -1475,7 +1462,6 @@ nf.Canvas = (function () {
|
||||||
behavior.scale(scale);
|
behavior.scale(scale);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zooms in a single zoom increment.
|
* Zooms in a single zoom increment.
|
||||||
*/
|
*/
|
||||||
|
@ -1500,7 +1486,6 @@ nf.Canvas = (function () {
|
||||||
height: 1
|
height: 1
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zooms out a single zoom increment.
|
* Zooms out a single zoom increment.
|
||||||
*/
|
*/
|
||||||
|
@ -1525,7 +1510,6 @@ nf.Canvas = (function () {
|
||||||
height: 1
|
height: 1
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zooms to fit the entire graph on the canvas.
|
* Zooms to fit the entire graph on the canvas.
|
||||||
*/
|
*/
|
||||||
|
@ -1572,7 +1556,6 @@ nf.Canvas = (function () {
|
||||||
height: canvasHeight / newScale
|
height: canvasHeight / newScale
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zooms to the actual size (1 to 1).
|
* Zooms to the actual size (1 to 1).
|
||||||
*/
|
*/
|
||||||
|
@ -1621,7 +1604,6 @@ nf.Canvas = (function () {
|
||||||
// center as appropriate
|
// center as appropriate
|
||||||
nf.CanvasUtils.centerBoundingBox(box);
|
nf.CanvasUtils.centerBoundingBox(box);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes the view based on the configured translation and scale.
|
* Refreshes the view based on the configured translation and scale.
|
||||||
*
|
*
|
||||||
|
|
|
@ -75,7 +75,7 @@ nf.Login = (function () {
|
||||||
var showUserRegistration = function () {
|
var showUserRegistration = function () {
|
||||||
showNiFiRegistration();
|
showNiFiRegistration();
|
||||||
|
|
||||||
$('#nifi-registration-title').hide();
|
$('div.nifi-submit-justification').hide();
|
||||||
$('#user-registration-container').show();
|
$('#user-registration-container').show();
|
||||||
$('#login-submission-button').text('Create');
|
$('#login-submission-button').text('Create');
|
||||||
};
|
};
|
||||||
|
@ -109,41 +109,15 @@ nf.Login = (function () {
|
||||||
'username': $('#username').val(),
|
'username': $('#username').val(),
|
||||||
'password': $('#password').val()
|
'password': $('#password').val()
|
||||||
}
|
}
|
||||||
}).done(function (response, status, xhr) {
|
}).done(function (jwt) {
|
||||||
var authorization = xhr.getResponseHeader('Authorization');
|
// store the jwt and reload the page
|
||||||
var badToken = false;
|
nf.Storage.setItem('jwt', jwt);
|
||||||
|
|
||||||
// ensure there was a token in the response
|
// reload as appropriate
|
||||||
if (authorization) {
|
if (top !== window) {
|
||||||
var tokens = authorization.split(/ /);
|
parent.window.location = '/nifi';
|
||||||
|
|
||||||
// ensure the token is the appropriate length
|
|
||||||
if (tokens.length === 2) {
|
|
||||||
// store the jwt and reload the page
|
|
||||||
nf.Storage.setItem('jwt', tokens[1]);
|
|
||||||
|
|
||||||
// reload as appropriate
|
|
||||||
if (top !== window) {
|
|
||||||
parent.window.location = '/nifi';
|
|
||||||
} else {
|
|
||||||
window.location = '/nifi';
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
badToken = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
badToken = true;
|
window.location = '/nifi';
|
||||||
}
|
|
||||||
|
|
||||||
if (badToken === true) {
|
|
||||||
$('#login-message-title').text('An unexpected error has occurred');
|
|
||||||
$('#login-message').text('The user token could not be parsed.');
|
|
||||||
|
|
||||||
// update visibility
|
|
||||||
$('#login-container').hide();
|
|
||||||
$('#login-submission-container').hide();
|
|
||||||
$('#login-message-container').show();
|
|
||||||
}
|
}
|
||||||
}).fail(function (xhr, status, error) {
|
}).fail(function (xhr, status, error) {
|
||||||
if (xhr.status === 400) {
|
if (xhr.status === 400) {
|
||||||
|
@ -164,13 +138,25 @@ nf.Login = (function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
var createUserAccount = function () {
|
var createUserAccount = function () {
|
||||||
|
var password = $('#registration-password').val();
|
||||||
|
var passwordConfirmation = $('#registration-password-confirmation').val();
|
||||||
|
|
||||||
|
// ensure the password matches
|
||||||
|
if (password !== passwordConfirmation) {
|
||||||
|
nf.Dialog.showOkDialog({
|
||||||
|
dialogContent: 'The specified passwords do not match.',
|
||||||
|
overlayBackground: false
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// attempt to create the user account registration
|
// attempt to create the user account registration
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: config.urls.registration,
|
url: config.urls.registration,
|
||||||
data: {
|
data: {
|
||||||
'username': $('#registration-username').val(),
|
'username': $('#registration-username').val(),
|
||||||
'password': $('#registration-password').val(),
|
'password': password,
|
||||||
'justification': $('#nifi-registration-justification').val()
|
'justification': $('#nifi-registration-justification').val()
|
||||||
}
|
}
|
||||||
}).done(function (response, status, xhr) {
|
}).done(function (response, status, xhr) {
|
||||||
|
@ -220,6 +206,33 @@ nf.Login = (function () {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the subject from the specified jwt. If the jwt is not as expected
|
||||||
|
* an empty string is returned.
|
||||||
|
*
|
||||||
|
* @param {string} jwt
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
var getJwtSubject = function (jwt) {
|
||||||
|
if (nf.Common.isDefinedAndNotNull(jwt)) {
|
||||||
|
var segments = jwt.split(/\./);
|
||||||
|
if (segments.length !== 3) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
var rawPayload = $.base64.atob(segments[1]);
|
||||||
|
var payload = JSON.parse(rawPayload);
|
||||||
|
|
||||||
|
if (nf.Common.isDefinedAndNotNull(payload['preferred_username'])) {
|
||||||
|
return payload['preferred_username'];
|
||||||
|
} else {
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
* Initializes the login page.
|
* Initializes the login page.
|
||||||
|
@ -231,6 +244,16 @@ nf.Login = (function () {
|
||||||
var needsLogin = false;
|
var needsLogin = false;
|
||||||
var needsNiFiRegistration = false;
|
var needsNiFiRegistration = false;
|
||||||
|
|
||||||
|
var logout = function () {
|
||||||
|
nf.Storage.removeItem('jwt');
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle logout
|
||||||
|
$('#nifi-user-submit-justification-logout').on('click', function () {
|
||||||
|
logout();
|
||||||
|
window.location = '/nifi/login';
|
||||||
|
});
|
||||||
|
|
||||||
var token = $.ajax({
|
var token = $.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
url: config.urls.token
|
url: config.urls.token
|
||||||
|
@ -250,7 +273,8 @@ nf.Login = (function () {
|
||||||
isAnonymous = true;
|
isAnonymous = true;
|
||||||
|
|
||||||
// request a token without including credentials, if successful then the user is using a certificate
|
// request a token without including credentials, if successful then the user is using a certificate
|
||||||
token.done(function () {
|
token.done(function (jwt) {
|
||||||
|
|
||||||
// the user is using a certificate/token, see if their account is active/pending/revoked/etc
|
// the user is using a certificate/token, see if their account is active/pending/revoked/etc
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
|
@ -263,6 +287,16 @@ nf.Login = (function () {
|
||||||
$('#login-message').text('Your account is active and you are already logged in.');
|
$('#login-message').text('Your account is active and you are already logged in.');
|
||||||
}).fail(function (xhr, status, error) {
|
}).fail(function (xhr, status, error) {
|
||||||
if (xhr.status === 401) {
|
if (xhr.status === 401) {
|
||||||
|
var user = getJwtSubject(jwt);
|
||||||
|
|
||||||
|
// show the user
|
||||||
|
$('#nifi-user-submit-justification').text(user);
|
||||||
|
|
||||||
|
// render the logout button if there is a token locally
|
||||||
|
if (nf.Storage.getItem('jwt') !== null) {
|
||||||
|
$('#nifi-user-submit-justification-logout').show();
|
||||||
|
}
|
||||||
|
|
||||||
// anonymous user and 401 means they need nifi registration
|
// anonymous user and 401 means they need nifi registration
|
||||||
needsNiFiRegistration = true;
|
needsNiFiRegistration = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -279,7 +313,12 @@ nf.Login = (function () {
|
||||||
}).always(function () {
|
}).always(function () {
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
});
|
});
|
||||||
}).fail(function () {
|
}).fail(function (tokenXhr) {
|
||||||
|
if (tokenXhr.status === 400) {
|
||||||
|
// no credentials supplied so 400 must be due to an invalid/expired token
|
||||||
|
logout();
|
||||||
|
}
|
||||||
|
|
||||||
// no token granted, user has no certificate and needs to login with their credentials
|
// no token granted, user has no certificate and needs to login with their credentials
|
||||||
needsLogin = true;
|
needsLogin = true;
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
|
@ -296,10 +335,25 @@ nf.Login = (function () {
|
||||||
// unable to get identity (and no anonymous user) see if we can offer login
|
// unable to get identity (and no anonymous user) see if we can offer login
|
||||||
if (xhr.status === 401) {
|
if (xhr.status === 401) {
|
||||||
// attempt to get a token for the current user without passing login credentials
|
// attempt to get a token for the current user without passing login credentials
|
||||||
token.done(function () {
|
token.done(function (jwt) {
|
||||||
|
var user = getJwtSubject(jwt);
|
||||||
|
|
||||||
|
// show the user
|
||||||
|
$('#nifi-user-submit-justification').text(user);
|
||||||
|
|
||||||
|
// render the logout button if there is a token locally
|
||||||
|
if (nf.Storage.getItem('jwt') !== null) {
|
||||||
|
$('#nifi-user-submit-justification-logout').show();
|
||||||
|
}
|
||||||
|
|
||||||
// 401 from identity request and 200 from token means they have a certificate/token but have not yet requested an account
|
// 401 from identity request and 200 from token means they have a certificate/token but have not yet requested an account
|
||||||
needsNiFiRegistration = true;
|
needsNiFiRegistration = true;
|
||||||
}).fail(function () {
|
}).fail(function (tokenXhr) {
|
||||||
|
if (tokenXhr.status === 400) {
|
||||||
|
// no credentials supplied so 400 must be due to an invalid/expired token
|
||||||
|
logout();
|
||||||
|
}
|
||||||
|
|
||||||
// no token granted, user needs to login with their credentials
|
// no token granted, user needs to login with their credentials
|
||||||
needsLogin = true;
|
needsLogin = true;
|
||||||
}).always(function () {
|
}).always(function () {
|
||||||
|
|
|
@ -206,7 +206,14 @@ nf.Common = {
|
||||||
|
|
||||||
// if an error occurs while the splash screen is visible close the canvas show the error message
|
// if an error occurs while the splash screen is visible close the canvas show the error message
|
||||||
if ($('#splash').is(':visible')) {
|
if ($('#splash').is(':visible')) {
|
||||||
$('#message-title').text('An unexpected error has occurred');
|
if (xhr.status === 401) {
|
||||||
|
$('#message-title').text('Unauthorized');
|
||||||
|
} else if (xhr.status === 403) {
|
||||||
|
$('#message-title').text('Access Denied');
|
||||||
|
} else {
|
||||||
|
$('#message-title').text('An unexpected error has occurred');
|
||||||
|
}
|
||||||
|
|
||||||
if ($.trim(xhr.responseText) === '') {
|
if ($.trim(xhr.responseText) === '') {
|
||||||
$('#message-content').text('Please check the logs.');
|
$('#message-content').text('Please check the logs.');
|
||||||
} else {
|
} else {
|
||||||
|
@ -249,7 +256,7 @@ nf.Common = {
|
||||||
$('#message-content').text(xhr.responseText);
|
$('#message-content').text(xhr.responseText);
|
||||||
}
|
}
|
||||||
} else if (xhr.status === 403) {
|
} else if (xhr.status === 403) {
|
||||||
$('#message-title').text('Forbidden');
|
$('#message-title').text('Access Denied');
|
||||||
if ($.trim(xhr.responseText) === '') {
|
if ($.trim(xhr.responseText) === '') {
|
||||||
$('#message-content').text('Unable to authorize you to use this NiFi.');
|
$('#message-content').text('Unable to authorize you to use this NiFi.');
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue