Repository: nifi
Updated Branches:
  refs/heads/NIFI-655 221459286 -> 93aa09dac


NIFI-655:
- Starting to add support for registration.
- Creating registration form.

Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/93aa09da
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/93aa09da
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/93aa09da

Branch: refs/heads/NIFI-655
Commit: 93aa09dace01c2abe301ee7781cc8ab54b48197a
Parents: 2214592
Author: Matt Gilman <[email protected]>
Authored: Wed Nov 4 22:03:52 2015 -0500
Committer: Matt Gilman <[email protected]>
Committed: Wed Nov 4 22:03:52 2015 -0500

----------------------------------------------------------------------
 .../authentication/LoginIdentityProvider.java   |   3 +-
 .../nifi/authorized/users/AuthorizedUsers.java  |   8 +
 .../src/main/xsd/users.xsd                      |   7 +-
 .../FileAuthorizationProvider.java              |  59 ++++-
 .../FileLoginIdentityProvider.java              |  40 ++-
 .../org/apache/nifi/web/server/JettyServer.java |   9 +-
 .../web/NiFiWebApiSecurityConfiguration.java    |  22 +-
 .../web/security/RegistrationStatusFilter.java  |  26 +-
 .../form/LoginAuthenticationFilter.java         | 111 ++++----
 .../web/security/form/RegistrationFilter.java   | 160 ++++++++++++
 .../nifi/web/security/jwt/JwtService.java       |   2 +-
 .../token/LoginAuthenticationToken.java         |  48 ++++
 .../src/main/webapp/WEB-INF/pages/login.jsp     |   4 +
 .../WEB-INF/partials/login/login-form.jsp       |   4 +
 .../partials/login/user-registration-form.jsp   |   9 +-
 .../nifi-web-ui/src/main/webapp/css/login.css   |  19 +-
 .../src/main/webapp/js/nf/canvas/nf-canvas.js   |  18 +-
 .../src/main/webapp/js/nf/login/nf-login.js     | 255 +++++++++++--------
 18 files changed, 569 insertions(+), 235 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java
----------------------------------------------------------------------
diff --git 
a/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java
 
b/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java
index 919d5b5..15a20a8 100644
--- 
a/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java
+++ 
b/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java
@@ -16,6 +16,7 @@
  */
 package org.apache.nifi.authentication;
 
+import org.apache.nifi.authorization.exception.IdentityAlreadyExistsException;
 import org.apache.nifi.authorization.exception.ProviderCreationException;
 import org.apache.nifi.authorization.exception.ProviderDestructionException;
 
@@ -36,7 +37,7 @@ public interface LoginIdentityProvider {
      *
      * @param credentials the login credentials
      */
-    void register(LoginCredentials credentials);
+    void register(LoginCredentials credentials) throws 
IdentityAlreadyExistsException;
 
     /**
      * Authenticates the specified login credentials.

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/java/org/apache/nifi/authorized/users/AuthorizedUsers.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/java/org/apache/nifi/authorized/users/AuthorizedUsers.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/java/org/apache/nifi/authorized/users/AuthorizedUsers.java
index e4be3f7..f19514e 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/java/org/apache/nifi/authorized/users/AuthorizedUsers.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/java/org/apache/nifi/authorized/users/AuthorizedUsers.java
@@ -207,6 +207,14 @@ public final class AuthorizedUsers {
         saveUsers(users);
     }
 
+    public synchronized void createOrUpdateUser(final FindUser finder, final 
CreateUser creator, final UpdateUser updater) {
+        try {
+            updateUser(finder, updater);
+        } catch (final UnknownIdentityException uie) {
+            createUser(creator);
+        }
+    }
+
     public synchronized void updateUser(final FindUser finder, final 
UpdateUser updater) {
         // update the user
         final Users users = getUsers();

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/xsd/users.xsd
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/xsd/users.xsd
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/xsd/users.xsd
index b06ebc8..509f97b 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/xsd/users.xsd
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/xsd/users.xsd
@@ -48,7 +48,7 @@
     <xs:complexType name="User">
         <xs:complexContent>
             <xs:extension base="NiFiUser">
-                <xs:attribute name="dn">
+                <xs:attribute name="dn" use="required">
                     <xs:simpleType>
                         <xs:restriction base="xs:string">
                             <xs:minLength value="1"/>
@@ -64,7 +64,7 @@
     <xs:complexType name="LoginUser">
         <xs:complexContent>
             <xs:extension base="NiFiUser">
-                <xs:attribute name="username">
+                <xs:attribute name="username" use="required">
                     <xs:simpleType>
                         <xs:restriction base="xs:string">
                             <xs:minLength value="1"/>
@@ -72,7 +72,7 @@
                         </xs:restriction>
                     </xs:simpleType>
                 </xs:attribute>
-                <xs:attribute name="password">
+                <xs:attribute name="password" use="required">
                     <xs:simpleType>
                         <xs:restriction base="xs:string">
                             <xs:minLength value="1"/>
@@ -80,6 +80,7 @@
                         </xs:restriction>
                     </xs:simpleType>
                 </xs:attribute>
+                <xs:attribute name="pending" type="xs:boolean" use="required"/>
             </xs:extension>
         </xs:complexContent>
     </xs:complexType>

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/src/main/java/org/apache/nifi/authorization/FileAuthorizationProvider.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/src/main/java/org/apache/nifi/authorization/FileAuthorizationProvider.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/src/main/java/org/apache/nifi/authorization/FileAuthorizationProvider.java
index d06b85f..3400ce8 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/src/main/java/org/apache/nifi/authorization/FileAuthorizationProvider.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/src/main/java/org/apache/nifi/authorization/FileAuthorizationProvider.java
@@ -40,6 +40,7 @@ import 
org.apache.nifi.authorized.users.AuthorizedUsers.FindUsers;
 import org.apache.nifi.authorized.users.AuthorizedUsers.HasUser;
 import org.apache.nifi.authorized.users.AuthorizedUsers.UpdateUser;
 import org.apache.nifi.authorized.users.AuthorizedUsers.UpdateUsers;
+import org.apache.nifi.user.generated.LoginUser;
 import org.apache.nifi.user.generated.NiFiUser;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -182,15 +183,37 @@ public class FileAuthorizationProvider implements 
AuthorityProvider {
 
     @Override
     public void addUser(final String dn, final String group) throws 
IdentityAlreadyExistsException, AuthorityAccessException {
-        authorizedUsers.createUser(new CreateUser() {
+        authorizedUsers.createOrUpdateUser(new FindUser() {
             @Override
-            public NiFiUser createUser() {
-                // ensure the user doesn't already exist
-                if (authorizedUsers.hasUser(new HasUserByIdentity(dn))) {
-                    throw new 
IdentityAlreadyExistsException(String.format("User identity already exists: 
%s", dn));
+            public NiFiUser findUser(final List<NiFiUser> users) throws 
UnknownIdentityException {
+                // attempt to get the user and ensure it was located
+                NiFiUser desiredUser = null;
+                for (final NiFiUser user : users) {
+                    if 
(dn.equalsIgnoreCase(authorizedUsers.getUserIdentity(user))) {
+                        desiredUser = user;
+                        break;
+                    }
+                }
+
+                // user does not exist, will create
+                if (desiredUser == null) {
+                    throw new UnknownIdentityException("This exception will 
trigger the creator to be invoked.");
+                }
+
+                // user exists, verify its still pending
+                if (LoginUser.class.isAssignableFrom(desiredUser.getClass())) {
+                    if (((LoginUser) desiredUser).isPending()) {
+                        return desiredUser;
+                    }
                 }
 
-                // only support adding PreAuthenticatedUser's via this API - 
LoginUser's are added
+                // user exists and account is valid... no good
+                throw new IdentityAlreadyExistsException(String.format("User 
identity already exists: %s", dn));
+            }
+        }, new CreateUser() {
+            @Override
+            public NiFiUser createUser() {
+                // only support adding PreAuthenticated User's via this API - 
LoginUser's are added
                 // via the LoginIdentityProvider
                 final ObjectFactory objFactory = new ObjectFactory();
                 final User newUser = objFactory.createUser();
@@ -212,6 +235,13 @@ public class FileAuthorizationProvider implements 
AuthorityProvider {
 
                 return newUser;
             }
+        }, new UpdateUser() {
+            @Override
+            public void updateUser(final NiFiUser user) {
+                // only support updating Login Users's via this API - need to 
mark the account as non pending
+                LoginUser loginUser = (LoginUser) user;
+                loginUser.setPending(false);
+            }
         });
     }
 
@@ -306,6 +336,13 @@ public class FileAuthorizationProvider implements 
AuthorityProvider {
         this.properties = properties;
     }
 
+    private boolean isPendingLoginUser(final NiFiUser user) {
+        if (LoginUser.class.isAssignableFrom(user.getClass())) {
+            return ((LoginUser) user).isPending();
+        }
+        return false;
+    }
+
     public class HasUserByIdentity implements HasUser {
 
         private final String identity;
@@ -324,7 +361,7 @@ public class FileAuthorizationProvider implements 
AuthorityProvider {
             // attempt to get the user and ensure it was located
             NiFiUser desiredUser = null;
             for (final NiFiUser user : users) {
-                if 
(identity.equalsIgnoreCase(authorizedUsers.getUserIdentity(user))) {
+                if 
(identity.equalsIgnoreCase(authorizedUsers.getUserIdentity(user)) && 
!isPendingLoginUser(user)) {
                     desiredUser = user;
                     break;
                 }
@@ -352,7 +389,7 @@ public class FileAuthorizationProvider implements 
AuthorityProvider {
             // attempt to get the user and ensure it was located
             NiFiUser desiredUser = null;
             for (final NiFiUser user : users) {
-                if 
(identity.equalsIgnoreCase(authorizedUsers.getUserIdentity(user))) {
+                if 
(identity.equalsIgnoreCase(authorizedUsers.getUserIdentity(user)) && 
!isPendingLoginUser(user)) {
                     desiredUser = user;
                     break;
                 }
@@ -366,7 +403,7 @@ public class FileAuthorizationProvider implements 
AuthorityProvider {
         }
     }
 
-    public static class FindUsersByGroup implements FindUsers {
+    public class FindUsersByGroup implements FindUsers {
 
         private final String group;
 
@@ -384,7 +421,7 @@ public class FileAuthorizationProvider implements 
AuthorityProvider {
             // get all users with this group
             List<NiFiUser> userGroup = new ArrayList<>();
             for (final NiFiUser user : users) {
-                if (group.equals(user.getGroup())) {
+                if (group.equals(user.getGroup()) && 
!isPendingLoginUser(user)) {
                     userGroup.add(user);
                 }
             }
@@ -419,7 +456,7 @@ public class FileAuthorizationProvider implements 
AuthorityProvider {
             List<NiFiUser> userList = new ArrayList<>();
             for (final NiFiUser user : users) {
                 final String userIdentity = 
authorizedUsers.getUserIdentity(user);
-                if (copy.contains(userIdentity)) {
+                if (copy.contains(userIdentity) && !isPendingLoginUser(user)) {
                     copy.remove(userIdentity);
                     userList.add(user);
                 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-identity-provider/src/main/java/org/apache/nifi/authentication/FileLoginIdentityProvider.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-identity-provider/src/main/java/org/apache/nifi/authentication/FileLoginIdentityProvider.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-identity-provider/src/main/java/org/apache/nifi/authentication/FileLoginIdentityProvider.java
index 6e72880..1f38f37 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-identity-provider/src/main/java/org/apache/nifi/authentication/FileLoginIdentityProvider.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-identity-provider/src/main/java/org/apache/nifi/authentication/FileLoginIdentityProvider.java
@@ -19,9 +19,11 @@ package org.apache.nifi.authentication;
 import java.io.IOException;
 import java.util.List;
 import org.apache.nifi.authentication.annotation.LoginIdentityProviderContext;
+import org.apache.nifi.authorization.exception.IdentityAlreadyExistsException;
 import org.apache.nifi.authorization.exception.ProviderCreationException;
 import org.apache.nifi.authorization.exception.ProviderDestructionException;
 import org.apache.nifi.authorized.users.AuthorizedUsers;
+import org.apache.nifi.authorized.users.AuthorizedUsers.CreateUser;
 import org.apache.nifi.authorized.users.AuthorizedUsers.HasUser;
 import org.apache.nifi.user.generated.LoginUser;
 import org.apache.nifi.user.generated.NiFiUser;
@@ -57,11 +59,43 @@ public class FileLoginIdentityProvider implements 
LoginIdentityProvider {
 
     @Override
     public boolean supportsRegistration() {
-        return false;
+        return true;
     }
 
     @Override
-    public void register(LoginCredentials credentials) {
+    public void register(final LoginCredentials credentials) throws 
IdentityAlreadyExistsException {
+        authorizedUsers.createUser(new CreateUser() {
+            @Override
+            public NiFiUser createUser() {
+                final HasUser hasUser = new HasUser() {
+                    @Override
+                    public boolean hasUser(List<NiFiUser> users) {
+                        for (final NiFiUser user : users) {
+                            // only consider LoginUsers
+                            if 
(LoginUser.class.isAssignableFrom(user.getClass())) {
+                                final LoginUser loginUser = (LoginUser) user;
+                                if 
(credentials.getUsername().equals(loginUser.getUsername())) {
+                                    return true;
+                                }
+                            }
+                        }
+                        return false;
+                    }
+                };
+
+                // if the user already exists
+                if (authorizedUsers.hasUser(hasUser)) {
+                    throw new IdentityAlreadyExistsException(String.format("A 
user account for %s already exists.", credentials.getUsername()));
+                }
+
+                // TODO - need to properly encrypt and hash the user password 
for storage
+                final LoginUser user = new LoginUser();
+                user.setUsername(credentials.getUsername());
+                user.setPassword(credentials.getPassword());
+                user.setPending(true);
+                return user;
+            }
+        });
     }
 
     @Override
@@ -78,7 +112,7 @@ public class FileLoginIdentityProvider implements 
LoginIdentityProvider {
                     if (LoginUser.class.isAssignableFrom(user.getClass())) {
                         final LoginUser loginUser = (LoginUser) user;
 
-                        // TODO - need to properly encrypt and hash password
+                        // TODO - need to properly encrypt and hash the 
supplied password for comparison
                         final String loginUserPassword = 
loginUser.getPassword();
                         if 
(credentials.getUsername().equals(loginUser.getUsername()) && 
credentials.getPassword().equals(loginUserPassword)) {
                             return true;

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
index b2b3013..e8d2618 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
@@ -614,9 +614,12 @@ public class JettyServer implements NiFiServer {
     private SslContextFactory createSslContextFactory() {
         final SslContextFactory contextFactory = new SslContextFactory();
 
-        // client auth
-        contextFactory.setWantClientAuth(true);
-        contextFactory.setNeedClientAuth(false);
+        // require client auth when not supporting login or anonymous access
+        if 
(StringUtils.isBlank(props.getProperty(NiFiProperties.SECURITY_USER_LOGIN_IDENTITY_PROVIDER))
 && props.getAnonymousAuthorities().isEmpty()) {
+            contextFactory.setNeedClientAuth(true);
+        } else {
+            contextFactory.setWantClientAuth(true);
+        }
 
         /* below code sets JSSE system properties when values are provided */
         // keystore properties

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
index 732c30e..3d7544e 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
@@ -25,6 +25,7 @@ import 
org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter;
 import org.apache.nifi.web.security.NiFiAuthenticationEntryPoint;
 import org.apache.nifi.web.security.RegistrationStatusFilter;
 import org.apache.nifi.web.security.form.LoginAuthenticationFilter;
+import org.apache.nifi.web.security.form.RegistrationFilter;
 import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
 import org.apache.nifi.web.security.jwt.JwtService;
 import org.apache.nifi.web.security.node.NodeAuthorizedUserFilter;
@@ -138,17 +139,22 @@ public class NiFiWebApiSecurityConfiguration extends 
WebSecurityConfigurerAdapte
     }
 
     private Filter buildRegistrationFilter(final String url) {
-        return null;
+        final RegistrationFilter registrationFilter = new 
RegistrationFilter(url);
+        registrationFilter.setJwtService(jwtService);
+        registrationFilter.setLoginIdentityProvider(loginIdentityProvider);
+        registrationFilter.setUserService(userService);
+        return registrationFilter;
     }
 
     private Filter buildRegistrationStatusFilter(final String url) {
-        final RegistrationStatusFilter registrationFilter = new 
RegistrationStatusFilter(url);
-        registrationFilter.setCertificateExtractor(certificateExtractor);
-        registrationFilter.setPrincipalExtractor(principalExtractor);
-        registrationFilter.setCertificateValidator(certificateValidator);
-        registrationFilter.setProperties(properties);
-        registrationFilter.setUserDetailsService(userDetailsService);
-        return registrationFilter;
+        final RegistrationStatusFilter registrationStatusFilter = new 
RegistrationStatusFilter(url);
+        registrationStatusFilter.setCertificateExtractor(certificateExtractor);
+        registrationStatusFilter.setPrincipalExtractor(principalExtractor);
+        registrationStatusFilter.setCertificateValidator(certificateValidator);
+        registrationStatusFilter.setProperties(properties);
+        registrationStatusFilter.setJwtService(jwtService);
+        registrationStatusFilter.setUserDetailsService(userDetailsService);
+        return registrationStatusFilter;
     }
 
     private NodeAuthorizedUserFilter buildNodeAuthorizedUserFilter() {

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/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 6a9e6ab..e914db5 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
@@ -30,6 +30,7 @@ import javax.servlet.http.HttpServletResponse;
 import org.apache.nifi.authentication.LoginCredentials;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.util.StringUtils;
+import org.apache.nifi.web.security.jwt.JwtService;
 import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
 import org.apache.nifi.web.security.x509.X509CertificateExtractor;
 import org.apache.nifi.web.security.x509.X509CertificateValidator;
@@ -54,6 +55,7 @@ public class RegistrationStatusFilter extends 
AbstractAuthenticationProcessingFi
     private static final Logger logger = 
LoggerFactory.getLogger(RegistrationStatusFilter.class);
 
     private NiFiProperties properties;
+    private JwtService jwtService;
     private AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> 
userDetailsService;
     private X509CertificateValidator certificateValidator;
     private X509CertificateExtractor certificateExtractor;
@@ -78,21 +80,22 @@ public class RegistrationStatusFilter extends 
AbstractAuthenticationProcessingFi
 
         // if no certificate, just check the credentials
         if (certificate == null) {
-            final LoginCredentials credentials = getLoginCredentials(request);
+            final String principal = jwtService.getAuthentication(request);
 
             // ensure we have something we can work with (certificate or 
crendentials)
-            if (credentials == null) {
+            if (principal == null) {
                 throw new BadCredentialsException("Unable to check 
registration status as no credentials were included with the request.");
             }
 
             // without a certificate, this is not a proxied request
-            final List<String> chain = 
Arrays.asList(credentials.getUsername());
+            final List<String> chain = Arrays.asList(principal);
 
             // check authorization for this user
             checkAuthorization(chain);
 
             // no issues with authorization
-            return new RegistrationStatusAuthenticationToken(credentials);
+            final LoginCredentials tokenCredentials = new 
LoginCredentials(principal, null);
+            return new RegistrationStatusAuthenticationToken(tokenCredentials);
         } else {
             // we have a certificate so let's consider a proxy chain
             final String principal = extractPrincipal(certificate);
@@ -147,17 +150,6 @@ public class RegistrationStatusFilter extends 
AbstractAuthenticationProcessingFi
         return 
ProxiedEntitiesUtils.formatProxyDn(certificatePrincipal.toString());
     }
 
-    private LoginCredentials getLoginCredentials(HttpServletRequest request) {
-        final String username = request.getParameter("username");
-        final String password = request.getParameter("password");
-
-        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
-            return null;
-        } else {
-            return new LoginCredentials(username, password);
-        }
-    }
-
     @Override
     protected void successfulAuthentication(final HttpServletRequest request, 
final HttpServletResponse response, final FilterChain chain, final 
Authentication authentication)
             throws IOException, ServletException {
@@ -238,6 +230,10 @@ public class RegistrationStatusFilter extends 
AbstractAuthenticationProcessingFi
         }
     }
 
+    public void setJwtService(JwtService jwtService) {
+        this.jwtService = jwtService;
+    }
+
     public void setCertificateValidator(X509CertificateValidator 
certificateValidator) {
         this.certificateValidator = certificateValidator;
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/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 46e5b42..388b81e 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
@@ -16,6 +16,7 @@
  */
 package org.apache.nifi.web.security.form;
 
+import org.apache.nifi.web.security.token.LoginAuthenticationToken;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.security.cert.CertificateExpiredException;
@@ -36,7 +37,7 @@ import 
org.apache.nifi.web.security.x509.X509CertificateExtractor;
 import org.apache.nifi.web.security.x509.X509CertificateValidator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.security.authentication.AbstractAuthenticationToken;
+import 
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
 import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
@@ -84,42 +85,50 @@ public class LoginAuthenticationFilter extends 
AbstractAuthenticationProcessingF
             // look for a certificate
             final X509Certificate certificate = 
certificateExtractor.extractClientCertificate(request);
 
+            // if there is no certificate, look for an existing token
             if (certificate == null) {
-                throw new PreAuthenticatedCredentialsNotFoundException("Unable 
to extract client certificate after processing request with no login 
credentials specified.");
-            }
-
-            // extract the principal
-            final String principal = extractPrincipal(certificate);
-
-            try {
-                certificateValidator.validateClientCertificate(request, 
certificate);
-            } catch (CertificateExpiredException cee) {
-                final String message = String.format("Client certificate for 
(%s) is expired.", principal);
-                logger.info(message, cee);
-                if (logger.isDebugEnabled()) {
-                    logger.debug("", cee);
-                }
-                return null;
-            } catch (CertificateNotYetValidException cnyve) {
-                final String message = String.format("Client certificate for 
(%s) is not yet valid.", principal);
-                logger.info(message, cnyve);
-                if (logger.isDebugEnabled()) {
-                    logger.debug("", cnyve);
+                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.");
                 }
-                return null;
-            } catch (final Exception e) {
-                logger.info(e.getMessage());
-                if (logger.isDebugEnabled()) {
-                    logger.debug("", e);
+                
+                final LoginCredentials tokenCredentials = new 
LoginCredentials(principal, null);
+                return new LoginAuthenticationToken(tokenCredentials);
+            } else {
+                // extract the principal
+                final String principal = extractPrincipal(certificate);
+
+                try {
+                    certificateValidator.validateClientCertificate(request, 
certificate);
+                } catch (CertificateExpiredException cee) {
+                    final String message = String.format("Client certificate 
for (%s) is expired.", principal);
+                    logger.info(message, cee);
+                    if (logger.isDebugEnabled()) {
+                        logger.debug("", cee);
+                    }
+                    return null;
+                } catch (CertificateNotYetValidException cnyve) {
+                    final String message = String.format("Client certificate 
for (%s) is not yet valid.", principal);
+                    logger.info(message, cnyve);
+                    if (logger.isDebugEnabled()) {
+                        logger.debug("", cnyve);
+                    }
+                    return null;
+                } catch (final Exception e) {
+                    logger.info(e.getMessage());
+                    if (logger.isDebugEnabled()) {
+                        logger.debug("", e);
+                    }
+                    return null;
                 }
-                return null;
-            }
 
-            // authorize the proxy if necessary
-            
authorizeProxyIfNecessary(ProxiedEntitiesUtils.buildProxyChain(request, 
principal));
+                // authorize the proxy if necessary
+                
authorizeProxyIfNecessary(ProxiedEntitiesUtils.buildProxyChain(request, 
principal));
 
-            final LoginCredentials preAuthenticatedCredentials = new 
LoginCredentials(principal, null);
-            return new LoginAuthenticationToken(preAuthenticatedCredentials);
+                final LoginCredentials preAuthenticatedCredentials = new 
LoginCredentials(principal, null);
+                return new 
LoginAuthenticationToken(preAuthenticatedCredentials);
+            }
         } else {
             if (loginIdentityProvider.authenticate(credentials)) {
                 return new LoginAuthenticationToken(credentials);
@@ -178,39 +187,15 @@ public class LoginAuthenticationFilter extends 
AbstractAuthenticationProcessingF
 
     @Override
     protected void unsuccessfulAuthentication(final HttpServletRequest 
request, final HttpServletResponse response, final AuthenticationException 
failed) throws IOException, ServletException {
-        // set the response status
-        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
         response.setContentType("text/plain");
-
+        
         final PrintWriter out = response.getWriter();
-        out.println("Unable to authenticate.");
-    }
-
-    /**
-     * This is an Authentication Token for logging in. Once a user is 
authenticated, they can be issues an ID token.
-     */
-    public static class LoginAuthenticationToken extends 
AbstractAuthenticationToken {
-
-        final LoginCredentials credentials;
-
-        public LoginAuthenticationToken(final LoginCredentials credentials) {
-            super(null);
-            setAuthenticated(true);
-            this.credentials = credentials;
-        }
-
-        public LoginCredentials getLoginCredentials() {
-            return credentials;
-        }
-
-        @Override
-        public Object getCredentials() {
-            return credentials.getPassword();
-        }
-
-        @Override
-        public Object getPrincipal() {
-            return credentials.getUsername();
+        out.println(failed.getMessage());
+        
+        if (failed instanceof BadCredentialsException || failed instanceof 
AuthenticationCredentialsNotFoundException) {
+            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+        } else {
+            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/RegistrationFilter.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/RegistrationFilter.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/RegistrationFilter.java
new file mode 100644
index 0000000..39adb68
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/RegistrationFilter.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.form;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.nifi.admin.service.AccountDisabledException;
+import org.apache.nifi.admin.service.AccountNotFoundException;
+import org.apache.nifi.admin.service.AccountPendingException;
+import org.apache.nifi.admin.service.AdministrationException;
+import org.apache.nifi.admin.service.UserService;
+import org.apache.nifi.authentication.LoginCredentials;
+import org.apache.nifi.authentication.LoginIdentityProvider;
+import org.apache.nifi.authorization.exception.IdentityAlreadyExistsException;
+import org.apache.nifi.util.StringUtils;
+import org.apache.nifi.web.security.jwt.JwtService;
+import org.apache.nifi.web.security.token.LoginAuthenticationToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.AccountStatusException;
+import 
org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import 
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
+
+/**
+ * Exchanges a successful login with the configured provider for a ID token 
for accessing the API.
+ */
+public class RegistrationFilter extends AbstractAuthenticationProcessingFilter 
{
+
+    private static final Logger logger = 
LoggerFactory.getLogger(RegistrationFilter.class);
+
+    private LoginIdentityProvider loginIdentityProvider;
+    private JwtService jwtService;
+    private UserService userService;
+
+    public RegistrationFilter(final String defaultFilterProcessesUrl) {
+        super(defaultFilterProcessesUrl);
+
+        // do not continue filter chain... simply exchanging authentication 
for token
+        setContinueChainBeforeSuccessfulAuthentication(false);
+    }
+
+    @Override
+    public Authentication attemptAuthentication(final HttpServletRequest 
request, final HttpServletResponse response) throws AuthenticationException, 
IOException, ServletException {
+        // only suppport registration when running securely
+        if (!request.isSecure()) {
+            return null;
+        }
+
+        // look for the credentials in the request
+        final LoginCredentials credentials = getLoginCredentials(request);
+
+        // if the credentials were not part of the request, attempt to log in 
with the certificate in the request
+        if (credentials == null) {
+            throw new UsernameNotFoundException("User login credentials not 
found in request.");
+        } else {
+            try {
+                // attempt to register the user
+                loginIdentityProvider.register(credentials);
+            } catch (final IdentityAlreadyExistsException iaee) {
+                // if the identity already exists, try to create the nifi 
account request
+            }
+
+            try {
+                // see if the account already exists so we're able to return 
the current status
+                userService.checkAuthorization(credentials.getUsername());
+
+                // account exists and is valid
+                throw new AccountStatusException(String.format("An account for 
%s already exists.", credentials.getUsername())) {
+                };
+            } catch (AdministrationException ase) {
+                throw new AuthenticationServiceException(ase.getMessage(), 
ase);
+            } catch (AccountDisabledException | AccountPendingException e) {
+                throw new AccountStatusException(e.getMessage(), e) {
+                };
+            } catch (AccountNotFoundException anfe) {
+                // create the pending user account
+                
userService.createPendingUserAccount(credentials.getUsername(), 
request.getParameter("justification"));
+
+                // create the login token
+                return new LoginAuthenticationToken(credentials);
+            }
+        }
+    }
+
+    private LoginCredentials getLoginCredentials(HttpServletRequest request) {
+        final String username = request.getParameter("username");
+        final String password = request.getParameter("password");
+
+        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
+            return null;
+        } else {
+            return new LoginCredentials(username, password);
+        }
+    }
+
+    @Override
+    protected void successfulAuthentication(final HttpServletRequest request, 
final HttpServletResponse response, final FilterChain chain, final 
Authentication authentication)
+            throws IOException, ServletException {
+
+        // 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());
+
+        // set the appropriate response status
+        if (failed instanceof UsernameNotFoundException) {
+            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+        } else if (failed instanceof AccountStatusException) {
+            // account exists (maybe valid, pending, revoked)
+            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+        } else {
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+        }
+    }
+
+    public void setJwtService(JwtService jwtService) {
+        this.jwtService = jwtService;
+    }
+
+    public void setLoginIdentityProvider(LoginIdentityProvider 
loginIdentityProvider) {
+        this.loginIdentityProvider = loginIdentityProvider;
+    }
+
+    public void setUserService(UserService userService) {
+        this.userService = userService;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/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 1b4f41f..2012d69 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
@@ -50,7 +50,7 @@ public class JwtService {
      * @param authentication The authentication to generate a token for
      */
     public void addToken(final HttpServletResponse response, final 
Authentication authentication) {
-        // TODO : actually create real token
+        // TODO : actually create real token... in header or response body?
 
         // create a token the specified authentication
         String token = authentication.getName();

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/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
new file mode 100644
index 0000000..528b60b
--- /dev/null
+++ 
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
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.token;
+
+import org.apache.nifi.authentication.LoginCredentials;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+
+/**
+ * This is an Authentication Token for logging in. Once a user is 
authenticated, they can be issues an ID token.
+ */
+public class LoginAuthenticationToken extends AbstractAuthenticationToken {
+
+    final LoginCredentials credentials;
+
+    public LoginAuthenticationToken(final LoginCredentials credentials) {
+        super(null);
+        setAuthenticated(true);
+        this.credentials = credentials;
+    }
+
+    public LoginCredentials getLoginCredentials() {
+        return credentials;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return credentials.getPassword();
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return credentials.getUsername();
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/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 2c52032..925f93c 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
@@ -28,6 +28,7 @@
         <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.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>
         <script type="text/javascript" 
src="js/jquery/qtip2/jquery.qtip.min.js"></script>
         <script type="text/javascript" 
src="js/jquery/ui-smoothness/jquery-ui-1.10.4.min.js"></script>
@@ -42,5 +43,8 @@
             <jsp:include 
page="/WEB-INF/partials/login/nifi-registration-form.jsp"/>
             <jsp:include page="/WEB-INF/partials/login/login-submission.jsp"/>
         </div>
+        <jsp:include page="/WEB-INF/partials/ok-dialog.jsp"/>
+        <div id="faded-background"></div>
+        <div id="glass-pane"></div>
     </body>
 </html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-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/login-form.jsp
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-form.jsp
index f8f06f3..8480501 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-form.jsp
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-form.jsp
@@ -27,6 +27,10 @@
         <div class="setting-name">Password</div>
         <div class="setting-field">
             <input type="password" id="password"/>
+            <div id="create-account-message" class="hidden">
+                <div style="font-style: italic;">Don't have an account?</div>
+                <div><span id="create-account-link" class="link">Create 
one</span> to request access.</div>
+            </div>
         </div>
     </div>
 </div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/user-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/user-registration-form.jsp
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/user-registration-form.jsp
index 89517e4..7930e39 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/user-registration-form.jsp
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/user-registration-form.jsp
@@ -16,18 +16,19 @@
 --%>
 <%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
 <div id="user-registration-container" class="hidden">
-    <div class="login-title">Log In</div>
+    <div class="login-title">Create Account</div>
     <div class="setting">
         <div class="setting-name">Username</div>
         <div class="setting-field">
-            <input type="text" id="username"/>
+            <input type="text" id="registration-username"/>
         </div>
     </div>
     <div class="setting">
         <div class="setting-name">Password</div>
         <div class="setting-field">
-            <input type="password" id="password"/><br/>
-            <input type="password" id="password-confirmation" 
placeholder="Confirm password"/>
+            <input type="password" id="registration-password" 
style="margin-bottom: 5px;"/>
+            <br/>
+            <input type="password" id="registration-password-confirmation" 
placeholder="Confirm password"/>
         </div>
     </div>
 </div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/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 e8466df..2fbe107 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
@@ -19,7 +19,7 @@
     Login Styles
 */
 
-body.login-body {
+#login-contents-container {
     position: absolute;
     top: 0px;
     left: 0px;
@@ -27,13 +27,8 @@ body.login-body {
     right: 0px;
     background: #fff url(../images/bg-error.png) left top no-repeat;
     font-family: Verdana, Geneva, sans-serif;
-    color: #191919;
-    z-index: 999999;
-}
-
-#login-contents-container {
-    margin-top: 100px;
-    margin-left: 100px;
+    padding-top: 100px;
+    padding-left: 100px;
 }
 
 #login-message-title {
@@ -61,6 +56,14 @@ body.login-body input, body.login-body textarea {
     width: 400px;
 }
 
+#create-account-message {
+    margin-top: 2px;
+}
+
+#create-account-link {
+    text-decoration: underline;
+}
+
 /*
     User Registration
 */

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/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 b714c46..46578ab 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
@@ -1044,7 +1044,6 @@ nf.Canvas = (function () {
                 dataType: 'json'
             });
             
-            
             // load the identity and authorities for the current user
             var userXhr = $.Deferred(function(deferred) {
                 $.when(authoritiesXhr, identityXhr).done(function 
(authoritiesResult, identityResult) {
@@ -1059,22 +1058,15 @@ nf.Canvas = (function () {
                     // if the user is logged, we want to determine if they 
were logged in using a certificate
                     if (identityResponse.identity !== 'anonymous') {
                         
$('#current-user').text(identityResponse.identity).show();
-                        
-                        // attempt to get a token for the current user without 
passing login credentials
-                        $.ajax({
-                            type: 'GET',
-                            url: config.urls.token
-                        }).fail(function () {
-                            // if this request succeeds, it means the user is 
logged in using their certificate.
-                            // if this request fails, it means the user is 
logged in with login credentials so we want to render a logout button.
+
+                        // render the logout button if there is a token locally
+                        if (nf.Storage.getItem('jwt') !== null) {
                             $('#logout-link-container').show();
-                        }).always(function () {
-                            deferred.resolve();
-                        });
+                        }
                     } else {
                         
$('#current-user').text(nf.Canvas.ANONYMOUS_USER_TEXT).show();
-                        deferred.resolve();
                     }
+                    deferred.resolve();
                 }).fail(function (xhr, status, error) {
                     // there is no anonymous access and we don't know this 
user - open the login page which handles login/registration/etc
                     if (xhr.status === 401) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/93aa09da/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 b844cba..4f4cb1d 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
@@ -44,131 +44,180 @@ nf.Login = (function () {
         // if this nifi supports registration, render the registration form
         if (supportsRegistration === true) {
             initializeUserRegistration();
-
-            // automatically include support for nifi registration
             initializeNiFiRegistration();
 
-            // hide the submit justification title
-            $('#nifi-registration-title').hide();
+            // show the create account message
+            $('#create-account-message').show();
 
-            // update the submit button text
-            $('#login-submission-button').text('Create');
+            // toggle between login and signup
+            $('#create-account-link').on('click', function () {
+                showUserRegistration();
+            });
         }
-        
-        // show the login form
+    };
+
+    var showLogin = function () {
         $('#login-container').show();
+        $('#user-registration-container').hide();
+        $('#nifi-registration-container').hide();
+        $('#login-submission-button').text('Log in');
     };
 
     var initializeUserRegistration = function () {
-
-        // show the user registration form
-        $('#user-registration-container').show();
     };
 
     var initializeNiFiRegistration = function () {
         $('#nifi-registration-justification').count({
             charCountField: '#remaining-characters'
         });
+    };
 
-        // update the button text
-        $('#login-submission-button').text('Submit');
+    var showUserRegistration = function () {
+        showNiFiRegistration();
+
+        $('#nifi-registration-title').hide();
+        $('#user-registration-container').show();
+        $('#login-submission-button').text('Create');
+    };
 
-        // show the nifi registration container
+    var showNiFiRegistration = function () {
+        $('#login-container').hide();
         $('#nifi-registration-container').show();
+        $('#login-submission-button').text('Submit');
     };
 
     var initializeSubmission = function () {
-        $('#login-submission-button').one('click', function () {
+        $('#login-submission-button').on('click', function () {
             if ($('#login-container').is(':visible')) {
-                var username = $('#username').val();
-                var password = $('#password').val();
-
-                // login submit
-                $.ajax({
-                    type: 'POST',
-                    url: '../nifi-api/token',
-                    data: {
-                        'username': username,
-                        'password': password
-                    }
-                }).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]);
-                            
-                            // reload as appropriate
-                            if (top !== window) {
-                                parent.window.location = '/nifi';
-                            } else {
-                                window.location = '/nifi';
-                            }
-                            return;
-                        } else {
-                            badToken = true;
-                        }
-                    } else {
-                        badToken = true;
-                    }
-
-                    if (badToken === true) {
-                        $('#login-message-title').text('An unexpected error 
has occurred');
-                        $('#login-message').text('The id token could not be 
parsed.');
-                        
-                        // update visibility
-                        $('#login-container').hide();
-                        $('#login-submission-container').hide();
-                        $('#login-message-container').show();
-                    }
-                }).fail(function (xhr, status, error) {
-                    $('#login-message-title').text('An unexpected error has 
occurred');
-                    $('#login-message').text(xhr.responseText);
-                    
-                    // update visibility
-                    $('#login-container').hide();
-                    $('#login-submission-container').hide();
-                    $('#login-message-container').show();
-                });
+                login();
             } else if ($('#user-registration-container').is(':visible')) {
-                var justification = $('#registration-justification').val();
-                
-                // new user account submit
+                createUserAccount();
             } else if ($('#nifi-registration-container').is(':visible')) {
-                // attempt to create the user account registration
-                $.ajax({
-                    type: 'POST',
-                    url: config.urls.users,
-                    data: {
-                        'justification': $('#registration-justification').val()
-                    }
-                }).done(function (response) {
-                    var markup = 'An administrator will process your request 
shortly.';
-                    if (isAnonymous === true) {
-                        markup += '<br/><br/>In the meantime you can continue 
accessing anonymously.';
+                submitJustification();
+            }
+        });
+
+        $('#login-submission-container').show();
+    };
+
+    var login = function () {
+        // login submit
+        $.ajax({
+            type: 'POST',
+            url: config.urls.token,
+            data: {
+                '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]);
+
+                    // reload as appropriate
+                    if (top !== window) {
+                        parent.window.location = '/nifi';
+                    } else {
+                        window.location = '/nifi';
                     }
+                    return;
+                } else {
+                    badToken = true;
+                }
+            } else {
+                badToken = true;
+            }
 
-                    $('#login-message-title').text('Thanks!');
-                    $('#login-message').html(markup);
-                }).fail(function (xhr, status, error) {
-                    $('#login-message-title').text('An unexpected error has 
occurred');
-                    $('#login-message').text(xhr.responseText);
-                }).always(function () {
-                    // update form visibility
-                    $('#nifi-registration-container').hide();
-                    $('#login-submission-container').hide();
-                    $('#login-message-container').show();
+            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) {
+            if (xhr.status === 400) {
+                nf.Dialog.showOkDialog({
+                    dialogContent: nf.Common.escapeHtml(xhr.responseText),
+                    overlayBackground: false
                 });
+            } else {
+                $('#login-message-title').text('Unable to log in');
+                $('#login-message').text(xhr.responseText);
+
+                // update visibility
+                $('#login-container').hide();
+                $('#login-submission-container').hide();
+                $('#login-message-container').show();
             }
         });
+    };
 
-        $('#login-submission-container').show();
+    var createUserAccount = function () {
+        // attempt to create the user account registration
+        $.ajax({
+            type: 'POST',
+            url: config.urls.registration,
+            data: {
+                'username': $('#registration-username').val(),
+                'password': $('#registration-password').val(),
+                'justification': $('#nifi-registration-justification').val()
+            }
+        }).done(function (response, status, xhr) {
+            var markup = 'An administrator will process your request shortly.';
+            if (isAnonymous === true) {
+                markup += '<br/><br/>In the meantime you can continue 
accessing anonymously.';
+            }
+
+            $('#login-message-title').text('Thanks!');
+            $('#login-message').html(markup);
+        }).fail(function (xhr, status, error) {
+            $('#login-message-title').text('Unable to create user account');
+            $('#login-message').text(xhr.responseText);
+        }).always(function () {
+            // update form visibility
+            $('#user-registration-container').hide();
+            $('#nifi-registration-container').hide();
+            $('#login-submission-container').hide();
+            $('#login-message-container').show();
+        });
+    };
+
+    var submitJustification = function () {
+        // attempt to create the nifi account registration
+        $.ajax({
+            type: 'POST',
+            url: config.urls.users,
+            data: {
+                'justification': $('#nifi-registration-justification').val()
+            }
+        }).done(function (response) {
+            var markup = 'An administrator will process your request shortly.';
+            if (isAnonymous === true) {
+                markup += '<br/><br/>In the meantime you can continue 
accessing anonymously.';
+            }
+
+            $('#login-message-title').text('Thanks!');
+            $('#login-message').html(markup);
+        }).fail(function (xhr, status, error) {
+            $('#login-message-title').text('Unable to submit justification');
+            $('#login-message').text(xhr.responseText);
+        }).always(function () {
+            // update form visibility
+            $('#nifi-registration-container').hide();
+            $('#login-submission-container').hide();
+            $('#login-message-container').show();
+        });
     };
 
     return {
@@ -202,7 +251,7 @@ nf.Login = (function () {
 
                         // request a token without including credentials, if 
successful then the user is using a certificate
                         token.done(function () {
-                            // the user is using a certificate, 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({
                                 type: 'GET',
                                 url: config.urls.registrationStatus
@@ -212,7 +261,6 @@ nf.Login = (function () {
                                 // account is active and good
                                 $('#login-message-title').text('Success');
                                 $('#login-message').text('Your account is 
active and you are already logged in.');
-                                deferred.resolve();
                             }).fail(function (xhr, status, error) {
                                 if (xhr.status === 401) {
                                     // anonymous user and 401 means they need 
nifi registration
@@ -228,6 +276,7 @@ nf.Login = (function () {
                                         
$('#login-message').text(xhr.responseText);
                                     }
                                 }
+                            }).always(function () {
                                 deferred.resolve();
                             });
                         }).fail(function () {
@@ -248,7 +297,7 @@ nf.Login = (function () {
                     if (xhr.status === 401) {
                         // attempt to get a token for the current user without 
passing login credentials
                         token.done(function () {
-                            // 401 from identity request and 200 from token 
means they have a certificate 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;
                         }).fail(function () {
                             // no token granted, user needs to login with 
their credentials
@@ -271,7 +320,7 @@ nf.Login = (function () {
                     }
                 });
             }).promise();
-            
+
             var loginConfigXhr = $.ajax({
                 type: 'GET',
                 url: config.urls.loginConfig,
@@ -282,7 +331,7 @@ nf.Login = (function () {
             $.when(loginConfigXhr, pageStateInit).done(function (loginResult) {
                 var loginResponse = loginResult[0];
                 var loginConfig = loginResponse.config;
-                
+
                 // if login is required, verify its supported
                 if (loginConfig.supportsLogin === false && needsLogin === 
true) {
                     $('#login-message-title').text('Access Denied');
@@ -290,13 +339,15 @@ nf.Login = (function () {
                     showMessage = true;
                     needsLogin = false;
                 }
-                
+
                 if (showMessage === true) {
                     initializeMessage();
                 } else if (needsLogin === true) {
                     initializeLogin(loginConfig.supportsRegistration);
+                    showLogin();
                 } else if (needsNiFiRegistration === true) {
                     initializeNiFiRegistration();
+                    showNiFiRegistration();
                 }
 
                 if (needsLogin === true || needsNiFiRegistration === true) {

Reply via email to