You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mc...@apache.org on 2015/11/06 00:26:12 UTC
nifi git commit: 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.
Repository: nifi
Updated Branches:
refs/heads/NIFI-655 93aa09dac -> b6d09b86b
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.
Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/b6d09b86
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/b6d09b86
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/b6d09b86
Branch: refs/heads/NIFI-655
Commit: b6d09b86b6b0b6873dda192d28e704653ac9522d
Parents: 93aa09d
Author: Matt Gilman <ma...@gmail.com>
Authored: Thu Nov 5 18:26:00 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Thu Nov 5 18:26:00 2015 -0500
----------------------------------------------------------------------
LICENSE | 22 ++
nifi-assembly/LICENSE | 22 ++
.../nifi-web/nifi-web-security/pom.xml | 5 +
.../web/security/RegistrationStatusFilter.java | 8 +-
.../form/LoginAuthenticationFilter.java | 26 +-
.../nifi/web/security/jwt/JwtService.java | 54 +++-
.../token/LoginAuthenticationToken.java | 8 +
.../resources/nifi-web-security-context.xml | 4 +-
.../src/main/resources/META-INF/LICENSE | 22 ++
.../src/main/webapp/WEB-INF/pages/login.jsp | 1 +
.../WEB-INF/partials/canvas/canvas-header.jsp | 4 +-
.../partials/login/nifi-registration-form.jsp | 12 +-
.../nifi-web-ui/src/main/webapp/css/header.css | 10 +
.../nifi-web-ui/src/main/webapp/css/login.css | 16 +-
.../src/main/webapp/js/jquery/jquery.base64.js | 123 +++++++++
.../src/main/webapp/js/nf/canvas/nf-canvas.js | 256 +++++++++----------
.../src/main/webapp/js/nf/login/nf-login.js | 132 +++++++---
.../src/main/webapp/js/nf/nf-common.js | 11 +-
18 files changed, 522 insertions(+), 214 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/LICENSE
----------------------------------------------------------------------
diff --git a/LICENSE b/LICENSE
index 59741e6..f4be753 100644
--- a/LICENSE
+++ b/LICENSE
@@ -374,6 +374,28 @@ For details see http://jqueryui.com
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 '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.
Copyright (c) 2010 Michael Leibman, http://github.com/mleibman/slickgrid
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-assembly/LICENSE
----------------------------------------------------------------------
diff --git a/nifi-assembly/LICENSE b/nifi-assembly/LICENSE
index 5abc79a..a7a73b8 100644
--- a/nifi-assembly/LICENSE
+++ b/nifi-assembly/LICENSE
@@ -374,6 +374,28 @@ For details see http://jqueryui.com
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 '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.
Copyright (c) 2010 Michael Leibman, http://github.com/mleibman/slickgrid
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml
index c0db615..c954d0d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml
@@ -78,6 +78,11 @@
<artifactId>nifi-framework-core</artifactId>
</dependency>
<dependency>
+ <groupId>io.jsonwebtoken</groupId>
+ <artifactId>jjwt</artifactId>
+ <version>0.6.0</version>
+ </dependency>
+ <dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
</dependency>
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/RegistrationStatusFilter.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/RegistrationStatusFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/RegistrationStatusFilter.java
index e914db5..606d2e3 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/RegistrationStatusFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/RegistrationStatusFilter.java
@@ -98,7 +98,7 @@ public class RegistrationStatusFilter extends AbstractAuthenticationProcessingFi
return new RegistrationStatusAuthenticationToken(tokenCredentials);
} else {
// we have a certificate so let's consider a proxy chain
- final String principal = extractPrincipal(certificate);
+ final String principal = principalExtractor.extractPrincipal(certificate).toString();
try {
// validate the certificate
@@ -144,12 +144,6 @@ public class RegistrationStatusFilter extends AbstractAuthenticationProcessingFi
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
protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain, final Authentication authentication)
throws IOException, ServletException {
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/LoginAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/LoginAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/LoginAuthenticationFilter.java
index 388b81e..fb45363 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/LoginAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/LoginAuthenticationFilter.java
@@ -44,7 +44,6 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
-import org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException;
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 (certificate == null) {
final String principal = jwtService.getAuthentication(request);
-
+
if (principal == null) {
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);
return new LoginAuthenticationToken(tokenCredentials);
} else {
// extract the principal
- final String principal = extractPrincipal(certificate);
+ final String principal = principalExtractor.extractPrincipal(certificate).toString();
try {
certificateValidator.validateClientCertificate(request, certificate);
@@ -151,16 +150,14 @@ public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingF
} catch (final UsernameNotFoundException unfe) {
// 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
+ } 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) {
final String username = request.getParameter("username");
final String password = request.getParameter("password");
@@ -178,20 +175,15 @@ public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingF
// generate JWT for response
jwtService.addToken(response, authentication);
-
- // mark as successful
- response.setStatus(HttpServletResponse.SC_CREATED);
- response.setContentType("text/plain");
- response.setContentLength(0);
}
@Override
protected void unsuccessfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException failed) throws IOException, ServletException {
response.setContentType("text/plain");
-
+
final PrintWriter out = response.getWriter();
out.println(failed.getMessage());
-
+
if (failed instanceof BadCredentialsException || failed instanceof AuthenticationCredentialsNotFoundException) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
} else {
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java
index 2012d69..8afa15a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java
@@ -16,9 +16,22 @@
*/
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.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.util.NiFiProperties;
import org.springframework.security.core.Authentication;
/**
@@ -28,6 +41,16 @@ public class JwtService {
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.
*
@@ -35,12 +58,16 @@ public class JwtService {
* @return The user identifier from the token
*/
public String getAuthentication(final HttpServletRequest request) {
- // TODO : actually extract/verify token
-
// extract/verify token from incoming request
final String authorization = request.getHeader(AUTHORIZATION);
- final String username = StringUtils.substringAfterLast(authorization, " ");
- return username;
+ final String token = StringUtils.substringAfterLast(authorization, " ");
+
+ 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 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) {
- // TODO : actually create real token... in header or response body?
+ public void addToken(final HttpServletResponse response, final Authentication authentication) throws IOException {
+ // set expiration to one day from now
+ final Calendar calendar = Calendar.getInstance();
+ calendar.add(Calendar.DATE, expires);
// 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
- 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");
}
+
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/LoginAuthenticationToken.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/LoginAuthenticationToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/LoginAuthenticationToken.java
index 528b60b..f908d79 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/LoginAuthenticationToken.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/LoginAuthenticationToken.java
@@ -17,6 +17,7 @@
package org.apache.nifi.web.security.token;
import org.apache.nifi.authentication.LoginCredentials;
+import org.apache.nifi.security.util.CertificateUtils;
import org.springframework.security.authentication.AbstractAuthenticationToken;
/**
@@ -45,4 +46,11 @@ public class LoginAuthenticationToken extends AbstractAuthenticationToken {
public Object getPrincipal() {
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());
+ }
+
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
index 45d3ba3..fa0b5b8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
@@ -40,7 +40,9 @@
</bean>
<!-- 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 -->
<bean id="loginIdentityProvider" class="org.apache.nifi.web.security.spring.LoginIdentityProviderFactoryBean">
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/META-INF/LICENSE
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/META-INF/LICENSE b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/META-INF/LICENSE
index 40d0725..6769105 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/META-INF/LICENSE
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/META-INF/LICENSE
@@ -351,6 +351,28 @@ For details see http://jqueryui.com
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 '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.
Copyright (c) 2010 Michael Leibman, http://github.com/mleibman/slickgrid
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp
index 925f93c..e2b7b9b 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp
@@ -27,6 +27,7 @@
<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" />
<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.center.js"></script>
<script type="text/javascript" src="js/jquery/modal/jquery.modal.js?${project.version}"></script>
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp
index 81296d2..204b1b3 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp
@@ -45,7 +45,9 @@
<div id="header-links-container">
<ul>
<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 id="login-link-container">
<span id="login-link" class="link">login</span>
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/nifi-registration-form.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/nifi-registration-form.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/nifi-registration-form.jsp
index afa7687..59191ce 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/nifi-registration-form.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/nifi-registration-form.jsp
@@ -16,7 +16,17 @@
--%>
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
<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-name">Justification</div>
<div class="setting-field">
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css
index 3f0b299..8f2450c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css
@@ -506,7 +506,17 @@ div.search-glass-pane {
/* 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 {
+ float: left;
margin-right: 8px;
font-weight: bold;
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/login.css
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/login.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/login.css
index 2fbe107..38ce410 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/login.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/login.css
@@ -73,10 +73,24 @@ body.login-body input, body.login-body textarea {
*/
#nifi-registration-container {
- margin-top: 10px;
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 {
height: 200px;
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/jquery.base64.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/jquery.base64.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/jquery.base64.js
new file mode 100644
index 0000000..32ceab0
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/jquery.base64.js
@@ -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));
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
index 46578ab..c316ef2 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
@@ -64,7 +64,6 @@ nf.Canvas = (function () {
bulletinBoard: '../nifi-api/controller/bulletin-board',
banners: '../nifi-api/controller/banners',
controller: '../nifi-api/controller',
- token: '../nifi-api/token',
controllerConfig: '../nifi-api/controller/config',
loginConfig: '../nifi-api/controller/login/config',
cluster: '../nifi-api/cluster',
@@ -191,7 +190,7 @@ nf.Canvas = (function () {
if (!refreshContainer.is(':visible')) {
$('#stats-last-refreshed').addClass('alert');
var refreshMessage = "This flow has been modified by '" + revision.lastModifier + "'. Please refresh.";
-
+
// update the tooltip
var refreshRequiredIcon = $('#refresh-required-icon');
if (refreshRequiredIcon.data('qtip')) {
@@ -201,10 +200,10 @@ nf.Canvas = (function () {
content: refreshMessage
}, nf.CanvasUtils.config.systemTooltipConfig));
}
-
+
refreshContainer.show();
}
-
+
// insert the refresh needed text in the settings - if necessary
if (!settingsRefreshIcon.is(':visible')) {
$('#settings-last-refreshed').addClass('alert');
@@ -336,7 +335,7 @@ nf.Canvas = (function () {
'offset': '100%',
'stop-color': '#ffffff'
});
-
+
// define the gradient for the expiration icon
var expirationBackground = defs.append('linearGradient')
.attr({
@@ -346,7 +345,7 @@ nf.Canvas = (function () {
'x2': '0%',
'y2': '100%'
});
-
+
expirationBackground.append('stop')
.attr({
'offset': '0%',
@@ -400,105 +399,105 @@ nf.Canvas = (function () {
// prevent further propagation (to parents and others handlers
// on the same element to prevent zoom behavior)
d3.event.stopImmediatePropagation();
-
+
// prevents the browser from changing to a text selection cursor
d3.event.preventDefault();
}
})
- .on('mousemove.selection', function () {
- // update selection box if shift is held down
- if (d3.event.shiftKey) {
- // get the selection box
- var selectionBox = d3.select('rect.selection');
- if (!selectionBox.empty()) {
- // get the original position
- var originalPosition = selectionBox.datum();
- var position = d3.mouse(canvas.node());
-
- var d = {};
- if (originalPosition[0] < position[0]) {
- d.x = originalPosition[0];
- d.width = position[0] - originalPosition[0];
- } else {
- d.x = 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];
- }
+ .on('mousemove.selection', function () {
+ // update selection box if shift is held down
+ if (d3.event.shiftKey) {
+ // get the selection box
+ var selectionBox = d3.select('rect.selection');
+ if (!selectionBox.empty()) {
+ // get the original position
+ var originalPosition = selectionBox.datum();
+ var position = d3.mouse(canvas.node());
+
+ var d = {};
+ if (originalPosition[0] < position[0]) {
+ d.x = originalPosition[0];
+ d.width = position[0] - originalPosition[0];
+ } else {
+ d.x = position[0];
+ d.width = originalPosition[0] - position[0];
+ }
- // 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]) {
+ d.y = originalPosition[1];
+ d.height = position[1] - originalPosition[1];
+ } else {
+ d.y = position[1];
+ d.height = originalPosition[1] - position[1];
+ }
- // 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)
- };
+ // update the selection box
+ selectionBox.attr(d);
- // 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);
- });
+ // 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;
+ }
- // 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]);
+ // reset the canvas click flag
+ canvasClicked = false;
- // determine the bounding box
- var x = d3.extent(points, function (pt) {
- return pt.x;
- });
- var y = d3.extent(points, function (pt) {
- return pt.y;
- });
+ // 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)
+ };
- // 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);
- });
+ // 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;
+ });
- // remove the selection box
- selectionBox.remove();
- } else if (panning === false) {
- // deselect as necessary if we are not panning
- nf.CanvasUtils.getSelection().classed('selected', false);
- }
+ // 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();
- });
+ // update the toolbar
+ nf.CanvasToolbar.refresh();
+ });
// define a function for update the graph dimensions
var updateGraphSize = function () {
@@ -513,7 +512,7 @@ nf.Canvas = (function () {
var top = parseInt(canvasContainer.css('top'), 10);
var windowHeight = $(window).height();
var canvasHeight = (windowHeight - (bottom + top));
-
+
// canvas/svg
canvasContainer.css({
'height': canvasHeight + 'px',
@@ -539,7 +538,7 @@ nf.Canvas = (function () {
}
}).on('keydown', function (evt) {
var isCtrl = evt.ctrlKey || evt.metaKey;
-
+
// consider escape, before checking dialogs
if (!isCtrl && evt.keyCode === 27) {
// esc
@@ -555,7 +554,7 @@ nf.Canvas = (function () {
// first consider read only property detail dialog
if ($('div.property-detail').is(':visible')) {
nf.Common.removeAllPropertyDetailDialogs();
-
+
// prevent further bubbling as we're already handled it
evt.stopPropagation();
evt.preventDefault();
@@ -573,7 +572,7 @@ nf.Canvas = (function () {
var dialogMax = null;
// identify the top most cancellable
- $.each(cancellables, function(_, cancellable) {
+ $.each(cancellables, function (_, cancellable) {
var dialog = $(cancellable);
var zIndex = dialog.css('zIndex');
@@ -618,10 +617,10 @@ nf.Canvas = (function () {
}
}
}
-
+
return;
}
-
+
// if a dialog is open, disable canvas shortcuts
if ($('.dialog').is(':visible')) {
return;
@@ -836,7 +835,7 @@ nf.Canvas = (function () {
bulletinIcon.show();
}
}
-
+
// update controller service and reporting task bulletins
nf.Settings.setBulletins(controllerStatus.controllerServiceBulletins, controllerStatus.reportingTaskBulletins);
@@ -937,22 +936,18 @@ nf.Canvas = (function () {
};
return {
-
ANONYMOUS_USER_TEXT: 'Anonymous user',
CANVAS_OFFSET: 0,
-
/**
* Determines if the current broswer supports SVG.
*/
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.
*/
hideSplash: function () {
$('#splash').fadeOut();
},
-
/**
* Stop polling for revision.
*/
@@ -960,7 +955,6 @@ nf.Canvas = (function () {
// set polling flag
revisionPolling = false;
},
-
/**
* Remove the status poller.
*/
@@ -968,7 +962,6 @@ nf.Canvas = (function () {
// set polling flag
statusPolling = false;
},
-
/**
* 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.
@@ -977,7 +970,7 @@ nf.Canvas = (function () {
return $.Deferred(function (deferred) {
// hide the context menu
nf.ContextMenu.hide();
-
+
// get the process group to refresh everything
var processGroupXhr = reloadProcessGroup(nf.Canvas.getGroupId());
var statusXhr = reloadFlowStatus();
@@ -1011,7 +1004,6 @@ nf.Canvas = (function () {
});
}).promise();
},
-
/**
* Reloads the status.
*/
@@ -1025,7 +1017,6 @@ nf.Canvas = (function () {
});
}).promise();
},
-
/**
* Initialize NiFi.
*/
@@ -1036,16 +1027,16 @@ nf.Canvas = (function () {
url: config.urls.identity,
dataType: 'json'
});
-
+
// get the current user's authorities
var authoritiesXhr = $.ajax({
type: 'GET',
url: config.urls.authorities,
dataType: 'json'
});
-
+
// 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) {
var authoritiesResponse = authoritiesResult[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 (identityResponse.identity !== 'anonymous') {
+ // rendner the users name
$('#current-user').text(identityResponse.identity).show();
// render the logout button if there is a token locally
@@ -1064,6 +1056,16 @@ nf.Canvas = (function () {
$('#logout-link-container').show();
}
} 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();
}
deferred.resolve();
@@ -1076,7 +1078,7 @@ nf.Canvas = (function () {
}
});
}).promise();
-
+
userXhr.done(function () {
// get the controller config to register the status poller
var configXhr = $.ajax({
@@ -1109,7 +1111,7 @@ nf.Canvas = (function () {
}
});
}).promise();
-
+
// ensure the config requests are loaded
$.when(configXhr, loginXhr, userXhr).done(function (configResult, loginResult) {
var configResponse = configResult[0];
@@ -1193,7 +1195,6 @@ nf.Canvas = (function () {
}).fail(nf.Common.handleAjaxError);
}).fail(nf.Common.handleAjaxError);
},
-
/**
* Defines the gradient colors used to render processors.
*
@@ -1202,7 +1203,6 @@ nf.Canvas = (function () {
defineProcessorColors: function (colors) {
setColors(colors, 'processor');
},
-
/**
* Defines the gradient colors used to render label.
*
@@ -1211,7 +1211,6 @@ nf.Canvas = (function () {
defineLabelColors: function (colors) {
setColors(colors, 'label');
},
-
/**
* Return whether this instance of NiFi is clustered.
*
@@ -1220,14 +1219,12 @@ nf.Canvas = (function () {
isClustered: function () {
return clustered === true;
},
-
/**
* Returns whether site to site communications is secure.
*/
isSecureSiteToSite: function () {
return secureSiteToSite;
},
-
/**
* Set the group id.
*
@@ -1236,14 +1233,12 @@ nf.Canvas = (function () {
setGroupId: function (gi) {
groupId = gi;
},
-
/**
* Get the group id.
*/
getGroupId: function () {
return groupId;
},
-
/**
* Set the group name.
*
@@ -1252,14 +1247,12 @@ nf.Canvas = (function () {
setGroupName: function (gn) {
groupName = gn;
},
-
/**
* Get the group name.
*/
getGroupName: function () {
return groupName;
},
-
/**
* Set the parent group id.
*
@@ -1268,16 +1261,14 @@ nf.Canvas = (function () {
setParentGroupId: function (pgi) {
parentGroupId = pgi;
},
-
/**
* Get the parent group id.
*/
getParentGroupId: function () {
return parentGroupId;
},
-
View: (function () {
-
+
/**
* Updates component visibility based on their proximity to the screen's viewport.
*/
@@ -1344,8 +1335,8 @@ nf.Canvas = (function () {
.classed('entering', function () {
return visible && !wasVisible;
}).classed('leaving', function () {
- return !visible && wasVisible;
- });
+ return !visible && wasVisible;
+ });
};
// get the all components
@@ -1432,7 +1423,6 @@ nf.Canvas = (function () {
// add the behavior to the canvas and disable dbl click zoom
svg.call(behavior).on('dblclick.zoom', null);
},
-
/**
* Whether or not a component should be rendered based solely on the current scale.
*
@@ -1441,7 +1431,6 @@ nf.Canvas = (function () {
shouldRenderPerScale: function () {
return nf.Canvas.View.scale() >= MIN_SCALE_TO_RENDER;
},
-
/**
* Updates component visibility based on the current translation/scale.
*/
@@ -1449,7 +1438,6 @@ nf.Canvas = (function () {
updateComponentVisibility();
nf.Graph.pan();
},
-
/**
* Sets/gets the current translation.
*
@@ -1462,7 +1450,6 @@ nf.Canvas = (function () {
behavior.translate(translate);
}
},
-
/**
* Sets/gets the current scale.
*
@@ -1475,7 +1462,6 @@ nf.Canvas = (function () {
behavior.scale(scale);
}
},
-
/**
* Zooms in a single zoom increment.
*/
@@ -1500,7 +1486,6 @@ nf.Canvas = (function () {
height: 1
});
},
-
/**
* Zooms out a single zoom increment.
*/
@@ -1525,7 +1510,6 @@ nf.Canvas = (function () {
height: 1
});
},
-
/**
* Zooms to fit the entire graph on the canvas.
*/
@@ -1572,7 +1556,6 @@ nf.Canvas = (function () {
height: canvasHeight / newScale
});
},
-
/**
* Zooms to the actual size (1 to 1).
*/
@@ -1621,7 +1604,6 @@ nf.Canvas = (function () {
// center as appropriate
nf.CanvasUtils.centerBoundingBox(box);
},
-
/**
* Refreshes the view based on the configured translation and scale.
*
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js
index 4f4cb1d..6152867 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js
@@ -75,7 +75,7 @@ nf.Login = (function () {
var showUserRegistration = function () {
showNiFiRegistration();
- $('#nifi-registration-title').hide();
+ $('div.nifi-submit-justification').hide();
$('#user-registration-container').show();
$('#login-submission-button').text('Create');
};
@@ -109,41 +109,15 @@ nf.Login = (function () {
'username': $('#username').val(),
'password': $('#password').val()
}
- }).done(function (response, status, xhr) {
- var authorization = xhr.getResponseHeader('Authorization');
- var badToken = false;
-
- // ensure there was a token in the response
- if (authorization) {
- var tokens = authorization.split(/ /);
-
- // ensure the token is the appropriate length
- if (tokens.length === 2) {
- // store the jwt and reload the page
- nf.Storage.setItem('jwt', tokens[1]);
+ }).done(function (jwt) {
+ // store the jwt and reload the page
+ nf.Storage.setItem('jwt', jwt);
- // reload as appropriate
- if (top !== window) {
- parent.window.location = '/nifi';
- } else {
- window.location = '/nifi';
- }
- return;
- } else {
- badToken = true;
- }
+ // reload as appropriate
+ if (top !== window) {
+ parent.window.location = '/nifi';
} else {
- badToken = true;
- }
-
- 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();
+ window.location = '/nifi';
}
}).fail(function (xhr, status, error) {
if (xhr.status === 400) {
@@ -164,13 +138,25 @@ nf.Login = (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
$.ajax({
type: 'POST',
url: config.urls.registration,
data: {
'username': $('#registration-username').val(),
- 'password': $('#registration-password').val(),
+ 'password': password,
'justification': $('#nifi-registration-justification').val()
}
}).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 {
/**
* Initializes the login page.
@@ -231,6 +244,16 @@ nf.Login = (function () {
var needsLogin = 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({
type: 'GET',
url: config.urls.token
@@ -250,7 +273,8 @@ nf.Login = (function () {
isAnonymous = true;
// 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
$.ajax({
type: 'GET',
@@ -263,6 +287,16 @@ nf.Login = (function () {
$('#login-message').text('Your account is active and you are already logged in.');
}).fail(function (xhr, status, error) {
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
needsNiFiRegistration = true;
} else {
@@ -279,7 +313,12 @@ nf.Login = (function () {
}).always(function () {
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
needsLogin = true;
deferred.resolve();
@@ -296,10 +335,25 @@ nf.Login = (function () {
// unable to get identity (and no anonymous user) see if we can offer login
if (xhr.status === 401) {
// 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
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
needsLogin = true;
}).always(function () {
http://git-wip-us.apache.org/repos/asf/nifi/blob/b6d09b86/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
index c798a61..8c023e7 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
@@ -206,7 +206,14 @@ nf.Common = {
// if an error occurs while the splash screen is visible close the canvas show the error message
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) === '') {
$('#message-content').text('Please check the logs.');
} else {
@@ -249,7 +256,7 @@ nf.Common = {
$('#message-content').text(xhr.responseText);
}
} else if (xhr.status === 403) {
- $('#message-title').text('Forbidden');
+ $('#message-title').text('Access Denied');
if ($.trim(xhr.responseText) === '') {
$('#message-content').text('Unable to authorize you to use this NiFi.');
} else {