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/12/01 17:43:39 UTC

[01/51] [abbrv] nifi git commit: NIFI-655: - Added an endpoint for access details including configuration, creating tokens, and checking status. - Updated DTOs and client side to utilize new endpoints.

Repository: nifi
Updated Branches:
  refs/heads/master e5281f1fc -> 7726d069c


http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/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 9e1dab6..b61988f 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
@@ -31,8 +31,9 @@ nf.Login = (function () {
             registration: '../nifi-api/registration',
             identity: '../nifi-api/controller/identity',
             users: '../nifi-api/controller/users',
-            token: '../nifi-api/token',
-            loginConfig: '../nifi-api/controller/login/config'
+            token: '../nifi-api/access/token',
+            accessStatus: '../nifi-api/access',
+            accessConfig: '../nifi-api/access/config'
         }
     };
 
@@ -210,167 +211,71 @@ nf.Login = (function () {
         init: function () {
             nf.Storage.init();
 
-            var showMessage = false;
-            var needsLogin = false;
-            var needsNiFiRegistration = false;
-
             if (nf.Storage.getItem('jwt') !== null) {
                 showLogoutLink();
             }
 
-            var token = $.ajax({
-                type: 'GET',
-                url: config.urls.token
-            });
-
-            var identity = $.ajax({
+            // access status
+            var accessStatus = $.ajax({
                 type: 'GET',
-                url: config.urls.identity,
+                url: config.urls.accessStatus,
                 dataType: 'json'
+            }).fail(function (xhr, status, error) {
+                $('#login-message-title').text('Unable to check Access Status');
+                $('#login-message').text(xhr.responseText);
+                initializeMessage();
             });
-
-            var pageStateInit = $.Deferred(function (deferred) {
-                // get the current user's identity
-                identity.done(function (response) {
-                    // if the user is anonymous see if they need to login or if they are working with a certificate
-                    if (response.identity === 'anonymous') {
-                        supportsAnonymous = true;
-
-                        // request a token without including credentials, if successful then the user is using a certificate
-                        token.done(function (jwt) {
-
-                            // the user is using a certificate/token, see if their account is active/pending/revoked/etc
-                            $.ajax({
-                                type: 'GET',
-                                url: config.urls.registrationStatus
-                            }).done(function () {
-                                showMessage = true;
-
-                                // account is active and good
-                                $('#login-message-title').text('Success');
-                                $('#login-message').text('Your account is active and you are already logged in.');
-                            }).fail(function (xhr, status, error) {
-                                if (xhr.status === 401) {
-                                    var user = nf.Common.getJwtSubject(jwt);
-
-                                    // show the user
-                                    $('#nifi-user-submit-justification').text(user);
-
-                                    // anonymous user and 401 means they need nifi registration
-                                    needsNiFiRegistration = true;
-                                } else {
-                                    showMessage = true;
-
-                                    // anonymous user and non-401 means they already have an account and it's pending/revoked
-                                    $('#login-message-title').text('Access Denied');
-                                    if ($.trim(xhr.responseText) === '') {
-                                        $('#login-message').text('Unable to check registration status.');
-                                    } else {
-                                        $('#login-message').text(xhr.responseText);
-                                    }
-                                }
-                            }).always(function () {
-                                deferred.resolve();
-                            });
-                        }).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();
-                        });
-                    } else {
-                        showMessage = true;
-
-                        // the user is not anonymous and has an active account (though maybe role-less)
-                        $('#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) {
-                    // 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 (jwt) {
-                            var user = nf.Common.getJwtSubject(jwt);
-
-                            // show the user
-                            $('#nifi-user-submit-justification').text(user);
-
-                            // 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 (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 () {
-                            deferred.resolve();
-                        });
-                    } else if (xhr.status === 403) {
-                        // attempt to get a token for the current user without passing login credentials
-                        token.done(function () {
-                            showMessage = true;
-                            
-                            // the user is logged in with certificate or credentials but their account is pending/revoked. error message should indicate
-                            $('#login-message-title').text('Access Denied');
-                            if ($.trim(xhr.responseText) === '') {
-                                $('#login-message').text('Unable to authorize you to use this NiFi and anonymous access is disabled.');
-                            } else {
-                                $('#login-message').text(xhr.responseText);
-                            }
-                        }).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 () {
-                            deferred.resolve();
-                        });
-                    } else {
-                        showMessage = true;
-
-                        // the user is logged in with certificate or credentials but their account is pending/revoked. error message should indicate
-                        $('#login-message-title').text('Access Denied');
-                        if ($.trim(xhr.responseText) === '') {
-                            $('#login-message').text('Unable to authorize you to use this NiFi and anonymous access is disabled.');
-                        } else {
-                            $('#login-message').text(xhr.responseText);
-                        }
-
-                        deferred.resolve();
-                    }
-                });
-            }).promise();
-
-            var loginConfigXhr = $.ajax({
+            
+            // access config
+            var accessConfigXhr = $.ajax({
                 type: 'GET',
-                url: config.urls.loginConfig,
+                url: config.urls.accessConfig,
                 dataType: 'json'
             });
-
-            // render the page accordingly
-            $.when(loginConfigXhr, pageStateInit).done(function (loginResult) {
-                var loginResponse = loginResult[0];
-                var loginConfig = loginResponse.config;
-
+            
+            $.when(accessStatus, accessConfigXhr).done(function (accessStatusResult, accessConfigResult) {
+                var accessStatusResponse = accessStatusResult[0];
+                var accessStatus = accessStatusResponse.accessStatus;
+                
+                var accessConfigResponse = accessConfigResult[0];
+                var accessConfig = accessConfigResponse.config;
+                
+                // record whether this NiFi supports anonymous access
+                supportsAnonymous = accessConfig.supportsAnonymous;
+            
+                // possible login states
+                var needsLogin = false;
+                var needsNiFiRegistration = false;
+                var showMessage = false;
+                
+                // handle the status appropriately
+                if (accessStatus.status === 'UNKNOWN') {
+                    needsLogin = true;
+                } else if (accessStatus.status === 'UNREGISTERED') {
+                    needsNiFiRegistration = true;
+                    
+                    $('#nifi-user-submit-justification').text(accessStatus.username);
+                } else if (accessStatus.status === 'NOT_ACTIVE') {
+                    showMessage = true;
+                    
+                    $('#login-message-title').text('Access Denied');
+                    $('#login-message').text(accessStatus.message);
+                } else if (accessStatus.status === 'ACTIVE') {
+                    showMessage = true;
+                    
+                    $('#login-message-title').text('Success');
+                    $('#login-message').text('Your account is active and you are already logged in.');
+                }
+                
                 // if login is required, verify its supported
-                if (loginConfig.supportsLogin === false && needsLogin === true) {
+                if (accessConfig.supportsLogin === false && needsLogin === true) {
                     $('#login-message-title').text('Access Denied');
                     $('#login-message').text('This NiFi is not configured to support login.');
                     showMessage = true;
                     needsLogin = false;
                 }
 
+                // initialize the page as appropriate
                 if (showMessage === true) {
                     initializeMessage();
                 } else if (needsLogin === true) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/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 53128b9..3be6b91 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
@@ -102,7 +102,7 @@ nf.Common = (function () {
                 }
             },
             urls: {
-                token: '../nifi-api/token'
+                token: '../nifi-api/access/token'
             }
         },
 
@@ -163,7 +163,7 @@ nf.Common = (function () {
                     if (timeRemaining < interval) {
                         // if the token will expire before the next interval minus some bonus time, refresh now
                         $.ajax({
-                            type: 'GET',
+                            type: 'POST',
                             url: nf.Common.config.urls.token
                         }).done(function (jwt) {
                             nf.Storage.setItem('jwt', jwt, nf.Common.getJwtExpiration(jwt));


[26/51] [abbrv] nifi git commit: NIFI-655: - Refactoring web security to use Spring Security Java Configuration. - Introducing security in Web UI in order to get JWT.

Posted by mc...@apache.org.
http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.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/x509/ocsp/OcspCertificateValidator.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.java
index 81e4bd6..832a63c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.java
@@ -42,7 +42,6 @@ import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 import javax.net.ssl.X509TrustManager;
 import javax.security.auth.x500.X500Principal;
-import javax.servlet.http.HttpServletRequest;
 import org.apache.nifi.framework.security.util.SslContextFactory;
 import org.apache.nifi.web.security.x509.ocsp.OcspStatus.ValidationStatus;
 import org.apache.nifi.web.security.x509.ocsp.OcspStatus.VerificationStatus;
@@ -158,8 +157,7 @@ public class OcspCertificateValidator {
     }
 
     /**
-     * Loads the trusted certificate authorities according to the specified
-     * properties.
+     * Loads the trusted certificate authorities according to the specified properties.
      *
      * @param properties properties
      * @return map of certificate authorities
@@ -208,12 +206,10 @@ public class OcspCertificateValidator {
     /**
      * Validates the specified certificate using OCSP if configured.
      *
-     * @param request http request
+     * @param certificates the client certificates
      * @throws CertificateStatusException ex
      */
-    public void validate(final HttpServletRequest request) throws CertificateStatusException {
-        final X509Certificate[] certificates = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
-
+    public void validate(final X509Certificate[] certificates) throws CertificateStatusException {
         // only validate if configured to do so
         if (client != null && certificates != null && certificates.length > 0) {
             final X509Certificate subjectCertificate = getSubjectCertificate(certificates);
@@ -395,13 +391,9 @@ public class OcspCertificateValidator {
     }
 
     /**
-     * Gets the trusted responder certificate. The response contains the
-     * responder certificate, however we cannot blindly trust it. Instead, we
-     * use a configured trusted CA. If the responder certificate is a trusted
-     * CA, then we can use it. If the responder certificate is not directly
-     * trusted, we still may be able to trust it if it was issued by the same CA
-     * that issued the subject certificate. Other various checks may be required
-     * (this portion is currently not implemented).
+     * Gets the trusted responder certificate. The response contains the responder certificate, however we cannot blindly trust it. Instead, we use a configured trusted CA. If the responder
+     * certificate is a trusted CA, then we can use it. If the responder certificate is not directly trusted, we still may be able to trust it if it was issued by the same CA that issued the subject
+     * certificate. Other various checks may be required (this portion is currently not implemented).
      *
      * @param responderCertificate cert
      * @param issuerCertificate cert

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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 fa5b9e2..40f678c 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
@@ -13,70 +13,45 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<!-- marked as lazy so that security beans are not created when applications runs in non-secure mode -->
-<beans default-lazy-init="true"
-       xmlns="http://www.springframework.org/schema/beans"
+<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xmlns:sec="http://www.springframework.org/schema/security"
-       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
-    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd">
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
 
-    <!-- http security configuration -->
-    <sec:http create-session="stateless" auto-config="false" entry-point-ref="entryPoint">
-        <sec:anonymous enabled="false"/>
-        <sec:custom-filter ref="nodeAuthorizedUserFilter" before="PRE_AUTH_FILTER"/>
-        <sec:custom-filter ref="x509Filter" position="PRE_AUTH_FILTER"/>
-        <sec:custom-filter ref="anonymousFilter" position="ANONYMOUS_FILTER"/>
-    </sec:http>
+    <!-- certificate extractor -->
+    <bean id="certificateExtractor" class="org.apache.nifi.web.security.x509.X509CertificateExtractor"/>
 
-    <!-- enable method level security -->
-    <sec:global-method-security pre-post-annotations="enabled"/>
-
-    <!-- entry point reference -->
-    <bean id="entryPoint" class="org.apache.nifi.web.security.authentication.NiFiAuthenticationEntryPoint"/>
+    <!-- principal extractor -->
+    <bean id="principalExtractor" class="org.apache.nifi.web.security.x509.SubjectDnX509PrincipalExtractor"/>
     
-    <!-- authentication manager -->
-    <sec:authentication-manager alias="authenticationManager">
-        <sec:authentication-provider ref="preauthAuthProvider"/>
-    </sec:authentication-manager>
-
-    <!-- pre-authentication provider -->
-    <bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
-        <property name="preAuthenticatedUserDetailsService">
-            <bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
-                <property name="userDetailsService" ref="userAuthorizationService"/>
-            </bean>
-        </property>
+    <!-- ocsp validator -->
+    <bean id="ocspValidator" class="org.apache.nifi.web.security.x509.ocsp.OcspCertificateValidator">
+        <constructor-arg ref="nifiProperties"/>
     </bean>
-
-    <!-- user details service -->
-    <bean id="userAuthorizationService" class="org.apache.nifi.web.security.authorization.NiFiAuthorizationService">
-        <property name="userService" ref="userService"/>
-        <property name="properties" ref="nifiProperties"/>
+        
+    <!-- x509 validator -->
+    <bean id="certificateValidator" class="org.apache.nifi.web.security.x509.X509CertificateValidator">
+        <property name="ocspValidator" ref="ocspValidator"/> 
     </bean>
-
-    <!-- performs ocsp certificate validation -->
-    <bean id="ocspCertificateValidator" class="org.apache.nifi.web.security.x509.ocsp.OcspCertificateValidator">
-        <constructor-arg ref="nifiProperties"/>
+    
+    <!-- x509 identity provider -->
+    <bean id="certificateIdentityProvider" class="org.apache.nifi.web.security.x509.X509IdentityProvider">
+        <property name="principalExtractor" ref="principalExtractor"/>
+        <property name="certificateValidator" ref="certificateValidator"/>
     </bean>
-
-    <!-- custom x509 filter for checking for proxied users -->
-    <bean id="x509Filter" class="org.apache.nifi.web.security.x509.X509AuthenticationFilter">
-        <property name="authenticationManager" ref="authenticationManager"/>
-        <property name="properties" ref="nifiProperties"/>
+    
+    <!-- user details service -->
+    <bean id="userDetailsService" class="org.apache.nifi.web.security.authorization.NiFiAuthorizationService">
         <property name="userService" ref="userService"/>
-        <property name="certificateValidator" ref="ocspCertificateValidator"/>
-        <property name="continueFilterChainOnUnsuccessfulAuthentication" value="false"/>
-    </bean>
-
-    <!-- custom filter for checking for proxied users that are already authenticated -->
-    <bean id="nodeAuthorizedUserFilter" class="org.apache.nifi.web.security.authorization.NodeAuthorizedUserFilter">
         <property name="properties" ref="nifiProperties"/>
     </bean>
-
-    <!-- custom anonymous filter to assign default roles based on current operating mode -->
-    <bean id="anonymousFilter" class="org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter">
-        <property name="userService" ref="userService"/>
+    
+    <!-- jwt service -->
+    <bean id="jwtService" class="org.apache.nifi.web.security.jwt.JwtService">
+        <constructor-arg ref="keyService"/>
+    </bean>
+    
+    <!-- login identity provider -->
+    <bean id="loginIdentityProvider" class="org.apache.nifi.web.security.spring.LoginIdentityProviderFactoryBean">
         <property name="properties" ref="nifiProperties"/>
     </bean>
     

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/xsd/login-identity-providers.xsd
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/xsd/login-identity-providers.xsd b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/xsd/login-identity-providers.xsd
new file mode 100644
index 0000000..628f390
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/xsd/login-identity-providers.xsd
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+      http://www.apache.org/licenses/LICENSE-2.0
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <!-- role -->
+    <xs:complexType name="Provider">
+        <xs:sequence>
+            <xs:element name="identifier" type="NonEmptyStringType"/>
+            <xs:element name="class" type="NonEmptyStringType"/>
+            <xs:element name="property" type="Property" minOccurs="0" maxOccurs="unbounded" />
+        </xs:sequence>
+    </xs:complexType>
+
+    <!-- Name/Value properties-->
+    <xs:complexType name="Property">
+        <xs:simpleContent>
+            <xs:extension base="xs:string">
+                <xs:attribute name="name" type="NonEmptyStringType"></xs:attribute>
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+
+    <xs:simpleType name="NonEmptyStringType">
+        <xs:restriction base="xs:string">
+            <xs:minLength value="1"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <!-- login identity provider -->
+    <xs:element name="loginIdentityProviders">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="provider" type="Provider" minOccurs="0" maxOccurs="unbounded"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+</xs:schema>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
index 6d0c3cb..5456552 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.nifi.web.security.authorization;
 
+import java.util.Arrays;
 import org.apache.nifi.admin.service.AccountDisabledException;
 import org.apache.nifi.admin.service.AccountNotFoundException;
 import org.apache.nifi.admin.service.AccountPendingException;
@@ -24,8 +25,8 @@ import org.apache.nifi.admin.service.UserService;
 import org.apache.nifi.authorization.Authority;
 import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.util.NiFiProperties;
-import org.apache.nifi.web.security.DnUtils;
 import org.apache.nifi.web.security.UntrustedProxyException;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
 import org.apache.nifi.web.security.user.NiFiUserDetails;
 import org.junit.Assert;
 import org.junit.Before;
@@ -66,10 +67,10 @@ public class NiFiAuthorizationServiceTest {
             @Override
             public Object answer(InvocationOnMock invocation) throws Throwable {
                 Object[] args = invocation.getArguments();
-                String dn = (String) args[0];
+                String identity = (String) args[0];
 
-                if (null != dn) {
-                    switch (dn) {
+                if (null != identity) {
+                    switch (identity) {
                         case USER_NOT_FOUND:
                         case PROXY_NOT_FOUND:
                             throw new AccountNotFoundException("");
@@ -81,13 +82,13 @@ public class NiFiAuthorizationServiceTest {
                             throw new AdministrationException();
                         case USER:
                             final NiFiUser monitor = new NiFiUser();
-                            monitor.setDn(dn);
+                            monitor.setIdentity(identity);
                             monitor.getAuthorities().add(Authority.ROLE_MONITOR);
                             return monitor;
                         case PROXY:
                         case PROXY_PROXY:
                             final NiFiUser proxy = new NiFiUser();
-                            proxy.setDn(dn);
+                            proxy.setIdentity(identity);
                             proxy.getAuthorities().add(Authority.ROLE_PROXY);
                             return proxy;
                     }
@@ -103,15 +104,18 @@ public class NiFiAuthorizationServiceTest {
         authorizationService.setUserService(userService);
     }
 
+    private NiFiAuthenticationRequestToken createRequestAuthentication(final String... identities) {
+        return new NiFiAuthenticationRequestToken(Arrays.asList(identities));
+    }
+
     /**
-     * Ensures the authorization service correctly handles users invalid dn
-     * chain.
+     * Ensures the authorization service correctly handles users invalid identity chain.
      *
      * @throws Exception ex
      */
     @Test(expected = UntrustedProxyException.class)
     public void testInvalidDnChain() throws Exception {
-        authorizationService.loadUserByUsername(USER);
+        authorizationService.loadUserDetails(createRequestAuthentication());
     }
 
     /**
@@ -121,7 +125,7 @@ public class NiFiAuthorizationServiceTest {
      */
     @Test(expected = UsernameNotFoundException.class)
     public void testAccountNotFound() throws Exception {
-        authorizationService.loadUserByUsername(DnUtils.formatProxyDn(USER_NOT_FOUND));
+        authorizationService.loadUserDetails(createRequestAuthentication(USER_NOT_FOUND));
     }
 
     /**
@@ -131,7 +135,7 @@ public class NiFiAuthorizationServiceTest {
      */
     @Test(expected = AccountStatusException.class)
     public void testAccountDisabled() throws Exception {
-        authorizationService.loadUserByUsername(DnUtils.formatProxyDn(USER_DISABLED));
+        authorizationService.loadUserDetails(createRequestAuthentication(USER_DISABLED));
     }
 
     /**
@@ -141,18 +145,17 @@ public class NiFiAuthorizationServiceTest {
      */
     @Test(expected = AccountStatusException.class)
     public void testAccountPending() throws Exception {
-        authorizationService.loadUserByUsername(DnUtils.formatProxyDn(USER_PENDING));
+        authorizationService.loadUserDetails(createRequestAuthentication(USER_PENDING));
     }
 
     /**
-     * Ensures the authorization service correctly handles account
-     * administration exception.
+     * Ensures the authorization service correctly handles account administration exception.
      *
      * @throws Exception ex
      */
     @Test(expected = AuthenticationServiceException.class)
     public void testAccountAdminException() throws Exception {
-        authorizationService.loadUserByUsername(DnUtils.formatProxyDn(USER_ADMIN_EXCEPTION));
+        authorizationService.loadUserDetails(createRequestAuthentication(USER_ADMIN_EXCEPTION));
     }
 
     /**
@@ -162,10 +165,10 @@ public class NiFiAuthorizationServiceTest {
      */
     @Test
     public void testNoProxy() throws Exception {
-        final NiFiUserDetails details = (NiFiUserDetails) authorizationService.loadUserByUsername(DnUtils.formatProxyDn(USER));
+        final NiFiUserDetails details = (NiFiUserDetails) authorizationService.loadUserDetails(createRequestAuthentication(USER));
         final NiFiUser user = details.getNiFiUser();
 
-        Assert.assertEquals(USER, user.getDn());
+        Assert.assertEquals(USER, user.getIdentity());
         Assert.assertNull(user.getChain());
     }
 
@@ -176,21 +179,18 @@ public class NiFiAuthorizationServiceTest {
      */
     @Test(expected = UntrustedProxyException.class)
     public void testInvalidProxy() throws Exception {
-        final String dnChain = DnUtils.formatProxyDn(USER) + DnUtils.formatProxyDn(USER);
-        authorizationService.loadUserByUsername(dnChain);
+        authorizationService.loadUserDetails(createRequestAuthentication(USER, USER));
     }
 
     /**
-     * Ensures the authorization service correctly handles proxy not found by
-     * attempting to create an account request for the proxy.
+     * Ensures the authorization service correctly handles proxy not found by attempting to create an account request for the proxy.
      *
      * @throws Exception ex
      */
-    @Test(expected = UsernameNotFoundException.class)
+    @Test(expected = UntrustedProxyException.class)
     public void testProxyNotFound() throws Exception {
         try {
-            final String dnChain = DnUtils.formatProxyDn(USER) + DnUtils.formatProxyDn(PROXY_NOT_FOUND);
-            authorizationService.loadUserByUsername(DnUtils.formatProxyDn(dnChain));
+            authorizationService.loadUserDetails(createRequestAuthentication(USER, PROXY_NOT_FOUND));
         } finally {
             Mockito.verify(userService).createPendingUserAccount(Mockito.eq(PROXY_NOT_FOUND), Mockito.anyString());
         }
@@ -203,19 +203,18 @@ public class NiFiAuthorizationServiceTest {
      */
     @Test
     public void testProxy() throws Exception {
-        final String dnChain = DnUtils.formatProxyDn(USER) + DnUtils.formatProxyDn(PROXY);
-        final NiFiUserDetails details = (NiFiUserDetails) authorizationService.loadUserByUsername(dnChain);
+        final NiFiUserDetails details = (NiFiUserDetails) authorizationService.loadUserDetails(createRequestAuthentication(USER, PROXY));
         final NiFiUser user = details.getNiFiUser();
 
         // verify the user
-        Assert.assertEquals(USER, user.getDn());
+        Assert.assertEquals(USER, user.getIdentity());
         Assert.assertNotNull(user.getChain());
 
         // get the proxy
         final NiFiUser proxy = user.getChain();
 
         // verify the proxy
-        Assert.assertEquals(PROXY, proxy.getDn());
+        Assert.assertEquals(PROXY, proxy.getIdentity());
         Assert.assertNull(proxy.getChain());
     }
 
@@ -226,26 +225,25 @@ public class NiFiAuthorizationServiceTest {
      */
     @Test
     public void testProxyProxy() throws Exception {
-        final String dnChain = DnUtils.formatProxyDn(USER) + DnUtils.formatProxyDn(PROXY) + DnUtils.formatProxyDn(PROXY_PROXY);
-        final NiFiUserDetails details = (NiFiUserDetails) authorizationService.loadUserByUsername(dnChain);
+        final NiFiUserDetails details = (NiFiUserDetails) authorizationService.loadUserDetails(createRequestAuthentication(USER, PROXY, PROXY_PROXY));
         final NiFiUser user = details.getNiFiUser();
 
         // verify the user
-        Assert.assertEquals(USER, user.getDn());
+        Assert.assertEquals(USER, user.getIdentity());
         Assert.assertNotNull(user.getChain());
 
         // get the proxy
         NiFiUser proxy = user.getChain();
 
         // verify the proxy
-        Assert.assertEquals(PROXY, proxy.getDn());
+        Assert.assertEquals(PROXY, proxy.getIdentity());
         Assert.assertNotNull(proxy.getChain());
 
         // get the proxies proxy
         proxy = proxy.getChain();
 
         // verify the proxies proxy
-        Assert.assertEquals(PROXY_PROXY, proxy.getDn());
+        Assert.assertEquals(PROXY_PROXY, proxy.getIdentity());
         Assert.assertNull(proxy.getChain());
     }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
new file mode 100644
index 0000000..59c66ef
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
@@ -0,0 +1,445 @@
+/*
+ * 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.jwt;
+
+import io.jsonwebtoken.JwtException;
+import org.apache.commons.codec.CharEncoding;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.nifi.admin.service.AdministrationException;
+import org.apache.nifi.admin.service.KeyService;
+import org.apache.nifi.key.Key;
+import org.apache.nifi.web.security.token.LoginAuthenticationToken;
+import org.codehaus.jettison.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
+
+public class JwtServiceTest {
+
+    private static final Logger logger = LoggerFactory.getLogger(JwtServiceTest.class);
+
+    /**
+     * These constant strings were generated using the tool at http://jwt.io
+     */
+    private static final String VALID_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRl"
+            + "ciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZ"
+            + "XJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoxLCJleHAiOjI0NDc4MDg3NjEsIm"
+            + "lhdCI6MTQ0NzgwODcwMX0.r6aGZ6FNNYMOpcXW8BK2VYaQeX1uO0Aw1KJfjB3Q1DU";
+
+    // This token has an empty subject field
+    private static final String INVALID_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZG"
+            + "VudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvI"
+            + "iwia2lkIjoxLCJleHAiOjI0NDc4MDg3NjEsImlhdCI6MTQ0NzgwODcwMX0"
+            + ".x_1p2M6E0vwWHWMujIUnSL3GkFoDqqICllRxo2SMNaw";
+
+    private static final String VALID_UNSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZC"
+            + "I6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJl"
+            + "c3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9";
+
+    // This token has an empty subject field
+    private static final String INVALID_UNSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVu"
+            + "dGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoi"
+            + "YWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9";
+
+    // Algorithm field is "none"
+    private static final String VALID_MALSIGNED_TOKEN = "eyJhbGciOiJub25lIn0"
+            + ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZC"
+            + "I6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJl"
+            + "c3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoxNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9"
+            + ".mPO_wMNMl_zjMNevhNvUoXbSJ9Kx6jAe5OxDIAzKQbI";
+
+    // Algorithm field is "none" and no signature is present
+    private static final String VALID_MALSIGNED_NO_SIG_TOKEN = "eyJhbGciOiJub25lIn0"
+            + ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY"
+            + "2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIj"
+            + "oiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.";
+
+    // This token has an empty subject field
+    private static final String INVALID_MALSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVud"
+            + "Gl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYW"
+            + "xvcHJlc3RvIiwiZXhwIjoxNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.WAwmUY4KHKV2oARNodkqDkbZsfRXGZfD2Ccy64GX9QF";
+
+    // This token is signed but expired
+    private static final String EXPIRED_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik"
+            + "1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvc"
+            + "HJlc3RvIiwia2lkIjoxLCJleHAiOjE0NDc4MDg3NjEsImlhdCI6MTQ0NzgwODcw"
+            + "MX0.ZPDIhNKuL89vTGXcuztOYaGifwcrQy_gid4j8Sspmto";
+
+    // Subject is "mgilman" but signed with "alopresto" key
+    private static final String IMPOSTER_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiJtZ2lsbWFuIiwiaXNzIjoiTW9ja0lkZW50aXR5UHJvdmlkZXIiLCJ"
+            + "hdWQiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsInByZWZlcnJlZF91c2VybmFtZSI"
+            + "6ImFsb3ByZXN0byIsImtpZCI6MSwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc"
+            + "4MDg3MDF9.aw5OAvLTnb_sHmSQOQzW-A7NImiZgXJ2ngbbNL2Ymkc";
+
+    // Issuer field is set to unknown provider
+    private static final String UNKNOWN_ISSUER_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJVbmtub3duSWRlbnRpdHlQcm92aWRlciIsIm"
+            + "F1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxv"
+            + "cHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9"
+            + ".SAd9tyNwSaijWet9wvAWSNmpxmPSK4XQuLx7h3ARqBo";
+
+    // Issuer field is absent
+    private static final String NO_ISSUER_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiJhbG9wcmVzdG8iLCJhdWQiOiJNb2NrSWRlbnRpdHlQcm92a"
+            + "WRlciIsInByZWZlcnJlZF91c2VybmFtZSI6ImFsb3ByZXN0byIsImtpZCI"
+            + "6MSwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.6kDjDanA"
+            + "g0NQDb3C8FmgbBAYDoIfMAEkF4WMVALsbJA";
+
+    private static final String DEFAULT_HEADER = "{\"alg\":\"HS256\"}";
+    private static final String DEFAULT_IDENTITY = "alopresto";
+
+    private static final String TOKEN_DELIMITER = ".";
+
+    private static final String HMAC_SECRET = "test_hmac_shared_secret";
+
+    private KeyService mockKeyService;
+
+    // Class under test
+    private JwtService jwtService;
+
+    private String generateHS256Token(String rawHeader, String rawPayload, boolean isValid, boolean isSigned) {
+        return generateHS256Token(rawHeader, rawPayload, HMAC_SECRET, isValid, isSigned);
+    }
+
+    private String generateHS256Token(String rawHeader, String rawPayload, String hmacSecret, boolean isValid,
+            boolean isSigned) {
+        try {
+            logger.info("Generating token for " + rawHeader + " + " + rawPayload);
+
+            String base64Header = Base64.encodeBase64URLSafeString(rawHeader.getBytes(CharEncoding.UTF_8));
+            String base64Payload = Base64.encodeBase64URLSafeString(rawPayload.getBytes(CharEncoding.UTF_8));
+            // TODO: Support valid/invalid manipulation
+
+            final String body = base64Header + TOKEN_DELIMITER + base64Payload;
+
+            String signature = generateHMAC(hmacSecret, body);
+
+            return body + TOKEN_DELIMITER + signature;
+        } catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
+            final String errorMessage = "Could not generate the token";
+            logger.error(errorMessage, e);
+            fail(errorMessage);
+            return null;
+        }
+    }
+
+    private String generateHMAC(String hmacSecret, String body) throws NoSuchAlgorithmException,
+            UnsupportedEncodingException, InvalidKeyException {
+        Mac hmacSHA256 = Mac.getInstance("HmacSHA256");
+        SecretKeySpec secret_key = new SecretKeySpec(hmacSecret.getBytes("UTF-8"), "HmacSHA256");
+        hmacSHA256.init(secret_key);
+        return Base64.encodeBase64URLSafeString(hmacSHA256.doFinal(body.getBytes("UTF-8")));
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        final Key key = new Key();
+        key.setId(1);
+        key.setIdentity(DEFAULT_IDENTITY);
+        key.setKey(HMAC_SECRET);
+
+        mockKeyService = Mockito.mock(KeyService.class);
+        when(mockKeyService.getKey(anyInt())).thenReturn(key);
+        when(mockKeyService.getOrCreateKey(anyString())).thenReturn(key);
+        jwtService = new JwtService(mockKeyService);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+
+    }
+
+    @Test
+    public void testShouldGetAuthenticationForValidToken() throws Exception {
+        // Arrange
+        String token = VALID_SIGNED_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+        assertEquals("Identity", DEFAULT_IDENTITY, identity);
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForInvalidToken() throws Exception {
+        // Arrange
+        String token = INVALID_SIGNED_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+        // Should fail
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForEmptyToken() throws Exception {
+        // Arrange
+        String token = "";
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+        // Should fail
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForUnsignedToken() throws Exception {
+        // Arrange
+        String token = VALID_UNSIGNED_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+        // Should fail
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForMalsignedToken() throws Exception {
+        // Arrange
+        String token = VALID_MALSIGNED_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+        // Should fail
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForTokenWithWrongAlgorithm() throws Exception {
+        // Arrange
+        String token = VALID_MALSIGNED_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+        // Should fail
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForTokenWithWrongAlgorithmAndNoSignature() throws Exception {
+        // Arrange
+        String token = VALID_MALSIGNED_NO_SIG_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+        // Should fail
+    }
+
+    @Ignore("Not yet implemented")
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForTokenFromUnknownIdentityProvider() throws Exception {
+        // Arrange
+        String token = UNKNOWN_ISSUER_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+        // Should fail
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForTokenFromEmptyIdentityProvider() throws Exception {
+        // Arrange
+        String token = NO_ISSUER_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+        // Should fail
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForExpiredToken() throws Exception {
+        // Arrange
+        String token = EXPIRED_SIGNED_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+        // Should fail
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForImposterToken() throws Exception {
+        // Arrange
+        String token = IMPOSTER_SIGNED_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+        // Should fail
+    }
+
+    @Test
+    public void testShouldGenerateSignedToken() throws Exception {
+        // Arrange
+
+        // Token expires in 60 seconds
+        final int EXPIRATION_MILLIS = 60000;
+        LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken("alopresto",
+                EXPIRATION_MILLIS,
+                "MockIdentityProvider");
+        logger.debug("Generating token for " + loginAuthenticationToken);
+
+        final String EXPECTED_HEADER = DEFAULT_HEADER;
+
+        // Convert the expiration time from ms to s
+        final long TOKEN_EXPIRATION_SEC = (long) (loginAuthenticationToken.getExpiration() / 1000.0);
+
+        // Act
+        String token = jwtService.generateSignedToken(loginAuthenticationToken);
+        logger.debug("Generated JWT: " + token);
+
+        // Run after the SUT generates the token to ensure the same issued at time
+        // Split the token, decode the middle section, and form a new String
+        final String DECODED_PAYLOAD = new String(Base64.decodeBase64(token.split("\\.")[1].getBytes()));
+        final long ISSUED_AT_SEC = Long.valueOf(DECODED_PAYLOAD.substring(DECODED_PAYLOAD.lastIndexOf(":") + 1,
+                DECODED_PAYLOAD.length() - 1));
+        logger.trace("Actual token was issued at " + ISSUED_AT_SEC);
+
+        // Always use LinkedHashMap to enforce order of the keys because the signature depends on order
+        Map<String, Object> claims = new LinkedHashMap<>();
+        claims.put("sub", "alopresto");
+        claims.put("iss", "MockIdentityProvider");
+        claims.put("aud", "MockIdentityProvider");
+        claims.put("preferred_username", "alopresto");
+        claims.put("kid", 1);
+        claims.put("exp", TOKEN_EXPIRATION_SEC);
+        claims.put("iat", ISSUED_AT_SEC);
+        logger.trace("JSON Object to String: " + new JSONObject(claims).toString());
+
+        final String EXPECTED_PAYLOAD = new JSONObject(claims).toString();
+        final String EXPECTED_TOKEN_STRING = generateHS256Token(EXPECTED_HEADER, EXPECTED_PAYLOAD, true, true);
+        logger.debug("Expected JWT: " + EXPECTED_TOKEN_STRING);
+
+        // Assert
+        assertEquals("JWT token", EXPECTED_TOKEN_STRING, token);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testShouldNotGenerateTokenWithNullAuthenticationToken() throws Exception {
+        // Arrange
+        LoginAuthenticationToken nullLoginAuthenticationToken = null;
+        logger.debug("Generating token for " + nullLoginAuthenticationToken);
+
+        // Act
+        jwtService.generateSignedToken(nullLoginAuthenticationToken);
+
+        // Assert
+        // Should throw exception
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGenerateTokenWithEmptyIdentity() throws Exception {
+        // Arrange
+        final int EXPIRATION_MILLIS = 60000;
+        LoginAuthenticationToken emptyIdentityLoginAuthenticationToken = new LoginAuthenticationToken("",
+                EXPIRATION_MILLIS, "MockIdentityProvider");
+        logger.debug("Generating token for " + emptyIdentityLoginAuthenticationToken);
+
+        // Act
+        jwtService.generateSignedToken(emptyIdentityLoginAuthenticationToken);
+
+        // Assert
+        // Should throw exception
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGenerateTokenWithNullIdentity() throws Exception {
+        // Arrange
+        final int EXPIRATION_MILLIS = 60000;
+        LoginAuthenticationToken nullIdentityLoginAuthenticationToken = new LoginAuthenticationToken(null,
+                EXPIRATION_MILLIS, "MockIdentityProvider");
+        logger.debug("Generating token for " + nullIdentityLoginAuthenticationToken);
+
+        // Act
+        jwtService.generateSignedToken(nullIdentityLoginAuthenticationToken);
+
+        // Assert
+        // Should throw exception
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGenerateTokenWithMissingKey() throws Exception {
+        // Arrange
+        final int EXPIRATION_MILLIS = 60000;
+        LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken("alopresto",
+                EXPIRATION_MILLIS,
+                "MockIdentityProvider");
+        logger.debug("Generating token for " + loginAuthenticationToken);
+
+        // Set up the bad key service
+        KeyService missingKeyService = Mockito.mock(KeyService.class);
+        when(missingKeyService.getOrCreateKey(anyString())).thenThrow(new AdministrationException("Could not find a "
+                + "key for that user"));
+        jwtService = new JwtService(missingKeyService);
+
+        // Act
+        jwtService.generateSignedToken(loginAuthenticationToken);
+
+        // Assert
+        // Should throw exception
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/resources/logback-test.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/resources/logback-test.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..fe5d27e
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/resources/logback-test.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+
+<configuration>
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%-4r [%t] %-5p %c - %m%n</pattern>
+        </encoder>
+    </appender>
+    
+    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+        <file>./target/log</file>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%date %level [%thread] %logger{40} %msg%n</pattern>
+        </encoder>
+    </appender>
+    
+    
+    <logger name="org.apache.nifi" level="TRACE"/>
+    <root level="INFO">
+        <appender-ref ref="CONSOLE"/>
+    </root>
+</configuration>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
index 195f14b..a4ef516 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
@@ -32,6 +32,7 @@
         <templates.filter>templates.properties</templates.filter>
         <users.filter>users.properties</users.filter>
         <bulletin.board.filter>bulletin-board.properties</bulletin.board.filter>
+        <login.filter>login.properties</login.filter>
         <provenance.filter>provenance.properties</provenance.filter>
     </properties>
     <build>
@@ -54,6 +55,7 @@
             <filter>src/main/resources/filters/${templates.filter}</filter>
             <filter>src/main/resources/filters/${users.filter}</filter>
             <filter>src/main/resources/filters/${bulletin.board.filter}</filter>
+            <filter>src/main/resources/filters/${login.filter}</filter>
             <filter>src/main/resources/filters/${provenance.filter}</filter>
         </filters>
         <plugins>
@@ -89,7 +91,8 @@
                                 **/cluster.jsp,
                                 **/templates.jsp,
                                 **/users.jsp,
-                                **/bulletin-board.jsp
+                                **/bulletin-board.jsp,
+                                **/login.jsp
                             </excludes>
                         </configuration>
                     </execution>
@@ -209,6 +212,14 @@
                             </includes>
                             <filtering>true</filtering>
                         </resource>
+                        <resource>
+                            <directory>src/main/webapp/WEB-INF/pages</directory>
+                            <targetPath>WEB-INF/pages</targetPath>
+                            <includes>
+                                <include>login.jsp</include>
+                            </includes>
+                            <filtering>true</filtering>
+                        </resource>
                     </webResources>
                 </configuration>
             </plugin>
@@ -229,6 +240,7 @@
                 <templates.filter>templates-min.properties</templates.filter>
                 <users.filter>users-min.properties</users.filter>
                 <bulletin.board.filter>bulletin-board-min.properties</bulletin.board.filter>
+                <login.filter>login-min.properties</login.filter>
                 <provenance.filter>provenance-min.properties</provenance.filter>
             </properties>
             <build>
@@ -263,11 +275,10 @@
                                                 <include>${staging.dir}/js/nf/canvas/nf-canvas-utils.js</include>
                                                 <include>${staging.dir}/js/nf/nf-dialog.js</include>
                                                 <include>${staging.dir}/js/nf/nf-shell.js</include>
-                                                <include>${staging.dir}/js/nf/canvas/nf-storage.js</include>
+                                                <include>${staging.dir}/js/nf/nf-storage.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-snippet.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-canvas-toolbox.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-custom-ui.js</include>
-                                                <include>${staging.dir}/js/nf/canvas/nf-registration.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-controller-service.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-reporting-task.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-processor-configuration.js</include>
@@ -317,6 +328,7 @@
                                                 <include>${staging.dir}/js/nf/nf-client.js</include>
                                                 <include>${staging.dir}/js/nf/nf-common.js</include>
                                                 <include>${staging.dir}/js/nf/nf-dialog.js</include>
+                                                <include>${staging.dir}/js/nf/nf-storage.js</include>
                                                 <include>${staging.dir}/js/nf/history/nf-history.js</include>
                                                 <include>${staging.dir}/js/nf/history/nf-history-table.js</include>
                                                 <include>${staging.dir}/js/nf/history/nf-history-model.js</include>
@@ -329,6 +341,7 @@
                                                 <include>${staging.dir}/js/nf/nf-client.js</include>
                                                 <include>${staging.dir}/js/nf/nf-common.js</include>
                                                 <include>${staging.dir}/js/nf/nf-dialog.js</include>
+                                                <include>${staging.dir}/js/nf/nf-storage.js</include>
                                                 <include>${staging.dir}/js/nf/provenance/nf-provenance.js</include>
                                                 <include>${staging.dir}/js/nf/provenance/nf-provenance-table.js</include>
                                             </includes>
@@ -340,6 +353,7 @@
                                                 <include>${staging.dir}/js/nf/nf-client.js</include>
                                                 <include>${staging.dir}/js/nf/nf-common.js</include>
                                                 <include>${staging.dir}/js/nf/nf-dialog.js</include>
+                                                <include>${staging.dir}/js/nf/nf-storage.js</include>
                                                 <include>${staging.dir}/js/nf/nf-processor-details.js</include>
                                                 <include>${staging.dir}/js/nf/nf-connection-details.js</include>
                                                 <include>${staging.dir}/js/nf/summary/nf-summary.js</include>
@@ -354,6 +368,7 @@
                                                 <include>${staging.dir}/js/nf/nf-client.js</include>
                                                 <include>${staging.dir}/js/nf/nf-common.js</include>
                                                 <include>${staging.dir}/js/nf/nf-dialog.js</include>
+                                                <include>${staging.dir}/js/nf/nf-storage.js</include>
                                                 <include>${staging.dir}/js/nf/counters/nf-counters.js</include>
                                                 <include>${staging.dir}/js/nf/counters/nf-counters-table.js</include>
                                             </includes>
@@ -365,6 +380,7 @@
                                                 <include>${staging.dir}/js/nf/nf-client.js</include>
                                                 <include>${staging.dir}/js/nf/nf-common.js</include>
                                                 <include>${staging.dir}/js/nf/nf-dialog.js</include>
+                                                <include>${staging.dir}/js/nf/nf-storage.js</include>
                                                 <include>${staging.dir}/js/nf/templates/nf-templates.js</include>
                                                 <include>${staging.dir}/js/nf/templates/nf-templates-table.js</include>
                                             </includes>
@@ -376,6 +392,7 @@
                                                 <include>${staging.dir}/js/nf/nf-client.js</include>
                                                 <include>${staging.dir}/js/nf/nf-common.js</include>
                                                 <include>${staging.dir}/js/nf/nf-dialog.js</include>
+                                                <include>${staging.dir}/js/nf/nf-storage.js</include>
                                                 <include>${staging.dir}/js/nf/cluster/nf-cluster.js</include>
                                                 <include>${staging.dir}/js/nf/cluster/nf-cluster-table.js</include>
                                             </includes>
@@ -387,6 +404,7 @@
                                                 <include>${staging.dir}/js/nf/nf-client.js</include>
                                                 <include>${staging.dir}/js/nf/nf-common.js</include>
                                                 <include>${staging.dir}/js/nf/nf-dialog.js</include>
+                                                <include>${staging.dir}/js/nf/nf-storage.js</include>
                                                 <include>${staging.dir}/js/nf/users/nf-users.js</include>
                                                 <include>${staging.dir}/js/nf/users/nf-users-table.js</include>
                                             </includes>
@@ -398,11 +416,23 @@
                                                 <include>${staging.dir}/js/nf/nf-client.js</include>
                                                 <include>${staging.dir}/js/nf/nf-common.js</include>
                                                 <include>${staging.dir}/js/nf/nf-dialog.js</include>
+                                                <include>${staging.dir}/js/nf/nf-storage.js</include>
                                                 <include>${staging.dir}/js/nf/bulletin-board/nf-bulletin-board.js</include>
                                             </includes>
                                         </aggregation>
                                         <aggregation>
                                             <insertNewLine>true</insertNewLine>
+                                            <output>${project.build.directory}/${project.build.finalName}/js/nf/login/nf-login-all.js</output>
+                                            <includes>
+                                                <include>${staging.dir}/js/nf/nf-client.js</include>
+                                                <include>${staging.dir}/js/nf/nf-common.js</include>
+                                                <include>${staging.dir}/js/nf/nf-dialog.js</include>
+                                                <include>${staging.dir}/js/nf/nf-storage.js</include>
+                                                <include>${staging.dir}/js/nf/login/nf-login.js</include>
+                                            </includes>
+                                        </aggregation>
+                                        <aggregation>
+                                            <insertNewLine>true</insertNewLine>
                                             <output>${project.build.directory}/${project.build.finalName}/css/nf-canvas-all.css</output>
                                             <includes>
                                                 <include>${staging.dir}/css/reporting-task.css</include>
@@ -418,7 +448,6 @@
                                                 <include>${staging.dir}/css/connection-configuration.css</include>
                                                 <include>${staging.dir}/css/connection-details.css</include>
                                                 <include>${staging.dir}/css/shell.css</include>
-                                                <include>${staging.dir}/css/registration.css</include>
                                                 <include>${staging.dir}/css/dialog.css</include>
                                                 <include>${staging.dir}/css/new-processor-dialog.css</include>
                                                 <include>${staging.dir}/css/new-controller-service-dialog.css</include>
@@ -517,6 +546,16 @@
                                                 <include>${staging.dir}/css/bulletin-board.css</include>
                                             </includes>
                                         </aggregation>
+                                        <aggregation>
+                                            <insertNewLine>true</insertNewLine>
+                                            <output>${project.build.directory}/${project.build.finalName}/css/nf-login-all.css</output>
+                                            <includes>
+                                                <include>${staging.dir}/css/main.css</include>
+                                                <include>${staging.dir}/css/banner.css</include>
+                                                <include>${staging.dir}/css/dialog.css</include>
+                                                <include>${staging.dir}/css/login.css</include>
+                                            </includes>
+                                        </aggregation>
                                     </aggregations>
                                 </configuration>
                             </execution>
@@ -556,6 +595,8 @@
                                 css/nf-users-all.css.gz,
                                 css/nf-bulletin-board-all.css,
                                 css/nf-bulletin-board-all.css.gz,
+                                css/nf-login-all.css,
+                                css/nf-login-all.css.gz,
                                 js/*,
                                 js/d3/**/*,
                                 js/codemirror/**/*,
@@ -584,6 +625,8 @@
                                 js/nf/users/nf-users-all.js.gz,
                                 js/nf/bulletin-board/nf-bulletin-board-all.js,
                                 js/nf/bulletin-board/nf-bulletin-board-all.js.gz,
+                                js/nf/login/nf-login-all.js,
+                                js/nf/login/nf-login-all.js.gz,
                                 images/*,
                                 resources/*,
                                 images/*,
@@ -615,7 +658,6 @@
             the application classpath or the maven jetty plugin classpath defined
             above.
         -->
-        
         <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
@@ -651,6 +693,5 @@
             <artifactId>javax.servlet.jsp.jstl-api</artifactId>
             <scope>provided</scope>
         </dependency>
-        
     </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/bulletin-board.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/bulletin-board.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/bulletin-board.properties
index 55df244..96a550f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/bulletin-board.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/bulletin-board.properties
@@ -16,6 +16,7 @@
 nf.bulletin.board.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-common.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/bulletin-board/nf-bulletin-board.js?${project.version}"></script>
 nf.bulletin.board.style.tags=<link rel="stylesheet" href="css/reset.css?${project.version}" type="text/css" />\n\
 <link rel="stylesheet" href="css/main.css?${project.version}" type="text/css" />\n\

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
index fd2bc17..bf61846 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
@@ -19,11 +19,10 @@ nf.canvas.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?
 <script type="text/javascript" src="js/nf/canvas/nf-canvas-utils.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-shell.js?${project.version}"></script>\n\
-<script type="text/javascript" src="js/nf/canvas/nf-storage.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-snippet.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-canvas-toolbox.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-custom-ui.js?${project.version}"></script>\n\
-<script type="text/javascript" src="js/nf/canvas/nf-registration.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-controller-service.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-reporting-task.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-processor-configuration.js?${project.version}"></script>\n\

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/cluster.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/cluster.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/cluster.properties
index f1481d8..9327be6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/cluster.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/cluster.properties
@@ -16,6 +16,7 @@
 nf.cluster.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-common.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/cluster/nf-cluster.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/cluster/nf-cluster-table.js?${project.version}"></script>
 nf.cluster.style.tags=<link rel="stylesheet" href="css/reset.css?${project.version}" type="text/css" />\n\

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/counters.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/counters.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/counters.properties
index 8b07e57..be22f55 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/counters.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/counters.properties
@@ -16,6 +16,7 @@
 nf.counters.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-common.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/counters/nf-counters.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/counters/nf-counters-table.js?${project.version}"></script>
 nf.counters.style.tags=<link rel="stylesheet" href="css/reset.css?${project.version}" type="text/css" />\n\

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/history.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/history.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/history.properties
index 818ee58..9bbe640 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/history.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/history.properties
@@ -16,6 +16,7 @@
 nf.history.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-common.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/history/nf-history.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/history/nf-history-table.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/history/nf-history-model.js?${project.version}"></script>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/login-min.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/login-min.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/login-min.properties
new file mode 100644
index 0000000..4dafb02
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/login-min.properties
@@ -0,0 +1,18 @@
+# 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.
+
+nf.login.script.tags=<script type="text/javascript" src="js/nf/login/nf-login-all.js?${project.version}"></script>
+nf.login.style.tags=<link rel="stylesheet" href="css/nf-login-all.css?${project.version}" type="text/css" />\n\
+<link rel="stylesheet" href="css/message-pane.css?${project.version}" type="text/css" />
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/login.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/login.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/login.properties
new file mode 100644
index 0000000..28ff4b9
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/login.properties
@@ -0,0 +1,25 @@
+# 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.
+
+nf.login.script.tags=<script type="text/javascript" src="js/nf/nf-common.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/login/nf-login.js?${project.version}"></script>
+nf.login.style.tags=<link rel="stylesheet" href="css/reset.css?${project.version}" type="text/css" />\n\
+<link rel="stylesheet" href="css/main.css?${project.version}" type="text/css" />\n\
+<link rel="stylesheet" href="css/banner.css?${project.version}" type="text/css" />\n\
+<link rel="stylesheet" href="css/dialog.css?${project.version}" type="text/css" />\n\
+<link rel="stylesheet" href="css/message-pane.css?${project.version}" type="text/css" />\n\
+<link rel="stylesheet" href="css/login.css?${project.version}" type="text/css" />
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties
index 655d3bf..daa7c43 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties
@@ -16,6 +16,7 @@
 nf.provenance.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-common.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/provenance/nf-provenance.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/provenance/nf-provenance-table.js?${project.version}"></script>
 nf.provenance.style.tags=<link rel="stylesheet" href="css/reset.css?${project.version}" type="text/css" />\n\

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties
index 90d35d9..2417ade 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties
@@ -16,6 +16,7 @@
 nf.summary.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-common.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-processor-details.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-connection-details.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/summary/nf-summary.js?${project.version}"></script>\n\

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/templates.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/templates.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/templates.properties
index 79c59e7..007bd69 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/templates.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/templates.properties
@@ -16,6 +16,7 @@
 nf.templates.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-common.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/templates/nf-templates.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/templates/nf-templates-table.js?${project.version}"></script>
 nf.templates.style.tags=<link rel="stylesheet" href="css/reset.css?${project.version}" type="text/css" />\n\

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users.properties
index a4e586a..9824a8b 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users.properties
@@ -16,6 +16,7 @@
 nf.users.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-common.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/users/nf-users.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/users/nf-users-table.js?${project.version}"></script>
 nf.users.style.tags=<link rel="stylesheet" href="css/reset.css?${project.version}" type="text/css" />\n\

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/bulletin-board.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/bulletin-board.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/bulletin-board.jsp
index 6df0e51..3eaa225 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/bulletin-board.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/bulletin-board.jsp
@@ -28,6 +28,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.center.js"></script>
         <script type="text/javascript" src="js/jquery/combo/jquery.combo.js?${project.version}"></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/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
index 1bf63a6..f9970df 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
@@ -40,8 +40,8 @@
         <script type="text/javascript" src="js/codemirror/lib/codemirror-compressed.js"></script>
         <script type="text/javascript" src="js/jquery/jquery-2.1.1.min.js"></script>
         <script type="text/javascript" src="js/jquery/ui-smoothness/jquery-ui-1.10.4.min.js"></script>
+        <script type="text/javascript" src="js/jquery/jquery.base64.js"></script>
         <script type="text/javascript" src="js/jquery/jquery.center.js"></script>
-        <script type="text/javascript" src="js/jquery/jquery.count.js"></script>
         <script type="text/javascript" src="js/jquery/jquery.ellipsis.js"></script>
         <script type="text/javascript" src="js/jquery/jquery.each.js"></script>
         <script type="text/javascript" src="js/jquery/jquery.tab.js"></script>
@@ -73,7 +73,6 @@
             <img id="splash-img" src="images/loadAnimation.gif" alt="Loading..."/>
         </div>
         <jsp:include page="/WEB-INF/partials/message-pane.jsp"/>
-        <jsp:include page="/WEB-INF/partials/canvas/registration.jsp"/>
         <jsp:include page="/WEB-INF/partials/banners-main.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/canvas-header.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/about-dialog.jsp"/>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp
index 55ffabe..acf2e55 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp
@@ -30,6 +30,7 @@
         <link rel="stylesheet" href="js/jquery/slickgrid/css/slick.grid.css" type="text/css" />
         <link rel="stylesheet" href="js/jquery/slickgrid/css/slick-default-theme.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.center.js"></script>
         <script type="text/javascript" src="js/jquery/combo/jquery.combo.js?${project.version}"></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/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp
index 7362cf2..e3113f9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp
@@ -30,6 +30,7 @@
         <link rel="stylesheet" href="js/jquery/slickgrid/css/slick.grid.css" type="text/css" />
         <link rel="stylesheet" href="js/jquery/slickgrid/css/slick-default-theme.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.center.js"></script>
         <script type="text/javascript" src="js/jquery/combo/jquery.combo.js?${project.version}"></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/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp
index 935512a..ac28bb7 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp
@@ -30,6 +30,7 @@
         <link rel="stylesheet" href="js/jquery/slickgrid/css/slick.grid.css" type="text/css" />
         <link rel="stylesheet" href="js/jquery/slickgrid/css/slick-default-theme.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.center.js"></script>
         <script type="text/javascript" src="js/jquery/modal/jquery.modal.js?${project.version}"></script>
         <script type="text/javascript" src="js/jquery/combo/jquery.combo.js?${project.version}"></script>


[27/51] [abbrv] nifi git commit: NIFI-655: - Refactoring web security to use Spring Security Java Configuration. - Introducing security in Web UI in order to get JWT.

Posted by mc...@apache.org.
http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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
new file mode 100644
index 0000000..9635354
--- /dev/null
+++ 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
@@ -0,0 +1,162 @@
+/*
+ * 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.jwt;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.ExpiredJwtException;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwsHeader;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.MalformedJwtException;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.SignatureException;
+import io.jsonwebtoken.SigningKeyResolverAdapter;
+import io.jsonwebtoken.UnsupportedJwtException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.admin.service.AdministrationException;
+import org.apache.nifi.admin.service.KeyService;
+import org.apache.nifi.web.security.token.LoginAuthenticationToken;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Calendar;
+import org.apache.nifi.key.Key;
+
+/**
+ *
+ */
+public class JwtService {
+    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(JwtService.class);
+
+    private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
+    private static final String KEY_ID_CLAIM = "kid";
+    private static final String USERNAME_CLAIM = "preferred_username";
+
+    private final KeyService keyService;
+
+    public JwtService(final KeyService keyService) {
+        this.keyService = keyService;
+    }
+
+    public String getAuthenticationFromToken(final String base64EncodedToken) throws JwtException {
+        // The library representations of the JWT should be kept internal to this service.
+        try {
+            final Jws<Claims> jws = parseTokenFromBase64EncodedString(base64EncodedToken);
+
+            if (jws == null) {
+                throw new JwtException("Unable to parse token");
+            }
+
+            // Additional validation that subject is present
+            if (StringUtils.isEmpty(jws.getBody().getSubject())) {
+                throw new JwtException("No subject available in token");
+            }
+
+            // TODO: Validate issuer against active registry?
+            if (StringUtils.isEmpty(jws.getBody().getIssuer())) {
+                // TODO: Remove after testing
+//                logger.info("Decoded JWT payload: " + jws.toString());
+                throw new JwtException("No issuer available in token");
+            }
+            return jws.getBody().getSubject();
+        } catch (JwtException e) {
+            logger.debug("The Base64 encoded JWT: " + base64EncodedToken);
+            final String errorMessage = "There was an error validating the JWT";
+            logger.error(errorMessage, e);
+            throw e;
+        }
+    }
+
+    private Jws<Claims> parseTokenFromBase64EncodedString(final String base64EncodedToken) throws JwtException {
+        try {
+            return Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() {
+                @Override
+                public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {
+                    final String identity = claims.getSubject();
+
+                    // Get the key based on the key id in the claims
+                    final Integer keyId = claims.get(KEY_ID_CLAIM, Integer.class);
+                    final Key key = keyService.getKey(keyId);
+
+                    // Ensure we were able to find a key that was previously issued by this key service for this user
+                    if (key == null || key.getKey() == null) {
+                        throw new UnsupportedJwtException("Unable to determine signing key for " + identity + " [kid: " + keyId + "]");
+                    }
+
+                    return key.getKey().getBytes(StandardCharsets.UTF_8);
+                }
+            }).parseClaimsJws(base64EncodedToken);
+        } catch (final MalformedJwtException | UnsupportedJwtException | SignatureException | ExpiredJwtException | IllegalArgumentException | AdministrationException e) {
+            // TODO: Exercise all exceptions to ensure none leak key material to logs
+            final String errorMessage = "There was an error validating the JWT";
+            throw new JwtException(errorMessage, e);
+        }
+    }
+
+    /**
+     * Generates a signed JWT token from the provided (Spring Security) login authentication token.
+     *
+     * @param authenticationToken an instance of the Spring Security token after login credentials have been verified against the respective information source
+     * @return a signed JWT containing the user identity and the identity provider, Base64-encoded
+     * @throws JwtException if there is a problem generating the signed token
+     */
+    public String generateSignedToken(final LoginAuthenticationToken authenticationToken) throws JwtException {
+        if (authenticationToken == null) {
+            throw new IllegalArgumentException("Cannot generate a JWT for a null authentication token");
+        }
+
+        // Set expiration from the token
+        final Calendar expiration = Calendar.getInstance();
+        expiration.setTimeInMillis(authenticationToken.getExpiration());
+
+        final Object principal = authenticationToken.getPrincipal();
+        if (principal == null || StringUtils.isEmpty(principal.toString())) {
+            final String errorMessage = "Cannot generate a JWT for a token with an empty identity issued by " + authenticationToken.getIssuer();
+            logger.error(errorMessage);
+            throw new JwtException(errorMessage);
+        }
+
+        // Create a JWT with the specified authentication
+        final String identity = principal.toString();
+        final String username = authenticationToken.getName();
+
+        try {
+            // Get/create the key for this user
+            final Key key = keyService.getOrCreateKey(identity);
+            final byte[] keyBytes = key.getKey().getBytes(StandardCharsets.UTF_8);
+
+            logger.trace("Generating JWT for " + authenticationToken);
+
+            // TODO: Implement "jti" claim with nonce to prevent replay attacks and allow blacklisting of revoked tokens
+
+            // Build the token
+            return Jwts.builder().setSubject(identity)
+                    .setIssuer(authenticationToken.getIssuer())
+                    .setAudience(authenticationToken.getIssuer())
+                    .claim(USERNAME_CLAIM, username)
+                    .claim(KEY_ID_CLAIM, key.getId())
+                    .setExpiration(expiration.getTime())
+                    .setIssuedAt(Calendar.getInstance().getTime())
+                    .signWith(SIGNATURE_ALGORITHM, keyBytes).compact();
+        } catch (NullPointerException | AdministrationException e) {
+            final String errorMessage = "Could not retrieve the signing key for JWT for " + identity;
+            logger.error(errorMessage, e);
+            throw new JwtException(errorMessage, e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.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/node/NodeAuthorizedUserFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java
new file mode 100644
index 0000000..a3e6c3c
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java
@@ -0,0 +1,127 @@
+/*
+ * 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.node;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.security.cert.X509Certificate;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.nifi.controller.FlowController;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.apache.nifi.web.security.user.NiFiUserDetails;
+import org.apache.nifi.user.NiFiUser;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.token.NiFiAuthorizationToken;
+import org.apache.nifi.web.security.x509.X509CertificateExtractor;
+import org.apache.nifi.web.security.x509.X509IdentityProvider;
+import org.apache.nifi.web.util.WebUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+import org.springframework.web.filter.GenericFilterBean;
+
+/**
+ * Custom filter to extract a user's authorities from the request where the user was authenticated by the cluster manager and populate the threadlocal with the authorized user. If the request contains
+ * the appropriate header with authorities and the application instance is a node connected to the cluster, then the authentication/authorization steps remaining in the filter chain are skipped.
+ *
+ * Checking if the application instance is a connected node is important because it prevents external clients from faking the request headers and bypassing the authentication processing chain.
+ */
+public class NodeAuthorizedUserFilter extends GenericFilterBean {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(NodeAuthorizedUserFilter.class);
+
+    public static final String PROXY_USER_DETAILS = "X-ProxiedEntityUserDetails";
+
+    private NiFiProperties properties;
+    private X509CertificateExtractor certificateExtractor;
+    private X509IdentityProvider certificateIdentityProvider;
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+
+        // get the proxied user's authorities
+        final String hexEncodedUserDetails = httpServletRequest.getHeader(PROXY_USER_DETAILS);
+
+        // check if the request has the necessary header information and this instance is configured as a node
+        if (StringUtils.isNotBlank(hexEncodedUserDetails) && properties.isNode()) {
+
+            // get the flow controller from the Spring context
+            final ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
+            final FlowController flowController = ctx.getBean("flowController", FlowController.class);
+
+            // check that we are connected to the cluster
+            if (flowController.getNodeId() != null) {
+                try {
+                    // attempt to extract the client certificate
+                    final X509Certificate[] certificate = certificateExtractor.extractClientCertificate(httpServletRequest);
+                    if (certificate != null) {
+                        // authenticate the certificate
+                        final AuthenticationResponse authenticationResponse = certificateIdentityProvider.authenticate(certificate);
+
+                        // only consider the pre-authorized user when the request came directly from the NCM according to the DN in the certificate
+                        final String clusterManagerIdentity = flowController.getClusterManagerDN();
+                        if (clusterManagerIdentity != null && clusterManagerIdentity.equals(authenticationResponse.getIdentity())) {
+                            // deserialize hex encoded object
+                            final Serializable userDetailsObj = WebUtils.deserializeHexToObject(hexEncodedUserDetails);
+
+                            // if we have a valid object, set the authentication token and bypass the remaining authentication processing chain
+                            if (userDetailsObj instanceof NiFiUserDetails) {
+                                final NiFiUserDetails userDetails = (NiFiUserDetails) userDetailsObj;
+                                final NiFiUser user = userDetails.getNiFiUser();
+
+                                // log the request attempt - response details will be logged later
+                                logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)", user.getIdentity(), httpServletRequest.getMethod(),
+                                        httpServletRequest.getRequestURL().toString(), request.getRemoteAddr()));
+
+                                // create the authorized nifi token
+                                final NiFiAuthorizationToken token = new NiFiAuthorizationToken(userDetails);
+                                SecurityContextHolder.getContext().setAuthentication(token);
+                            }
+                        }
+                    }
+                } catch (final ClassNotFoundException cnfe) {
+                    LOGGER.warn("Classpath issue detected because failed to deserialize authorized user in request header due to: " + cnfe, cnfe);
+                } catch (final IllegalArgumentException iae) {
+                    // unable to authenticate a serialized user from the incoming request
+                }
+            }
+        }
+
+        chain.doFilter(request, response);
+    }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    public void setCertificateIdentityProvider(X509IdentityProvider certificateIdentityProvider) {
+        this.certificateIdentityProvider = certificateIdentityProvider;
+    }
+
+    public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
+        this.certificateExtractor = certificateExtractor;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.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/spring/LoginIdentityProviderFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
new file mode 100644
index 0000000..92a27ae
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
@@ -0,0 +1,312 @@
+/*
+ * 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.spring;
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.apache.nifi.authentication.LoginCredentials;
+import org.apache.nifi.authentication.LoginIdentityProvider;
+import org.apache.nifi.authentication.LoginIdentityProviderConfigurationContext;
+import org.apache.nifi.authentication.LoginIdentityProviderInitializationContext;
+import org.apache.nifi.authentication.LoginIdentityProviderLookup;
+import org.apache.nifi.authentication.annotation.LoginIdentityProviderContext;
+import org.apache.nifi.authentication.generated.LoginIdentityProviders;
+import org.apache.nifi.authentication.generated.Property;
+import org.apache.nifi.authentication.generated.Provider;
+import org.apache.nifi.authorization.exception.ProviderCreationException;
+import org.apache.nifi.authorization.exception.ProviderDestructionException;
+import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.NarCloseable;
+import org.apache.nifi.util.NiFiProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.FactoryBean;
+import org.xml.sax.SAXException;
+
+/**
+ *
+ */
+public class LoginIdentityProviderFactoryBean implements FactoryBean, DisposableBean, LoginIdentityProviderLookup {
+
+    private static final Logger logger = LoggerFactory.getLogger(LoginIdentityProviderFactoryBean.class);
+    private static final String LOGIN_IDENTITY_PROVIDERS_XSD = "/login-identity-providers.xsd";
+    private static final String JAXB_GENERATED_PATH = "org.apache.nifi.authentication.generated";
+    private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext();
+
+    /**
+     * Load the JAXBContext.
+     */
+    private static JAXBContext initializeJaxbContext() {
+        try {
+            return JAXBContext.newInstance(JAXB_GENERATED_PATH, LoginIdentityProviderFactoryBean.class.getClassLoader());
+        } catch (JAXBException e) {
+            throw new RuntimeException("Unable to create JAXBContext.");
+        }
+    }
+
+    private NiFiProperties properties;
+    private LoginIdentityProvider loginIdentityProvider;
+    private final Map<String, LoginIdentityProvider> loginIdentityProviders = new HashMap<>();
+
+    @Override
+    public LoginIdentityProvider getLoginIdentityProvider(String identifier) {
+        return loginIdentityProviders.get(identifier);
+    }
+
+    @Override
+    public Object getObject() throws Exception {
+        if (loginIdentityProvider == null) {
+            // look up the login identity provider to use
+            final String loginIdentityProviderIdentifier = properties.getProperty(NiFiProperties.SECURITY_USER_LOGIN_IDENTITY_PROVIDER);
+
+            // ensure the login identity provider class name was specified
+            if (StringUtils.isNotBlank(loginIdentityProviderIdentifier)) {
+                final LoginIdentityProviders loginIdentityProviderConfiguration = loadLoginIdentityProvidersConfiguration();
+
+                // create each login identity provider
+                for (final Provider provider : loginIdentityProviderConfiguration.getProvider()) {
+                    loginIdentityProviders.put(provider.getIdentifier(), createLoginIdentityProvider(provider.getIdentifier(), provider.getClazz()));
+                }
+
+                // configure each login identity provider
+                for (final Provider provider : loginIdentityProviderConfiguration.getProvider()) {
+                    final LoginIdentityProvider instance = loginIdentityProviders.get(provider.getIdentifier());
+                    instance.onConfigured(loadLoginIdentityProviderConfiguration(provider));
+                }
+
+                // get the login identity provider instance
+                loginIdentityProvider = getLoginIdentityProvider(loginIdentityProviderIdentifier);
+
+                // ensure it was found
+                if (loginIdentityProvider == null) {
+                    throw new Exception(String.format("The specified login identity provider '%s' could not be found.", loginIdentityProviderIdentifier));
+                }
+            }
+        }
+
+        return loginIdentityProvider;
+    }
+
+    private LoginIdentityProviders loadLoginIdentityProvidersConfiguration() throws Exception {
+        final File loginIdentityProvidersConfigurationFile = properties.getLoginIdentityProviderConfiguraitonFile();
+
+        // load the users from the specified file
+        if (loginIdentityProvidersConfigurationFile.exists()) {
+            try {
+                // find the schema
+                final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+                final Schema schema = schemaFactory.newSchema(LoginIdentityProviders.class.getResource(LOGIN_IDENTITY_PROVIDERS_XSD));
+
+                // attempt to unmarshal
+                final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller();
+                unmarshaller.setSchema(schema);
+                final JAXBElement<LoginIdentityProviders> element = unmarshaller.unmarshal(new StreamSource(loginIdentityProvidersConfigurationFile), LoginIdentityProviders.class);
+                return element.getValue();
+            } catch (SAXException | JAXBException e) {
+                throw new Exception("Unable to load the login identity provider configuration file at: " + loginIdentityProvidersConfigurationFile.getAbsolutePath());
+            }
+        } else {
+            throw new Exception("Unable to find the login identity provider configuration file at " + loginIdentityProvidersConfigurationFile.getAbsolutePath());
+        }
+    }
+
+    private LoginIdentityProvider createLoginIdentityProvider(final String identifier, final String loginIdentityProviderClassName) throws Exception {
+        // get the classloader for the specified login identity provider
+        final ClassLoader loginIdentityProviderClassLoader = ExtensionManager.getClassLoader(loginIdentityProviderClassName);
+        if (loginIdentityProviderClassLoader == null) {
+            throw new Exception(String.format("The specified login identity provider class '%s' is not known to this nifi.", loginIdentityProviderClassName));
+        }
+
+        // get the current context classloader
+        final ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
+
+        final LoginIdentityProvider instance;
+        try {
+            // set the appropriate class loader
+            Thread.currentThread().setContextClassLoader(loginIdentityProviderClassLoader);
+
+            // attempt to load the class
+            Class<?> rawLoginIdentityProviderClass = Class.forName(loginIdentityProviderClassName, true, loginIdentityProviderClassLoader);
+            Class<? extends LoginIdentityProvider> loginIdentityProviderClass = rawLoginIdentityProviderClass.asSubclass(LoginIdentityProvider.class);
+
+            // otherwise create a new instance
+            Constructor constructor = loginIdentityProviderClass.getConstructor();
+            instance = (LoginIdentityProvider) constructor.newInstance();
+
+            // method injection
+            performMethodInjection(instance, loginIdentityProviderClass);
+
+            // field injection
+            performFieldInjection(instance, loginIdentityProviderClass);
+
+            // call post construction lifecycle event
+            instance.initialize(new StandardLoginIdentityProviderInitializationContext(identifier, this));
+        } finally {
+            if (currentClassLoader != null) {
+                Thread.currentThread().setContextClassLoader(currentClassLoader);
+            }
+        }
+
+        return withNarLoader(instance);
+    }
+
+    private LoginIdentityProviderConfigurationContext loadLoginIdentityProviderConfiguration(final Provider provider) {
+        final Map<String, String> providerProperties = new HashMap<>();
+
+        for (final Property property : provider.getProperty()) {
+            providerProperties.put(property.getName(), property.getValue());
+        }
+
+        return new StandardLoginIdentityProviderConfigurationContext(provider.getIdentifier(), providerProperties);
+    }
+
+    private void performMethodInjection(final LoginIdentityProvider instance, final Class loginIdentityProviderClass)
+            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+
+        for (final Method method : loginIdentityProviderClass.getMethods()) {
+            if (method.isAnnotationPresent(LoginIdentityProviderContext.class)) {
+                // make the method accessible
+                final boolean isAccessible = method.isAccessible();
+                method.setAccessible(true);
+
+                try {
+                    final Class<?>[] argumentTypes = method.getParameterTypes();
+
+                    // look for setters (single argument)
+                    if (argumentTypes.length == 1) {
+                        final Class<?> argumentType = argumentTypes[0];
+
+                        // look for well known types
+                        if (NiFiProperties.class.isAssignableFrom(argumentType)) {
+                            // nifi properties injection
+                            method.invoke(instance, properties);
+                        }
+                    }
+                } finally {
+                    method.setAccessible(isAccessible);
+                }
+            }
+        }
+
+        final Class parentClass = loginIdentityProviderClass.getSuperclass();
+        if (parentClass != null && LoginIdentityProvider.class.isAssignableFrom(parentClass)) {
+            performMethodInjection(instance, parentClass);
+        }
+    }
+
+    private void performFieldInjection(final LoginIdentityProvider instance, final Class loginIdentityProviderClass) throws IllegalArgumentException, IllegalAccessException {
+        for (final Field field : loginIdentityProviderClass.getDeclaredFields()) {
+            if (field.isAnnotationPresent(LoginIdentityProviderContext.class)) {
+                // make the method accessible
+                final boolean isAccessible = field.isAccessible();
+                field.setAccessible(true);
+
+                try {
+                    // get the type
+                    final Class<?> fieldType = field.getType();
+
+                    // only consider this field if it isn't set yet
+                    if (field.get(instance) == null) {
+                        // look for well known types
+                        if (NiFiProperties.class.isAssignableFrom(fieldType)) {
+                            // nifi properties injection
+                            field.set(instance, properties);
+                        }
+                    }
+
+                } finally {
+                    field.setAccessible(isAccessible);
+                }
+            }
+        }
+
+        final Class parentClass = loginIdentityProviderClass.getSuperclass();
+        if (parentClass != null && LoginIdentityProvider.class.isAssignableFrom(parentClass)) {
+            performFieldInjection(instance, parentClass);
+        }
+    }
+
+    private LoginIdentityProvider withNarLoader(final LoginIdentityProvider baseProvider) {
+        return new LoginIdentityProvider() {
+
+            @Override
+            public AuthenticationResponse authenticate(LoginCredentials credentials) {
+                try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
+                    return baseProvider.authenticate(credentials);
+                }
+            }
+
+            @Override
+            public void initialize(LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException {
+                try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
+                    baseProvider.initialize(initializationContext);
+                }
+            }
+
+            @Override
+            public void onConfigured(LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException {
+                try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
+                    baseProvider.onConfigured(configurationContext);
+                }
+            }
+
+            @Override
+            public void preDestruction() throws ProviderDestructionException {
+                try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
+                    baseProvider.preDestruction();
+                }
+            }
+        };
+    }
+
+    @Override
+    public Class getObjectType() {
+        return LoginIdentityProvider.class;
+    }
+
+    @Override
+    public boolean isSingleton() {
+        return true;
+    }
+
+    @Override
+    public void destroy() throws Exception {
+        if (loginIdentityProvider != null) {
+            loginIdentityProvider.preDestruction();
+        }
+    }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/StandardLoginIdentityProviderConfigurationContext.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/spring/StandardLoginIdentityProviderConfigurationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/StandardLoginIdentityProviderConfigurationContext.java
new file mode 100644
index 0000000..5c662c7
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/StandardLoginIdentityProviderConfigurationContext.java
@@ -0,0 +1,51 @@
+/*
+ * 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.spring;
+
+import java.util.Collections;
+import java.util.Map;
+import org.apache.nifi.authentication.LoginIdentityProviderConfigurationContext;
+
+/**
+ *
+ */
+public class StandardLoginIdentityProviderConfigurationContext implements LoginIdentityProviderConfigurationContext {
+
+    private final String identifier;
+    private final Map<String, String> properties;
+
+    public StandardLoginIdentityProviderConfigurationContext(String identifier, Map<String, String> properties) {
+        this.identifier = identifier;
+        this.properties = properties;
+    }
+
+    @Override
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public Map<String, String> getProperties() {
+        return Collections.unmodifiableMap(properties);
+    }
+
+    @Override
+    public String getProperty(String property) {
+        return properties.get(property);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/StandardLoginIdentityProviderInitializationContext.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/spring/StandardLoginIdentityProviderInitializationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/StandardLoginIdentityProviderInitializationContext.java
new file mode 100644
index 0000000..af54df9
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/StandardLoginIdentityProviderInitializationContext.java
@@ -0,0 +1,45 @@
+/*
+ * 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.spring;
+
+import org.apache.nifi.authentication.LoginIdentityProviderInitializationContext;
+import org.apache.nifi.authentication.LoginIdentityProviderLookup;
+
+/**
+ *
+ */
+public class StandardLoginIdentityProviderInitializationContext implements LoginIdentityProviderInitializationContext {
+
+    private final String identifier;
+    private final LoginIdentityProviderLookup lookup;
+
+    public StandardLoginIdentityProviderInitializationContext(String identifier, final LoginIdentityProviderLookup lookup) {
+        this.identifier = identifier;
+        this.lookup = lookup;
+    }
+
+    @Override
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public LoginIdentityProviderLookup getAuthorityProviderLookup() {
+        return lookup;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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..3591239
--- /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,123 @@
+/*
+ * 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.security.util.CertificateUtils;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+/**
+ * This is an Authentication Token for logging in. Once a user is authenticated, they can be issued an ID token.
+ */
+public class LoginAuthenticationToken extends AbstractAuthenticationToken {
+
+    private final String identity;
+    private final String username;
+    private final long expiration;
+    private final String issuer;
+
+    /**
+     * Creates a representation of the authentication token for a user.
+     *
+     * @param identity   The unique identifier for this user
+     * @param expiration The relative time to expiration in milliseconds
+     * @param issuer     The IdentityProvider implementation that generated this token
+     */
+    public LoginAuthenticationToken(final String identity, final long expiration, final String issuer) {
+        this(identity, null, expiration, issuer);
+    }
+
+    /**
+     * Creates a representation of the authentication token for a user.
+     *
+     * @param identity   The unique identifier for this user (cannot be null or empty)
+     * @param username   The preferred username for this user
+     * @param expiration The relative time to expiration in milliseconds
+     * @param issuer     The IdentityProvider implementation that generated this token
+     */
+    public LoginAuthenticationToken(final String identity, final String username, final long expiration, final String issuer) {
+        super(null);
+        setAuthenticated(true);
+        this.identity = identity;
+        this.username = username;
+        this.issuer = issuer;
+        Calendar now = Calendar.getInstance();
+        this.expiration = now.getTimeInMillis() + expiration;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return null;
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return identity;
+    }
+
+    /**
+     * Returns the expiration instant in milliseconds. This value is an absolute point in time (i.e. Nov
+     * 16, 2015 11:30:00.000 GMT), not a relative time (i.e. 60 minutes). It is calculated by adding the
+     * relative expiration from the constructor to the timestamp at object creation.
+     *
+     * @return the expiration in millis
+     */
+    public long getExpiration() {
+        return expiration;
+    }
+
+    public String getIssuer() {
+        return issuer;
+    }
+
+    @Override
+    public String getName() {
+        if (username == null) {
+            // if the username is a DN this will extract the username or CN... if not will return what was passed
+            return CertificateUtils.extractUsername(identity);
+        } else {
+            return username;
+        }
+    }
+
+    @Override
+    public String toString() {
+        Calendar expirationTime = Calendar.getInstance();
+        expirationTime.setTimeInMillis(getExpiration());
+        long remainingTime = expirationTime.getTimeInMillis() - Calendar.getInstance().getTimeInMillis();
+
+        SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss.SSS");
+        dateFormat.setTimeZone(expirationTime.getTimeZone());
+        String expirationTimeString = dateFormat.format(expirationTime.getTime());
+
+        return new StringBuilder("LoginAuthenticationToken for ")
+                .append(getName())
+                .append(" issued by ")
+                .append(getIssuer())
+                .append(" expiring at ")
+                .append(expirationTimeString)
+                .append(" [")
+                .append(getExpiration())
+                .append(" ms, ")
+                .append(remainingTime)
+                .append(" ms remaining]")
+                .toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationRequestToken.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/NewAccountAuthenticationRequestToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationRequestToken.java
new file mode 100644
index 0000000..6fee4ec
--- /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/NewAccountAuthenticationRequestToken.java
@@ -0,0 +1,40 @@
+/*
+ * 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.web.security.user.NewAccountRequest;
+
+/**
+ * This is an Authentication Token for a user that is requesting authentication in order to submit a new account request.
+ */
+public class NewAccountAuthenticationRequestToken extends NiFiAuthenticationRequestToken {
+
+    final NewAccountRequest newAccountRequest;
+
+    public NewAccountAuthenticationRequestToken(final NewAccountRequest newAccountRequest) {
+        super(newAccountRequest.getChain());
+        this.newAccountRequest = newAccountRequest;
+    }
+
+    public String getJustification() {
+        return newAccountRequest.getJustification();
+    }
+
+    public NewAccountRequest getNewAccountRequest() {
+        return newAccountRequest;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationToken.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/NewAccountAuthenticationToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationToken.java
new file mode 100644
index 0000000..5fe3a1d
--- /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/NewAccountAuthenticationToken.java
@@ -0,0 +1,46 @@
+/*
+ * 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.web.security.user.NewAccountRequest;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+
+/**
+ * This is an Authentication Token for a user that has been authenticated but is not authorized to access the NiFi APIs. Typically, this authentication token is used successfully when requesting a
+ * NiFi account. Requesting any other endpoint would be rejected due to lack of roles.
+ */
+public class NewAccountAuthenticationToken extends AbstractAuthenticationToken {
+
+    final NewAccountRequest newAccountRequest;
+
+    public NewAccountAuthenticationToken(final NewAccountRequest newAccountRequest) {
+        super(null);
+        super.setAuthenticated(true);
+        this.newAccountRequest = newAccountRequest;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return null;
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return newAccountRequest;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthenticationRequestToken.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/NiFiAuthenticationRequestToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthenticationRequestToken.java
new file mode 100644
index 0000000..3ae6491
--- /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/NiFiAuthenticationRequestToken.java
@@ -0,0 +1,54 @@
+/*
+ * 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 java.util.Collections;
+import java.util.List;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+
+/**
+ * An authentication token that is used as an authentication request. The request chain is specified during creation and is used authenticate the user(s). If the user is authenticated, the token is
+ * used to authorized the user(s).
+ */
+public class NiFiAuthenticationRequestToken extends AbstractAuthenticationToken {
+
+    private final List<String> chain;
+
+    public NiFiAuthenticationRequestToken(final List<String> chain) {
+        super(null);
+        this.chain = chain;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return null;
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return chain;
+    }
+
+    public List<String> getChain() {
+        return Collections.unmodifiableList(chain);
+    }
+
+    @Override
+    public final void setAuthenticated(boolean authenticated) {
+        throw new IllegalArgumentException("Cannot change the authenticated state.");
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthorizationToken.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/NiFiAuthorizationToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthorizationToken.java
new file mode 100644
index 0000000..0cb0353
--- /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/NiFiAuthorizationToken.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.token;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.userdetails.UserDetails;
+
+/**
+ * An authentication token that represents an Authenticated and Authorized user of the NiFi Apis. The authorities are based off the specified UserDetails.
+ */
+public class NiFiAuthorizationToken extends AbstractAuthenticationToken {
+
+    final UserDetails nifiUserDetails;
+
+    public NiFiAuthorizationToken(final UserDetails nifiUserDetails) {
+        super(nifiUserDetails.getAuthorities());
+        super.setAuthenticated(true);
+        setDetails(nifiUserDetails);
+        this.nifiUserDetails = nifiUserDetails;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return nifiUserDetails.getPassword();
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return nifiUserDetails;
+    }
+
+    @Override
+    public final void setAuthenticated(boolean authenticated) {
+        throw new IllegalArgumentException("Cannot change the authenticated state.");
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NewAccountRequest.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/user/NewAccountRequest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NewAccountRequest.java
new file mode 100644
index 0000000..3ec147a
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NewAccountRequest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.user;
+
+import java.util.List;
+
+/**
+ *
+ */
+public class NewAccountRequest {
+
+    private final List<String> chain;
+    private final String justification;
+
+    public NewAccountRequest(final List<String> chain, final String justification) {
+        this.chain = chain;
+        this.justification = justification;
+    }
+
+    public List<String> getChain() {
+        return chain;
+    }
+
+    public String getJustification() {
+        return justification;
+    }
+
+    public String getUsername() {
+        // the end user is the first item in the chain
+        return chain.get(0);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserDetails.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/user/NiFiUserDetails.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserDetails.java
index c69b1e6..b559269 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserDetails.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserDetails.java
@@ -73,10 +73,9 @@ public class NiFiUserDetails implements UserDetails {
 
     @Override
     public String getUsername() {
-        return user.getDn();
+        return user.getIdentity();
     }
 
-    // TODO: not sure how to handle these yet
     @Override
     public boolean isAccountNonExpired() {
         return true;

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserUtils.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/user/NiFiUserUtils.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserUtils.java
index bf1fe43..341663e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserUtils.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserUtils.java
@@ -25,8 +25,7 @@ import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 
 /**
- * Utility methods for retrieving information about the current application
- * user.
+ * Utility methods for retrieving information about the current application user.
  *
  */
 public final class NiFiUserUtils {
@@ -58,8 +57,7 @@ public final class NiFiUserUtils {
     }
 
     /**
-     * Returns the current NiFiUser or null if the current user is not a
-     * NiFiUser.
+     * Returns the current NiFiUser or null if the current user is not a NiFiUser.
      *
      * @return user
      */
@@ -79,6 +77,27 @@ public final class NiFiUserUtils {
         return user;
     }
 
+    /**
+     * Returns the NewAccountRequest or null if this is not a new account request.
+     *
+     * @return new account request
+     */
+    public static NewAccountRequest getNewAccountRequest() {
+        NewAccountRequest newAccountRequest = null;
+
+        // obtain the principal in the current authentication
+        final SecurityContext context = SecurityContextHolder.getContext();
+        final Authentication authentication = context.getAuthentication();
+        if (authentication != null) {
+            Object principal = authentication.getPrincipal();
+            if (principal instanceof NewAccountRequest) {
+                newAccountRequest = (NewAccountRequest) principal;
+            }
+        }
+
+        return newAccountRequest;
+    }
+
     public static String getNiFiUserName() {
         // get the nifi user to extract the username
         NiFiUser user = NiFiUserUtils.getNiFiUser();

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.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/x509/X509AuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
index 72baecb..dd7d47e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
@@ -16,302 +16,66 @@
  */
 package org.apache.nifi.web.security.x509;
 
-import org.apache.nifi.web.security.x509.ocsp.OcspCertificateValidator;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.X509Certificate;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
+import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import org.apache.nifi.admin.service.AdministrationException;
-import org.apache.nifi.admin.service.UserService;
-import org.apache.nifi.web.security.DnUtils;
-import org.apache.nifi.web.security.UntrustedProxyException;
-import org.apache.nifi.util.NiFiProperties;
-import org.apache.commons.lang3.StringUtils;
-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.preauth.AbstractPreAuthenticatedProcessingFilter;
-import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.apache.nifi.web.security.InvalidAuthenticationException;
+import org.apache.nifi.web.security.NiFiAuthenticationFilter;
+import org.apache.nifi.web.security.ProxiedEntitiesUtils;
+import org.apache.nifi.web.security.token.NewAccountAuthenticationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.user.NewAccountRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
- * Custom X509 filter that will inspect the HTTP headers for a proxied user
- * before extracting the user details from the client certificate.
+ * Custom X509 filter that will inspect the HTTP headers for a proxied user before extracting the user details from the client certificate.
  */
-public class X509AuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {
+public class X509AuthenticationFilter extends NiFiAuthenticationFilter {
 
-    public static final String PROXY_ENTITIES_CHAIN = "X-ProxiedEntitiesChain";
-    public static final String PROXY_ENTITIES_ACCEPTED = "X-ProxiedEntitiesAccepted";
-    public static final String PROXY_ENTITIES_DETAILS = "X-ProxiedEntitiesDetails";
+    private static final Logger logger = LoggerFactory.getLogger(X509AuthenticationFilter.class);
 
-    private final X509CertificateExtractor certificateExtractor = new X509CertificateExtractor();
-    private final X509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
-    private OcspCertificateValidator certificateValidator;
-    private NiFiProperties properties;
-    private UserService userService;
+    private X509CertificateExtractor certificateExtractor;
+    private X509IdentityProvider certificateIdentityProvider;
 
     @Override
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-        final HttpServletResponse httpResponse = (HttpServletResponse) response;
-
-        // determine if this request is attempting to create a new account
-        if (isNewAccountRequest((HttpServletRequest) request)) {
-            // determine if this nifi supports new account requests
-            if (properties.getSupportNewAccountRequests()) {
-                // ensure there is a certificate in the request
-                X509Certificate certificate = certificateExtractor.extractClientCertificate((HttpServletRequest) request);
-                if (certificate != null) {
-                    // extract the principal from the certificate
-                    Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
-                    String principal = certificatePrincipal.toString();
-
-                    // log the new user account request
-                    logger.info("Requesting new user account for " + principal);
-
-                    try {
-                        // get the justification
-                        String justification = request.getParameter("justification");
-                        if (justification == null) {
-                            justification = StringUtils.EMPTY;
-                        }
-
-                        // create the pending user account
-                        userService.createPendingUserAccount(principal, justification);
-
-                        // generate a response
-                        httpResponse.setStatus(HttpServletResponse.SC_CREATED);
-                        httpResponse.setContentType("text/plain");
-
-                        // write the response message
-                        PrintWriter out = response.getWriter();
-                        out.println("Not authorized. User account created. Authorization pending.");
-                    } catch (IllegalArgumentException iae) {
-                        handleUserServiceError((HttpServletRequest) request, httpResponse, HttpServletResponse.SC_BAD_REQUEST, iae.getMessage());
-                    } catch (AdministrationException ae) {
-                        handleUserServiceError((HttpServletRequest) request, httpResponse, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ae.getMessage());
-                    }
-                } else {
-                    // can this really happen?
-                    handleMissingCertificate((HttpServletRequest) request, httpResponse);
-                }
-            } else {
-                handleUserServiceError((HttpServletRequest) request, httpResponse, HttpServletResponse.SC_NOT_FOUND, "This NiFi does not support new account requests.");
-            }
-        } else {
-            try {
-                // this not a request to create a user account - try to authorize
-                super.doFilter(request, response, chain);
-            } catch (AuthenticationException ae) {
-                // continue the filter chain since anonymous access should be supported
-                if (!properties.getNeedClientAuth()) {
-                    chain.doFilter(request, response);
-                } else {
-                    // create an appropriate response for the given exception
-                    handleUnsuccessfulAuthentication((HttpServletRequest) request, httpResponse, ae);
-                }
-            }
-        }
-    }
-
-    @Override
-    protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
-        String principal;
-
-        // extract the cert
-        X509Certificate certificate = certificateExtractor.extractClientCertificate(request);
-
-        // ensure the cert was found
-        if (certificate == null) {
+    public NiFiAuthenticationRequestToken attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
+        // only suppport x509 login when running securely
+        if (!request.isSecure()) {
             return null;
         }
 
-        // extract the principal
-        Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
-        principal = DnUtils.formatProxyDn(certificatePrincipal.toString());
-
-        try {
-            // ensure the cert is valid
-            certificate.checkValidity();
-        } 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);
-            }
+        // look for a client certificate
+        final X509Certificate[] certificates = certificateExtractor.extractClientCertificate(request);
+        if (certificates == null) {
             return null;
         }
 
-        // validate the certificate in question
+        // attempt to authenticate if certificates were found
+        final AuthenticationResponse authenticationResponse;
         try {
-            certificateValidator.validate(request);
-        } catch (final Exception e) {
-            logger.info(e.getMessage());
-            if (logger.isDebugEnabled()) {
-                logger.debug("", e);
-            }
-            return null;
+            authenticationResponse = certificateIdentityProvider.authenticate(certificates);
+        } catch (final IllegalArgumentException iae) {
+            throw new InvalidAuthenticationException(iae.getMessage(), iae);
         }
 
-        // look for a proxied user
-        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
-            principal = request.getHeader(PROXY_ENTITIES_CHAIN) + principal;
-        }
-
-        // log the request attempt - response details will be logged later
-        logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)", principal, request.getMethod(),
-                request.getRequestURL().toString(), request.getRemoteAddr()));
-
-        return principal;
-    }
-
-    @Override
-    protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
-        return certificateExtractor.extractClientCertificate(request);
-    }
-
-    @Override
-    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
-        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
-            response.setHeader(PROXY_ENTITIES_ACCEPTED, Boolean.TRUE.toString());
-        }
-        super.successfulAuthentication(request, response, authResult);
-    }
-
-    @Override
-    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
-        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
-            response.setHeader(PROXY_ENTITIES_DETAILS, failed.getMessage());
-        }
-        super.unsuccessfulAuthentication(request, response, failed);
-    }
-
-    /**
-     * Determines if the specified request is attempting to register a new user
-     * account.
-     *
-     * @param request http request
-     * @return true if new user
-     */
-    private boolean isNewAccountRequest(HttpServletRequest request) {
-        if ("POST".equalsIgnoreCase(request.getMethod())) {
-            String path = request.getPathInfo();
-            if (StringUtils.isNotBlank(path)) {
-                if ("/controller/users".equals(path)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Handles requests that were unable to be authorized.
-     *
-     * @param request request
-     * @param response response
-     * @param ae ex
-     * @throws IOException ex
-     */
-    private void handleUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException ae) throws IOException {
-        // set the response status
-        response.setContentType("text/plain");
-
-        // write the response message
-        PrintWriter out = response.getWriter();
-
-        // use the type of authentication exception to determine the response code
-        if (ae instanceof UsernameNotFoundException) {
-            if (properties.getSupportNewAccountRequests()) {
-                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-                out.println("Not authorized.");
-            } else {
-                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-                out.println("Access is denied.");
-            }
-        } else if (ae instanceof AccountStatusException) {
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println(ae.getMessage());
-        } else if (ae instanceof UntrustedProxyException) {
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println(ae.getMessage());
-        } else if (ae instanceof AuthenticationServiceException) {
-            logger.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
-            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-            out.println(String.format("Unable to authorize: %s", ae.getMessage()));
+        final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(request, authenticationResponse.getIdentity());
+        if (isNewAccountRequest(request)) {
+            return new NewAccountAuthenticationRequestToken(new NewAccountRequest(proxyChain, getJustification(request)));
         } else {
-            logger.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println("Access is denied.");
+            return new NiFiAuthenticationRequestToken(proxyChain);
         }
-
-        // log the failure
-        logger.info(String.format("Rejecting access to web api: %s", ae.getMessage()));
-
-        // optionally log the stack trace
-        if (logger.isDebugEnabled()) {
-            logger.debug(StringUtils.EMPTY, ae);
-        }
-    }
-
-    private void handleUserServiceError(HttpServletRequest request, HttpServletResponse response, int responseCode, String message) throws IOException {
-        // set the response status
-        response.setContentType("text/plain");
-        response.setStatus(responseCode);
-
-        // write the response message
-        PrintWriter out = response.getWriter();
-        out.println(message);
-
-        // log the failure
-        logger.info(String.format("Unable to process request because %s", message));
-    }
-
-    /**
-     * Handles requests that failed because they were bad input.
-     *
-     * @param request request
-     * @param response response
-     * @throws IOException ioe
-     */
-    private void handleMissingCertificate(HttpServletRequest request, HttpServletResponse response) throws IOException {
-        // set the response status
-        response.setContentType("text/plain");
-        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-
-        // write the response message
-        PrintWriter out = response.getWriter();
-        out.println("Unable to process request because the user certificate was not specified.");
-
-        // log the failure
-        logger.info("Unable to process request because the user certificate was not specified.");
     }
 
     /* setters */
-    public void setProperties(NiFiProperties properties) {
-        this.properties = properties;
-    }
-
-    public void setUserService(UserService userService) {
-        this.userService = userService;
+    public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
+        this.certificateExtractor = certificateExtractor;
     }
 
-    public void setCertificateValidator(OcspCertificateValidator certificateValidator) {
-        this.certificateValidator = certificateValidator;
+    public void setCertificateIdentityProvider(X509IdentityProvider certificateIdentityProvider) {
+        this.certificateIdentityProvider = certificateIdentityProvider;
     }
 
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.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/x509/X509CertificateExtractor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.java
index b40d5a5..98d0154 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.java
@@ -35,11 +35,11 @@ public class X509CertificateExtractor {
      * @param request http request
      * @return cert
      */
-    public X509Certificate extractClientCertificate(HttpServletRequest request) {
+    public X509Certificate[] extractClientCertificate(HttpServletRequest request) {
         X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
 
         if (certs != null && certs.length > 0) {
-            return certs[0];
+            return certs;
         }
 
         if (logger.isDebugEnabled()) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateValidator.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/x509/X509CertificateValidator.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateValidator.java
new file mode 100644
index 0000000..cb56a11
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateValidator.java
@@ -0,0 +1,58 @@
+/*
+ * 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.x509;
+
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import org.apache.nifi.web.security.x509.ocsp.CertificateStatusException;
+import org.apache.nifi.web.security.x509.ocsp.OcspCertificateValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extracts client certificates from Http requests.
+ */
+public class X509CertificateValidator {
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    private OcspCertificateValidator ocspValidator;
+
+    /**
+     * Extract the client certificate from the specified HttpServletRequest or null if none is specified.
+     *
+     * @param certificates the client certificates
+     * @throws java.security.cert.CertificateExpiredException cert is expired
+     * @throws java.security.cert.CertificateNotYetValidException cert is not yet valid
+     * @throws org.apache.nifi.web.security.x509.ocsp.CertificateStatusException ocsp validation issue
+     */
+    public void validateClientCertificate(final X509Certificate[] certificates)
+            throws CertificateExpiredException, CertificateNotYetValidException, CertificateStatusException {
+
+        // ensure the cert is valid
+        certificates[0].checkValidity();
+
+        // perform ocsp validator if necessary
+        ocspValidator.validate(certificates);
+    }
+
+    public void setOcspValidator(OcspCertificateValidator ocspValidator) {
+        this.ocspValidator = ocspValidator;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.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/x509/X509IdentityProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
new file mode 100644
index 0000000..db0b529
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
@@ -0,0 +1,94 @@
+/*
+ * 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.x509;
+
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.concurrent.TimeUnit;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
+
+/**
+ * Identity provider for extract the authenticating a ServletRequest with a X509Certificate.
+ */
+public class X509IdentityProvider {
+
+    private static final Logger logger = LoggerFactory.getLogger(X509IdentityProvider.class);
+
+    private final String issuer = getClass().getSimpleName();
+
+    private X509CertificateValidator certificateValidator;
+    private X509PrincipalExtractor principalExtractor;
+
+    /**
+     * Authenticates the specified request by checking certificate validity.
+     *
+     * @param certificates the client certificates
+     * @return an authentication response
+     * @throws IllegalArgumentException the request did not contain a valid certificate (or no certificate)
+     */
+    public AuthenticationResponse authenticate(final X509Certificate[] certificates) throws IllegalArgumentException {
+        // ensure the cert was found
+        if (certificates == null || certificates.length == 0) {
+            throw new IllegalArgumentException("The specified request does not contain a client certificate.");
+        }
+
+        // extract the principal
+        final Object certificatePrincipal = principalExtractor.extractPrincipal(certificates[0]);
+        final String principal = certificatePrincipal.toString();
+
+        try {
+            certificateValidator.validateClientCertificate(certificates);
+        } 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);
+            }
+            throw new IllegalArgumentException(message, cee);
+        } 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);
+            }
+            throw new IllegalArgumentException(message, cnyve);
+        } catch (final Exception e) {
+            logger.info(e.getMessage());
+            if (logger.isDebugEnabled()) {
+                logger.debug("", e);
+            }
+            throw new IllegalArgumentException(e.getMessage(), e);
+        }
+
+        // build the authentication response
+        return new AuthenticationResponse(principal, principal, TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS), issuer);
+    }
+
+    /* setters */
+    public void setCertificateValidator(X509CertificateValidator certificateValidator) {
+        this.certificateValidator = certificateValidator;
+    }
+
+    public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) {
+        this.principalExtractor = principalExtractor;
+    }
+
+}


[50/51] [abbrv] nifi git commit: NIFI-655: - Renaming spring tokens to avoid confusion over authentication and authorization.

Posted by mc...@apache.org.
NIFI-655:
- Renaming spring tokens to avoid confusion over authentication and authorization.

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

Branch: refs/heads/master
Commit: e22b51f3a7fee3b7079ea2007d88ffad4f60596b
Parents: 85eb8de
Author: Matt Gilman <ma...@gmail.com>
Authored: Tue Dec 1 10:08:36 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Tue Dec 1 10:08:36 2015 -0500

----------------------------------------------------------------------
 .../web/NiFiWebApiSecurityConfiguration.java    |  4 +-
 .../org/apache/nifi/web/api/AccessResource.java | 10 ++--
 .../web/security/NiFiAuthenticationFilter.java  | 10 ++--
 .../security/NiFiAuthenticationProvider.java    | 24 ++++-----
 .../authorization/NiFiAuthorizationService.java |  6 +--
 .../security/jwt/JwtAuthenticationFilter.java   | 10 ++--
 .../NewAccountAuthenticationRequestToken.java   | 40 ---------------
 .../token/NewAccountAuthenticationToken.java    | 46 -----------------
 .../NewAccountAuthorizationRequestToken.java    | 40 +++++++++++++++
 .../token/NewAccountAuthorizationToken.java     | 46 +++++++++++++++++
 .../token/NiFiAuthenticationRequestToken.java   | 54 --------------------
 .../token/NiFiAuthortizationRequestToken.java   | 54 ++++++++++++++++++++
 .../security/x509/X509AuthenticationFilter.java | 10 ++--
 .../NiFiAuthorizationServiceTest.java           |  6 +--
 14 files changed, 180 insertions(+), 180 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/e22b51f3/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 0680b74..1488aba 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
@@ -24,7 +24,7 @@ import org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter;
 import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
 import org.apache.nifi.web.security.jwt.JwtService;
 import org.apache.nifi.web.security.node.NodeAuthorizedUserFilter;
-import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
 import org.apache.nifi.web.security.x509.X509AuthenticationFilter;
 import org.apache.nifi.web.security.x509.X509CertificateExtractor;
 import org.apache.nifi.web.security.x509.X509IdentityProvider;
@@ -157,7 +157,7 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
     }
 
     @Autowired
-    public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) {
+    public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthortizationRequestToken> userDetailsService) {
         this.userDetailsService = userDetailsService;
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/e22b51f3/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
index c67a314..7bf9690 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -61,7 +61,7 @@ import org.apache.nifi.web.security.UntrustedProxyException;
 import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
 import org.apache.nifi.web.security.jwt.JwtService;
 import org.apache.nifi.web.security.token.LoginAuthenticationToken;
-import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
 import org.apache.nifi.web.security.x509.X509CertificateExtractor;
 import org.apache.nifi.web.security.x509.X509IdentityProvider;
 import org.slf4j.Logger;
@@ -93,7 +93,7 @@ public class AccessResource extends ApplicationResource {
     private X509IdentityProvider certificateIdentityProvider;
     private JwtService jwtService;
 
-    private AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService;
+    private AuthenticationUserDetailsService<NiFiAuthortizationRequestToken> userDetailsService;
 
     /**
      * Retrieves the access configuration for this NiFi.
@@ -285,7 +285,7 @@ public class AccessResource extends ApplicationResource {
      * @throws AuthenticationException if the proxy chain is not authorized
      */
     private UserDetails checkAuthorization(final List<String> proxyChain) throws AuthenticationException {
-        return userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
+        return userDetailsService.loadUserDetails(new NiFiAuthortizationRequestToken(proxyChain));
     }
 
     /**
@@ -399,7 +399,7 @@ public class AccessResource extends ApplicationResource {
     private void authorizeProxyIfNecessary(final List<String> proxyChain) throws AuthenticationException {
         if (proxyChain.size() > 1) {
             try {
-                userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
+                userDetailsService.loadUserDetails(new NiFiAuthortizationRequestToken(proxyChain));
             } catch (final UsernameNotFoundException unfe) {
                 // if a username not found exception was thrown, the proxies were authorized and now
                 // we can issue a new token to the end user which they will use to identify themselves
@@ -435,7 +435,7 @@ public class AccessResource extends ApplicationResource {
         this.certificateIdentityProvider = certificateIdentityProvider;
     }
 
-    public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) {
+    public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthortizationRequestToken> userDetailsService) {
         this.userDetailsService = userDetailsService;
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/e22b51f3/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.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/NiFiAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
index f0000f8..d63f01e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
@@ -27,7 +27,7 @@ import javax.servlet.http.HttpServletResponse;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.util.NiFiProperties;
-import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
 import org.apache.nifi.web.security.user.NiFiUserUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -82,7 +82,7 @@ public abstract class NiFiAuthenticationFilter extends GenericFilterBean {
     private void authenticate(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws IOException, ServletException {
         String dnChain = null;
         try {
-            final NiFiAuthenticationRequestToken authenticated = attemptAuthentication(request);
+            final NiFiAuthortizationRequestToken authenticated = attemptAuthentication(request);
             if (authenticated != null) {
                 dnChain = ProxiedEntitiesUtils.formatProxyDn(StringUtils.join(authenticated.getChain(), "><"));
 
@@ -118,14 +118,14 @@ public abstract class NiFiAuthenticationFilter extends GenericFilterBean {
 
     /**
      * Attempt to authenticate the client making the request. If the request does not contain an authentication attempt, this method should return null. If the request contains an authentication
-     * request, the implementation should convert it to a NiFiAuthenticationRequestToken (which is used when authorizing the client). Implementations should throw InvalidAuthenticationException when
+     * request, the implementation should convert it to a NiFiAuthorizationRequestToken (which is used when authorizing the client). Implementations should throw InvalidAuthenticationException when
      * the request contains an authentication request but it could not be authenticated.
      *
      * @param request The request
-     * @return The NiFiAuthenticationRequestToken used to later authorized the client
+     * @return The NiFiAutorizationRequestToken used to later authorized the client
      * @throws InvalidAuthenticationException If the request contained an authentication attempt, but could not authenticate
      */
-    public abstract NiFiAuthenticationRequestToken attemptAuthentication(HttpServletRequest request);
+    public abstract NiFiAuthortizationRequestToken attemptAuthentication(HttpServletRequest request);
 
     protected void successfulAuthorization(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
         if (log.isDebugEnabled()) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/e22b51f3/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.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/NiFiAuthenticationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
index eb0684b..0887901 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
@@ -16,9 +16,9 @@
  */
 package org.apache.nifi.web.security;
 
-import org.apache.nifi.web.security.token.NewAccountAuthenticationRequestToken;
-import org.apache.nifi.web.security.token.NewAccountAuthenticationToken;
-import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.token.NewAccountAuthorizationRequestToken;
+import org.apache.nifi.web.security.token.NewAccountAuthorizationToken;
+import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
 import org.apache.nifi.web.security.token.NiFiAuthorizationToken;
 import org.springframework.security.authentication.AuthenticationProvider;
 import org.springframework.security.core.Authentication;
@@ -32,29 +32,29 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
  */
 public class NiFiAuthenticationProvider implements AuthenticationProvider {
 
-    private final AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService;
+    private final AuthenticationUserDetailsService<NiFiAuthortizationRequestToken> userDetailsService;
 
-    public NiFiAuthenticationProvider(final AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) {
+    public NiFiAuthenticationProvider(final AuthenticationUserDetailsService<NiFiAuthortizationRequestToken> userDetailsService) {
         this.userDetailsService = userDetailsService;
     }
 
     @Override
     public Authentication authenticate(Authentication authentication) throws AuthenticationException {
-        final NiFiAuthenticationRequestToken request = (NiFiAuthenticationRequestToken) authentication;
+        final NiFiAuthortizationRequestToken request = (NiFiAuthortizationRequestToken) authentication;
 
         try {
             // defer to the nifi user details service to authorize the user
             final UserDetails userDetails = userDetailsService.loadUserDetails(request);
 
-            // build an authentication for accesing nifi
+            // build a token for accesing nifi
             final NiFiAuthorizationToken result = new NiFiAuthorizationToken(userDetails);
             result.setDetails(request.getDetails());
             return result;
         } catch (final UsernameNotFoundException unfe) {
-            // if the authentication request is for a new account and it could not be authorized because the user was not found,
-            // return the token so the new account could be created. this must go here toe nsure that any proxies have been authorized
+            // if the authorization request is for a new account and it could not be authorized because the user was not found,
+            // return the token so the new account could be created. this must go here to ensure that any proxies have been authorized
             if (isNewAccountAuthenticationToken(request)) {
-                return new NewAccountAuthenticationToken(((NewAccountAuthenticationRequestToken) authentication).getNewAccountRequest());
+                return new NewAccountAuthorizationToken(((NewAccountAuthorizationRequestToken) authentication).getNewAccountRequest());
             } else {
                 throw unfe;
             }
@@ -62,12 +62,12 @@ public class NiFiAuthenticationProvider implements AuthenticationProvider {
     }
 
     private boolean isNewAccountAuthenticationToken(final Authentication authentication) {
-        return NewAccountAuthenticationRequestToken.class.isAssignableFrom(authentication.getClass());
+        return NewAccountAuthorizationRequestToken.class.isAssignableFrom(authentication.getClass());
     }
 
     @Override
     public boolean supports(Class<?> authentication) {
-        return NiFiAuthenticationRequestToken.class.isAssignableFrom(authentication);
+        return NiFiAuthortizationRequestToken.class.isAssignableFrom(authentication);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/e22b51f3/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.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/authorization/NiFiAuthorizationService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
index 23d9e61..75c01bf 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
@@ -30,7 +30,7 @@ import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.security.UntrustedProxyException;
 import org.apache.nifi.web.security.user.NiFiUserDetails;
-import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.dao.DataAccessException;
@@ -44,7 +44,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
 /**
  * UserDetailsService that will verify user identity and grant user authorities.
  */
-public class NiFiAuthorizationService implements AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> {
+public class NiFiAuthorizationService implements AuthenticationUserDetailsService<NiFiAuthortizationRequestToken> {
 
     private static final Logger logger = LoggerFactory.getLogger(NiFiAuthorizationService.class);
 
@@ -63,7 +63,7 @@ public class NiFiAuthorizationService implements AuthenticationUserDetailsServic
      * @throws org.springframework.dao.DataAccessException ex
      */
     @Override
-    public synchronized UserDetails loadUserDetails(NiFiAuthenticationRequestToken request) throws UsernameNotFoundException, DataAccessException {
+    public synchronized UserDetails loadUserDetails(NiFiAuthortizationRequestToken request) throws UsernameNotFoundException, DataAccessException {
         NiFiUserDetails userDetails = null;
         final List<String> chain = new ArrayList<>(request.getChain());
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/e22b51f3/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.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/JwtAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
index 155610a..faf3cde 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
@@ -19,8 +19,8 @@ package org.apache.nifi.web.security.jwt;
 import io.jsonwebtoken.JwtException;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.web.security.NiFiAuthenticationFilter;
-import org.apache.nifi.web.security.token.NewAccountAuthenticationRequestToken;
-import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.token.NewAccountAuthorizationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
 import org.apache.nifi.web.security.user.NewAccountRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,7 +40,7 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
     private JwtService jwtService;
 
     @Override
-    public NiFiAuthenticationRequestToken attemptAuthentication(final HttpServletRequest request) {
+    public NiFiAuthortizationRequestToken attemptAuthentication(final HttpServletRequest request) {
         // only suppport jwt login when running securely
         if (!request.isSecure()) {
             return null;
@@ -66,9 +66,9 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
                 final String jwtPrincipal = jwtService.getAuthenticationFromToken(token);
 
                 if (isNewAccountRequest(request)) {
-                    return new NewAccountAuthenticationRequestToken(new NewAccountRequest(Arrays.asList(jwtPrincipal), getJustification(request)));
+                    return new NewAccountAuthorizationRequestToken(new NewAccountRequest(Arrays.asList(jwtPrincipal), getJustification(request)));
                 } else {
-                    return new NiFiAuthenticationRequestToken(Arrays.asList(jwtPrincipal));
+                    return new NiFiAuthortizationRequestToken(Arrays.asList(jwtPrincipal));
                 }
             } catch (JwtException e) {
                 throw new InvalidAuthenticationException(e.getMessage(), e);

http://git-wip-us.apache.org/repos/asf/nifi/blob/e22b51f3/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationRequestToken.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/NewAccountAuthenticationRequestToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationRequestToken.java
deleted file mode 100644
index 6fee4ec..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationRequestToken.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web.security.token;
-
-import org.apache.nifi.web.security.user.NewAccountRequest;
-
-/**
- * This is an Authentication Token for a user that is requesting authentication in order to submit a new account request.
- */
-public class NewAccountAuthenticationRequestToken extends NiFiAuthenticationRequestToken {
-
-    final NewAccountRequest newAccountRequest;
-
-    public NewAccountAuthenticationRequestToken(final NewAccountRequest newAccountRequest) {
-        super(newAccountRequest.getChain());
-        this.newAccountRequest = newAccountRequest;
-    }
-
-    public String getJustification() {
-        return newAccountRequest.getJustification();
-    }
-
-    public NewAccountRequest getNewAccountRequest() {
-        return newAccountRequest;
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/e22b51f3/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationToken.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/NewAccountAuthenticationToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationToken.java
deleted file mode 100644
index 5fe3a1d..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthenticationToken.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web.security.token;
-
-import org.apache.nifi.web.security.user.NewAccountRequest;
-import org.springframework.security.authentication.AbstractAuthenticationToken;
-
-/**
- * This is an Authentication Token for a user that has been authenticated but is not authorized to access the NiFi APIs. Typically, this authentication token is used successfully when requesting a
- * NiFi account. Requesting any other endpoint would be rejected due to lack of roles.
- */
-public class NewAccountAuthenticationToken extends AbstractAuthenticationToken {
-
-    final NewAccountRequest newAccountRequest;
-
-    public NewAccountAuthenticationToken(final NewAccountRequest newAccountRequest) {
-        super(null);
-        super.setAuthenticated(true);
-        this.newAccountRequest = newAccountRequest;
-    }
-
-    @Override
-    public Object getCredentials() {
-        return null;
-    }
-
-    @Override
-    public Object getPrincipal() {
-        return newAccountRequest;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/e22b51f3/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthorizationRequestToken.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/NewAccountAuthorizationRequestToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthorizationRequestToken.java
new file mode 100644
index 0000000..35c371d
--- /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/NewAccountAuthorizationRequestToken.java
@@ -0,0 +1,40 @@
+/*
+ * 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.web.security.user.NewAccountRequest;
+
+/**
+ * An authentication token that is used as an authorization request when submitting a new account.
+ */
+public class NewAccountAuthorizationRequestToken extends NiFiAuthortizationRequestToken {
+
+    final NewAccountRequest newAccountRequest;
+
+    public NewAccountAuthorizationRequestToken(final NewAccountRequest newAccountRequest) {
+        super(newAccountRequest.getChain());
+        this.newAccountRequest = newAccountRequest;
+    }
+
+    public String getJustification() {
+        return newAccountRequest.getJustification();
+    }
+
+    public NewAccountRequest getNewAccountRequest() {
+        return newAccountRequest;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/e22b51f3/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthorizationToken.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/NewAccountAuthorizationToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthorizationToken.java
new file mode 100644
index 0000000..de0fde6
--- /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/NewAccountAuthorizationToken.java
@@ -0,0 +1,46 @@
+/*
+ * 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.web.security.user.NewAccountRequest;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+
+/**
+ * This is an Authentication Token for a user that has been authenticated but is not authorized to access the NiFi APIs. Typically, this authentication token is used successfully when requesting a
+ * NiFi account. Requesting any other endpoint would be rejected due to lack of roles.
+ */
+public class NewAccountAuthorizationToken extends AbstractAuthenticationToken {
+
+    final NewAccountRequest newAccountRequest;
+
+    public NewAccountAuthorizationToken(final NewAccountRequest newAccountRequest) {
+        super(null);
+        super.setAuthenticated(true);
+        this.newAccountRequest = newAccountRequest;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return null;
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return newAccountRequest;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/e22b51f3/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthenticationRequestToken.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/NiFiAuthenticationRequestToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthenticationRequestToken.java
deleted file mode 100644
index 3ae6491..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthenticationRequestToken.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web.security.token;
-
-import java.util.Collections;
-import java.util.List;
-import org.springframework.security.authentication.AbstractAuthenticationToken;
-
-/**
- * An authentication token that is used as an authentication request. The request chain is specified during creation and is used authenticate the user(s). If the user is authenticated, the token is
- * used to authorized the user(s).
- */
-public class NiFiAuthenticationRequestToken extends AbstractAuthenticationToken {
-
-    private final List<String> chain;
-
-    public NiFiAuthenticationRequestToken(final List<String> chain) {
-        super(null);
-        this.chain = chain;
-    }
-
-    @Override
-    public Object getCredentials() {
-        return null;
-    }
-
-    @Override
-    public Object getPrincipal() {
-        return chain;
-    }
-
-    public List<String> getChain() {
-        return Collections.unmodifiableList(chain);
-    }
-
-    @Override
-    public final void setAuthenticated(boolean authenticated) {
-        throw new IllegalArgumentException("Cannot change the authenticated state.");
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/e22b51f3/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthortizationRequestToken.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/NiFiAuthortizationRequestToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthortizationRequestToken.java
new file mode 100644
index 0000000..a1459a4
--- /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/NiFiAuthortizationRequestToken.java
@@ -0,0 +1,54 @@
+/*
+ * 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 java.util.Collections;
+import java.util.List;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+
+/**
+ * An authentication token that is used as an authorization request. The request has already been authenticated and is now going to be authorized.
+ * The request chain is specified during creation and is used authorize the user(s).
+ */
+public class NiFiAuthortizationRequestToken extends AbstractAuthenticationToken {
+
+    private final List<String> chain;
+
+    public NiFiAuthortizationRequestToken(final List<String> chain) {
+        super(null);
+        this.chain = chain;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return null;
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return chain;
+    }
+
+    public List<String> getChain() {
+        return Collections.unmodifiableList(chain);
+    }
+
+    @Override
+    public final void setAuthenticated(boolean authenticated) {
+        throw new IllegalArgumentException("Cannot change the authenticated state.");
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/e22b51f3/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.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/x509/X509AuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
index 708b607..2c792f6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
@@ -23,8 +23,8 @@ import org.apache.nifi.authentication.AuthenticationResponse;
 import org.apache.nifi.web.security.InvalidAuthenticationException;
 import org.apache.nifi.web.security.NiFiAuthenticationFilter;
 import org.apache.nifi.web.security.ProxiedEntitiesUtils;
-import org.apache.nifi.web.security.token.NewAccountAuthenticationRequestToken;
-import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.token.NewAccountAuthorizationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
 import org.apache.nifi.web.security.user.NewAccountRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,7 +40,7 @@ public class X509AuthenticationFilter extends NiFiAuthenticationFilter {
     private X509IdentityProvider certificateIdentityProvider;
 
     @Override
-    public NiFiAuthenticationRequestToken attemptAuthentication(final HttpServletRequest request) {
+    public NiFiAuthortizationRequestToken attemptAuthentication(final HttpServletRequest request) {
         // only suppport x509 login when running securely
         if (!request.isSecure()) {
             return null;
@@ -62,9 +62,9 @@ public class X509AuthenticationFilter extends NiFiAuthenticationFilter {
 
         final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(request, authenticationResponse.getIdentity());
         if (isNewAccountRequest(request)) {
-            return new NewAccountAuthenticationRequestToken(new NewAccountRequest(proxyChain, getJustification(request)));
+            return new NewAccountAuthorizationRequestToken(new NewAccountRequest(proxyChain, getJustification(request)));
         } else {
-            return new NiFiAuthenticationRequestToken(proxyChain);
+            return new NiFiAuthortizationRequestToken(proxyChain);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/e22b51f3/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
index 5456552..414d9f8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
@@ -26,7 +26,7 @@ import org.apache.nifi.authorization.Authority;
 import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.security.UntrustedProxyException;
-import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
 import org.apache.nifi.web.security.user.NiFiUserDetails;
 import org.junit.Assert;
 import org.junit.Before;
@@ -104,8 +104,8 @@ public class NiFiAuthorizationServiceTest {
         authorizationService.setUserService(userService);
     }
 
-    private NiFiAuthenticationRequestToken createRequestAuthentication(final String... identities) {
-        return new NiFiAuthenticationRequestToken(Arrays.asList(identities));
+    private NiFiAuthortizationRequestToken createRequestAuthentication(final String... identities) {
+        return new NiFiAuthortizationRequestToken(Arrays.asList(identities));
     }
 
     /**


[11/51] [abbrv] nifi git commit: NIFI-655: - Refactoring key service to expose the key id. - Handling client side expiration better. - Removing specialized active directory provider and abstract ldap provider.

Posted by mc...@apache.org.
http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
index 628fc02..cbd5ea4 100644
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
@@ -27,59 +27,72 @@ import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import javax.net.ssl.SSLContext;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.apache.nifi.authentication.LoginCredentials;
+import org.apache.nifi.authentication.LoginIdentityProvider;
 import org.apache.nifi.authentication.LoginIdentityProviderConfigurationContext;
+import org.apache.nifi.authentication.LoginIdentityProviderInitializationContext;
+import org.apache.nifi.authentication.exception.IdentityAccessException;
+import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException;
 import org.apache.nifi.authorization.exception.ProviderCreationException;
+import org.apache.nifi.authorization.exception.ProviderDestructionException;
 import org.apache.nifi.security.util.SslContextFactory;
-import org.apache.nifi.security.util.SslContextFactory.ClientAuth;
 import org.apache.nifi.util.FormatUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ldap.CommunicationException;
 import org.springframework.ldap.core.support.AbstractTlsDirContextAuthenticationStrategy;
 import org.springframework.ldap.core.support.DefaultTlsDirContextAuthenticationStrategy;
 import org.springframework.ldap.core.support.DigestMd5DirContextAuthenticationStrategy;
 import org.springframework.ldap.core.support.LdapContextSource;
 import org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
 import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider;
 import org.springframework.security.ldap.authentication.BindAuthenticator;
 import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
 import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
 import org.springframework.security.ldap.search.LdapUserSearch;
+import org.springframework.security.ldap.userdetails.LdapUserDetails;
 
 /**
- * LDAP based implementation of a login identity provider.
+ * Abstract LDAP based implementation of a login identity provider.
  */
-public class LdapProvider extends AbstractLdapProvider {
+public class LdapProvider implements LoginIdentityProvider {
 
+    private static final Logger logger = LoggerFactory.getLogger(LdapProvider.class);
     private static final String TLS = "TLS";
 
-    @Override
-    protected AbstractLdapAuthenticationProvider getLdapAuthenticationProvider(LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException {
-        final LdapContextSource context = new LdapContextSource();
+    private AbstractLdapAuthenticationProvider provider;
+    private long expiration;
 
-        final Map<String, Object> baseEnvironment = new HashMap<>();
-
-        // connection time out
-        final String rawConnectTimeout = configurationContext.getProperty("Connect Timeout");
+    @Override
+    public final void initialize(final LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException {
+    }
 
-        // TODO: Refactor to utility method to remove duplicate code
-        if (StringUtils.isNotBlank(rawConnectTimeout)) {
-            try {
-                final Long connectTimeout = FormatUtils.getTimeDuration(rawConnectTimeout, TimeUnit.MILLISECONDS);
-                baseEnvironment.put("com.sun.jndi.ldap.connect.timeout", connectTimeout.toString());
-            } catch (final IllegalArgumentException iae) {
-                throw new ProviderCreationException(String.format("The Connect Timeout '%s' is not a valid time duration", rawConnectTimeout));
-            }
+    @Override
+    public final void onConfigured(final LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException {
+        final String rawExpiration = configurationContext.getProperty("Expiration Duration");
+        if (StringUtils.isBlank(rawExpiration)) {
+            throw new ProviderCreationException("The Expiration Duration must be specified.");
         }
 
-        // read time out
-        final String rawReadTimeout = configurationContext.getProperty("Read Timeout");
-        if (StringUtils.isNotBlank(rawReadTimeout)) {
-            try {
-                final Long readTimeout = FormatUtils.getTimeDuration(rawReadTimeout, TimeUnit.MILLISECONDS);
-                baseEnvironment.put("com.sun.jndi.ldap.read.timeout", readTimeout.toString());
-            } catch (final IllegalArgumentException iae) {
-                throw new ProviderCreationException(String.format("The Read Timeout '%s' is not a valid time duration", rawReadTimeout));
-            }
+        try {
+            expiration = FormatUtils.getTimeDuration(rawExpiration, TimeUnit.MILLISECONDS);
+        } catch (final IllegalArgumentException iae) {
+            throw new ProviderCreationException(String.format("The Expiration Duration '%s' is not a valid time duration", rawExpiration));
         }
 
+        final LdapContextSource context = new LdapContextSource();
+
+        final Map<String, Object> baseEnvironment = new HashMap<>();
+
+        // connect/read time out
+        setTimeout(configurationContext, baseEnvironment, "Connect Timeout", "com.sun.jndi.ldap.connect.timeout");
+        setTimeout(configurationContext, baseEnvironment, "Read Timeout", "com.sun.jndi.ldap.read.timeout");
+
         // set the base environment is necessary
         if (!baseEnvironment.isEmpty()) {
             context.setBaseEnvironmentProperties(baseEnvironment);
@@ -140,7 +153,7 @@ public class LdapProvider extends AbstractLdapProvider {
                                     sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType, TLS);
                                 } else {
                                     try {
-                                        final ClientAuth clientAuth = ClientAuth.valueOf(rawClientAuth);
+                                        final SslContextFactory.ClientAuth clientAuth = SslContextFactory.ClientAuth.valueOf(rawClientAuth);
                                         sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType,
                                                 rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, clientAuth, TLS);
                                     } catch (final IllegalArgumentException iae) {
@@ -205,7 +218,56 @@ public class LdapProvider extends AbstractLdapProvider {
         }
 
         // create the underlying provider
-        final LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(authenticator);
-        return ldapAuthenticationProvider;
+        provider = new LdapAuthenticationProvider(authenticator);
     }
+
+    private void setTimeout(final LoginIdentityProviderConfigurationContext configurationContext,
+            final Map<String, Object> baseEnvironment,
+            final String configurationProperty,
+            final String environmentKey) {
+        
+        final String rawTimeout = configurationContext.getProperty(configurationProperty);
+        if (StringUtils.isNotBlank(rawTimeout)) {
+            try {
+                final Long timeout = FormatUtils.getTimeDuration(rawTimeout, TimeUnit.MILLISECONDS);
+                baseEnvironment.put(environmentKey, timeout.toString());
+            } catch (final IllegalArgumentException iae) {
+                throw new ProviderCreationException(String.format("The %s '%s' is not a valid time duration", configurationProperty, rawTimeout));
+            }
+        }
+    }
+    
+    @Override
+    public final AuthenticationResponse authenticate(final LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException {
+        if (provider == null) {
+            throw new IdentityAccessException("The LDAP authentication provider is not initialized.");
+        }
+
+        try {
+            // perform the authentication
+            final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword());
+            final Authentication authentication = provider.authenticate(token);
+
+            // attempt to get the ldap user details to get the DN
+            if (authentication.getPrincipal() instanceof LdapUserDetails) {
+                final LdapUserDetails userDetails = (LdapUserDetails) authentication.getPrincipal();
+                return new AuthenticationResponse(userDetails.getDn(), credentials.getUsername(), expiration);
+            } else {
+                return new AuthenticationResponse(authentication.getName(), credentials.getUsername(), expiration);
+            }
+        } catch (final CommunicationException | AuthenticationServiceException e) {
+            logger.error(e.getMessage());
+            if (logger.isDebugEnabled()) {
+                logger.debug(StringUtils.EMPTY, e);
+            }
+            throw new IdentityAccessException("Unable to query the configured directory server. See the logs for additional details.", e);
+        } catch (final BadCredentialsException bce) {
+            throw new InvalidLoginCredentialsException(bce.getMessage(), bce);
+        }
+    }
+
+    @Override
+    public final void preDestruction() throws ProviderDestructionException {
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider
index 2158cf7..b5ca1fe 100644
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider
@@ -12,5 +12,4 @@
 # 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.
-org.apache.nifi.ldap.LdapProvider
-org.apache.nifi.ldap.ActiveDirectoryProvider
\ No newline at end of file
+org.apache.nifi.ldap.LdapProvider
\ No newline at end of file


[40/51] [abbrv] nifi git commit: NIFI-655: - Fixing issue with filter bean initialization when clustered.

Posted by mc...@apache.org.
NIFI-655:
- Fixing issue with filter bean initialization when clustered.

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

Branch: refs/heads/master
Commit: c1cc165edb2fdacdcc958f8b4b1dcf97bf40eb5f
Parents: 6bce858
Author: Matt Gilman <ma...@gmail.com>
Authored: Fri Nov 27 10:05:58 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Fri Nov 27 10:05:58 2015 -0500

----------------------------------------------------------------------
 .../web/NiFiWebApiSecurityConfiguration.java    | 72 ++++++++++++--------
 .../web/security/NiFiAuthenticationFilter.java  | 10 +--
 2 files changed, 47 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/c1cc165e/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 bf12dee..73e9640 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
@@ -58,6 +58,11 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
     private X509IdentityProvider certificateIdentityProvider;
     private LoginIdentityProvider loginIdentityProvider;
 
+    private NodeAuthorizedUserFilter nodeAuthorizedUserFilter;
+    private JwtAuthenticationFilter jwtAuthenticationFilter;
+    private X509AuthenticationFilter x509AuthenticationFilter;
+    private NiFiAnonymousUserFilter anonymousAuthenticationFilter;
+
     public NiFiWebApiSecurityConfiguration() {
         super(true); // disable defaults
     }
@@ -80,17 +85,17 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
                     .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
 
         // cluster authorized user
-        http.addFilterBefore(buildNodeAuthorizedUserFilter(), AnonymousAuthenticationFilter.class);
+        http.addFilterBefore(nodeAuthorizedUserFilterBean(), AnonymousAuthenticationFilter.class);
 
         // anonymous
-        http.anonymous().authenticationFilter(buildAnonymousFilter());
+        http.anonymous().authenticationFilter(anonymousFilterBean());
 
         // x509
-        http.addFilterAfter(buildX509Filter(), AnonymousAuthenticationFilter.class);
+        http.addFilterAfter(x509FilterBean(), AnonymousAuthenticationFilter.class);
 
         // jwt - consider when configured for log in
         if (loginIdentityProvider != null) {
-            http.addFilterAfter(buildJwtFilter(), AnonymousAuthenticationFilter.class);
+            http.addFilterAfter(jwtFilterBean(), AnonymousAuthenticationFilter.class);
         }
     }
 
@@ -106,35 +111,48 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
         auth.authenticationProvider(new NiFiAuthenticationProvider(userDetailsService));
     }
 
-    private NodeAuthorizedUserFilter buildNodeAuthorizedUserFilter() {
-        final NodeAuthorizedUserFilter nodeFilter = new NodeAuthorizedUserFilter();
-        nodeFilter.setProperties(properties);
-        nodeFilter.setCertificateExtractor(certificateExtractor);
-        nodeFilter.setCertificateIdentityProvider(certificateIdentityProvider);
-        return nodeFilter;
+    @Bean
+    public NodeAuthorizedUserFilter nodeAuthorizedUserFilterBean() throws Exception {
+        if (nodeAuthorizedUserFilter == null) {
+            nodeAuthorizedUserFilter = new NodeAuthorizedUserFilter();
+            nodeAuthorizedUserFilter.setProperties(properties);
+            nodeAuthorizedUserFilter.setCertificateExtractor(certificateExtractor);
+            nodeAuthorizedUserFilter.setCertificateIdentityProvider(certificateIdentityProvider);
+        }
+        return nodeAuthorizedUserFilter;
     }
 
-    private JwtAuthenticationFilter buildJwtFilter() throws Exception {
-        final JwtAuthenticationFilter jwtFilter = new JwtAuthenticationFilter();
-        jwtFilter.setProperties(properties);
-        jwtFilter.setJwtService(jwtService);
-        jwtFilter.setAuthenticationManager(authenticationManager());
-        return jwtFilter;
+    @Bean
+    public JwtAuthenticationFilter jwtFilterBean() throws Exception {
+        // only consider the jwt authentication filter when configured for login
+        if (jwtAuthenticationFilter == null && loginIdentityProvider != null) {
+            jwtAuthenticationFilter = new JwtAuthenticationFilter();
+            jwtAuthenticationFilter.setProperties(properties);
+            jwtAuthenticationFilter.setJwtService(jwtService);
+            jwtAuthenticationFilter.setAuthenticationManager(authenticationManager());
+        }
+        return jwtAuthenticationFilter;
     }
 
-    private X509AuthenticationFilter buildX509Filter() throws Exception {
-        final X509AuthenticationFilter x509Filter = new X509AuthenticationFilter();
-        x509Filter.setProperties(properties);
-        x509Filter.setCertificateExtractor(certificateExtractor);
-        x509Filter.setCertificateIdentityProvider(certificateIdentityProvider);
-        x509Filter.setAuthenticationManager(authenticationManager());
-        return x509Filter;
+    @Bean
+    public X509AuthenticationFilter x509FilterBean() throws Exception {
+        if (x509AuthenticationFilter == null) {
+            x509AuthenticationFilter = new X509AuthenticationFilter();
+            x509AuthenticationFilter.setProperties(properties);
+            x509AuthenticationFilter.setCertificateExtractor(certificateExtractor);
+            x509AuthenticationFilter.setCertificateIdentityProvider(certificateIdentityProvider);
+            x509AuthenticationFilter.setAuthenticationManager(authenticationManager());
+        }
+        return x509AuthenticationFilter;
     }
 
-    private AnonymousAuthenticationFilter buildAnonymousFilter() {
-        final NiFiAnonymousUserFilter anonymousFilter = new NiFiAnonymousUserFilter();
-        anonymousFilter.setUserService(userService);
-        return anonymousFilter;
+    @Bean
+    public NiFiAnonymousUserFilter anonymousFilterBean() throws Exception {
+        if (anonymousAuthenticationFilter == null) {
+            anonymousAuthenticationFilter = new NiFiAnonymousUserFilter();
+            anonymousAuthenticationFilter.setUserService(userService);
+        }
+        return anonymousAuthenticationFilter;
     }
 
     @Autowired

http://git-wip-us.apache.org/repos/asf/nifi/blob/c1cc165e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.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/NiFiAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
index 7ceca04..be781c2 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
@@ -18,9 +18,7 @@ package org.apache.nifi.web.security;
 
 import java.io.IOException;
 import java.io.PrintWriter;
-import javax.servlet.Filter;
 import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
@@ -40,11 +38,12 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.web.filter.GenericFilterBean;
 
 /**
  *
  */
-public abstract class NiFiAuthenticationFilter implements Filter {
+public abstract class NiFiAuthenticationFilter extends GenericFilterBean {
 
     private static final Logger logger = LoggerFactory.getLogger(NiFiAuthenticationFilter.class);
 
@@ -52,11 +51,6 @@ public abstract class NiFiAuthenticationFilter implements Filter {
     private NiFiProperties properties;
 
     @Override
-    public void init(final FilterConfig filterConfig) throws ServletException {
-        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
-    }
-
-    @Override
     public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
         if (logger.isDebugEnabled()) {
             logger.debug("Checking secure context token: " + SecurityContextHolder.getContext().getAuthentication());


[39/51] [abbrv] nifi git commit: NIFI-655: - Updated user guide with screenshots of login process. - Tweaked wording in admin guide. - Triggering login on enter press in login form.

Posted by mc...@apache.org.
NIFI-655:
- Updated user guide with screenshots of login process.
- Tweaked wording in admin guide.
- Triggering login on enter press in login form.

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

Branch: refs/heads/master
Commit: 6bce858e4ae37fd10a6651428741a99a4b893d52
Parents: c073253
Author: Matt Gilman <ma...@gmail.com>
Authored: Wed Nov 25 16:42:22 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Nov 25 16:42:22 2015 -0500

----------------------------------------------------------------------
 .../src/main/asciidoc/administration-guide.adoc |   4 +--
 .../main/asciidoc/images/anonymous-access.png   | Bin 0 -> 81725 bytes
 nifi-docs/src/main/asciidoc/images/login.png    | Bin 0 -> 93233 bytes
 .../src/main/asciidoc/images/request-access.png | Bin 0 -> 94494 bytes
 nifi-docs/src/main/asciidoc/user-guide.adoc     |  26 +++++++++++++++++++
 .../org/apache/nifi/web/api/AccessResource.java |   4 +--
 .../src/main/webapp/js/nf/login/nf-login.js     |  11 ++++++++
 7 files changed, 41 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/6bce858e/nifi-docs/src/main/asciidoc/administration-guide.adoc
----------------------------------------------------------------------
diff --git a/nifi-docs/src/main/asciidoc/administration-guide.adoc b/nifi-docs/src/main/asciidoc/administration-guide.adoc
index dcb1151..1e5d215 100644
--- a/nifi-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/administration-guide.adoc
@@ -150,8 +150,8 @@ NiFi provides several different configuration options for security purposes. The
                                 by the NiFi cluster protocol. If the Truststore properties are not set, this must be `false`. Otherwise, a value
                                 of `true` indicates that nodes in the cluster will be authenticated and must have certificates that are trusted 
                                 by the Truststores.
-|`nifi.security.anonymous.authorities` | Specifies the roles that should be granted to users that connect over HTTPS anonymously. All users will
-                                be given this level access, however if they have been granted a particular level of access by an administrator
+|`nifi.security.anonymous.authorities` | Specifies the roles that should be granted to users that connect over HTTPS anonymously. All users can make 
+                                use of anonymous access, however if they have been granted a particular level of access by an administrator
                                 it will take precedence if they access NiFi using a client certificate or once they have logged in. 
 |==================================================================================================================================================
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/6bce858e/nifi-docs/src/main/asciidoc/images/anonymous-access.png
----------------------------------------------------------------------
diff --git a/nifi-docs/src/main/asciidoc/images/anonymous-access.png b/nifi-docs/src/main/asciidoc/images/anonymous-access.png
new file mode 100644
index 0000000..1ecabcd
Binary files /dev/null and b/nifi-docs/src/main/asciidoc/images/anonymous-access.png differ

http://git-wip-us.apache.org/repos/asf/nifi/blob/6bce858e/nifi-docs/src/main/asciidoc/images/login.png
----------------------------------------------------------------------
diff --git a/nifi-docs/src/main/asciidoc/images/login.png b/nifi-docs/src/main/asciidoc/images/login.png
new file mode 100644
index 0000000..f474a9e
Binary files /dev/null and b/nifi-docs/src/main/asciidoc/images/login.png differ

http://git-wip-us.apache.org/repos/asf/nifi/blob/6bce858e/nifi-docs/src/main/asciidoc/images/request-access.png
----------------------------------------------------------------------
diff --git a/nifi-docs/src/main/asciidoc/images/request-access.png b/nifi-docs/src/main/asciidoc/images/request-access.png
new file mode 100644
index 0000000..f556894
Binary files /dev/null and b/nifi-docs/src/main/asciidoc/images/request-access.png differ

http://git-wip-us.apache.org/repos/asf/nifi/blob/6bce858e/nifi-docs/src/main/asciidoc/user-guide.adoc
----------------------------------------------------------------------
diff --git a/nifi-docs/src/main/asciidoc/user-guide.adoc b/nifi-docs/src/main/asciidoc/user-guide.adoc
index fd40526..64421d7 100644
--- a/nifi-docs/src/main/asciidoc/user-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/user-guide.adoc
@@ -157,6 +157,32 @@ are in the cluster and how many are currently connected.
 image::status-bar.png["NiFi Status Bar"]
 
 
+[[logging-in]]
+Logging In
+---------
+
+If NiFi is configured to run securely, users will be able to request access to the DataFlow. For information on configuring NiFi to run 
+securely, see the link:administration-guide.html[Admin Guide]. If NiFi supports anonymous access, users will be given access 
+accordingly and given an option to log in.
+
+image::anonymous-access.png["Anonymous Access"]
+
+Clicking the 'login' link will open the log in page. If the user is logging in with their username/password they will be presented with
+a form to do so. If NiFi is not configured to support anonymous access and the user is logging in with their username/password, they will 
+be immediately sent to the login form bypassing the canvas.
+
+image::login.png["Log In"]
+
+Once the user has logged in or if they are accessing NiFi using a client certificate loaded in their browser, they will be prompted
+to request access by submitting a justification if this is the first time they have accessed this NiFi. Fill in an optional justification 
+that the administrator will review while granting the account access. If NiFi is not configured to support anonymous access and the 
+user is using a client certificate, they will be immediately sent to the form to request access bypassing the canvas and login form.
+
+image::request-access.png["Request Access"]
+
+Press Submit to send the account request. If NiFi supports anonymous access, the user can continue accessing the DataFlow by closing the
+login page. Returning to the login page will check the status of the account request. If access has been granted, press the home link or
+reload the page to assume the new roles.
 
 
 [[building-dataflow]]

http://git-wip-us.apache.org/repos/asf/nifi/blob/6bce858e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
index c73ec47..326aa00 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -208,7 +208,7 @@ public class AccessResource extends ApplicationResource {
                         // no issues with authorization... verify authorities
                         accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
                         if (userDetails.getAuthorities().isEmpty()) {
-                            accessStatus.setMessage("Your account is active but is unauthorized as no authorities have been granted.");
+                            accessStatus.setMessage("Your account is active but currently does not have any level of access.");
                         } else {
                             accessStatus.setMessage("Your account is active and you are already logged in.");
                         }
@@ -237,7 +237,7 @@ public class AccessResource extends ApplicationResource {
                     // no issues with authorization... verify authorities
                     accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
                     if (userDetails.getAuthorities().isEmpty()) {
-                        accessStatus.setMessage("Your account is active but is unauthorized as no authorities have been granted.");
+                        accessStatus.setMessage("Your account is active but currently does not have any level of access.");
                     } else {
                         accessStatus.setMessage("Your account is active and you are already logged in.");
                     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/6bce858e/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 6c05664..f2c9d2a 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
@@ -87,6 +87,9 @@ nf.Login = (function () {
     };
 
     var login = function () {
+        // remove focus
+        $('#username, #password').blur();
+        
         // show the logging message...
         $('#login-progress-label').text('Logging in...');
         $('#login-progress-container').show();
@@ -218,6 +221,14 @@ nf.Login = (function () {
             if (nf.Storage.getItem('jwt') !== null) {
                 showLogoutLink();
             }
+            
+            // supporting logging in via enter press
+            $('#username, #password').on('keyup', function (e) {
+                var code = e.keyCode ? e.keyCode : e.which;
+                if (code === $.ui.keyCode.ENTER) {
+                    login();
+                }
+            });
 
             // access status
             var accessStatus = $.ajax({


[48/51] [abbrv] nifi git commit: NIFI-655: - Adding additional logging when proceeding as an anonymous user.

Posted by mc...@apache.org.
NIFI-655:
- Adding additional logging when proceeding as an anonymous user.

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

Branch: refs/heads/master
Commit: c100052dac7c1366767e78c088da50ffb27958b4
Parents: 2b0819a
Author: Matt Gilman <ma...@gmail.com>
Authored: Tue Dec 1 08:51:45 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Tue Dec 1 08:51:45 2015 -0500

----------------------------------------------------------------------
 .../web/security/NiFiAuthenticationFilter.java  | 32 ++++++++++++--------
 1 file changed, 20 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/c100052d/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.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/NiFiAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
index c9b5c88..f0000f8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
@@ -45,15 +45,15 @@ import org.springframework.web.filter.GenericFilterBean;
  */
 public abstract class NiFiAuthenticationFilter extends GenericFilterBean {
 
-    private static final Logger logger = LoggerFactory.getLogger(NiFiAuthenticationFilter.class);
+    private static final Logger log = LoggerFactory.getLogger(NiFiAuthenticationFilter.class);
 
     private AuthenticationManager authenticationManager;
     private NiFiProperties properties;
 
     @Override
     public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
-        if (logger.isDebugEnabled()) {
-            logger.debug("Checking secure context token: " + SecurityContextHolder.getContext().getAuthentication());
+        if (log.isDebugEnabled()) {
+            log.debug("Checking secure context token: " + SecurityContextHolder.getContext().getAuthentication());
         }
 
         if (requiresAuthentication((HttpServletRequest) request)) {
@@ -80,12 +80,14 @@ public abstract class NiFiAuthenticationFilter extends GenericFilterBean {
     }
 
     private void authenticate(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws IOException, ServletException {
+        String dnChain = null;
         try {
             final NiFiAuthenticationRequestToken authenticated = attemptAuthentication(request);
             if (authenticated != null) {
+                dnChain = ProxiedEntitiesUtils.formatProxyDn(StringUtils.join(authenticated.getChain(), "><"));
+
                 // log the request attempt - response details will be logged later
-                logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)",
-                        ProxiedEntitiesUtils.formatProxyDn(StringUtils.join(authenticated.getChain(), "><")), request.getMethod(),
+                log.info(String.format("Attempting request for (%s) %s %s (source ip: %s)", dnChain, request.getMethod(),
                         request.getRequestURL().toString(), request.getRemoteAddr()));
 
                 // attempt to authorize the user
@@ -101,6 +103,12 @@ public abstract class NiFiAuthenticationFilter extends GenericFilterBean {
         } catch (final AuthenticationException ae) {
             // other authentication exceptions... if we are already the anonymous user, allow through otherwise error out
             if (isAnonymousUser()) {
+                if (dnChain == null) {
+                    log.info(String.format("Continuing as anonymous user. Unable to authenticate %s: %s", dnChain, ae));
+                } else {
+                    log.info(String.format("Continuing as anonymous user. Unable to authenticate: %s", ae));
+                }
+
                 chain.doFilter(request, response);
             } else {
                 unsuccessfulAuthorization(request, response, ae);
@@ -120,8 +128,8 @@ public abstract class NiFiAuthenticationFilter extends GenericFilterBean {
     public abstract NiFiAuthenticationRequestToken attemptAuthentication(HttpServletRequest request);
 
     protected void successfulAuthorization(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
-        if (logger.isDebugEnabled()) {
-            logger.debug("Authentication success: " + authResult);
+        if (log.isDebugEnabled()) {
+            log.debug("Authentication success: " + authResult);
         }
 
         SecurityContextHolder.getContext().setAuthentication(authResult);
@@ -157,21 +165,21 @@ public abstract class NiFiAuthenticationFilter extends GenericFilterBean {
             response.setStatus(HttpServletResponse.SC_FORBIDDEN);
             out.println(ae.getMessage());
         } else if (ae instanceof AuthenticationServiceException) {
-            logger.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
+            log.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
             response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
             out.println(String.format("Unable to authorize: %s", ae.getMessage()));
         } else {
-            logger.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
+            log.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
             response.setStatus(HttpServletResponse.SC_FORBIDDEN);
             out.println("Access is denied.");
         }
 
         // log the failure
-        logger.info(String.format("Rejecting access to web api: %s", ae.getMessage()));
+        log.info(String.format("Rejecting access to web api: %s", ae.getMessage()));
 
         // optionally log the stack trace
-        if (logger.isDebugEnabled()) {
-            logger.debug(StringUtils.EMPTY, ae);
+        if (log.isDebugEnabled()) {
+            log.debug(StringUtils.EMPTY, ae);
         }
     }
 


[45/51] [abbrv] nifi git commit: NIFI-655: - Adding documentation around the behavior of the authentication filters. - Only passing along necessary parameters.

Posted by mc...@apache.org.
NIFI-655:
- Adding documentation around the behavior of the authentication filters.
- Only passing along necessary parameters.

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

Branch: refs/heads/master
Commit: 774d626f88c13098f69aac0fbb88902defa677df
Parents: c722b56
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 30 15:07:40 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 30 15:07:40 2015 -0500

----------------------------------------------------------------------
 .../nifi/web/security/NiFiAuthenticationFilter.java    | 13 +++++++++++--
 .../nifi/web/security/jwt/JwtAuthenticationFilter.java |  3 +--
 .../web/security/x509/X509AuthenticationFilter.java    |  3 +--
 3 files changed, 13 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/774d626f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.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/NiFiAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
index be781c2..c9b5c88 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
@@ -81,7 +81,7 @@ public abstract class NiFiAuthenticationFilter extends GenericFilterBean {
 
     private void authenticate(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws IOException, ServletException {
         try {
-            final NiFiAuthenticationRequestToken authenticated = attemptAuthentication(request, response);
+            final NiFiAuthenticationRequestToken authenticated = attemptAuthentication(request);
             if (authenticated != null) {
                 // log the request attempt - response details will be logged later
                 logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)",
@@ -108,7 +108,16 @@ public abstract class NiFiAuthenticationFilter extends GenericFilterBean {
         }
     }
 
-    public abstract NiFiAuthenticationRequestToken attemptAuthentication(HttpServletRequest request, HttpServletResponse response);
+    /**
+     * Attempt to authenticate the client making the request. If the request does not contain an authentication attempt, this method should return null. If the request contains an authentication
+     * request, the implementation should convert it to a NiFiAuthenticationRequestToken (which is used when authorizing the client). Implementations should throw InvalidAuthenticationException when
+     * the request contains an authentication request but it could not be authenticated.
+     *
+     * @param request The request
+     * @return The NiFiAuthenticationRequestToken used to later authorized the client
+     * @throws InvalidAuthenticationException If the request contained an authentication attempt, but could not authenticate
+     */
+    public abstract NiFiAuthenticationRequestToken attemptAuthentication(HttpServletRequest request);
 
     protected void successfulAuthorization(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
         if (logger.isDebugEnabled()) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/774d626f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.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/JwtAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
index 2f18406..155610a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
@@ -26,7 +26,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 import java.util.Arrays;
 import org.apache.nifi.web.security.InvalidAuthenticationException;
 
@@ -41,7 +40,7 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
     private JwtService jwtService;
 
     @Override
-    public NiFiAuthenticationRequestToken attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
+    public NiFiAuthenticationRequestToken attemptAuthentication(final HttpServletRequest request) {
         // only suppport jwt login when running securely
         if (!request.isSecure()) {
             return null;

http://git-wip-us.apache.org/repos/asf/nifi/blob/774d626f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.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/x509/X509AuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
index dd7d47e..708b607 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
@@ -19,7 +19,6 @@ package org.apache.nifi.web.security.x509;
 import java.security.cert.X509Certificate;
 import java.util.List;
 import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 import org.apache.nifi.authentication.AuthenticationResponse;
 import org.apache.nifi.web.security.InvalidAuthenticationException;
 import org.apache.nifi.web.security.NiFiAuthenticationFilter;
@@ -41,7 +40,7 @@ public class X509AuthenticationFilter extends NiFiAuthenticationFilter {
     private X509IdentityProvider certificateIdentityProvider;
 
     @Override
-    public NiFiAuthenticationRequestToken attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
+    public NiFiAuthenticationRequestToken attemptAuthentication(final HttpServletRequest request) {
         // only suppport x509 login when running securely
         if (!request.isSecure()) {
             return null;


[10/51] [abbrv] nifi git commit: NIFI-655. - Changed issuer field to use FQ class name because some classes return an empty string for getSimpleName(). - Finished refactoring JWT logic from request parsing logic in JwtService. - Updated AccessResource an

Posted by mc...@apache.org.
NIFI-655. - Changed issuer field to use FQ class name because some classes return an empty string for getSimpleName(). - Finished refactoring JWT logic from request parsing logic in JwtService. - Updated AccessResource and JwtAuthenticationFilter to call new JwtService methods decoupled from request header parsing. - Added extensive unit tests for JWT logic.

Signed-off-by: Matt Gilman <ma...@gmail.com>


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

Branch: refs/heads/master
Commit: 7d04dfeac012307201e84dd00883dd6a79e6e2cc
Parents: 3bc11e1
Author: Andy LoPresto <an...@andylopresto.com>
Authored: Wed Nov 18 00:45:38 2015 -0800
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Nov 18 08:31:39 2015 -0500

----------------------------------------------------------------------
 .../admin/service/impl/StandardKeyService.java  |  10 +-
 .../org/apache/nifi/web/api/AccessResource.java |  54 +++---
 .../security/jwt/JwtAuthenticationFilter.java   |  44 ++++-
 .../nifi/web/security/jwt/JwtService.java       |  64 +++----
 .../nifi/web/security/jwt/JwtServiceTest.java   | 182 ++++++++++++++++---
 5 files changed, 264 insertions(+), 90 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/7d04dfea/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java
index 1b2f8c9..7dff9d8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java
@@ -18,6 +18,9 @@ package org.apache.nifi.admin.service.impl;
 
 import org.apache.nifi.admin.dao.DataAccessException;
 import org.apache.nifi.admin.service.AdministrationException;
+import org.apache.nifi.admin.service.KeyService;
+import org.apache.nifi.admin.service.action.GetKeyAction;
+import org.apache.nifi.admin.service.action.GetOrCreateKeyAction;
 import org.apache.nifi.admin.service.transaction.Transaction;
 import org.apache.nifi.admin.service.transaction.TransactionBuilder;
 import org.apache.nifi.admin.service.transaction.TransactionException;
@@ -26,9 +29,6 @@ import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
-import org.apache.nifi.admin.service.KeyService;
-import org.apache.nifi.admin.service.action.GetKeyAction;
-import org.apache.nifi.admin.service.action.GetOrCreateKeyAction;
 
 /**
  *
@@ -45,6 +45,8 @@ public class StandardKeyService implements KeyService {
 
     @Override
     public String getKey(String identity) {
+        // TODO: Change this service to look up by "key ID" instead of identity
+        // TODO: Change the return type to a Key POJO to support key rotation
         Transaction transaction = null;
         String key = null;
 
@@ -75,6 +77,8 @@ public class StandardKeyService implements KeyService {
 
     @Override
     public String getOrCreateKey(String identity) {
+        // TODO: Change this service to look up by "key ID" instead of identity
+        // TODO: Change the return type to a Key POJO to support key rotation
         Transaction transaction = null;
         String key = null;
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/7d04dfea/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
index b4778ad..2e1c44e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -23,6 +23,7 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
+import io.jsonwebtoken.JwtException;
 import org.apache.nifi.util.NiFiProperties;
 import com.wordnik.swagger.annotations.Api;
 import com.wordnik.swagger.annotations.ApiOperation;
@@ -184,30 +185,33 @@ public class AccessResource extends ApplicationResource {
                     accessStatus.setStatus(AccessStatusDTO.Status.UNKNOWN.name());
                     accessStatus.setMessage("No credentials supplied, unknown user.");
                 } else {
-                    // TODO - use this token with the JWT service
+                    // Extract the Base64 encoded token from the Authorization header
                     final String token = StringUtils.substringAfterLast(authorization, " ");
 
-                    // TODO - do not call this method of the jwt service
-                    final String principal = jwtService.getAuthentication(httpServletRequest);
-
-                    // TODO - catch jwt exception?
-                    // ensure we have something we can work with (certificate or credentials)
-                    if (principal == null) {
-                        throw new IllegalArgumentException("The specific token is not valid.");
-                    } else {
-                        // set the user identity
-                        accessStatus.setIdentity(principal);
-                        accessStatus.setUsername(CertificateUtils.extractUsername(principal));
-
-                        // without a certificate, this is not a proxied request
-                        final List<String> chain = Arrays.asList(principal);
-
-                        // check authorization for this user
-                        checkAuthorization(chain);
-
-                        // no issues with authorization
-                        accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
-                        accessStatus.setMessage("Account is active and authorized");
+                    try {
+                        final String principal = jwtService.getAuthenticationFromToken(token);
+
+                        // ensure we have something we can work with (certificate or credentials)
+                        if (principal == null) {
+                            throw new IllegalArgumentException("The specific token is not valid.");
+                        } else {
+                            // set the user identity
+                            accessStatus.setIdentity(principal);
+                            accessStatus.setUsername(CertificateUtils.extractUsername(principal));
+
+                            // without a certificate, this is not a proxied request
+                            final List<String> chain = Arrays.asList(principal);
+
+                            // check authorization for this user
+                            checkAuthorization(chain);
+
+                            // no issues with authorization
+                            accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
+                            accessStatus.setMessage("Account is active and authorized");
+                        }
+                    } catch (JwtException e) {
+                        // TODO: Handle the exception from a failed JWT verification
+                        throw new AccessDeniedException("The JWT could not be verified", e);
                     }
                 }
             } else {
@@ -334,7 +338,8 @@ public class AccessResource extends ApplicationResource {
                 }
                 
                 // create the authentication token
-                loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getIdentity(), expiration, loginIdentityProvider.getClass().getSimpleName());
+                // TODO: Some Spring beans return "" for getClass().getSimpleName(). Using getName() temporarily
+                loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getIdentity(), expiration, loginIdentityProvider.getClass().getName());
             } catch (final InvalidLoginCredentialsException ilce) {
                 throw new IllegalArgumentException("The supplied username and password are not valid.", ilce);
             } catch (final IdentityAccessException iae) {
@@ -355,7 +360,8 @@ public class AccessResource extends ApplicationResource {
             authorizeProxyIfNecessary(proxyChain);
 
             // create the authentication token
-            loginAuthenticationToken = new LoginAuthenticationToken(proxyChain.get(0), authenticationResponse.getExpiration(), certificateIdentityProvider.getClass().getSimpleName());
+            // TODO: Some Spring beans return "" for getClass().getSimpleName(). Using getName() temporarily
+            loginAuthenticationToken = new LoginAuthenticationToken(proxyChain.get(0), authenticationResponse.getExpiration(), certificateIdentityProvider.getClass().getName());
         }
 
         // generate JWT for response

http://git-wip-us.apache.org/repos/asf/nifi/blob/7d04dfea/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.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/JwtAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
index 5a84e93..dea5bba 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
@@ -16,9 +16,8 @@
  */
 package org.apache.nifi.web.security.jwt;
 
-import java.util.Arrays;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import io.jsonwebtoken.JwtException;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.web.security.NiFiAuthenticationFilter;
 import org.apache.nifi.web.security.token.NewAccountAuthenticationRequestToken;
 import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
@@ -26,12 +25,18 @@ import org.apache.nifi.web.security.user.NewAccountRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Arrays;
+
 /**
  */
 public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
 
     private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
 
+    private static final String AUTHORIZATION = "Authorization";
+
     private JwtService jwtService;
 
     @Override
@@ -41,16 +46,35 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
             return null;
         }
 
+        // TODO: Refactor request header extraction logic to shared utility as it is duplicated in AccessResource
+
         // get the principal out of the user token
-        final String jwtPrincipal = jwtService.getAuthentication(request);
-        if (jwtPrincipal == null) {
-            return null;
-        }
+        // look for an authorization token
+        final String authorization = request.getHeader(AUTHORIZATION);
 
-        if (isNewAccountRequest(request)) {
-            return new NewAccountAuthenticationRequestToken(new NewAccountRequest(Arrays.asList(jwtPrincipal), getJustification(request)));
+        // if there is no authorization header, we don't know the user
+        if (authorization == null) {
+            return null;
         } else {
-            return new NiFiAuthenticationRequestToken(Arrays.asList(jwtPrincipal));
+            // Extract the Base64 encoded token from the Authorization header
+            final String token = StringUtils.substringAfterLast(authorization, " ");
+
+            try {
+                final String jwtPrincipal = jwtService.getAuthenticationFromToken(token);
+                if (jwtPrincipal == null) {
+                    return null;
+                }
+
+                if (isNewAccountRequest(request)) {
+                    return new NewAccountAuthenticationRequestToken(new NewAccountRequest(Arrays.asList(jwtPrincipal), getJustification(request)));
+                } else {
+                    return new NiFiAuthenticationRequestToken(Arrays.asList(jwtPrincipal));
+                }
+            } catch (JwtException e) {
+                // TODO: Is this the correct way to handle an unverified token?
+                logger.error("Could not verify JWT", e);
+                return null;
+            }
         }
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/7d04dfea/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 acbbcfe..f006e5b 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
@@ -33,7 +33,6 @@ import org.apache.nifi.admin.service.KeyService;
 import org.apache.nifi.web.security.token.LoginAuthenticationToken;
 import org.slf4j.LoggerFactory;
 
-import javax.servlet.http.HttpServletRequest;
 import java.nio.charset.StandardCharsets;
 import java.util.Calendar;
 
@@ -44,7 +43,8 @@ public class JwtService {
     private static final org.slf4j.Logger logger = LoggerFactory.getLogger(JwtService.class);
 
     private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
-    private final static String AUTHORIZATION = "Authorization";
+    private static final String KEY_ID_CLAIM = "kid";
+    private static final String USERNAME_CLAIM = "preferred_username";
 
     private final KeyService keyService;
 
@@ -52,49 +52,50 @@ public class JwtService {
         this.keyService = keyService;
     }
 
-    /**
-     * Gets the Authentication by extracting a JWT token from the specified request.
-     *
-     * @param request Request to extract the token from
-     * @return The user identifier from the token
-     */
-    public String getAuthentication(final HttpServletRequest request) {
-        // TODO: Refactor request token extraction out of this service
-        // extract/verify token from incoming request
-        final String authorization = request.getHeader(AUTHORIZATION);
-        final String base64EncodedToken = StringUtils.substringAfterLast(authorization, " ");
-
-        return getAuthenticationFromToken(base64EncodedToken);
-    }
-
-    public String getAuthenticationFromToken(final String base64EncodedToken) {
+    public String getAuthenticationFromToken(final String base64EncodedToken) throws JwtException {
         // The library representations of the JWT should be kept internal to this service.
         try {
             final Jws<Claims> jws = parseTokenFromBase64EncodedString(base64EncodedToken);
+
+            if (jws == null) {
+                throw new JwtException("Unable to parse token");
+            }
+
+            // Additional validation that subject is present
+            if (StringUtils.isEmpty(jws.getBody().getSubject())) {
+                throw new JwtException("No subject available in token");
+            }
+
+            // TODO: Validate issuer against active registry?
+            if (StringUtils.isEmpty(jws.getBody().getIssuer())) {
+                // TODO: Remove after testing
+//                logger.info("Decoded JWT payload: " + jws.toString());
+                throw new JwtException("No issuer available in token");
+            }
             return jws.getBody().getSubject();
         } catch (JwtException e) {
             logger.debug("The Base64 encoded JWT: " + base64EncodedToken);
-            final String errorMessage = "There was an error parsing the Base64-encoded JWT";
+            final String errorMessage = "There was an error validating the JWT";
             logger.error(errorMessage, e);
-            throw new JwtException(errorMessage, e);
+            throw e;
         }
     }
 
     private Jws<Claims> parseTokenFromBase64EncodedString(final String base64EncodedToken) throws JwtException {
         try {
-            // TODO: Check algorithm for validity
-            // TODO: Ensure signature verification occurs
             return Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() {
                 @Override
                 public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {
                     final String identity = claims.getSubject();
 
+                    // TODO: Currently the kid field is identical to identity, but will be a unique key ID when key rotation is implemented
+                    final String keyId = claims.get(KEY_ID_CLAIM, String.class);
                     // The key is unique per identity and should be retrieved from the key service
-                    final String key = keyService.getKey(identity);
+                    final String key = keyService.getKey(keyId);
 
                     // Ensure we were able to find a key that was previously issued by this key service for this user
                     if (key == null) {
-                        throw new UnsupportedJwtException("Unable to determine signing key for " + identity);
+                        throw new UnsupportedJwtException("Unable to determine signing key for " + identity + " [kid: " + keyId + "]");
                     }
 
                     return key.getBytes(StandardCharsets.UTF_8);
@@ -102,8 +103,7 @@ public class JwtService {
             }).parseClaimsJws(base64EncodedToken);
         } catch (final MalformedJwtException | UnsupportedJwtException | SignatureException | ExpiredJwtException | IllegalArgumentException | AdministrationException e) {
             // TODO: Exercise all exceptions to ensure none leak key material to logs
-            final String errorMessage = "There was an error parsing the Base64-encoded JWT";
-            logger.error(errorMessage, e);
+            final String errorMessage = "There was an error validating the JWT";
             throw new JwtException(errorMessage, e);
         }
     }
@@ -111,9 +111,9 @@ public class JwtService {
     /**
      * Generates a signed JWT token from the provided (Spring Security) login authentication token.
      *
-     * @param authenticationToken
+     * @param authenticationToken an instance of the Spring Security token after login credentials have been verified against the respective information source
      * @return a signed JWT containing the user identity and the identity provider, Base64-encoded
-     * @throws JwtException
+     * @throws JwtException if there is a problem generating the signed token
      */
     public String generateSignedToken(final LoginAuthenticationToken authenticationToken) throws JwtException {
         if (authenticationToken == null) {
@@ -144,17 +144,19 @@ public class JwtService {
 
             // TODO: Implement "jti" claim with nonce to prevent replay attacks and allow blacklisting of revoked tokens
 
+            // TODO: Change kid field to key ID when KeyService is refactored
+
             // Build the token
             return Jwts.builder().setSubject(identity)
                     .setIssuer(authenticationToken.getIssuer())
                     .setAudience(authenticationToken.getIssuer())
-                    .claim("preferred_username", username)
+                    .claim(USERNAME_CLAIM, username)
+                    .claim(KEY_ID_CLAIM, identity)
                     .setExpiration(expiration.getTime())
                     .setIssuedAt(Calendar.getInstance().getTime())
                     .signWith(SIGNATURE_ALGORITHM, keyBytes).compact();
         } catch (NullPointerException | AdministrationException e) {
-            // TODO: Remove exception handling and pass through
-            final String errorMessage = "Could not retrieve the signing key for JWT";
+            final String errorMessage = "Could not retrieve the signing key for JWT for " + identity;
             logger.error(errorMessage, e);
             throw new JwtException(errorMessage, e);
         }

http://git-wip-us.apache.org/repos/asf/nifi/blob/7d04dfea/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
index a7e763e..c9107b9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
@@ -9,6 +9,7 @@ import org.apache.nifi.web.security.token.LoginAuthenticationToken;
 import org.codehaus.jettison.json.JSONObject;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.slf4j.Logger;
@@ -38,14 +39,38 @@ public class JwtServiceTest {
      * These constant strings were generated using the tool at http://jwt.io
      */
 
-    private static final String VALID_SIGNED_TOKEN = "";
-    private static final String INVALID_SIGNED_TOKEN = "";
-    private static final String VALID_UNSIGNED_TOKEN = "";
-    private static final String INVALID_UNSIGNED_TOKEN = "";
-    private static final String VALID_MALSIGNED_TOKEN = "";
-    private static final String INVALID_MALSIGNED_TOKEN = "";
+    private static final String VALID_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.8fUgH9jLvE1essgrcoV8OCyDhXvSXUH_1xqeqDqWycU";
+
+    // This token has an empty subject field
+    private static final String INVALID_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.jcjRBLtDzREmdjkJf3xry-ucyCmSRygBaP-HCWBkwlI";
+
+    private static final String VALID_UNSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9";
+
+    // This token has an empty subject field
+    private static final String INVALID_UNSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9";
+
+    // Algorithm field is "none"
+    private static final String VALID_MALSIGNED_TOKEN = "eyJhbGciOiJub25lIn0.eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoxNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.mPO_wMNMl_zjMNevhNvUoXbSJ9Kx6jAe5OxDIAzKQbI";
+
+    // Algorithm field is "none" and no signature is present
+    private static final String VALID_MALSIGNED_NO_SIG_TOKEN = "eyJhbGciOiJub25lIn0.eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.";
+
+    // This token has an empty subject field
+    private static final String INVALID_MALSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoxNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.WAwmUY4KHKV2oARNodkqDkbZsfRXGZfD2Ccy64GX9QF";
+
+    private static final String EXPIRED_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoxNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.y3M1TlzXZ80cVTkfcNxaHpq6aAlM1y2HGCZWEOcvmSU";
+
+    // Subject is "mgilman" but signed with "alopresto" key
+    private static final String IMPOSTER_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJtZ2lsbWFuIiwiaXNzIjoiTW9ja0lkZW50aXR5UHJvdmlkZXIiLCJhdWQiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsInByZWZlcnJlZF91c2VybmFtZSI6ImFsb3ByZXN0byIsImtpZCI6ImFsb3ByZXN0byIsImV4cCI6MjQ0NzgwODc2MSwiaWF0IjoxNDQ3ODA4NzAxfQ.l-9nHmYTEMgLshX8qCEqbc2O4BH_GYBVQIFkUKsJvLA";
+
+    // Issuer field is set to unknown provider
+    private static final String UNKNOWN_ISSUER_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJVbmtub3duSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.SAd9tyNwSaijWet9wvAWSNmpxmPSK4XQuLx7h3ARqBo";
+
+    // Issuer field is absent
+    private static final String NO_ISSUER_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbG9wcmVzdG8iLCJhdWQiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsInByZWZlcnJlZF91c2VybmFtZSI6ImFsb3ByZXN0byIsImtpZCI6ImFsb3ByZXN0byIsImV4cCI6MjQ0NzgwODc2MSwiaWF0IjoxNDQ3ODA4NzAxfQ.Hdha7K69sz6224vidvuZ6A6UdGLdZ_U1egS0txuVXAk";
 
     private static final String DEFAULT_HEADER = "{\"alg\":\"HS256\"}";
+    private static final String DEFAULT_IDENTITY = "alopresto";
 
     private static final String TOKEN_DELIMITER = ".";
 
@@ -93,6 +118,7 @@ public class JwtServiceTest {
     @Before
     public void setUp() throws Exception {
         mockKeyService = Mockito.mock(KeyService.class);
+        when(mockKeyService.getKey(anyString())).thenReturn(HMAC_SECRET);
         when(mockKeyService.getOrCreateKey(anyString())).thenReturn(HMAC_SECRET);
         jwtService = new JwtService(mockKeyService);
     }
@@ -104,45 +130,156 @@ public class JwtServiceTest {
 
     @Test
     public void testShouldGetAuthenticationForValidToken() throws Exception {
+        // Arrange
+        String token = VALID_SIGNED_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
 
+        // Assert
+        assertEquals("Identity", DEFAULT_IDENTITY, identity);
     }
 
-    @Test
+    @Test(expected = JwtException.class)
     public void testShouldNotGetAuthenticationForInvalidToken() throws Exception {
         // Arrange
         String token = INVALID_SIGNED_TOKEN;
 
-        String header = "{" +
-                "  \"alg\":\"HS256\"" +
-                "}";
-        String payload = "{" +
-                "  \"sub\":\"alopresto\"," +
-                "  \"preferred_username\":\"alopresto\"," +
-                "  \"exp\":2895419760" +
-                "}";
-
         // Act
-        logger.info("Test token: " + generateHS256Token(header, payload, true, true));
-
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
 
         // Assert
 
-
+        // Should fail
     }
 
-    @Test
+    @Test(expected = JwtException.class)
     public void testShouldNotGetAuthenticationForEmptyToken() throws Exception {
+        // Arrange
+        String token = "";
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
 
+        // Assert
+
+        // Should fail
     }
 
-    @Test
+    @Test(expected = JwtException.class)
     public void testShouldNotGetAuthenticationForUnsignedToken() throws Exception {
+        // Arrange
+        String token = VALID_UNSIGNED_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
 
+        // Should fail
     }
 
-    @Test
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForMalsignedToken() throws Exception {
+        // Arrange
+        String token = VALID_MALSIGNED_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+
+        // Should fail
+    }
+
+    @Test(expected = JwtException.class)
     public void testShouldNotGetAuthenticationForTokenWithWrongAlgorithm() throws Exception {
+        // Arrange
+        String token = VALID_MALSIGNED_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+
+        // Should fail
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForTokenWithWrongAlgorithmAndNoSignature() throws Exception {
+        // Arrange
+        String token = VALID_MALSIGNED_NO_SIG_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+
+        // Should fail
+    }
+
+    @Ignore("Not yet implemented")
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForTokenFromUnknownIdentityProvider() throws Exception {
+        // Arrange
+        String token = UNKNOWN_ISSUER_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+
+        // Should fail
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForTokenFromEmptyIdentityProvider() throws Exception {
+        // Arrange
+        String token = NO_ISSUER_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+
+        // Should fail
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForExpiredToken() throws Exception {
+        // Arrange
+        String token = EXPIRED_SIGNED_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
+
+        // Should fail
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGetAuthenticationForImposterToken() throws Exception {
+        // Arrange
+        String token = IMPOSTER_SIGNED_TOKEN;
+
+        // Act
+        String identity = jwtService.getAuthenticationFromToken(token);
+        logger.debug("Extracted identity: " + identity);
+
+        // Assert
 
+        // Should fail
     }
 
     @Test
@@ -176,6 +313,7 @@ public class JwtServiceTest {
         claims.put("iss", "MockIdentityProvider");
         claims.put("aud", "MockIdentityProvider");
         claims.put("preferred_username", "alopresto");
+        claims.put("kid", "alopresto");
         claims.put("exp", TOKEN_EXPIRATION_SEC);
         claims.put("iat", ISSUED_AT_SEC);
         logger.trace("JSON Object to String: " + new JSONObject(claims).toString());


[13/51] [abbrv] nifi git commit: NIFI-655. - Updated JwtService and JwtServiceTest to use Key POJO instead of raw String key from KeyService.

Posted by mc...@apache.org.
NIFI-655. - Updated JwtService and JwtServiceTest to use Key POJO instead of raw String key from KeyService.

Signed-off-by: Matt Gilman <ma...@gmail.com>


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

Branch: refs/heads/master
Commit: 0fa68a5bac79178836e02627d0030e595a1d8348
Parents: c94d027
Author: Andy LoPresto <an...@andylopresto.com>
Authored: Wed Nov 18 12:19:00 2015 -0800
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Nov 18 15:41:08 2015 -0500

----------------------------------------------------------------------
 .../org/apache/nifi/web/api/AccessResource.java |   4 +-
 .../token/LoginAuthenticationToken.java         |   4 +-
 .../nifi/web/security/jwt/JwtServiceTest.java   | 120 ++++++++++++++-----
 3 files changed, 97 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/0fa68a5b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
index 27d7d29..5e52186 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -336,7 +336,7 @@ public class AccessResource extends ApplicationResource {
                 }
 
                 // create the authentication token
-                // TODO: Some Spring beans return "" for getClass().getSimpleName(). Using getName() temporarily
+                // TODO: Some Spring beans return "" for getClass().getSimpleName(). Using getName() temporarily, the way that NAR loader works, this value will always be an anonymous inner class
                 loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getIdentity(), expiration, loginIdentityProvider.getClass().getName());
             } catch (final InvalidLoginCredentialsException ilce) {
                 throw new IllegalArgumentException("The supplied username and password are not valid.", ilce);
@@ -358,7 +358,7 @@ public class AccessResource extends ApplicationResource {
             authorizeProxyIfNecessary(proxyChain);
 
             // create the authentication token
-            // TODO: Some Spring beans return "" for getClass().getSimpleName(). Using getName() temporarily
+            // TODO: Some Spring beans return "" for getClass().getSimpleName(). Using getName() temporarilyy, the way that NAR loader works, this value will always be an anonymous inner class
             loginAuthenticationToken = new LoginAuthenticationToken(proxyChain.get(0), authenticationResponse.getExpiration(), certificateIdentityProvider.getClass().getName());
         }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/0fa68a5b/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 a06ee89..3591239 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
@@ -72,7 +72,9 @@ public class LoginAuthenticationToken extends AbstractAuthenticationToken {
     }
 
     /**
-     * Returns the expiration instant in milliseconds. This value is an absolute point in time (i.e. Nov 16, 2015 11:30:00.000 GMT), not a relative time (i.e. 60 minutes). It is calculated by adding the relative expiration from the constructor to the timestamp at object creation.
+     * Returns the expiration instant in milliseconds. This value is an absolute point in time (i.e. Nov
+     * 16, 2015 11:30:00.000 GMT), not a relative time (i.e. 60 minutes). It is calculated by adding the
+     * relative expiration from the constructor to the timestamp at object creation.
      *
      * @return the expiration in millis
      */

http://git-wip-us.apache.org/repos/asf/nifi/blob/0fa68a5b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
index ea3e122..6009c1f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
@@ -1,3 +1,19 @@
+/*
+ * 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.jwt;
 
 import io.jsonwebtoken.JwtException;
@@ -5,6 +21,7 @@ import org.apache.commons.codec.CharEncoding;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.nifi.admin.service.AdministrationException;
 import org.apache.nifi.admin.service.KeyService;
+import org.apache.nifi.key.Key;
 import org.apache.nifi.web.security.token.LoginAuthenticationToken;
 import org.codehaus.jettison.json.JSONObject;
 import org.junit.After;
@@ -22,17 +39,13 @@ import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 import java.util.LinkedHashMap;
 import java.util.Map;
-import org.apache.nifi.key.Key;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
-import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.when;
 
-/**
- * Created by alopresto on 11/11/15.
- */
 public class JwtServiceTest {
 
     private static final Logger logger = LoggerFactory.getLogger(JwtServiceTest.class);
@@ -41,35 +54,76 @@ public class JwtServiceTest {
      * These constant strings were generated using the tool at http://jwt.io
      */
 
-    private static final String VALID_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.8fUgH9jLvE1essgrcoV8OCyDhXvSXUH_1xqeqDqWycU";
+    private static final String VALID_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
+            ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRl" +
+            "ciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZ" +
+            "XJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoxLCJleHAiOjI0NDc4MDg3NjEsIm" +
+            "lhdCI6MTQ0NzgwODcwMX0.r6aGZ6FNNYMOpcXW8BK2VYaQeX1uO0Aw1KJfjB3Q1DU";
 
     // This token has an empty subject field
-    private static final String INVALID_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.jcjRBLtDzREmdjkJf3xry-ucyCmSRygBaP-HCWBkwlI";
+    private static final String INVALID_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
+            ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZG" +
+            "VudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvI" +
+            "iwia2lkIjoxLCJleHAiOjI0NDc4MDg3NjEsImlhdCI6MTQ0NzgwODcwMX0" +
+            ".x_1p2M6E0vwWHWMujIUnSL3GkFoDqqICllRxo2SMNaw";
 
-    private static final String VALID_UNSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9";
+    private static final String VALID_UNSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
+            ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZC" +
+            "I6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJl" +
+            "c3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9";
 
     // This token has an empty subject field
-    private static final String INVALID_UNSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9";
+    private static final String INVALID_UNSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
+            ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVu" +
+            "dGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoi" +
+            "YWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9";
 
     // Algorithm field is "none"
-    private static final String VALID_MALSIGNED_TOKEN = "eyJhbGciOiJub25lIn0.eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoxNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.mPO_wMNMl_zjMNevhNvUoXbSJ9Kx6jAe5OxDIAzKQbI";
+    private static final String VALID_MALSIGNED_TOKEN = "eyJhbGciOiJub25lIn0" +
+            ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZC" +
+            "I6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJl" +
+            "c3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoxNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9" +
+            ".mPO_wMNMl_zjMNevhNvUoXbSJ9Kx6jAe5OxDIAzKQbI";
 
     // Algorithm field is "none" and no signature is present
-    private static final String VALID_MALSIGNED_NO_SIG_TOKEN = "eyJhbGciOiJub25lIn0.eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.";
+    private static final String VALID_MALSIGNED_NO_SIG_TOKEN = "eyJhbGciOiJub25lIn0" +
+            ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY" +
+            "2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIj" +
+            "oiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.";
 
     // This token has an empty subject field
-    private static final String INVALID_MALSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoxNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.WAwmUY4KHKV2oARNodkqDkbZsfRXGZfD2Ccy64GX9QF";
-
-    private static final String EXPIRED_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoxNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.y3M1TlzXZ80cVTkfcNxaHpq6aAlM1y2HGCZWEOcvmSU";
+    private static final String INVALID_MALSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
+            ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVud" +
+            "Gl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYW" +
+            "xvcHJlc3RvIiwiZXhwIjoxNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.WAwmUY4KHKV2oARNodkqDkbZsfRXGZfD2Ccy64GX9QF";
+
+    // This token is signed but expired
+    private static final String EXPIRED_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
+            ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik" +
+            "1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvc" +
+            "HJlc3RvIiwia2lkIjoxLCJleHAiOjE0NDc4MDg3NjEsImlhdCI6MTQ0NzgwODcw" +
+            "MX0.ZPDIhNKuL89vTGXcuztOYaGifwcrQy_gid4j8Sspmto";
 
     // Subject is "mgilman" but signed with "alopresto" key
-    private static final String IMPOSTER_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJtZ2lsbWFuIiwiaXNzIjoiTW9ja0lkZW50aXR5UHJvdmlkZXIiLCJhdWQiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsInByZWZlcnJlZF91c2VybmFtZSI6ImFsb3ByZXN0byIsImtpZCI6ImFsb3ByZXN0byIsImV4cCI6MjQ0NzgwODc2MSwiaWF0IjoxNDQ3ODA4NzAxfQ.l-9nHmYTEMgLshX8qCEqbc2O4BH_GYBVQIFkUKsJvLA";
+    private static final String IMPOSTER_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
+            ".eyJzdWIiOiJtZ2lsbWFuIiwiaXNzIjoiTW9ja0lkZW50aXR5UHJvdmlkZXIiLCJ" +
+            "hdWQiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsInByZWZlcnJlZF91c2VybmFtZSI" +
+            "6ImFsb3ByZXN0byIsImtpZCI6MSwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc" +
+            "4MDg3MDF9.aw5OAvLTnb_sHmSQOQzW-A7NImiZgXJ2ngbbNL2Ymkc";
 
     // Issuer field is set to unknown provider
-    private static final String UNKNOWN_ISSUER_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJVbmtub3duSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.SAd9tyNwSaijWet9wvAWSNmpxmPSK4XQuLx7h3ARqBo";
+    private static final String UNKNOWN_ISSUER_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
+            ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJVbmtub3duSWRlbnRpdHlQcm92aWRlciIsIm" +
+            "F1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxv" +
+            "cHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9" +
+            ".SAd9tyNwSaijWet9wvAWSNmpxmPSK4XQuLx7h3ARqBo";
 
     // Issuer field is absent
-    private static final String NO_ISSUER_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbG9wcmVzdG8iLCJhdWQiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsInByZWZlcnJlZF91c2VybmFtZSI6ImFsb3ByZXN0byIsImtpZCI6ImFsb3ByZXN0byIsImV4cCI6MjQ0NzgwODc2MSwiaWF0IjoxNDQ3ODA4NzAxfQ.Hdha7K69sz6224vidvuZ6A6UdGLdZ_U1egS0txuVXAk";
+    private static final String NO_ISSUER_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
+            ".eyJzdWIiOiJhbG9wcmVzdG8iLCJhdWQiOiJNb2NrSWRlbnRpdHlQcm92a" +
+            "WRlciIsInByZWZlcnJlZF91c2VybmFtZSI6ImFsb3ByZXN0byIsImtpZCI" +
+            "6MSwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.6kDjDanA" +
+            "g0NQDb3C8FmgbBAYDoIfMAEkF4WMVALsbJA";
 
     private static final String DEFAULT_HEADER = "{\"alg\":\"HS256\"}";
     private static final String DEFAULT_IDENTITY = "alopresto";
@@ -88,7 +142,8 @@ public class JwtServiceTest {
         return generateHS256Token(rawHeader, rawPayload, HMAC_SECRET, isValid, isSigned);
     }
 
-    private String generateHS256Token(String rawHeader, String rawPayload, String hmacSecret, boolean isValid, boolean isSigned) {
+    private String generateHS256Token(String rawHeader, String rawPayload, String hmacSecret, boolean isValid,
+            boolean isSigned) {
         try {
             logger.info("Generating token for " + rawHeader + " + " + rawPayload);
 
@@ -109,7 +164,8 @@ public class JwtServiceTest {
         }
     }
 
-    private String generateHMAC(String hmacSecret, String body) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
+    private String generateHMAC(String hmacSecret, String body) throws NoSuchAlgorithmException,
+            UnsupportedEncodingException, InvalidKeyException {
         Mac hmacSHA256 = Mac.getInstance("HmacSHA256");
         SecretKeySpec secret_key = new SecretKeySpec(hmacSecret.getBytes("UTF-8"), "HmacSHA256");
         hmacSHA256.init(secret_key);
@@ -120,8 +176,8 @@ public class JwtServiceTest {
     @Before
     public void setUp() throws Exception {
         final Key key = new Key();
-        key.setId(0);
-        key.setIdentity(HMAC_SECRET);
+        key.setId(1);
+        key.setIdentity(DEFAULT_IDENTITY);
         key.setKey(HMAC_SECRET);
         
         mockKeyService = Mockito.mock(KeyService.class);
@@ -295,7 +351,9 @@ public class JwtServiceTest {
 
         // Token expires in 60 seconds
         final int EXPIRATION_MILLIS = 60000;
-        LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken("alopresto", EXPIRATION_MILLIS, "MockIdentityProvider");
+        LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken("alopresto",
+                EXPIRATION_MILLIS,
+                "MockIdentityProvider");
         logger.debug("Generating token for " + loginAuthenticationToken);
 
         final String EXPECTED_HEADER = DEFAULT_HEADER;
@@ -311,7 +369,8 @@ public class JwtServiceTest {
 
         // Split the token, decode the middle section, and form a new String
         final String DECODED_PAYLOAD = new String(Base64.decodeBase64(token.split("\\.")[1].getBytes()));
-        final long ISSUED_AT_SEC = Long.valueOf(DECODED_PAYLOAD.substring(DECODED_PAYLOAD.lastIndexOf(":") + 1, DECODED_PAYLOAD.length() - 1));
+        final long ISSUED_AT_SEC = Long.valueOf(DECODED_PAYLOAD.substring(DECODED_PAYLOAD.lastIndexOf(":") + 1,
+                DECODED_PAYLOAD.length() - 1));
         logger.trace("Actual token was issued at " + ISSUED_AT_SEC);
 
         // Always use LinkedHashMap to enforce order of the keys because the signature depends on order
@@ -320,7 +379,7 @@ public class JwtServiceTest {
         claims.put("iss", "MockIdentityProvider");
         claims.put("aud", "MockIdentityProvider");
         claims.put("preferred_username", "alopresto");
-        claims.put("kid", "alopresto");
+        claims.put("kid", 1);
         claims.put("exp", TOKEN_EXPIRATION_SEC);
         claims.put("iat", ISSUED_AT_SEC);
         logger.trace("JSON Object to String: " + new JSONObject(claims).toString());
@@ -351,7 +410,8 @@ public class JwtServiceTest {
     public void testShouldNotGenerateTokenWithEmptyIdentity() throws Exception {
         // Arrange
         final int EXPIRATION_MILLIS = 60000;
-        LoginAuthenticationToken emptyIdentityLoginAuthenticationToken = new LoginAuthenticationToken("", EXPIRATION_MILLIS, "MockIdentityProvider");
+        LoginAuthenticationToken emptyIdentityLoginAuthenticationToken = new LoginAuthenticationToken("",
+                EXPIRATION_MILLIS, "MockIdentityProvider");
         logger.debug("Generating token for " + emptyIdentityLoginAuthenticationToken);
 
         // Act
@@ -366,7 +426,8 @@ public class JwtServiceTest {
     public void testShouldNotGenerateTokenWithNullIdentity() throws Exception {
         // Arrange
         final int EXPIRATION_MILLIS = 60000;
-        LoginAuthenticationToken nullIdentityLoginAuthenticationToken = new LoginAuthenticationToken(null, EXPIRATION_MILLIS, "MockIdentityProvider");
+        LoginAuthenticationToken nullIdentityLoginAuthenticationToken = new LoginAuthenticationToken(null,
+                EXPIRATION_MILLIS, "MockIdentityProvider");
         logger.debug("Generating token for " + nullIdentityLoginAuthenticationToken);
 
         // Act
@@ -381,12 +442,15 @@ public class JwtServiceTest {
     public void testShouldNotGenerateTokenWithMissingKey() throws Exception {
         // Arrange
         final int EXPIRATION_MILLIS = 60000;
-        LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken("alopresto", EXPIRATION_MILLIS, "MockIdentityProvider");
+        LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken("alopresto",
+                EXPIRATION_MILLIS,
+                "MockIdentityProvider");
         logger.debug("Generating token for " + loginAuthenticationToken);
 
         // Set up the bad key service
         KeyService missingKeyService = Mockito.mock(KeyService.class);
-        when(missingKeyService.getOrCreateKey(anyString())).thenThrow(new AdministrationException("Could not find a key for that user"));
+        when(missingKeyService.getOrCreateKey(anyString())).thenThrow(new AdministrationException("Could not find a " +
+                "key for that user"));
         jwtService = new JwtService(missingKeyService);
 
         // Act


[47/51] [abbrv] nifi git commit: NIFI-655: - Removing unused imports.

Posted by mc...@apache.org.
NIFI-655:
- Removing unused imports.

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

Branch: refs/heads/master
Commit: 2b0819a5f2d2d22085caf3d7a9e7779caf91124e
Parents: 014b2ac
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 30 16:33:11 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 30 16:33:11 2015 -0500

----------------------------------------------------------------------
 .../main/java/org/apache/nifi/web/api/ApplicationResource.java    | 3 ---
 1 file changed, 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/2b0819a5/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
index 38c0ae3..256d41f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
@@ -44,7 +44,6 @@ import org.apache.nifi.action.Operation;
 import org.apache.nifi.cluster.context.ClusterContext;
 import org.apache.nifi.cluster.context.ClusterContextThreadLocal;
 import org.apache.nifi.cluster.manager.impl.WebClusterManager;
-import org.apache.nifi.web.security.ProxiedEntitiesUtils;
 import org.apache.nifi.web.security.user.NiFiUserDetails;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.api.entity.Entity;
@@ -53,9 +52,7 @@ import org.apache.nifi.web.util.WebUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
-import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
-import org.apache.nifi.web.security.user.NiFiUserUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.core.Authentication;


[20/51] [abbrv] nifi git commit: NIFI-655: - Pre-populating the login-identity-providers.xml file with necessary properties and documentation. - Renaming the Authentication Duration property name.

Posted by mc...@apache.org.
NIFI-655:
- Pre-populating the login-identity-providers.xml file with necessary properties and documentation.
- Renaming the Authentication Duration property name.

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

Branch: refs/heads/master
Commit: e61a36908917c4053417cf2717acdc4711962b2b
Parents: 91573cb
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 23 09:19:32 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 23 09:19:32 2015 -0500

----------------------------------------------------------------------
 .../resources/conf/login-identity-providers.xml | 70 ++++++++++++++++++++
 .../java/org/apache/nifi/ldap/LdapProvider.java |  6 +-
 2 files changed, 73 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/e61a3690/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/login-identity-providers.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/login-identity-providers.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/login-identity-providers.xml
index 191637b..926ed9a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/login-identity-providers.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/login-identity-providers.xml
@@ -19,4 +19,74 @@
     must be specified in the nifi.properties file.
 -->
 <loginIdentityProviders>
+    <!--
+        Identity Provider for users logging in with username/password against an LDAP server.
+        
+        'Authentication Strategy' - How the connection to the LDAP server is authenticated. Possible
+            values are ANONYMOUS, SIMPLE, or START_TLS.
+        
+        'Manager DN' - The DN of the manager that is used to bind to the LDAP server to search for users.
+        'Manager Password' - The password of the manager that is used to bind to the LDAP server to
+            search for users.
+            
+        'TLS - Keystore' - Path to the Keystore that is used when connecting to LDAP using START_TLS.
+        'TLS - Keystore Password' - Password for the Keystore that is used when connecting to LDAP
+            using START_TLS.
+        'TLS - Keystore Type' - Type of the Keystore that is used when connecting to LDAP using
+            START_TLS (i.e. JKS or PKCS12).
+        'TLS - Truststore' - Path to the Truststore that is used when connecting to LDAP using START_TLS.
+        'TLS - Truststore Password' - Password for the Truststore that is used when connecting to
+            LDAP using START_TLS.
+        'TLS - Truststore Type' - Type of the Truststore that is used when connecting to LDAP using
+            START_TLS (i.e. JKS or PKCS12).
+        'TLS - Client Auth' - Client authentication policy when connecting to LDAP using START_TLS.
+            Possible values are REQUIRED, WANT, NONE.
+        'TLS - Protocol' - Protocol to use when connecting to LDAP using START_TLS. (i.e. TLS,
+            TLSv1.1, TLSv1.2, etc).
+        'TLS - Shutdown Gracefully' - Specifies whether the TLS should be shut down gracefully 
+            before the target context is closed. Defaults to false.
+            
+        'Referral Strategy' - Strategy for handling referrals. Possible values are FOLLOW, IGNORE, THROW.
+        'Connect Timeout' - Duration of connect timeout. (i.e. 10 secs).
+        'Read Timeout' - Duration of read timeout. (i.e. 10 secs).
+       
+        'Url' - Url of the LDAP servier (i.e. ldap://<hostname>:<port>).
+        'User Search Base' - Base DN for searching for users (i.e. CN=Users,DC=example,DC=com).
+        'User Search Filter' - Filter for searching for users against the 'User Search Base'.
+            (i.e. sAMAccountName={0}). The user specified name is inserted into '{0}'.
+            
+        'Authentication Expiration' - The duration of how long the user authentication is valid
+            for. If the user never logs out, they will be required to log back in following
+            this duration.
+    -->
+    <!-- To enable the ldap-provider remove 2 lines. This is 1 of 2. 
+    <provider>
+        <identifier>ldap-provider</identifier>
+        <class>org.apache.nifi.ldap.LdapProvider</class>
+        <property name="Authentication Strategy">START_TLS</property>
+
+        <property name="Manager DN"></property>
+        <property name="Manager Password"></property>
+
+        <property name="TLS - Keystore"></property>
+        <property name="TLS - Keystore Password"></property>
+        <property name="TLS - Keystore Type"></property>
+        <property name="TLS - Truststore"></property>
+        <property name="TLS - Truststore Password"></property>
+        <property name="TLS - Truststore Type"></property>
+        <property name="TLS - Client Auth"></property>
+        <property name="TLS - Protocol"></property>
+        <property name="TLS - Shutdown Gracefully"></property>
+        
+        <property name="Referral Strategy">FOLLOW</property>
+        <property name="Connect Timeout">10 secs</property>
+        <property name="Read Timeout">10 secs</property>
+
+        <property name="Url"></property>
+        <property name="User Search Base"></property>
+        <property name="User Search Filter"></property>
+
+        <property name="Expiration Duration">1 day</property>
+    </provider>
+    To enable the ldap-provider remove 2 lines. This is 2 of 2. -->
 </loginIdentityProviders>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/e61a3690/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
index cfa0bf8..4dc7ea4 100644
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
@@ -75,9 +75,9 @@ public class LdapProvider implements LoginIdentityProvider {
 
     @Override
     public final void onConfigured(final LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException {
-        final String rawExpiration = configurationContext.getProperty("Expiration Duration");
+        final String rawExpiration = configurationContext.getProperty("Authentication Expiration");
         if (StringUtils.isBlank(rawExpiration)) {
-            throw new ProviderCreationException("The Expiration Duration must be specified.");
+            throw new ProviderCreationException("The Authentication Expiration must be specified.");
         }
 
         try {
@@ -98,7 +98,7 @@ public class LdapProvider implements LoginIdentityProvider {
         if (!baseEnvironment.isEmpty()) {
             context.setBaseEnvironmentProperties(baseEnvironment);
         }
-        
+
         // authentication strategy
         final String rawAuthenticationStrategy = configurationContext.getProperty("Authentication Strategy");
         final LdapAuthenticationStrategy authenticationStrategy;


[41/51] [abbrv] nifi git commit: NIFI-655: - Ensuring anonymous user label and login links are rendered when appropriate. - Ensuring responses are accurate when making requests with a token when user log in is not supported.

Posted by mc...@apache.org.
NIFI-655:
- Ensuring anonymous user label and login links are rendered when appropriate.
- Ensuring responses are accurate when making requests with a token when user log in is not supported.

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

Branch: refs/heads/master
Commit: 64beeef5937e972c502888abc84adf1328295d04
Parents: c1cc165
Author: Matt Gilman <ma...@gmail.com>
Authored: Fri Nov 27 14:13:40 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Fri Nov 27 14:13:40 2015 -0500

----------------------------------------------------------------------
 .../web/NiFiWebApiSecurityConfiguration.java    | 15 +++++-----
 .../org/apache/nifi/web/api/AccessResource.java |  5 ++++
 .../security/jwt/JwtAuthenticationFilter.java   |  4 +++
 .../nifi-web-ui/src/main/webapp/css/header.css  |  1 +
 .../webapp/js/nf/canvas/nf-canvas-header.js     | 31 ++++++++++----------
 .../src/main/webapp/js/nf/login/nf-login.js     |  2 +-
 6 files changed, 34 insertions(+), 24 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/64beeef5/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 73e9640..0680b74 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
@@ -93,10 +93,8 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
         // x509
         http.addFilterAfter(x509FilterBean(), AnonymousAuthenticationFilter.class);
 
-        // jwt - consider when configured for log in
-        if (loginIdentityProvider != null) {
-            http.addFilterAfter(jwtFilterBean(), AnonymousAuthenticationFilter.class);
-        }
+        // jwt
+        http.addFilterAfter(jwtFilterBean(), AnonymousAuthenticationFilter.class);
     }
 
     @Bean
@@ -124,12 +122,15 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
 
     @Bean
     public JwtAuthenticationFilter jwtFilterBean() throws Exception {
-        // only consider the jwt authentication filter when configured for login
-        if (jwtAuthenticationFilter == null && loginIdentityProvider != null) {
+        if (jwtAuthenticationFilter == null) {
             jwtAuthenticationFilter = new JwtAuthenticationFilter();
             jwtAuthenticationFilter.setProperties(properties);
-            jwtAuthenticationFilter.setJwtService(jwtService);
             jwtAuthenticationFilter.setAuthenticationManager(authenticationManager());
+
+            // only consider the tokens when configured for login
+            if (loginIdentityProvider != null) {
+                jwtAuthenticationFilter.setJwtService(jwtService);
+            }
         }
         return jwtAuthenticationFilter;
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/64beeef5/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
index 326aa00..f2b23c2 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -190,6 +190,11 @@ public class AccessResource extends ApplicationResource {
                     accessStatus.setStatus(AccessStatusDTO.Status.UNKNOWN.name());
                     accessStatus.setMessage("No credentials supplied, unknown user.");
                 } else {
+                    // not currently configured for username/password login, don't accept existing tokens
+                    if (loginIdentityProvider == null) {
+                        throw new IllegalStateException("This NiFi is not configured to support username/password logins.");
+                    }
+
                     try {
                         // Extract the Base64 encoded token from the Authorization header
                         final String token = StringUtils.substringAfterLast(authorization, " ");

http://git-wip-us.apache.org/repos/asf/nifi/blob/64beeef5/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.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/JwtAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
index 20675fb..246cbd7 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
@@ -56,6 +56,10 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
         if (authorization == null) {
             return null;
         } else {
+            if (jwtService == null) {
+                throw new InvalidAuthenticationException("NiFi is not configured to support username/password logins.");
+            }
+
             // Extract the Base64 encoded token from the Authorization header
             final String token = StringUtils.substringAfterLast(authorization, " ");
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/64beeef5/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 e0f8347..200f6bb 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
@@ -521,6 +521,7 @@ div.search-glass-pane {
     font-weight: bold;
     max-width: 250px;
     text-overflow: ellipsis;
+    line-height: normal;
     overflow: hidden;
 }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/64beeef5/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.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-header.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js
index 7d63534..09cf3c5 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js
@@ -141,28 +141,27 @@ nf.CanvasHeader = (function () {
                 nf.Shell.showPage(config.urls.helpDocument);
             });
 
-            // show the login link if supported and user is currently anonymous
-            var isAnonymous = $('#current-user').text() === nf.Common.ANONYMOUS_USER_TEXT;
-            if (supportsLogin === true && isAnonymous) {
-                // login link
-                $('#login-link').click(function () {
-                    nf.Shell.showPage('login', false);
-                });
-            } else {
-                $('#login-link-container').css('display', 'none');
-            }
-
-            // if login is not supported, don't show the current user
-            if (supportsLogin === false) {
-                $('#current-user-container').css('display', 'none');
-            }
-
+            // login link
+            $('#login-link').click(function () {
+                nf.Shell.showPage('login', false);
+            });
+            
             // logout link
             $('#logout-link').click(function () {
                 nf.Storage.removeItem("jwt");
                 window.location = '/nifi';
             });
 
+            // if the user is not anonymous or accessing via http
+            if ($('#current-user').text() !== nf.Common.ANONYMOUS_USER_TEXT || location.protocol === 'http:') {
+                $('#login-link-container').css('display', 'none');
+            }
+
+            // if accessing via http, don't show the current user
+            if (location.protocol === 'http:') {
+                $('#current-user-container').css('display', 'none');
+            }
+
             // initialize the new template dialog
             $('#new-template-dialog').modal({
                 headerText: 'Create Template',

http://git-wip-us.apache.org/repos/asf/nifi/blob/64beeef5/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 f2c9d2a..697794c 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
@@ -285,7 +285,7 @@ nf.Login = (function () {
                 // if login is required, verify its supported
                 if (accessConfig.supportsLogin === false && needsLogin === true) {
                     $('#login-message-title').text('Access Denied');
-                    $('#login-message').text('This NiFi is not configured to support login.');
+                    $('#login-message').text('This NiFi is not configured to support username/password logins.');
                     showMessage = true;
                     needsLogin = false;
                 }


[38/51] [abbrv] nifi git commit: NIFI-655: - Update admin guide with documentation for username/password authentication. - Setting default anonymous roles to none. - Making account status messages to users more clear. - Deleting user keys when an admin r

Posted by mc...@apache.org.
NIFI-655:
- Update admin guide with documentation for username/password authentication.
- Setting default anonymous roles to none.
- Making account status messages to users more clear.
- Deleting user keys when an admin revokes/deletes an account.
- Updating authentication filter to error back whenever authentication fails.

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

Branch: refs/heads/master
Commit: c073253366acf3bab28e530c1deab512735e5d7c
Parents: 1312bde
Author: Matt Gilman <ma...@gmail.com>
Authored: Wed Nov 25 14:17:23 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Nov 25 14:17:23 2015 -0500

----------------------------------------------------------------------
 nifi-assembly/pom.xml                           |   2 +-
 .../org/apache/nifi/util/NiFiProperties.java    |   8 +-
 .../src/main/asciidoc/administration-guide.adoc | 111 ++++++++++++-
 .../nifi/admin/KeyDataSourceFactoryBean.java    | 154 -------------------
 .../nifi/admin/UserDataSourceFactoryBean.java   |  20 ++-
 .../java/org/apache/nifi/admin/dao/KeyDAO.java  |   7 +
 .../nifi/admin/dao/impl/StandardKeyDAO.java     |  25 +++
 .../apache/nifi/admin/service/KeyService.java   |  42 -----
 .../apache/nifi/admin/service/UserService.java  |  31 +++-
 .../admin/service/action/DeleteKeysAction.java  |  46 ++++++
 .../admin/service/action/DeleteUserAction.java  |   5 +
 .../admin/service/action/DisableUserAction.java |   5 +
 .../service/action/DisableUserGroupAction.java  |  13 +-
 .../admin/service/impl/StandardKeyService.java  | 126 ---------------
 .../admin/service/impl/StandardUserService.java |  97 +++++++++++-
 .../resources/nifi-administration-context.xml   |  16 --
 .../service/action/DisableUserActionTest.java   |   8 +
 .../src/main/resources/conf/nifi.properties     |   1 +
 .../web/NiFiWebApiSecurityConfiguration.java    |   4 -
 .../org/apache/nifi/web/api/AccessResource.java |  36 +++--
 .../security/NiFiAuthenticationEntryPoint.java  |  71 ---------
 .../web/security/NiFiAuthenticationFilter.java  |  19 ++-
 .../nifi/web/security/jwt/JwtService.java       |  16 +-
 .../resources/nifi-web-security-context.xml     |   2 +-
 .../nifi/web/security/jwt/JwtServiceTest.java   |  14 +-
 .../src/main/webapp/js/nf/login/nf-login.js     |   4 +-
 26 files changed, 416 insertions(+), 467 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-assembly/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-assembly/pom.xml b/nifi-assembly/pom.xml
index 86aa8bc..5f3d5ba 100644
--- a/nifi-assembly/pom.xml
+++ b/nifi-assembly/pom.xml
@@ -362,7 +362,7 @@ language governing permissions and limitations under the License. -->
         <nifi.security.user.login.identity.provider></nifi.security.user.login.identity.provider>
         <nifi.security.x509.principal.extractor />
         <nifi.security.support.new.account.requests />
-        <nifi.security.anonymous.authorities>ROLE_MONITOR,ROLE_DFM,ROLE_ADMIN,ROLE_PROVENANCE,ROLE_NIFI</nifi.security.anonymous.authorities>
+        <nifi.security.anonymous.authorities></nifi.security.anonymous.authorities>
         <nifi.security.ocsp.responder.url />
         <nifi.security.ocsp.responder.certificate />
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
index 809dd09..71f0a59 100644
--- a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
+++ b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
@@ -530,7 +530,13 @@ public class NiFiProperties extends Properties {
 
         final String rawAnonymousAuthorities = getProperty(SECURITY_ANONYMOUS_AUTHORITIES);
         if (!StringUtils.isEmpty(rawAnonymousAuthorities)) {
-            authorities = new HashSet<>(Arrays.asList(rawAnonymousAuthorities.split(",")));
+            authorities = new HashSet<>();
+
+            // parse the raw authorities and trim them
+            final List<String> authoritiesList = Arrays.asList(rawAnonymousAuthorities.split(","));
+            for (final String authority : authoritiesList) {
+                authorities.add(authority.trim());
+            }
         } else {
             authorities = Collections.EMPTY_SET;
         }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-docs/src/main/asciidoc/administration-guide.adoc
----------------------------------------------------------------------
diff --git a/nifi-docs/src/main/asciidoc/administration-guide.adoc b/nifi-docs/src/main/asciidoc/administration-guide.adoc
index b36c28e..dcb1151 100644
--- a/nifi-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/administration-guide.adoc
@@ -146,10 +146,13 @@ NiFi provides several different configuration options for security purposes. The
                             attempt to connect will be provided access as the 'Anonymous' user.
 |`nifi.security.truststoreType` | The type of the Truststore. Must be either `PKCS12` or `JKS`.
 |`nifi.security.truststorePasswd` | The password for the Truststore.
-|`nifi.security.needClientAuth` | Specifies whether or not connecting clients must authenticate themselves. If the Truststore properties are not set, 
-                                this must be `false`. Otherwise, a value of `true` indicates that users will be authenticated and must have 
-                                certificates that are trusted by the Truststore loaded into their web browsers. A value of `false` indicates
-                                that all users should be given access as the 'Anonymous' user.  
+|`nifi.security.needClientAuth` | Specifies whether or not connecting clients must authenticate themselves. Specifically this property is used
+                                by the NiFi cluster protocol. If the Truststore properties are not set, this must be `false`. Otherwise, a value
+                                of `true` indicates that nodes in the cluster will be authenticated and must have certificates that are trusted 
+                                by the Truststores.
+|`nifi.security.anonymous.authorities` | Specifies the roles that should be granted to users that connect over HTTPS anonymously. All users will
+                                be given this level access, however if they have been granted a particular level of access by an administrator
+                                it will take precedence if they access NiFi using a client certificate or once they have logged in. 
 |==================================================================================================================================================
 
 Once the above properties have been configured, we can enable the User Interface to be accessed over HTTPS instead of HTTP. This is accomplished
@@ -159,14 +162,99 @@ be accessible from all network interfaces, a value of `0.0.0.0` should be used.
 
 NOTE: It is important when enabling HTTPS that the `nifi.web.http.port` property be unset.
 
+Similar to `nifi.security.needClientAuth`, the web server can be configured to require certificate based client authentication for users accessing
+the User Interface. In order to do this it must be configured to not support username/password authentication (see below) and not grant access to 
+anonymous users (see `nifi.security.anonymous.authorities` above). Either of these options will configure the web server to WANT certificate based client 
+authentication. This will allow it to support users with certificates and those without that may be logging in with their credentials or those accessing 
+anonymously. If username/password authentication and anonymous access are not configured, the web server will REQUIRE certificate based client authentication.
+
 Now that the User Interface has been secured, we can easily secure Site-to-Site connections and inner-cluster communications, as well. This is
 accomplished by setting the `nifi.remote.input.secure` and `nifi.cluster.protocol.is.secure` properties, respectively, to `true`.
 
 
+User Authentication
+-------------------
+
+NiFi supports user authentication via client certificates or via username/password. Username/password authentication is performed by a 'Login Identity
+Provider'. The Login Identity Provider is a pluggable mechanism for authenticating users via their username/password. Which Login Identity Provider
+to use is configured in two properties in the _nifi.properties_ file.
+
+The `nifi.login.identity.provider.configuration.file` property specifies the configuration file for Login Identity Providers. 
+The `nifi.security.user.login.identity.provider` property indicates which of the configured Login Identity Provider should be
+used. If this property is not configured, NiFi will not support username/password authentication and will require client 
+certificates for authenticating users over HTTPS. By default, this property is not configured meaning that username/password must be
+explicity enabled.
+
+NiFi does not perform user authentication over HTTP. Using HTTP all users will be granted all roles.
+
+Below is an example and description of configuring a Login Identity Provider that integrates with a Directory Server to authenticate users.
+
+----
+<provider>
+    <identifier>ldap-provider</identifier>
+    <class>org.apache.nifi.ldap.LdapProvider</class>
+    <property name="Authentication Strategy">START_TLS</property>
+
+    <property name="Manager DN"></property>
+    <property name="Manager Password"></property>
+
+    <property name="TLS - Keystore"></property>
+    <property name="TLS - Keystore Password"></property>
+    <property name="TLS - Keystore Type"></property>
+    <property name="TLS - Truststore"></property>
+    <property name="TLS - Truststore Password"></property>
+    <property name="TLS - Truststore Type"></property>
+    <property name="TLS - Client Auth"></property>
+    <property name="TLS - Protocol"></property>
+    <property name="TLS - Shutdown Gracefully"></property>
+
+    <property name="Referral Strategy">FOLLOW</property>
+    <property name="Connect Timeout">10 secs</property>
+    <property name="Read Timeout">10 secs</property>
+
+    <property name="Url"></property>
+    <property name="User Search Base"></property>
+    <property name="User Search Filter"></property>
+
+    <property name="Authentication Expiration">12 hours</property>
+</provider>
+----
+
+With this configuration, username/password authentication can be enabled by referencing this provider in _nifi.properties_.
+
+----
+nifi.security.user.login.identity.provider=ldap-provider
+----
+
+[options="header,footer"]
+|==================================================================================================================================================
+| Property Name | Description
+|`Authentication Strategy` | How the connection to the LDAP server is authenticated. Possible values are ANONYMOUS, SIMPLE, or START_TLS.
+|`Manager DN` | The DN of the manager that is used to bind to the LDAP server to search for users.
+|`Manager Password` | The password of the manager that is used to bind to the LDAP server to search for users.
+|`TLS - Keystore` | Path to the Keystore that is used when connecting to LDAP using START_TLS.
+|`TLS - Keystore Password` | Password for the Keystore that is used when connecting to LDAP using START_TLS.
+|`TLS - Keystore Type` | Type of the Keystore that is used when connecting to LDAP using START_TLS (i.e. JKS or PKCS12).
+|`TLS - Truststore` | Path to the Truststore that is used when connecting to LDAP using START_TLS.
+|`TLS - Truststore Password` | Password for the Truststore that is used when connecting to LDAP using START_TLS.
+|`TLS - Truststore Type` | Type of the Truststore that is used when connecting to LDAP using START_TLS (i.e. JKS or PKCS12).
+|`TLS - Client Auth` | Client authentication policy when connecting to LDAP using START_TLS. Possible values are REQUIRED, WANT, NONE.
+|`TLS - Protocol` | Protocol to use when connecting to LDAP using START_TLS. (i.e. TLS, TLSv1.1, TLSv1.2, etc).
+|`TLS - Shutdown Gracefully` | Specifies whether the TLS should be shut down gracefully before the target context is closed. Defaults to false.
+|`Referral Strategy` | Strategy for handling referrals. Possible values are FOLLOW, IGNORE, THROW.
+|`Connect Timeout` | Duration of connect timeout. (i.e. 10 secs).
+|`Read Timeout` | Duration of read timeout. (i.e. 10 secs).
+|`Url` | Url of the LDAP servier (i.e. ldap://<hostname>:<port>).
+|`User Search Base` | Base DN for searching for users (i.e. CN=Users,DC=example,DC=com).
+|`User Search Filter` | Filter for searching for users against the 'User Search Base'. (i.e. sAMAccountName={0}). The user specified name is inserted into '{0}'.
+|`Authentication Expiration` | The duration of how long the user authentication is valid for. If the user never logs out, they will be required to log back in following this duration.
+|==================================================================================================================================================
+
+
 Controlling Levels of Access
 ----------------------------
 
-Once NiFi is configured to run securely as discussed in the previous section, it is necessary
+Once NiFi is configured to run securely and an authentication mechanism is configured, it is necessary
 to configure who will have access to the system and what types of access those people will have.
 NiFi controls this through the user of an 'Authority Provider.' The Authority Provider is a pluggable
 mechanism for providing authorizations to different users. Which Authority Provider to use is configured
@@ -510,7 +598,9 @@ The first section of the _nifi.properties_ file is for the Core Properties. Thes
 |nifi.flowservice.writedelay.interval|When many changes are made to the flow.xml, this property specifies how long to wait before writing out the changes, so as to batch the changes into a single write. The default value is 500 ms.
 |nifi.administrative.yield.duration|If a component allows an unexpected exception to escape, it is considered a bug. As a result, the framework will pause (or administratively yield) the component for this amount of time. This is done so that the component does not use up massive amounts of system resources, since it is known to have problems in the existing state. The default value is 30 sec.
 |nifi.bored.yield.duration|When a component has no work to do (i.e., is "bored"), this is the amount of time it will wait before checking to see if it has new data to work on. This way, it does not use up CPU resources by checking for new work too often. When setting this property, be aware that it could add extra latency for components that do not constantly have work to do, as once they go into this "bored" state, they will wait this amount of time before checking for more work. The default value is 10 millis.
-|nifi.authority.provider.configuration.file*|This is the location of the file that specifies how user access is authenticated. The default value is ./conf/authority-providers.xml.
+|nifi.authority.provider.configuration.file*|This is the location of the file that specifies how user access is authorized. The default value is ./conf/authority-providers.xml.
+|nifi.login.identity.provider.configuration.file*|This is the location of the file that specifies how username/password authentication is performed. This file is 
+only consider if `nifi.security.user.login.identity.provider` configured with a provider identifier. The default value is ./conf/login-identity-providers.xml.
 |nifi.templates.directory*|This is the location of the directory where flow templates are saved. The default value is ./conf/templates.l
 |nifi.ui.banner.text|This is banner text that may be configured to display at the top of the User Interface. It is blank by default.
 |nifi.ui.autorefresh.interval|The interval at which the User Interface auto-refreshes. The default value is 30 sec.
@@ -698,11 +788,16 @@ Security Configuration section of this Administrator's Guide.
 |nifi.security.truststore*|The full path and name of the truststore. It is blank by default.
 |nifi.security.truststoreType|The truststore type. It is blank by default.
 |nifi.security.truststorePasswd|The truststore password. It is blank by default.
-|nifi.security.needClientAuth|This indicates whether client authentication is required. It is blank by default.
+|nifi.security.needClientAuth|This indicates whether client authentication in the cluster protocol. It is blank by default.
 |nifi.security.user.credential.cache.duration|The length of time to cache user credentials. The default value is 24 hours.
 |nifi.security.user.authority.provider|This indicates what type of authority provider to use. The default value is file-provider, which refers to the file
-configured in the core property nifi.authority.provider.configuration.file. Another authority provider may be used, such as when the NiFi instance is part of a cluster. But the default value of file-provider is fine for a standalone instance of NiFi.
+configured in the core property `nifi.authority.provider.configuration.file`. Another authority provider may be used, such as when the NiFi instance is part of a cluster. But the default value of file-provider is fine for a standalone instance of NiFi.
+|nifi.security.user.login.identity.provider|This indicates what type of login identity provider to use. The default value is blank, can be set to the identifier from a provider
+in the file specified in `nifi.login.identity.provider.configuration.file`. Setting this property will trigger NiFi to support username/password authentication.
 |nifi.security.support.new.account.requests|This indicates whether a secure NiFi is configured to allow users to request access. It is blank by default.
+|nifi.security.anonymous.authorities|This indicates what roles to grant to anonymous users accessing NiFi over HTTPS. It is blank by default, but could be
+set to any combination of ROLE_MONITOR, ROLE_DFM, ROLE_ADMIN, ROLE_PROVENANCE, ROLE_NIFI. Leaving this property blank will require that users accessing NiFi
+over HTTPS be authenticated either using a client certificate or their credentials against the configured log identity provider.
 |nifi.security.ocsp.responder.url|This is the URL for the Online Certificate Status Protocol (OCSP) responder if one is being used. It is blank by default.
 |nifi.security.ocsp.responder.certificate|This is the location of the OCSP responder certificate if one is being used. It is blank by default.
 |====

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/KeyDataSourceFactoryBean.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/KeyDataSourceFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/KeyDataSourceFactoryBean.java
deleted file mode 100644
index 688aa5a..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/KeyDataSourceFactoryBean.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.admin;
-
-import java.io.File;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import org.apache.commons.lang3.StringUtils;
-import org.h2.jdbcx.JdbcConnectionPool;
-import org.apache.nifi.util.NiFiProperties;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.FactoryBean;
-
-/**
- *
- */
-public class KeyDataSourceFactoryBean implements FactoryBean {
-
-    private static final Logger logger = LoggerFactory.getLogger(KeyDataSourceFactoryBean.class);
-    private static final String NF_USERNAME_PASSWORD = "nf";
-    private static final int MAX_CONNECTIONS = 5;
-
-    // database file name
-    private static final String KEY_DATABASE_FILE_NAME = "nifi-key";
-
-    // ----------
-    // keys table
-    // ----------
-    private static final String CREATE_KEY_TABLE = "CREATE TABLE KEY ("
-            + "ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
-            + "IDENTITY VARCHAR2(4096) NOT NULL UNIQUE, "
-            + "KEY VARCHAR2(100) NOT NULL"
-            + ")";
-
-    private JdbcConnectionPool connectionPool;
-
-    private NiFiProperties properties;
-
-    @Override
-    public Object getObject() throws Exception {
-        if (connectionPool == null) {
-
-            // locate the repository directory
-            String repositoryDirectoryPath = properties.getProperty(NiFiProperties.REPOSITORY_DATABASE_DIRECTORY);
-
-            // ensure the repository directory is specified
-            if (repositoryDirectoryPath == null) {
-                throw new NullPointerException("Database directory must be specified.");
-            }
-
-            // create a handle to the repository directory
-            File repositoryDirectory = new File(repositoryDirectoryPath);
-
-            // get a handle to the database file
-            File databaseFile = new File(repositoryDirectory, KEY_DATABASE_FILE_NAME);
-
-            // format the database url
-            String databaseUrl = "jdbc:h2:" + databaseFile + ";AUTOCOMMIT=OFF;DB_CLOSE_ON_EXIT=FALSE;LOCK_MODE=3";
-            String databaseUrlAppend = properties.getProperty(NiFiProperties.H2_URL_APPEND);
-            if (StringUtils.isNotBlank(databaseUrlAppend)) {
-                databaseUrl += databaseUrlAppend;
-            }
-
-            // create the pool
-            connectionPool = JdbcConnectionPool.create(databaseUrl, NF_USERNAME_PASSWORD, NF_USERNAME_PASSWORD);
-            connectionPool.setMaxConnections(MAX_CONNECTIONS);
-
-            Connection connection = null;
-            ResultSet rs = null;
-            Statement statement = null;
-            try {
-                // get a connection
-                connection = connectionPool.getConnection();
-                connection.setAutoCommit(false);
-
-                // create a statement for initializing the database
-                statement = connection.createStatement();
-
-                // determine if the tables need to be created
-                rs = connection.getMetaData().getTables(null, null, "KEY", null);
-                if (!rs.next()) {
-                    logger.info("Database not built for repository: " + databaseUrl + ".  Building now...");
-                    RepositoryUtils.closeQuietly(rs);
-
-                    // action table
-                    statement.execute(CREATE_KEY_TABLE);
-                } else {
-                    logger.info("Existing database found and connected to at: " + databaseUrl);
-                }
-
-                // commit any changes
-                connection.commit();
-            } catch (SQLException sqle) {
-                RepositoryUtils.rollback(connection, logger);
-                throw sqle;
-            } finally {
-                RepositoryUtils.closeQuietly(rs);
-                RepositoryUtils.closeQuietly(statement);
-                RepositoryUtils.closeQuietly(connection);
-            }
-        }
-
-        return connectionPool;
-    }
-
-    @Override
-    public Class getObjectType() {
-        return JdbcConnectionPool.class;
-    }
-
-    @Override
-    public boolean isSingleton() {
-        return true;
-    }
-
-    public void setProperties(NiFiProperties properties) {
-        this.properties = properties;
-    }
-
-    /**
-     * Disposes resources.
-     */
-    public void shutdown() {
-
-        // shutdown the connection pool
-        if (connectionPool != null) {
-            try {
-                connectionPool.dispose();
-            } catch (Exception e) {
-                logger.warn("Unable to dispose of connection pool: " + e.getMessage());
-                if (logger.isDebugEnabled()) {
-                    logger.warn(StringUtils.EMPTY, e);
-                }
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/UserDataSourceFactoryBean.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/UserDataSourceFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/UserDataSourceFactoryBean.java
index 6d8566e..d45719d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/UserDataSourceFactoryBean.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/UserDataSourceFactoryBean.java
@@ -88,6 +88,15 @@ public class UserDataSourceFactoryBean implements FactoryBean {
     private static final String RESIZE_IDENTITY_COLUMN = "ALTER TABLE USER MODIFY IDENTITY VARCHAR(4096)";
     private static final String RESIZE_USER_NAME_COLUMN = "ALTER TABLE USER MODIFY USER_NAME VARCHAR(4096)";
 
+    // ----------
+    // keys table
+    // ----------
+    private static final String CREATE_KEY_TABLE = "CREATE TABLE KEY ("
+            + "ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
+            + "IDENTITY VARCHAR2(4096) NOT NULL UNIQUE, "
+            + "KEY VARCHAR2(100) NOT NULL"
+            + ")";
+
     private JdbcConnectionPool connectionPool;
 
     private NiFiProperties properties;
@@ -112,7 +121,8 @@ public class UserDataSourceFactoryBean implements FactoryBean {
             if (rawAnonymousAuthorities.size() != anonymousAuthorities.size()) {
                 final Set<String> validAuthorities = Authority.convertAuthorities(anonymousAuthorities);
                 rawAnonymousAuthorities.removeAll(validAuthorities);
-                throw new IllegalStateException("Invalid authorities specified: " + StringUtils.join(rawAnonymousAuthorities, ", "));
+                throw new IllegalStateException(String.format("Invalid authorities specified for anonymous access: [%s]. Valid values are: [%s].",
+                        StringUtils.join(rawAnonymousAuthorities, ", "), StringUtils.join(Authority.values(), ", ")));
             }
 
             // create a handle to the repository directory
@@ -169,6 +179,14 @@ public class UserDataSourceFactoryBean implements FactoryBean {
                     statement.execute(String.format(INSERT_ANONYMOUS_AUTHORITY, authority.name()));
                 }
 
+                RepositoryUtils.closeQuietly(rs);
+
+                // determine if the key table need to be created
+                rs = connection.getMetaData().getTables(null, null, "KEY", null);
+                if (!rs.next()) {
+                    statement.execute(CREATE_KEY_TABLE);
+                }
+
                 // commit any changes
                 connection.commit();
             } catch (SQLException sqle) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java
index 2a24e0b..9626445 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java
@@ -46,4 +46,11 @@ public interface KeyDAO {
      * @return The key
      */
     Key createKey(String identity);
+
+    /**
+     * Deletes all keys for the specified user identity.
+     *
+     * @param identity The user identity
+     */
+    void deleteKeys(String identity);
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java
index f4bdc1d..cc337fd 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java
@@ -46,6 +46,9 @@ public class StandardKeyDAO implements KeyDAO {
             + "?, ?"
             + ")";
 
+    private static final String DELETE_KEYS = "DELETE FROM KEY "
+            + "WHERE IDENTITY = ?";
+
     private final Connection connection;
 
     public StandardKeyDAO(Connection connection) {
@@ -151,4 +154,26 @@ public class StandardKeyDAO implements KeyDAO {
             RepositoryUtils.closeQuietly(statement);
         }
     }
+
+    @Override
+    public void deleteKeys(String identity) {
+        // ensure there are some authorities to create
+        PreparedStatement statement = null;
+        try {
+            // add each authority for the specified user
+            statement = connection.prepareStatement(DELETE_KEYS);
+            statement.setString(1, identity);
+
+            // insert the authorities
+            int count = statement.executeUpdate();
+            System.out.println();
+        } catch (SQLException sqle) {
+            throw new DataAccessException(sqle);
+        } catch (DataAccessException dae) {
+            throw dae;
+        } finally {
+            RepositoryUtils.closeQuietly(statement);
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java
deleted file mode 100644
index ae64c41..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.admin.service;
-
-import org.apache.nifi.key.Key;
-
-/**
- * Supports retrieving and issues keys for signing user tokens.
- */
-public interface KeyService {
-
-    /**
-     * Gets a key for the specified user identity. Returns null if the user has not had a key issued
-     *
-     * @param id The key id
-     * @return The key or null
-     */
-    Key getKey(int id);
-
-    /**
-     * Gets a key for the specified user identity. If a key does not exist, one will be created.
-     *
-     * @param identity The user identity
-     * @return The key
-     * @throws AdministrationException if it failed to get/create the key
-     */
-    Key getOrCreateKey(String identity);
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/UserService.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/UserService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/UserService.java
index b02f192..4ea71af 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/UserService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/UserService.java
@@ -22,6 +22,7 @@ import java.util.Map;
 import java.util.Set;
 import org.apache.nifi.authorization.Authority;
 import org.apache.nifi.authorization.DownloadAuthorization;
+import org.apache.nifi.key.Key;
 import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.user.NiFiUserGroup;
 
@@ -47,14 +48,12 @@ public interface UserService {
     /**
      * @param dnChain user dn chain
      * @param attributes attributes for authorization request
-     * @return Determines if the users in the dnChain are authorized to download
-     * content with the specified attributes
+     * @return Determines if the users in the dnChain are authorized to download content with the specified attributes
      */
     DownloadAuthorization authorizeDownload(List<String> dnChain, Map<String, String> attributes);
 
     /**
-     * Updates a user group using the specified group comprised of the specified
-     * users. Returns all the users that are currently in the specified group.
+     * Updates a user group using the specified group comprised of the specified users. Returns all the users that are currently in the specified group.
      *
      * @param group group
      * @param userIds users
@@ -154,4 +153,28 @@ public interface UserService {
      * @throws AdministrationException ae
      */
     NiFiUser getUserByDn(String dn);
+
+    /**
+     * Gets a key for the specified user identity. Returns null if the user has not had a key issued
+     *
+     * @param id The key id
+     * @return The key or null
+     */
+    Key getKey(int id);
+
+    /**
+     * Gets a key for the specified user identity. If a key does not exist, one will be created.
+     *
+     * @param identity The user identity
+     * @return The key
+     * @throws AdministrationException if it failed to get/create the key
+     */
+    Key getOrCreateKey(String identity);
+
+    /**
+     * Deletes keys for the specified identity.
+     *
+     * @param identity The user identity
+     */
+    void deleteKey(String identity);
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DeleteKeysAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DeleteKeysAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DeleteKeysAction.java
new file mode 100644
index 0000000..cd13fa5
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DeleteKeysAction.java
@@ -0,0 +1,46 @@
+/*
+ * 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.admin.service.action;
+
+import org.apache.nifi.admin.dao.DAOFactory;
+import org.apache.nifi.admin.dao.DataAccessException;
+import org.apache.nifi.admin.dao.KeyDAO;
+import org.apache.nifi.authorization.AuthorityProvider;
+
+/**
+ *
+ */
+public class DeleteKeysAction implements AdministrationAction<Void> {
+
+    private final String identity;
+
+    /**
+     * Creates a new transactions for deleting keys for specified user.
+     *
+     * @param identity user identity
+     */
+    public DeleteKeysAction(String identity) {
+        this.identity = identity;
+    }
+
+    @Override
+    public Void execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) throws DataAccessException {
+        final KeyDAO keyDao = daoFactory.getKeyDAO();
+        keyDao.deleteKeys(identity);
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DeleteUserAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DeleteUserAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DeleteUserAction.java
index 0d59b43..c2695d0 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DeleteUserAction.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DeleteUserAction.java
@@ -19,6 +19,7 @@ package org.apache.nifi.admin.service.action;
 import org.apache.nifi.admin.dao.AuthorityDAO;
 import org.apache.nifi.admin.dao.DAOFactory;
 import org.apache.nifi.admin.dao.DataAccessException;
+import org.apache.nifi.admin.dao.KeyDAO;
 import org.apache.nifi.admin.dao.UserDAO;
 import org.apache.nifi.admin.service.AccountNotFoundException;
 import org.apache.nifi.authorization.AuthorityProvider;
@@ -59,6 +60,10 @@ public class DeleteUserAction implements AdministrationAction<Void> {
             throw new IllegalStateException(String.format("An active user cannot be removed. Revoke user access before attempting to remove."));
         }
 
+        // remove the user's keys
+        final KeyDAO keyDao = daoFactory.getKeyDAO();
+        keyDao.deleteKeys(user.getIdentity());
+
         // remove the user and their authorities
         authorityDAO.deleteAuthorities(userId);
         userDAO.deleteUser(userId);

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DisableUserAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DisableUserAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DisableUserAction.java
index 9e9b798..bf7eae3 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DisableUserAction.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DisableUserAction.java
@@ -18,6 +18,7 @@ package org.apache.nifi.admin.service.action;
 
 import org.apache.nifi.admin.dao.DAOFactory;
 import org.apache.nifi.admin.dao.DataAccessException;
+import org.apache.nifi.admin.dao.KeyDAO;
 import org.apache.nifi.admin.dao.UserDAO;
 import org.apache.nifi.admin.service.AccountNotFoundException;
 import org.apache.nifi.admin.service.AdministrationException;
@@ -61,6 +62,10 @@ public class DisableUserAction implements AdministrationAction<NiFiUser> {
         // update the user locally
         userDao.updateUser(user);
 
+        // remove the user's keys
+        KeyDAO keyDao = daoFactory.getKeyDAO();
+        keyDao.deleteKeys(user.getIdentity());
+
         try {
             // revoke the user in the authority provider
             authorityProvider.revokeUser(user.getIdentity());

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DisableUserGroupAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DisableUserGroupAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DisableUserGroupAction.java
index 385fce6..c6480ed 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DisableUserGroupAction.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DisableUserGroupAction.java
@@ -16,14 +16,17 @@
  */
 package org.apache.nifi.admin.service.action;
 
+import java.util.Set;
 import org.apache.nifi.admin.dao.DAOFactory;
 import org.apache.nifi.admin.dao.DataAccessException;
+import org.apache.nifi.admin.dao.KeyDAO;
 import org.apache.nifi.admin.dao.UserDAO;
 import org.apache.nifi.admin.service.AdministrationException;
 import org.apache.nifi.authorization.AuthorityProvider;
 import org.apache.nifi.authorization.exception.AuthorityAccessException;
 import org.apache.nifi.authorization.exception.UnknownIdentityException;
 import org.apache.nifi.user.AccountStatus;
+import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.user.NiFiUserGroup;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,14 +46,20 @@ public class DisableUserGroupAction implements AdministrationAction<NiFiUserGrou
 
     @Override
     public NiFiUserGroup execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) throws DataAccessException {
-        final NiFiUserGroup userGroup = new NiFiUserGroup();
-
         final UserDAO userDao = daoFactory.getUserDAO();
+        final Set<NiFiUser> users = userDao.findUsersForGroup(group);
+
+        // delete the keys for each user
+        final KeyDAO keyDao = daoFactory.getKeyDAO();
+        for (final NiFiUser user : users) {
+            keyDao.deleteKeys(user.getIdentity());
+        }
 
         // update the user group locally
         userDao.updateGroupStatus(group, AccountStatus.DISABLED);
 
         // populate the group details
+        final NiFiUserGroup userGroup = new NiFiUserGroup();
         userGroup.setGroup(group);
         userGroup.setUsers(userDao.findUsersForGroup(group));
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java
deleted file mode 100644
index ca0a124..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.admin.service.impl;
-
-import org.apache.nifi.admin.dao.DataAccessException;
-import org.apache.nifi.admin.service.AdministrationException;
-import org.apache.nifi.admin.service.KeyService;
-import org.apache.nifi.admin.service.action.GetKeyByIdAction;
-import org.apache.nifi.admin.service.action.GetOrCreateKeyAction;
-import org.apache.nifi.admin.service.transaction.Transaction;
-import org.apache.nifi.admin.service.transaction.TransactionBuilder;
-import org.apache.nifi.admin.service.transaction.TransactionException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import org.apache.nifi.key.Key;
-
-/**
- *
- */
-public class StandardKeyService implements KeyService {
-
-    private static final Logger logger = LoggerFactory.getLogger(StandardKeyService.class);
-
-    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
-    private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
-    private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
-
-    private TransactionBuilder transactionBuilder;
-
-    @Override
-    public Key getKey(int id) {
-        Transaction transaction = null;
-        Key key = null;
-
-        readLock.lock();
-        try {
-            // start the transaction
-            transaction = transactionBuilder.start();
-
-            // get the key
-            GetKeyByIdAction addActions = new GetKeyByIdAction(id);
-            key = transaction.execute(addActions);
-
-            // commit the transaction
-            transaction.commit();
-        } catch (TransactionException | DataAccessException te) {
-            rollback(transaction);
-            throw new AdministrationException(te);
-        } catch (Throwable t) {
-            rollback(transaction);
-            throw t;
-        } finally {
-            closeQuietly(transaction);
-            readLock.unlock();
-        }
-
-        return key;
-    }
-
-    @Override
-    public Key getOrCreateKey(String identity) {
-        Transaction transaction = null;
-        Key key = null;
-
-        writeLock.lock();
-        try {
-            // start the transaction
-            transaction = transactionBuilder.start();
-
-            // seed the accounts
-            GetOrCreateKeyAction addActions = new GetOrCreateKeyAction(identity);
-            key = transaction.execute(addActions);
-
-            // commit the transaction
-            transaction.commit();
-        } catch (TransactionException | DataAccessException te) {
-            rollback(transaction);
-            throw new AdministrationException(te);
-        } catch (Throwable t) {
-            rollback(transaction);
-            throw t;
-        } finally {
-            closeQuietly(transaction);
-            writeLock.unlock();
-        }
-
-        return key;
-    }
-
-    private void rollback(Transaction transaction) {
-        if (transaction != null) {
-            transaction.rollback();
-        }
-    }
-
-    private void closeQuietly(final Transaction transaction) {
-        if (transaction != null) {
-            try {
-                transaction.close();
-            } catch (final IOException ioe) {
-            }
-        }
-    }
-
-    public void setTransactionBuilder(TransactionBuilder transactionBuilder) {
-        this.transactionBuilder = transactionBuilder;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardUserService.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardUserService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardUserService.java
index 424816f..c37a562 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardUserService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardUserService.java
@@ -31,11 +31,14 @@ import org.apache.nifi.admin.service.AdministrationException;
 import org.apache.nifi.admin.service.UserService;
 import org.apache.nifi.admin.service.action.AuthorizeDownloadAction;
 import org.apache.nifi.admin.service.action.AuthorizeUserAction;
+import org.apache.nifi.admin.service.action.DeleteKeysAction;
 import org.apache.nifi.admin.service.action.DeleteUserAction;
 import org.apache.nifi.admin.service.action.DisableUserAction;
 import org.apache.nifi.admin.service.action.DisableUserGroupAction;
 import org.apache.nifi.admin.service.action.FindUserByDnAction;
 import org.apache.nifi.admin.service.action.FindUserByIdAction;
+import org.apache.nifi.admin.service.action.GetKeyByIdAction;
+import org.apache.nifi.admin.service.action.GetOrCreateKeyAction;
 import org.apache.nifi.admin.service.action.GetUserGroupAction;
 import org.apache.nifi.admin.service.action.GetUsersAction;
 import org.apache.nifi.admin.service.action.HasPendingUserAccounts;
@@ -52,6 +55,7 @@ import org.apache.nifi.admin.service.transaction.TransactionBuilder;
 import org.apache.nifi.admin.service.transaction.TransactionException;
 import org.apache.nifi.authorization.Authority;
 import org.apache.nifi.authorization.DownloadAuthorization;
+import org.apache.nifi.key.Key;
 import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.user.NiFiUserGroup;
 import org.apache.nifi.util.FormatUtils;
@@ -404,10 +408,8 @@ public class StandardUserService implements UserService {
     }
 
     /**
-     * Invalidates the user with the specified id. This is done to ensure a user
-     * account will need to be re-validated in case an error occurs while
-     * modifying a user account. This method should only be invoked from within
-     * a write lock.
+     * Invalidates the user with the specified id. This is done to ensure a user account will need to be re-validated in case an error occurs while modifying a user account. This method should only be
+     * invoked from within a write lock.
      *
      * @param id user account identifier
      */
@@ -616,6 +618,93 @@ public class StandardUserService implements UserService {
         }
     }
 
+    @Override
+    public Key getKey(int id) {
+        Transaction transaction = null;
+        Key key = null;
+
+        readLock.lock();
+        try {
+            // start the transaction
+            transaction = transactionBuilder.start();
+
+            // get the key
+            GetKeyByIdAction addActions = new GetKeyByIdAction(id);
+            key = transaction.execute(addActions);
+
+            // commit the transaction
+            transaction.commit();
+        } catch (TransactionException | DataAccessException te) {
+            rollback(transaction);
+            throw new AdministrationException(te);
+        } catch (Throwable t) {
+            rollback(transaction);
+            throw t;
+        } finally {
+            closeQuietly(transaction);
+            readLock.unlock();
+        }
+
+        return key;
+    }
+
+    @Override
+    public Key getOrCreateKey(String identity) {
+        Transaction transaction = null;
+        Key key = null;
+
+        writeLock.lock();
+        try {
+            // start the transaction
+            transaction = transactionBuilder.start();
+
+            // get or create a key
+            GetOrCreateKeyAction addActions = new GetOrCreateKeyAction(identity);
+            key = transaction.execute(addActions);
+
+            // commit the transaction
+            transaction.commit();
+        } catch (TransactionException | DataAccessException te) {
+            rollback(transaction);
+            throw new AdministrationException(te);
+        } catch (Throwable t) {
+            rollback(transaction);
+            throw t;
+        } finally {
+            closeQuietly(transaction);
+            writeLock.unlock();
+        }
+
+        return key;
+    }
+
+    @Override
+    public void deleteKey(String identity) {
+        Transaction transaction = null;
+
+        writeLock.lock();
+        try {
+            // start the transaction
+            transaction = transactionBuilder.start();
+
+            // delete the keys
+            DeleteKeysAction deleteKeys = new DeleteKeysAction(identity);
+            transaction.execute(deleteKeys);
+
+            // commit the transaction
+            transaction.commit();
+        } catch (TransactionException | DataAccessException te) {
+            rollback(transaction);
+            throw new AdministrationException(te);
+        } catch (Throwable t) {
+            rollback(transaction);
+            throw t;
+        } finally {
+            closeQuietly(transaction);
+            writeLock.unlock();
+        }
+    }
+
     private void rollback(final Transaction transaction) {
         if (transaction != null) {
             transaction.rollback();

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
index 6d7b739..1423cbe 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
@@ -37,11 +37,6 @@
         <property name="properties" ref="nifiProperties"/>
     </bean>
     
-    <!-- initialize the data source -->
-    <bean id="keyDataSource" class="org.apache.nifi.admin.KeyDataSourceFactoryBean" destroy-method="shutdown">
-        <property name="properties" ref="nifiProperties"/>
-    </bean>
-
     <!-- initialize the user transaction builder -->
     <bean id="userTransactionBuilder" class="org.apache.nifi.admin.service.transaction.impl.StandardTransactionBuilder">
         <property name="authorityProvider" ref="authorityProvider"/>
@@ -54,12 +49,6 @@
         <property name="dataSource" ref="auditDataSource"/>
     </bean>
     
-    <!-- initialize the key transaction builder -->
-    <bean id="keyTransactionBuilder" class="org.apache.nifi.admin.service.transaction.impl.StandardTransactionBuilder">
-        <property name="authorityProvider" ref="authorityProvider"/>
-        <property name="dataSource" ref="keyDataSource"/>
-    </bean>
-
     <!-- administration service -->
     <bean id="userService" class="org.apache.nifi.admin.service.impl.StandardUserService" init-method="seedUserAccounts">
         <property name="transactionBuilder" ref="userTransactionBuilder"/>
@@ -70,9 +59,4 @@
     <bean id="auditService" class="org.apache.nifi.admin.service.impl.StandardAuditService">
         <property name="transactionBuilder" ref="auditTransactionBuilder"/>
     </bean>
-
-    <!-- key service -->
-    <bean id="keyService" class="org.apache.nifi.admin.service.impl.StandardKeyService">
-        <property name="transactionBuilder" ref="keyTransactionBuilder"/>
-    </bean>
 </beans>

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/DisableUserActionTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/DisableUserActionTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/DisableUserActionTest.java
index ac2ab29..b5f0a7f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/DisableUserActionTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/DisableUserActionTest.java
@@ -26,9 +26,11 @@ import org.apache.nifi.authorization.exception.AuthorityAccessException;
 import org.apache.nifi.user.AccountStatus;
 import org.apache.nifi.user.NiFiUser;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.admin.dao.KeyDAO;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Matchers;
 import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
@@ -45,6 +47,7 @@ public class DisableUserActionTest {
 
     private DAOFactory daoFactory;
     private UserDAO userDao;
+    private KeyDAO keyDao;
     private AuthorityProvider authorityProvider;
 
     @Before
@@ -92,8 +95,13 @@ public class DisableUserActionTest {
         }).when(userDao).updateUser(Mockito.any(NiFiUser.class));
 
         // mock the dao factory
+        keyDao = Mockito.mock(KeyDAO.class);
+        Mockito.doNothing().when(keyDao).deleteKeys(Matchers.anyString());
+
+        // mock the dao factory
         daoFactory = Mockito.mock(DAOFactory.class);
         Mockito.when(daoFactory.getUserDAO()).thenReturn(userDao);
+        Mockito.when(daoFactory.getKeyDAO()).thenReturn(keyDao);
 
         // mock the authority provider
         authorityProvider = Mockito.mock(AuthorityProvider.class);

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
index b25d05a..48b6241 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
@@ -127,6 +127,7 @@ nifi.security.user.credential.cache.duration=${nifi.security.user.credential.cac
 nifi.security.user.authority.provider=${nifi.security.user.authority.provider}
 nifi.security.user.login.identity.provider=${nifi.security.user.login.identity.provider}
 nifi.security.support.new.account.requests=${nifi.security.support.new.account.requests}
+# Valid Authorities include: ROLE_MONITOR,ROLE_DFM,ROLE_ADMIN,ROLE_PROVENANCE,ROLE_NIFI
 nifi.security.anonymous.authorities=${nifi.security.anonymous.authorities}
 nifi.security.ocsp.responder.url=${nifi.security.ocsp.responder.url}
 nifi.security.ocsp.responder.certificate=${nifi.security.ocsp.responder.certificate}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/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 e8ed267..bf12dee 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
@@ -21,7 +21,6 @@ import org.apache.nifi.authentication.LoginIdentityProvider;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.security.NiFiAuthenticationProvider;
 import org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter;
-import org.apache.nifi.web.security.NiFiAuthenticationEntryPoint;
 import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
 import org.apache.nifi.web.security.jwt.JwtService;
 import org.apache.nifi.web.security.node.NodeAuthorizedUserFilter;
@@ -74,9 +73,6 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
     protected void configure(HttpSecurity http) throws Exception {
         http
                 .rememberMe().disable()
-                .exceptionHandling()
-                    .authenticationEntryPoint(new NiFiAuthenticationEntryPoint(properties))
-                    .and()
                 .authorizeRequests()
                     .anyRequest().fullyAuthenticated()
                     .and()

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
index e198438..c73ec47 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -70,6 +70,7 @@ import org.springframework.security.authentication.AccountStatusException;
 import org.springframework.security.authentication.AuthenticationServiceException;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
+import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 
 /**
@@ -201,12 +202,16 @@ public class AccessResource extends ApplicationResource {
                         // without a certificate, this is not a proxied request
                         final List<String> chain = Arrays.asList(principal);
 
-                        // check authorization for this user
-                        checkAuthorization(chain);
+                        // ensure the proxy chain is authorized
+                        final UserDetails userDetails = checkAuthorization(chain);
 
-                        // no issues with authorization
+                        // no issues with authorization... verify authorities
                         accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
-                        accessStatus.setMessage("Account is active and authorized");
+                        if (userDetails.getAuthorities().isEmpty()) {
+                            accessStatus.setMessage("Your account is active but is unauthorized as no authorities have been granted.");
+                        } else {
+                            accessStatus.setMessage("Your account is active and you are already logged in.");
+                        }
                     } catch (JwtException e) {
                         throw new InvalidAuthenticationException(e.getMessage(), e);
                     }
@@ -227,18 +232,27 @@ public class AccessResource extends ApplicationResource {
                     accessStatus.setUsername(CertificateUtils.extractUsername(proxyChain.get(0)));
 
                     // ensure the proxy chain is authorized
-                    checkAuthorization(proxyChain);
+                    final UserDetails userDetails = checkAuthorization(proxyChain);
 
-                    // no issues with authorization
+                    // no issues with authorization... verify authorities
                     accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
-                    accessStatus.setMessage("Account is active and authorized");
+                    if (userDetails.getAuthorities().isEmpty()) {
+                        accessStatus.setMessage("Your account is active but is unauthorized as no authorities have been granted.");
+                    } else {
+                        accessStatus.setMessage("Your account is active and you are already logged in.");
+                    }
                 } catch (final IllegalArgumentException iae) {
                     throw new InvalidAuthenticationException(iae.getMessage(), iae);
                 }
             }
         } catch (final UsernameNotFoundException unfe) {
-            accessStatus.setStatus(AccessStatusDTO.Status.UNREGISTERED.name());
-            accessStatus.setMessage(String.format("Unregistered user %s", accessStatus.getIdentity()));
+            if (properties.getSupportNewAccountRequests()) {
+                accessStatus.setStatus(AccessStatusDTO.Status.UNREGISTERED.name());
+                accessStatus.setMessage(String.format("Unregistered user %s", accessStatus.getIdentity()));
+            } else {
+                accessStatus.setStatus(AccessStatusDTO.Status.NOT_ACTIVE.name());
+                accessStatus.setMessage("This NiFi does not support new account requests.");
+            }
         } catch (final AccountStatusException ase) {
             accessStatus.setStatus(AccessStatusDTO.Status.NOT_ACTIVE.name());
             accessStatus.setMessage(ase.getMessage());
@@ -266,8 +280,8 @@ public class AccessResource extends ApplicationResource {
      * @param proxyChain the proxy chain
      * @throws AuthenticationException if the proxy chain is not authorized
      */
-    private void checkAuthorization(final List<String> proxyChain) throws AuthenticationException {
-        userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
+    private UserDetails checkAuthorization(final List<String> proxyChain) throws AuthenticationException {
+        return userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationEntryPoint.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/NiFiAuthenticationEntryPoint.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationEntryPoint.java
deleted file mode 100644
index ef1dfb2..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationEntryPoint.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web.security;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.apache.nifi.util.NiFiProperties;
-import org.apache.nifi.util.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.web.AuthenticationEntryPoint;
-
-/**
- * This is our own implementation of org.springframework.security.web.AuthenticationEntryPoint that allows us to send the response to the client exactly how we want to and log the results.
- */
-public class NiFiAuthenticationEntryPoint implements AuthenticationEntryPoint {
-
-    private static final Logger logger = LoggerFactory.getLogger(NiFiAuthenticationEntryPoint.class);
-
-    private final NiFiProperties properties;
-
-    public NiFiAuthenticationEntryPoint(NiFiProperties properties) {
-        this.properties = properties;
-    }
-
-    /**
-     * Always returns a 403 error code to the client.
-     *
-     * @param request request
-     * @param response response
-     * @param ae ae
-     * @throws java.io.IOException ex
-     * @throws javax.servlet.ServletException ex
-     */
-    @Override
-    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ae) throws IOException, ServletException {
-        // if the content type is not set, mark as access denied
-        if (StringUtils.isBlank(response.getContentType())) {
-            // write the response message
-            PrintWriter out = response.getWriter();
-            response.setContentType("text/plain");
-
-            // return authorized if the request is secure and this nifi supports new account requests
-            if (request.isSecure() && properties.getSupportNewAccountRequests()) {
-                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-                out.println("Not authorized.");
-            } else {
-                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-                out.println("Access is denied.");
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.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/NiFiAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
index f09d610..7ceca04 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
@@ -63,10 +63,11 @@ public abstract class NiFiAuthenticationFilter implements Filter {
         }
 
         if (requiresAuthentication((HttpServletRequest) request)) {
-            authenticate((HttpServletRequest) request, (HttpServletResponse) response);
+            authenticate((HttpServletRequest) request, (HttpServletResponse) response, chain);
+        } else {
+            chain.doFilter(request, response);
         }
 
-        chain.doFilter(request, response);
     }
 
     private boolean requiresAuthentication(final HttpServletRequest request) {
@@ -84,7 +85,7 @@ public abstract class NiFiAuthenticationFilter implements Filter {
         return user != null && NiFiUser.ANONYMOUS_USER_IDENTITY.equals(user.getIdentity());
     }
 
-    private void authenticate(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
+    private void authenticate(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws IOException, ServletException {
         try {
             final NiFiAuthenticationRequestToken authenticated = attemptAuthentication(request, response);
             if (authenticated != null) {
@@ -93,11 +94,21 @@ public abstract class NiFiAuthenticationFilter implements Filter {
                         ProxiedEntitiesUtils.formatProxyDn(StringUtils.join(authenticated.getChain(), "><")), request.getMethod(),
                         request.getRequestURL().toString(), request.getRemoteAddr()));
 
+                // attempt to authorize the user
                 final Authentication authorized = authenticationManager.authenticate(authenticated);
                 successfulAuthorization(request, response, authorized);
             }
+
+            // continue
+            chain.doFilter(request, response);
+        } catch (final InvalidAuthenticationException iae) {
+            // invalid authentication - always error out
+            unsuccessfulAuthorization(request, response, iae);
         } catch (final AuthenticationException ae) {
-            if (!isAnonymousUser()) {
+            // other authentication exceptions... if we are already the anonymous user, allow through otherwise error out
+            if (isAnonymousUser()) {
+                chain.doFilter(request, response);
+            } else {
                 unsuccessfulAuthorization(request, response, ae);
             }
         }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/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 9635354..4bbec21 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
@@ -29,28 +29,29 @@ import io.jsonwebtoken.SigningKeyResolverAdapter;
 import io.jsonwebtoken.UnsupportedJwtException;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.admin.service.AdministrationException;
-import org.apache.nifi.admin.service.KeyService;
 import org.apache.nifi.web.security.token.LoginAuthenticationToken;
 import org.slf4j.LoggerFactory;
 
 import java.nio.charset.StandardCharsets;
 import java.util.Calendar;
+import org.apache.nifi.admin.service.UserService;
 import org.apache.nifi.key.Key;
 
 /**
  *
  */
 public class JwtService {
+
     private static final org.slf4j.Logger logger = LoggerFactory.getLogger(JwtService.class);
 
     private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
     private static final String KEY_ID_CLAIM = "kid";
     private static final String USERNAME_CLAIM = "preferred_username";
 
-    private final KeyService keyService;
+    private final UserService userService;
 
-    public JwtService(final KeyService keyService) {
-        this.keyService = keyService;
+    public JwtService(final UserService userService) {
+        this.userService = userService;
     }
 
     public String getAuthenticationFromToken(final String base64EncodedToken) throws JwtException {
@@ -91,7 +92,7 @@ public class JwtService {
 
                     // Get the key based on the key id in the claims
                     final Integer keyId = claims.get(KEY_ID_CLAIM, Integer.class);
-                    final Key key = keyService.getKey(keyId);
+                    final Key key = userService.getKey(keyId);
 
                     // Ensure we were able to find a key that was previously issued by this key service for this user
                     if (key == null || key.getKey() == null) {
@@ -103,7 +104,7 @@ public class JwtService {
             }).parseClaimsJws(base64EncodedToken);
         } catch (final MalformedJwtException | UnsupportedJwtException | SignatureException | ExpiredJwtException | IllegalArgumentException | AdministrationException e) {
             // TODO: Exercise all exceptions to ensure none leak key material to logs
-            final String errorMessage = "There was an error validating the JWT";
+            final String errorMessage = "Unable to validate the access token.";
             throw new JwtException(errorMessage, e);
         }
     }
@@ -137,13 +138,12 @@ public class JwtService {
 
         try {
             // Get/create the key for this user
-            final Key key = keyService.getOrCreateKey(identity);
+            final Key key = userService.getOrCreateKey(identity);
             final byte[] keyBytes = key.getKey().getBytes(StandardCharsets.UTF_8);
 
             logger.trace("Generating JWT for " + authenticationToken);
 
             // TODO: Implement "jti" claim with nonce to prevent replay attacks and allow blacklisting of revoked tokens
-
             // Build the token
             return Jwts.builder().setSubject(identity)
                     .setIssuer(authenticationToken.getIssuer())

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/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 40f678c..d079293 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
@@ -47,7 +47,7 @@
     
     <!-- jwt service -->
     <bean id="jwtService" class="org.apache.nifi.web.security.jwt.JwtService">
-        <constructor-arg ref="keyService"/>
+        <constructor-arg ref="userService"/>
     </bean>
     
     <!-- login identity provider -->

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
index 59c66ef..658f3e6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
@@ -20,7 +20,7 @@ import io.jsonwebtoken.JwtException;
 import org.apache.commons.codec.CharEncoding;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.nifi.admin.service.AdministrationException;
-import org.apache.nifi.admin.service.KeyService;
+import org.apache.nifi.admin.service.UserService;
 import org.apache.nifi.key.Key;
 import org.apache.nifi.web.security.token.LoginAuthenticationToken;
 import org.codehaus.jettison.json.JSONObject;
@@ -131,7 +131,7 @@ public class JwtServiceTest {
 
     private static final String HMAC_SECRET = "test_hmac_shared_secret";
 
-    private KeyService mockKeyService;
+    private UserService mockUserService;
 
     // Class under test
     private JwtService jwtService;
@@ -177,10 +177,10 @@ public class JwtServiceTest {
         key.setIdentity(DEFAULT_IDENTITY);
         key.setKey(HMAC_SECRET);
 
-        mockKeyService = Mockito.mock(KeyService.class);
-        when(mockKeyService.getKey(anyInt())).thenReturn(key);
-        when(mockKeyService.getOrCreateKey(anyString())).thenReturn(key);
-        jwtService = new JwtService(mockKeyService);
+        mockUserService = Mockito.mock(UserService.class);
+        when(mockUserService.getKey(anyInt())).thenReturn(key);
+        when(mockUserService.getOrCreateKey(anyString())).thenReturn(key);
+        jwtService = new JwtService(mockUserService);
     }
 
     @After
@@ -431,7 +431,7 @@ public class JwtServiceTest {
         logger.debug("Generating token for " + loginAuthenticationToken);
 
         // Set up the bad key service
-        KeyService missingKeyService = Mockito.mock(KeyService.class);
+        UserService missingKeyService = Mockito.mock(UserService.class);
         when(missingKeyService.getOrCreateKey(anyString())).thenThrow(new AdministrationException("Could not find a "
                 + "key for that user"));
         jwtService = new JwtService(missingKeyService);

http://git-wip-us.apache.org/repos/asf/nifi/blob/c0732533/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 92712e6..6c05664 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
@@ -262,13 +262,13 @@ nf.Login = (function () {
                 } else if (accessStatus.status === 'NOT_ACTIVE') {
                     showMessage = true;
                     
-                    $('#login-message-title').text('Access Denied');
+                    $('#login-message-title').text('Unable to log in');
                     $('#login-message').text(accessStatus.message);
                 } else if (accessStatus.status === 'ACTIVE') {
                     showMessage = true;
                     
                     $('#login-message-title').text('Success');
-                    $('#login-message').text('Your account is active and you are already logged in.');
+                    $('#login-message').text(accessStatus.message);
                 }
                 
                 // if login is required, verify its supported


[31/51] [abbrv] nifi git commit: NIFI-655: - Refactoring web security to use Spring Security Java Configuration. - Introducing security in Web UI in order to get JWT.

Posted by mc...@apache.org.
http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java
new file mode 100644
index 0000000..2a24e0b
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java
@@ -0,0 +1,49 @@
+/*
+ * 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.admin.dao;
+
+import org.apache.nifi.key.Key;
+
+/**
+ * Key data access.
+ */
+public interface KeyDAO {
+
+    /**
+     * Gets the key for the specified user identity. Returns null if no key exists for the key id.
+     *
+     * @param id The key id
+     * @return The key or null
+     */
+    Key findKeyById(int id);
+
+    /**
+     * Gets the latest key for the specified identity. Returns null if no key exists for the user identity.
+     *
+     * @param identity The identity
+     * @return The key or null
+     */
+    Key findLatestKeyByIdentity(String identity);
+
+    /**
+     * Creates a key for the specified user identity.
+     *
+     * @param identity The user identity
+     * @return The key
+     */
+    Key createKey(String identity);
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/DAOFactoryImpl.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/DAOFactoryImpl.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/DAOFactoryImpl.java
index 2f3de0e..940e364 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/DAOFactoryImpl.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/DAOFactoryImpl.java
@@ -20,6 +20,7 @@ import java.sql.Connection;
 import org.apache.nifi.admin.dao.ActionDAO;
 import org.apache.nifi.admin.dao.AuthorityDAO;
 import org.apache.nifi.admin.dao.DAOFactory;
+import org.apache.nifi.admin.dao.KeyDAO;
 import org.apache.nifi.admin.dao.UserDAO;
 
 /**
@@ -48,4 +49,9 @@ public class DAOFactoryImpl implements DAOFactory {
         return new StandardUserDAO(connection);
     }
 
+    @Override
+    public KeyDAO getKeyDAO() {
+        return new StandardKeyDAO(connection);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardActionDAO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardActionDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardActionDAO.java
index fe693e1..8fdfd34 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardActionDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardActionDAO.java
@@ -63,7 +63,7 @@ public class StandardActionDAO implements ActionDAO {
     // action table
     // ------------
     private static final String INSERT_ACTION = "INSERT INTO ACTION ("
-            + "USER_DN, USER_NAME, SOURCE_ID, SOURCE_NAME, SOURCE_TYPE, OPERATION, ACTION_TIMESTAMP"
+            + "IDENTITY, USER_NAME, SOURCE_ID, SOURCE_NAME, SOURCE_TYPE, OPERATION, ACTION_TIMESTAMP"
             + ") VALUES ("
             + "?, "
             + "?, "
@@ -216,8 +216,8 @@ public class StandardActionDAO implements ActionDAO {
         try {
             // obtain a statement to insert to the action table
             statement = connection.prepareStatement(INSERT_ACTION, Statement.RETURN_GENERATED_KEYS);
-            statement.setString(1, StringUtils.left(action.getUserIdentity(), 255));
-            statement.setString(2, StringUtils.left(action.getUserName(), 100));
+            statement.setString(1, StringUtils.left(action.getUserIdentity(), 4096));
+            statement.setString(2, StringUtils.left(action.getUserName(), 4096));
             statement.setString(3, action.getSourceId());
             statement.setString(4, StringUtils.left(action.getSourceName(), 1000));
             statement.setString(5, action.getSourceType().toString());
@@ -561,7 +561,7 @@ public class StandardActionDAO implements ActionDAO {
 
                 FlowChangeAction action = new FlowChangeAction();
                 action.setId(actionId);
-                action.setUserIdentity(rs.getString("USER_DN"));
+                action.setUserIdentity(rs.getString("IDENTITY"));
                 action.setUserName(rs.getString("USER_NAME"));
                 action.setOperation(Operation.valueOf(rs.getString("OPERATION")));
                 action.setTimestamp(new Date(rs.getTimestamp("ACTION_TIMESTAMP").getTime()));
@@ -635,7 +635,7 @@ public class StandardActionDAO implements ActionDAO {
                 // populate the action
                 action = new FlowChangeAction();
                 action.setId(rs.getInt("ID"));
-                action.setUserIdentity(rs.getString("USER_DN"));
+                action.setUserIdentity(rs.getString("IDENTITY"));
                 action.setUserName(rs.getString("USER_NAME"));
                 action.setOperation(operation);
                 action.setTimestamp(new Date(rs.getTimestamp("ACTION_TIMESTAMP").getTime()));

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java
new file mode 100644
index 0000000..f4bdc1d
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java
@@ -0,0 +1,154 @@
+/*
+ * 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.admin.dao.impl;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.UUID;
+import org.apache.nifi.admin.RepositoryUtils;
+import org.apache.nifi.admin.dao.DataAccessException;
+import org.apache.nifi.admin.dao.KeyDAO;
+import org.apache.nifi.key.Key;
+
+/**
+ *
+ */
+public class StandardKeyDAO implements KeyDAO {
+
+    private static final String SELECT_KEY_FOR_USER_BY_ID = "SELECT ID, IDENTITY, KEY "
+            + "FROM KEY "
+            + "WHERE ID = ?";
+
+    private static final String SELECT_KEY_FOR_USER_BY_IDENTITY = "SELECT ID, IDENTITY, KEY "
+            + "FROM KEY "
+            + "WHERE IDENTITY = ?";
+
+    private static final String INSERT_KEY = "INSERT INTO KEY ("
+            + "IDENTITY, KEY"
+            + ") VALUES ("
+            + "?, ?"
+            + ")";
+
+    private final Connection connection;
+
+    public StandardKeyDAO(Connection connection) {
+        this.connection = connection;
+    }
+
+    @Override
+    public Key findKeyById(int id) {
+        Key key = null;
+
+        PreparedStatement statement = null;
+        ResultSet rs = null;
+        try {
+            // add each authority for the specified user
+            statement = connection.prepareStatement(SELECT_KEY_FOR_USER_BY_ID);
+            statement.setInt(1, id);
+
+            // execute the query
+            rs = statement.executeQuery();
+
+            // if the key was found, add it
+            if (rs.next()) {
+                key = new Key();
+                key.setId(rs.getInt("ID"));
+                key.setIdentity(rs.getString("IDENTITY"));
+                key.setKey(rs.getString("KEY"));
+            }
+        } catch (SQLException sqle) {
+            throw new DataAccessException(sqle);
+        } finally {
+            RepositoryUtils.closeQuietly(rs);
+            RepositoryUtils.closeQuietly(statement);
+        }
+
+        return key;
+    }
+
+    @Override
+    public Key findLatestKeyByIdentity(String identity) {
+        if (identity == null) {
+            throw new IllegalArgumentException("Specified identity cannot be null.");
+        }
+
+        Key key = null;
+
+        PreparedStatement statement = null;
+        ResultSet rs = null;
+        try {
+            // add each authority for the specified user
+            statement = connection.prepareStatement(SELECT_KEY_FOR_USER_BY_IDENTITY);
+            statement.setString(1, identity);
+
+            // execute the query
+            rs = statement.executeQuery();
+
+            // if the key was found, add it
+            if (rs.next()) {
+                key = new Key();
+                key.setId(rs.getInt("ID"));
+                key.setIdentity(rs.getString("IDENTITY"));
+                key.setKey(rs.getString("KEY"));
+            }
+        } catch (SQLException sqle) {
+            throw new DataAccessException(sqle);
+        } finally {
+            RepositoryUtils.closeQuietly(rs);
+            RepositoryUtils.closeQuietly(statement);
+        }
+
+        return key;
+    }
+
+    @Override
+    public Key createKey(final String identity) {
+        PreparedStatement statement = null;
+        ResultSet rs = null;
+        try {
+            final String keyValue = UUID.randomUUID().toString();
+
+            // add each authority for the specified user
+            statement = connection.prepareStatement(INSERT_KEY, Statement.RETURN_GENERATED_KEYS);
+            statement.setString(1, identity);
+            statement.setString(2, keyValue);
+
+            // insert the key
+            int updateCount = statement.executeUpdate();
+            rs = statement.getGeneratedKeys();
+
+            // verify the results
+            if (updateCount == 1 && rs.next()) {
+                final Key key = new Key();
+                key.setId(rs.getInt(1));
+                key.setIdentity(identity);
+                key.setKey(keyValue);
+                return key;
+            } else {
+                throw new DataAccessException("Unable to add key for user.");
+            }
+        } catch (SQLException sqle) {
+            throw new DataAccessException(sqle);
+        } finally {
+            RepositoryUtils.closeQuietly(rs);
+            RepositoryUtils.closeQuietly(statement);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardUserDAO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardUserDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardUserDAO.java
index f6a62df..20356e3 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardUserDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardUserDAO.java
@@ -45,9 +45,9 @@ public class StandardUserDAO implements UserDAO {
             + "FROM USER U "
             + "WHERE U.STATUS = 'PENDING'";
 
-    private static final String SELECT_USER_BY_DN = "SELECT "
+    private static final String SELECT_USER_BY_USER = "SELECT "
             + "U.ID, "
-            + "U.DN, "
+            + "U.IDENTITY, "
             + "U.USER_NAME, "
             + "U.USER_GROUP, "
             + "U.CREATION, "
@@ -59,11 +59,11 @@ public class StandardUserDAO implements UserDAO {
             + "FROM USER U "
             + "LEFT JOIN AUTHORITY A " // ensures that users without authorities are still matched
             + "ON U.ID = A.USER_ID "
-            + "WHERE U.DN = ?";
+            + "WHERE U.IDENTITY = ?";
 
     private static final String SELECT_USER_BY_ID = "SELECT "
             + "U.ID, "
-            + "U.DN, "
+            + "U.IDENTITY, "
             + "U.USER_NAME, "
             + "U.USER_GROUP, "
             + "U.CREATION, "
@@ -79,7 +79,7 @@ public class StandardUserDAO implements UserDAO {
 
     private static final String SELECT_USERS = "SELECT "
             + "U.ID, "
-            + "U.DN, "
+            + "U.IDENTITY, "
             + "U.USER_NAME, "
             + "U.USER_GROUP, "
             + "U.CREATION, "
@@ -91,7 +91,7 @@ public class StandardUserDAO implements UserDAO {
             + "FROM USER U "
             + "LEFT JOIN AUTHORITY A " // ensures that users without authorities are still matched
             + "ON U.ID = A.USER_ID "
-            + "WHERE U.DN <> ?";
+            + "WHERE U.IDENTITY <> ?";
 
     private static final String SELECT_USER_GROUPS = "SELECT DISTINCT "
             + "U.USER_GROUP "
@@ -99,7 +99,7 @@ public class StandardUserDAO implements UserDAO {
 
     private static final String SELECT_USER_GROUP = "SELECT "
             + "U.ID, "
-            + "U.DN, "
+            + "U.IDENTITY, "
             + "U.USER_NAME, "
             + "U.USER_GROUP, "
             + "U.CREATION, "
@@ -111,10 +111,10 @@ public class StandardUserDAO implements UserDAO {
             + "FROM USER U "
             + "LEFT JOIN AUTHORITY A " // ensures that users without authorities are still matched
             + "ON U.ID = A.USER_ID "
-            + "WHERE U.DN <> ? AND U.USER_GROUP = ?";
+            + "WHERE U.IDENTITY <> ? AND U.USER_GROUP = ?";
 
     private static final String INSERT_USER = "INSERT INTO USER ("
-            + "ID, DN, USER_NAME, USER_GROUP, CREATION, LAST_VERIFIED, JUSTIFICATION, STATUS"
+            + "ID, IDENTITY, USER_NAME, USER_GROUP, CREATION, LAST_VERIFIED, JUSTIFICATION, STATUS"
             + ") VALUES ("
             + "?, "
             + "?, "
@@ -127,7 +127,7 @@ public class StandardUserDAO implements UserDAO {
             + ")";
 
     private static final String UPDATE_USER = "UPDATE USER SET "
-            + "DN = ?, "
+            + "IDENTITY = ?, "
             + "USER_NAME = ?, "
             + "USER_GROUP = ?, "
             + "LAST_ACCESSED = ?, "
@@ -194,7 +194,7 @@ public class StandardUserDAO implements UserDAO {
         try {
             // create the connection and obtain a statement
             statement = connection.prepareStatement(SELECT_USERS);
-            statement.setString(1, NiFiUser.ANONYMOUS_USER_DN);
+            statement.setString(1, NiFiUser.ANONYMOUS_USER_IDENTITY);
 
             // execute the query
             rs = statement.executeQuery();
@@ -211,7 +211,7 @@ public class StandardUserDAO implements UserDAO {
                 if (user == null || !userId.equals(user.getId())) {
                     user = new NiFiUser();
                     user.setId(userId);
-                    user.setDn(rs.getString("DN"));
+                    user.setIdentity(rs.getString("IDENTITY"));
                     user.setUserName(rs.getString("USER_NAME"));
                     user.setUserGroup(rs.getString("USER_GROUP"));
                     user.setJustification(rs.getString("JUSTIFICATION"));
@@ -287,7 +287,7 @@ public class StandardUserDAO implements UserDAO {
         try {
             // create the connection and obtain a statement
             statement = connection.prepareStatement(SELECT_USER_GROUP);
-            statement.setString(1, NiFiUser.ANONYMOUS_USER_DN);
+            statement.setString(1, NiFiUser.ANONYMOUS_USER_IDENTITY);
             statement.setString(2, group);
 
             // execute the query
@@ -305,7 +305,7 @@ public class StandardUserDAO implements UserDAO {
                 if (user == null || !userId.equals(user.getId())) {
                     user = new NiFiUser();
                     user.setId(userId);
-                    user.setDn(rs.getString("DN"));
+                    user.setIdentity(rs.getString("IDENTITY"));
                     user.setUserName(rs.getString("USER_NAME"));
                     user.setUserGroup(rs.getString("USER_GROUP"));
                     user.setJustification(rs.getString("JUSTIFICATION"));
@@ -366,7 +366,7 @@ public class StandardUserDAO implements UserDAO {
                 if (user == null) {
                     user = new NiFiUser();
                     user.setId(rs.getString("ID"));
-                    user.setDn(rs.getString("DN"));
+                    user.setIdentity(rs.getString("IDENTITY"));
                     user.setUserName(rs.getString("USER_NAME"));
                     user.setUserGroup(rs.getString("USER_GROUP"));
                     user.setJustification(rs.getString("JUSTIFICATION"));
@@ -409,7 +409,7 @@ public class StandardUserDAO implements UserDAO {
         ResultSet rs = null;
         try {
             // create the connection and obtain a statement
-            statement = connection.prepareStatement(SELECT_USER_BY_DN);
+            statement = connection.prepareStatement(SELECT_USER_BY_USER);
             statement.setString(1, dn);
 
             // execute the query
@@ -424,7 +424,7 @@ public class StandardUserDAO implements UserDAO {
                 if (user == null) {
                     user = new NiFiUser();
                     user.setId(rs.getString("ID"));
-                    user.setDn(rs.getString("DN"));
+                    user.setIdentity(rs.getString("IDENTITY"));
                     user.setUserName(rs.getString("USER_NAME"));
                     user.setUserGroup(rs.getString("USER_GROUP"));
                     user.setJustification(rs.getString("JUSTIFICATION"));
@@ -463,20 +463,25 @@ public class StandardUserDAO implements UserDAO {
 
     @Override
     public NiFiUser createUser(NiFiUser user) throws DataAccessException {
-        if (user.getDn() == null) {
-            throw new IllegalArgumentException("User dn must be specified.");
+        if (user.getIdentity() == null) {
+            throw new IllegalArgumentException("User identity must be specified.");
+        }
+
+        // ensure the user identity is not too lengthy
+        if (user.getIdentity().length() > 4096) {
+            throw new IllegalArgumentException("User identity must be less than 4096 characters.");
         }
 
         PreparedStatement statement = null;
         ResultSet rs = null;
         try {
-            final String id = UUID.nameUUIDFromBytes(user.getDn().getBytes(StandardCharsets.UTF_8)).toString();
+            final String id = UUID.nameUUIDFromBytes(user.getIdentity().getBytes(StandardCharsets.UTF_8)).toString();
 
             // create a statement
             statement = connection.prepareStatement(INSERT_USER, Statement.RETURN_GENERATED_KEYS);
             statement.setString(1, id);
-            statement.setString(2, StringUtils.left(user.getDn(), 255));
-            statement.setString(3, StringUtils.left(user.getUserName(), 100));
+            statement.setString(2, StringUtils.left(user.getIdentity(), 4096));
+            statement.setString(3, StringUtils.left(user.getUserName(), 4096));
             statement.setString(4, StringUtils.left(user.getUserGroup(), 100));
             if (user.getLastVerified() != null) {
                 statement.setTimestamp(5, new java.sql.Timestamp(user.getLastVerified().getTime()));
@@ -531,8 +536,8 @@ public class StandardUserDAO implements UserDAO {
         try {
             // create a statement
             statement = connection.prepareStatement(UPDATE_USER);
-            statement.setString(1, StringUtils.left(user.getDn(), 255));
-            statement.setString(2, StringUtils.left(user.getUserName(), 100));
+            statement.setString(1, StringUtils.left(user.getIdentity(), 4096));
+            statement.setString(2, StringUtils.left(user.getUserName(), 4096));
             statement.setString(3, StringUtils.left(user.getUserGroup(), 100));
             statement.setString(6, StringUtils.left(user.getJustification(), 500));
             statement.setString(7, user.getStatus().toString());

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java
new file mode 100644
index 0000000..ae64c41
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java
@@ -0,0 +1,42 @@
+/*
+ * 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.admin.service;
+
+import org.apache.nifi.key.Key;
+
+/**
+ * Supports retrieving and issues keys for signing user tokens.
+ */
+public interface KeyService {
+
+    /**
+     * Gets a key for the specified user identity. Returns null if the user has not had a key issued
+     *
+     * @param id The key id
+     * @return The key or null
+     */
+    Key getKey(int id);
+
+    /**
+     * Gets a key for the specified user identity. If a key does not exist, one will be created.
+     *
+     * @param identity The user identity
+     * @return The key
+     * @throws AdministrationException if it failed to get/create the key
+     */
+    Key getOrCreateKey(String identity);
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/AbstractUserAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/AbstractUserAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/AbstractUserAction.java
index b970dc1..69c6c1f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/AbstractUserAction.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/AbstractUserAction.java
@@ -76,14 +76,14 @@ public abstract class AbstractUserAction<T> implements AdministrationAction<T> {
      */
     protected void verifyAccount(AuthorityProvider authorityProvider, NiFiUser user) {
         // load the roles for the user
-        Set<Authority> authorities = authorityProvider.getAuthorities(user.getDn());
+        Set<Authority> authorities = authorityProvider.getAuthorities(user.getIdentity());
 
         // update the user's authorities
         user.getAuthorities().clear();
         user.getAuthorities().addAll(authorities);
 
         // get the user group
-        user.setUserGroup(authorityProvider.getGroupForUser(user.getDn()));
+        user.setUserGroup(authorityProvider.getGroupForUser(user.getIdentity()));
 
         // update the users status in case they were previously pending or disabled
         user.setStatus(AccountStatus.ACTIVE);

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/AuthorizeUserAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/AuthorizeUserAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/AuthorizeUserAction.java
index fe32772..ed4dfa1 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/AuthorizeUserAction.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/AuthorizeUserAction.java
@@ -37,11 +37,11 @@ import org.apache.nifi.user.NiFiUser;
  */
 public class AuthorizeUserAction extends AbstractUserAction<NiFiUser> {
 
-    private final String dn;
+    private final String identity;
     private final int cacheDurationSeconds;
 
-    public AuthorizeUserAction(String dn, int cacheDurationSeconds) {
-        this.dn = dn;
+    public AuthorizeUserAction(String identity, int cacheDurationSeconds) {
+        this.identity = identity;
         this.cacheDurationSeconds = cacheDurationSeconds;
     }
 
@@ -50,14 +50,14 @@ public class AuthorizeUserAction extends AbstractUserAction<NiFiUser> {
         UserDAO userDao = daoFactory.getUserDAO();
 
         // get the user
-        NiFiUser user = userDao.findUserByDn(dn);
+        NiFiUser user = userDao.findUserByDn(identity);
 
         // verify the user was found
         if (user == null) {
             // determine whether this users exists
             boolean doesDnExist = false;
             try {
-                doesDnExist = authorityProvider.doesDnExist(dn);
+                doesDnExist = authorityProvider.doesDnExist(identity);
             } catch (AuthorityAccessException aae) {
                 throw new AdministrationException(String.format("Unable to access authority details: %s", aae.getMessage()), aae);
             }
@@ -66,8 +66,8 @@ public class AuthorizeUserAction extends AbstractUserAction<NiFiUser> {
             if (doesDnExist) {
                 // create the user
                 user = new NiFiUser();
-                user.setDn(dn);
-                user.setUserName(CertificateUtils.extractUsername(dn));
+                user.setIdentity(identity);
+                user.setUserName(CertificateUtils.extractUsername(identity));
                 user.setJustification("User details specified by authority provider.");
 
                 try {
@@ -86,12 +86,12 @@ public class AuthorizeUserAction extends AbstractUserAction<NiFiUser> {
                     createUser.execute(daoFactory, authorityProvider);
                 } catch (UnknownIdentityException uie) {
                     // strange since the provider just reported this dn existed but handleing anyways...
-                    throw new AccountNotFoundException(String.format("Unable to verify access for %s.", dn));
+                    throw new AccountNotFoundException(String.format("Unable to verify access for %s.", identity));
                 } catch (AuthorityAccessException aae) {
                     throw new AdministrationException(String.format("Unable to access authority details: %s", aae.getMessage()), aae);
                 }
             } else {
-                throw new AccountNotFoundException(String.format("Unable to verify access for %s.", dn));
+                throw new AccountNotFoundException(String.format("Unable to verify access for %s.", identity));
             }
         } else {
             Throwable providerError = null;
@@ -134,7 +134,7 @@ public class AuthorizeUserAction extends AbstractUserAction<NiFiUser> {
             updateUserAuthorities.execute(daoFactory, authorityProvider);
 
             if (providerError != null) {
-                throw new AccountDisabledException(String.format("User credentials for %s were not found. This account has been disabled.", user.getDn()), providerError);
+                throw new AccountDisabledException(String.format("User credentials for %s were not found. This account has been disabled.", user.getIdentity()), providerError);
             }
         }
 
@@ -165,9 +165,9 @@ public class AuthorizeUserAction extends AbstractUserAction<NiFiUser> {
      */
     private void checkAccountStatus(NiFiUser user) {
         if (AccountStatus.DISABLED.equals(user.getStatus())) {
-            throw new AccountDisabledException(String.format("Account for %s is disabled.", user.getDn()));
+            throw new AccountDisabledException(String.format("The account for %s has been disabled.", user.getIdentity()));
         } else if (AccountStatus.PENDING.equals(user.getStatus())) {
-            throw new AccountPendingException(String.format("Account for %s is pending.", user.getDn()));
+            throw new AccountPendingException(String.format("The account for %s is currently pending approval.", user.getIdentity()));
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DisableUserAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DisableUserAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DisableUserAction.java
index c31f107..9e9b798 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DisableUserAction.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/DisableUserAction.java
@@ -63,12 +63,12 @@ public class DisableUserAction implements AdministrationAction<NiFiUser> {
 
         try {
             // revoke the user in the authority provider
-            authorityProvider.revokeUser(user.getDn());
+            authorityProvider.revokeUser(user.getIdentity());
         } catch (UnknownIdentityException uie) {
             // user identity is not known
-            logger.info(String.format("User %s has already been removed from the authority provider.", user.getDn()));
+            logger.info(String.format("User %s has already been removed from the authority provider.", user.getIdentity()));
         } catch (AuthorityAccessException aae) {
-            throw new AdministrationException(String.format("Unable to revoke user '%s': %s", user.getDn(), aae.getMessage()), aae);
+            throw new AdministrationException(String.format("Unable to revoke user '%s': %s", user.getIdentity(), aae.getMessage()), aae);
         }
 
         return user;

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdAction.java
new file mode 100644
index 0000000..8763b9d
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdAction.java
@@ -0,0 +1,42 @@
+/*
+ * 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.admin.service.action;
+
+import org.apache.nifi.admin.dao.DAOFactory;
+import org.apache.nifi.authorization.AuthorityProvider;
+
+import org.apache.nifi.admin.dao.KeyDAO;
+import org.apache.nifi.key.Key;
+
+/**
+ * Gets a key for the specified key id.
+ */
+public class GetKeyByIdAction implements AdministrationAction<Key> {
+
+    private final int id;
+
+    public GetKeyByIdAction(int id) {
+        this.id = id;
+    }
+
+    @Override
+    public Key execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) {
+        final KeyDAO keyDao = daoFactory.getKeyDAO();
+        return keyDao.findKeyById(id);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdentityAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdentityAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdentityAction.java
new file mode 100644
index 0000000..9bcb0b3
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdentityAction.java
@@ -0,0 +1,42 @@
+/*
+ * 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.admin.service.action;
+
+import org.apache.nifi.admin.dao.DAOFactory;
+import org.apache.nifi.authorization.AuthorityProvider;
+
+import org.apache.nifi.admin.dao.KeyDAO;
+import org.apache.nifi.key.Key;
+
+/**
+ * Gets a key for the specified key id.
+ */
+public class GetKeyByIdentityAction implements AdministrationAction<Key> {
+
+    private final String identity;
+
+    public GetKeyByIdentityAction(String identity) {
+        this.identity = identity;
+    }
+
+    @Override
+    public Key execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) {
+        final KeyDAO keyDao = daoFactory.getKeyDAO();
+        return keyDao.findLatestKeyByIdentity(identity);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetOrCreateKeyAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetOrCreateKeyAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetOrCreateKeyAction.java
new file mode 100644
index 0000000..bb85b6f
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetOrCreateKeyAction.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.admin.service.action;
+
+import org.apache.nifi.admin.dao.DAOFactory;
+import org.apache.nifi.authorization.AuthorityProvider;
+
+import org.apache.nifi.admin.dao.KeyDAO;
+import org.apache.nifi.key.Key;
+
+/**
+ * Gets a key for the specified user identity.
+ */
+public class GetOrCreateKeyAction implements AdministrationAction<Key> {
+
+    private final String identity;
+
+    public GetOrCreateKeyAction(String identity) {
+        this.identity = identity;
+    }
+
+    @Override
+    public Key execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) {
+        final KeyDAO keyDao = daoFactory.getKeyDAO();
+
+        Key key = keyDao.findLatestKeyByIdentity(identity);
+        if (key == null) {
+            key = keyDao.createKey(identity);
+        }
+
+        return key;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/RequestUserAccountAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/RequestUserAccountAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/RequestUserAccountAction.java
index 3dce6d9..198a32d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/RequestUserAccountAction.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/RequestUserAccountAction.java
@@ -30,11 +30,11 @@ import org.apache.nifi.user.NiFiUser;
  */
 public class RequestUserAccountAction implements AdministrationAction<NiFiUser> {
 
-    private final String dn;
+    private final String identity;
     private final String justification;
 
-    public RequestUserAccountAction(String dn, String justification) {
-        this.dn = dn;
+    public RequestUserAccountAction(String identity, String justification) {
+        this.identity = identity;
         this.justification = justification;
     }
 
@@ -43,15 +43,15 @@ public class RequestUserAccountAction implements AdministrationAction<NiFiUser>
         UserDAO userDao = daoFactory.getUserDAO();
 
         // determine if this user already exists
-        NiFiUser user = userDao.findUserByDn(dn);
+        NiFiUser user = userDao.findUserByDn(identity);
         if (user != null) {
-            throw new IllegalArgumentException(String.format("User account for %s already exists.", dn));
+            throw new IllegalArgumentException(String.format("User account for %s already exists.", identity));
         }
 
         // create the user
         user = new NiFiUser();
-        user.setDn(dn);
-        user.setUserName(CertificateUtils.extractUsername(dn));
+        user.setIdentity(identity);
+        user.setUserName(CertificateUtils.extractUsername(identity));
         user.setJustification(justification);
         user.setStatus(AccountStatus.PENDING);
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/SeedUserAccountsAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/SeedUserAccountsAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/SeedUserAccountsAction.java
index 6665ac0..c16cc71 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/SeedUserAccountsAction.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/SeedUserAccountsAction.java
@@ -46,7 +46,7 @@ public class SeedUserAccountsAction extends AbstractUserAction<Void> {
     @Override
     public Void execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) throws DataAccessException {
         UserDAO userDao = daoFactory.getUserDAO();
-        Set<String> authorizedDns = new HashSet<>();
+        Set<String> authorizedIdentities = new HashSet<>();
 
         // get the current user cache
         final Set<NiFiUser> existingUsers;
@@ -62,7 +62,7 @@ public class SeedUserAccountsAction extends AbstractUserAction<Void> {
         try {
             // all users for all roles
             for (final Authority authority : Authority.values()) {
-                authorizedDns.addAll(authorityProvider.getUsers(authority));
+                authorizedIdentities.addAll(authorityProvider.getUsers(authority));
             }
         } catch (AuthorityAccessException aae) {
             // unable to access the authority provider... honor the cache
@@ -73,25 +73,25 @@ public class SeedUserAccountsAction extends AbstractUserAction<Void> {
         final Set<NiFiUser> accountsToRevoke = new HashSet<>(existingUsers);
 
         // persist the users
-        for (String dn : authorizedDns) {
+        for (String identity : authorizedIdentities) {
             NiFiUser user = null;
             try {
                 // locate the user for this dn
-                user = userDao.findUserByDn(dn);
+                user = userDao.findUserByDn(identity);
                 boolean newAccount = false;
 
                 // if the user does not exist, create a new account
                 if (user == null) {
-                    logger.info(String.format("Creating user account: %s", dn));
+                    logger.info(String.format("Creating user account: %s", identity));
                     newAccount = true;
 
                     // create the user
                     user = new NiFiUser();
-                    user.setDn(dn);
-                    user.setUserName(CertificateUtils.extractUsername(dn));
+                    user.setIdentity(identity);
+                    user.setUserName(CertificateUtils.extractUsername(identity));
                     user.setJustification("User details specified by authority provider.");
                 } else {
-                    logger.info(String.format("User account already created: %s. Updating authorities...", dn));
+                    logger.info(String.format("User account already created: %s. Updating authorities...", identity));
                 }
 
                 // verify the account
@@ -142,7 +142,7 @@ public class SeedUserAccountsAction extends AbstractUserAction<Void> {
             }
 
             try {
-                logger.info(String.format("User not authorized with configured provider: %s. Disabling account...", user.getDn()));
+                logger.info(String.format("User not authorized with configured provider: %s. Disabling account...", user.getIdentity()));
 
                 // disable the account and reset its last verified timestamp since it was not found
                 // in the current configured authority provider

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/UngroupUserAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/UngroupUserAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/UngroupUserAction.java
index 01eaf5f..2604a47 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/UngroupUserAction.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/UngroupUserAction.java
@@ -56,11 +56,11 @@ public class UngroupUserAction extends AbstractUserAction<Void> {
 
         try {
             // update the authority provider
-            authorityProvider.ungroupUser(user.getDn());
+            authorityProvider.ungroupUser(user.getIdentity());
         } catch (UnknownIdentityException uie) {
-            throw new AccountNotFoundException(String.format("Unable to ungroup user '%s': %s", user.getDn(), uie.getMessage()), uie);
+            throw new AccountNotFoundException(String.format("Unable to ungroup user '%s': %s", user.getIdentity(), uie.getMessage()), uie);
         } catch (AuthorityAccessException aae) {
-            throw new AdministrationException(String.format("Unable to ungroup user '%s': %s", user.getDn(), aae.getMessage()), aae);
+            throw new AdministrationException(String.format("Unable to ungroup user '%s': %s", user.getIdentity(), aae.getMessage()), aae);
         }
 
         return null;

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/UpdateUserAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/UpdateUserAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/UpdateUserAction.java
index cef21d7..ecb91e6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/UpdateUserAction.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/UpdateUserAction.java
@@ -61,41 +61,41 @@ public class UpdateUserAction extends AbstractUserAction<NiFiUser> {
         }
 
         // determine whether this users exists
-        boolean doesDnExist = false;
+        boolean doesIdentityExist = false;
         try {
-            doesDnExist = authorityProvider.doesDnExist(user.getDn());
+            doesIdentityExist = authorityProvider.doesDnExist(user.getIdentity());
         } catch (AuthorityAccessException aae) {
             throw new AdministrationException(String.format("Unable to access authority details: %s", aae.getMessage()), aae);
         }
 
         // if the user already doesn't exist, add them
-        if (!doesDnExist) {
+        if (!doesIdentityExist) {
             try {
                 // add the account account and group if necessary
-                authorityProvider.addUser(user.getDn(), user.getUserGroup());
+                authorityProvider.addUser(user.getIdentity(), user.getUserGroup());
             } catch (final IdentityAlreadyExistsException iaee) {
-                logger.warn(String.format("User '%s' already exists in the authority provider.  Continuing with user update.", user.getDn()));
+                logger.warn(String.format("User '%s' already exists in the authority provider.  Continuing with user update.", user.getIdentity()));
             } catch (AuthorityAccessException aae) {
-                throw new AdministrationException(String.format("Unable to access authorities for '%s': %s", user.getDn(), aae.getMessage()), aae);
+                throw new AdministrationException(String.format("Unable to access authorities for '%s': %s", user.getIdentity(), aae.getMessage()), aae);
             }
         }
 
         try {
             // update the authority provider as approprivate
-            authorityProvider.setAuthorities(user.getDn(), authorities);
+            authorityProvider.setAuthorities(user.getIdentity(), authorities);
         } catch (UnknownIdentityException uie) {
-            throw new AccountNotFoundException(String.format("Unable to modify authorities for '%s': %s.", user.getDn(), uie.getMessage()), uie);
+            throw new AccountNotFoundException(String.format("Unable to modify authorities for '%s': %s.", user.getIdentity(), uie.getMessage()), uie);
         } catch (AuthorityAccessException aae) {
-            throw new AdministrationException(String.format("Unable to access authorities for '%s': %s.", user.getDn(), aae.getMessage()), aae);
+            throw new AdministrationException(String.format("Unable to access authorities for '%s': %s.", user.getIdentity(), aae.getMessage()), aae);
         }
 
         try {
             // get the user group
-            user.setUserGroup(authorityProvider.getGroupForUser(user.getDn()));
+            user.setUserGroup(authorityProvider.getGroupForUser(user.getIdentity()));
         } catch (UnknownIdentityException uie) {
-            throw new AccountNotFoundException(String.format("Unable to determine the group for '%s': %s.", user.getDn(), uie.getMessage()), uie);
+            throw new AccountNotFoundException(String.format("Unable to determine the group for '%s': %s.", user.getIdentity(), uie.getMessage()), uie);
         } catch (AuthorityAccessException aae) {
-            throw new AdministrationException(String.format("Unable to access the group for '%s': %s.", user.getDn(), aae.getMessage()), aae);
+            throw new AdministrationException(String.format("Unable to access the group for '%s': %s.", user.getIdentity(), aae.getMessage()), aae);
         }
 
         // since all the authorities were updated accordingly, set the authorities

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/UpdateUserGroupAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/UpdateUserGroupAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/UpdateUserGroupAction.java
index 56b214c..1d7941f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/UpdateUserGroupAction.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/UpdateUserGroupAction.java
@@ -61,7 +61,7 @@ public class UpdateUserGroupAction extends AbstractUserAction<Void> {
 
         // record the new users being added to this group
         final Set<NiFiUser> newUsers = new HashSet<>();
-        final Set<String> newUserDns = new HashSet<>();
+        final Set<String> newUserIdentities = new HashSet<>();
 
         // if the user ids have been specified we need to create/update a group using the specified group name
         if (userIds != null) {
@@ -81,13 +81,13 @@ public class UpdateUserGroupAction extends AbstractUserAction<Void> {
 
                 try {
                     // if the user is unknown to the authority provider we cannot continue
-                    if (!authorityProvider.doesDnExist(user.getDn()) || AccountStatus.DISABLED.equals(user.getStatus())) {
-                        throw new IllegalStateException(String.format("Unable to group these users because access for '%s' is not %s.", user.getDn(), AccountStatus.ACTIVE.toString()));
+                    if (!authorityProvider.doesDnExist(user.getIdentity()) || AccountStatus.DISABLED.equals(user.getStatus())) {
+                        throw new IllegalStateException(String.format("Unable to group these users because access for '%s' is not %s.", user.getIdentity(), AccountStatus.ACTIVE.toString()));
                     }
 
                     // record the user being added to this group
                     newUsers.add(user);
-                    newUserDns.add(user.getDn());
+                    newUserIdentities.add(user.getIdentity());
                 } catch (final AuthorityAccessException aae) {
                     throw new AdministrationException(String.format("Unable to access authority details: %s", aae.getMessage()), aae);
                 }
@@ -95,11 +95,11 @@ public class UpdateUserGroupAction extends AbstractUserAction<Void> {
 
             try {
                 // update the authority provider
-                authorityProvider.setUsersGroup(newUserDns, group);
+                authorityProvider.setUsersGroup(newUserIdentities, group);
             } catch (UnknownIdentityException uie) {
-                throw new AccountNotFoundException(String.format("Unable to set user group '%s': %s", StringUtils.join(newUserDns, ", "), uie.getMessage()), uie);
+                throw new AccountNotFoundException(String.format("Unable to set user group '%s': %s", StringUtils.join(newUserIdentities, ", "), uie.getMessage()), uie);
             } catch (AuthorityAccessException aae) {
-                throw new AdministrationException(String.format("Unable to set user group '%s': %s", StringUtils.join(newUserDns, ", "), aae.getMessage()), aae);
+                throw new AdministrationException(String.format("Unable to set user group '%s': %s", StringUtils.join(newUserIdentities, ", "), aae.getMessage()), aae);
             }
         }
 
@@ -118,35 +118,35 @@ public class UpdateUserGroupAction extends AbstractUserAction<Void> {
             if (authorities != null) {
                 try {
                     // update the authority provider as approprivate
-                    authorityProvider.setAuthorities(user.getDn(), authorities);
+                    authorityProvider.setAuthorities(user.getIdentity(), authorities);
 
                     // since all the authorities were updated accordingly, set the authorities
                     user.getAuthorities().clear();
                     user.getAuthorities().addAll(authorities);
                 } catch (UnknownIdentityException uie) {
-                    throw new AccountNotFoundException(String.format("Unable to modify authorities for '%s': %s.", user.getDn(), uie.getMessage()), uie);
+                    throw new AccountNotFoundException(String.format("Unable to modify authorities for '%s': %s.", user.getIdentity(), uie.getMessage()), uie);
                 } catch (AuthorityAccessException aae) {
-                    throw new AdministrationException(String.format("Unable to access authorities for '%s': %s.", user.getDn(), aae.getMessage()), aae);
+                    throw new AdministrationException(String.format("Unable to access authorities for '%s': %s.", user.getIdentity(), aae.getMessage()), aae);
                 }
             } else {
                 try {
                     // refresh the authorities according to the provider
                     user.getAuthorities().clear();
-                    user.getAuthorities().addAll(authorityProvider.getAuthorities(user.getDn()));
+                    user.getAuthorities().addAll(authorityProvider.getAuthorities(user.getIdentity()));
                 } catch (UnknownIdentityException uie) {
-                    throw new AccountNotFoundException(String.format("Unable to determine the authorities for '%s': %s.", user.getDn(), uie.getMessage()), uie);
+                    throw new AccountNotFoundException(String.format("Unable to determine the authorities for '%s': %s.", user.getIdentity(), uie.getMessage()), uie);
                 } catch (AuthorityAccessException aae) {
-                    throw new AdministrationException(String.format("Unable to access authorities for '%s': %s.", user.getDn(), aae.getMessage()), aae);
+                    throw new AdministrationException(String.format("Unable to access authorities for '%s': %s.", user.getIdentity(), aae.getMessage()), aae);
                 }
             }
 
             try {
                 // get the user group
-                user.setUserGroup(authorityProvider.getGroupForUser(user.getDn()));
+                user.setUserGroup(authorityProvider.getGroupForUser(user.getIdentity()));
             } catch (UnknownIdentityException uie) {
-                throw new AccountNotFoundException(String.format("Unable to determine the group for '%s': %s.", user.getDn(), uie.getMessage()), uie);
+                throw new AccountNotFoundException(String.format("Unable to determine the group for '%s': %s.", user.getIdentity(), uie.getMessage()), uie);
             } catch (AuthorityAccessException aae) {
-                throw new AdministrationException(String.format("Unable to access the group for '%s': %s.", user.getDn(), aae.getMessage()), aae);
+                throw new AdministrationException(String.format("Unable to access the group for '%s': %s.", user.getIdentity(), aae.getMessage()), aae);
             }
 
             // update the users status in case they were previously pending or disabled

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java
new file mode 100644
index 0000000..ca0a124
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java
@@ -0,0 +1,126 @@
+/*
+ * 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.admin.service.impl;
+
+import org.apache.nifi.admin.dao.DataAccessException;
+import org.apache.nifi.admin.service.AdministrationException;
+import org.apache.nifi.admin.service.KeyService;
+import org.apache.nifi.admin.service.action.GetKeyByIdAction;
+import org.apache.nifi.admin.service.action.GetOrCreateKeyAction;
+import org.apache.nifi.admin.service.transaction.Transaction;
+import org.apache.nifi.admin.service.transaction.TransactionBuilder;
+import org.apache.nifi.admin.service.transaction.TransactionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import org.apache.nifi.key.Key;
+
+/**
+ *
+ */
+public class StandardKeyService implements KeyService {
+
+    private static final Logger logger = LoggerFactory.getLogger(StandardKeyService.class);
+
+    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+    private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
+    private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
+
+    private TransactionBuilder transactionBuilder;
+
+    @Override
+    public Key getKey(int id) {
+        Transaction transaction = null;
+        Key key = null;
+
+        readLock.lock();
+        try {
+            // start the transaction
+            transaction = transactionBuilder.start();
+
+            // get the key
+            GetKeyByIdAction addActions = new GetKeyByIdAction(id);
+            key = transaction.execute(addActions);
+
+            // commit the transaction
+            transaction.commit();
+        } catch (TransactionException | DataAccessException te) {
+            rollback(transaction);
+            throw new AdministrationException(te);
+        } catch (Throwable t) {
+            rollback(transaction);
+            throw t;
+        } finally {
+            closeQuietly(transaction);
+            readLock.unlock();
+        }
+
+        return key;
+    }
+
+    @Override
+    public Key getOrCreateKey(String identity) {
+        Transaction transaction = null;
+        Key key = null;
+
+        writeLock.lock();
+        try {
+            // start the transaction
+            transaction = transactionBuilder.start();
+
+            // seed the accounts
+            GetOrCreateKeyAction addActions = new GetOrCreateKeyAction(identity);
+            key = transaction.execute(addActions);
+
+            // commit the transaction
+            transaction.commit();
+        } catch (TransactionException | DataAccessException te) {
+            rollback(transaction);
+            throw new AdministrationException(te);
+        } catch (Throwable t) {
+            rollback(transaction);
+            throw t;
+        } finally {
+            closeQuietly(transaction);
+            writeLock.unlock();
+        }
+
+        return key;
+    }
+
+    private void rollback(Transaction transaction) {
+        if (transaction != null) {
+            transaction.rollback();
+        }
+    }
+
+    private void closeQuietly(final Transaction transaction) {
+        if (transaction != null) {
+            try {
+                transaction.close();
+            } catch (final IOException ioe) {
+            }
+        }
+    }
+
+    public void setTransactionBuilder(TransactionBuilder transactionBuilder) {
+        this.transactionBuilder = transactionBuilder;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java
new file mode 100644
index 0000000..9ce7a9a
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java
@@ -0,0 +1,69 @@
+/*
+ * 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.key;
+
+import java.io.Serializable;
+
+/**
+ * An signing key for a NiFi user.
+ */
+public class Key implements Serializable {
+
+    private int id;
+    private String identity;
+    private String key;
+
+    /**
+     * The key id.
+     *
+     * @return the id
+     */
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    /**
+     * The identity of the user this key is associated with.
+     *
+     * @return the identity
+     */
+    public String getIdentity() {
+        return identity;
+    }
+
+    public void setIdentity(String identity) {
+        this.identity = identity;
+    }
+
+    /**
+     * The signing key.
+     *
+     * @return the signing key
+     */
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/NiFiUser.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/NiFiUser.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/NiFiUser.java
index a47bde9..231b133 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/NiFiUser.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/NiFiUser.java
@@ -29,10 +29,10 @@ import org.apache.commons.lang3.StringUtils;
  */
 public class NiFiUser implements Serializable {
 
-    public static final String ANONYMOUS_USER_DN = "anonymous";
+    public static final String ANONYMOUS_USER_IDENTITY = "anonymous";
 
     private String id;
-    private String dn;
+    private String identity;
     private String userName;
     private String userGroup;
     private String justification;
@@ -55,12 +55,12 @@ public class NiFiUser implements Serializable {
         this.creation = creation;
     }
 
-    public String getDn() {
-        return dn;
+    public String getIdentity() {
+        return identity;
     }
 
-    public void setDn(String dn) {
-        this.dn = dn;
+    public void setIdentity(String identity) {
+        this.identity = identity;
     }
 
     public String getUserName() {
@@ -143,7 +143,7 @@ public class NiFiUser implements Serializable {
             return false;
         }
         final NiFiUser other = (NiFiUser) obj;
-        if (!Objects.equals(this.dn, other.dn)) {
+        if (!Objects.equals(this.identity, other.identity)) {
             return false;
         }
         return true;
@@ -152,13 +152,13 @@ public class NiFiUser implements Serializable {
     @Override
     public int hashCode() {
         int hash = 7;
-        hash = 53 * hash + Objects.hashCode(this.dn);
+        hash = 53 * hash + Objects.hashCode(this.identity);
         return hash;
     }
 
     @Override
     public String toString() {
-        return String.format("dn[%s], userName[%s], justification[%s], authorities[%s]", getDn(), getUserName(), getJustification(), StringUtils.join(getAuthorities(), ", "));
+        return String.format("identity[%s], userName[%s], justification[%s], authorities[%s]", getIdentity(), getUserName(), getJustification(), StringUtils.join(getAuthorities(), ", "));
     }
 
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
index 8cb4b97..6d7b739 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
@@ -36,6 +36,11 @@
     <bean id="auditDataSource" class="org.apache.nifi.admin.AuditDataSourceFactoryBean" destroy-method="shutdown" depends-on="userDataSource">
         <property name="properties" ref="nifiProperties"/>
     </bean>
+    
+    <!-- initialize the data source -->
+    <bean id="keyDataSource" class="org.apache.nifi.admin.KeyDataSourceFactoryBean" destroy-method="shutdown">
+        <property name="properties" ref="nifiProperties"/>
+    </bean>
 
     <!-- initialize the user transaction builder -->
     <bean id="userTransactionBuilder" class="org.apache.nifi.admin.service.transaction.impl.StandardTransactionBuilder">
@@ -48,6 +53,12 @@
         <property name="authorityProvider" ref="authorityProvider"/>
         <property name="dataSource" ref="auditDataSource"/>
     </bean>
+    
+    <!-- initialize the key transaction builder -->
+    <bean id="keyTransactionBuilder" class="org.apache.nifi.admin.service.transaction.impl.StandardTransactionBuilder">
+        <property name="authorityProvider" ref="authorityProvider"/>
+        <property name="dataSource" ref="keyDataSource"/>
+    </bean>
 
     <!-- administration service -->
     <bean id="userService" class="org.apache.nifi.admin.service.impl.StandardUserService" init-method="seedUserAccounts">
@@ -59,4 +70,9 @@
     <bean id="auditService" class="org.apache.nifi.admin.service.impl.StandardAuditService">
         <property name="transactionBuilder" ref="auditTransactionBuilder"/>
     </bean>
+
+    <!-- key service -->
+    <bean id="keyService" class="org.apache.nifi.admin.service.impl.StandardKeyService">
+        <property name="transactionBuilder" ref="keyTransactionBuilder"/>
+    </bean>
 </beans>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/AuthorizeUserActionTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/AuthorizeUserActionTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/AuthorizeUserActionTest.java
index 28ea4a9..8d3c15a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/AuthorizeUserActionTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/AuthorizeUserActionTest.java
@@ -54,17 +54,17 @@ public class AuthorizeUserActionTest {
     private static final String USER_ID_10 = "10";
     private static final String USER_ID_11 = "11";
 
-    private static final String USER_DN_1 = "authority access exception while searching for user";
-    private static final String USER_DN_2 = "unknown user";
-    private static final String USER_DN_3 = "user removed after checking existence";
-    private static final String USER_DN_4 = "access exception getting authorities";
-    private static final String USER_DN_5 = "error creating user account";
-    private static final String USER_DN_6 = "create user general sequence";
-    private static final String USER_DN_7 = "existing user requires verification";
-    private static final String USER_DN_8 = "existing user does not require verification";
-    private static final String USER_DN_9 = "existing pending user";
-    private static final String USER_DN_10 = "existing disabled user";
-    private static final String USER_DN_11 = "existing user is now unknown in the authority provider";
+    private static final String USER_IDENTITY_1 = "authority access exception while searching for user";
+    private static final String USER_IDENTITY_2 = "unknown user";
+    private static final String USER_IDENTITY_3 = "user removed after checking existence";
+    private static final String USER_IDENTITY_4 = "access exception getting authorities";
+    private static final String USER_IDENTITY_5 = "error creating user account";
+    private static final String USER_IDENTITY_6 = "create user general sequence";
+    private static final String USER_IDENTITY_7 = "existing user requires verification";
+    private static final String USER_IDENTITY_8 = "existing user does not require verification";
+    private static final String USER_IDENTITY_9 = "existing pending user";
+    private static final String USER_IDENTITY_10 = "existing disabled user";
+    private static final String USER_IDENTITY_11 = "existing user is now unknown in the authority provider";
 
     private DAOFactory daoFactory;
     private UserDAO userDao;
@@ -85,18 +85,18 @@ public class AuthorizeUserActionTest {
                 if (USER_ID_7.equals(id)) {
                     user = new NiFiUser();
                     user.setId(USER_ID_7);
-                    user.setDn(USER_DN_7);
+                    user.setIdentity(USER_IDENTITY_7);
                     user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_MONITOR));
                 } else if (USER_ID_8.equals(id)) {
                     user = new NiFiUser();
                     user.setId(USER_ID_8);
-                    user.setDn(USER_DN_8);
+                    user.setIdentity(USER_IDENTITY_8);
                     user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_MONITOR));
                     user.setLastVerified(new Date());
                 } else if (USER_ID_11.equals(id)) {
                     user = new NiFiUser();
                     user.setId(USER_ID_11);
-                    user.setDn(USER_DN_11);
+                    user.setIdentity(USER_IDENTITY_11);
                     user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_MONITOR));
                     user.setStatus(AccountStatus.ACTIVE);
                 }
@@ -112,35 +112,35 @@ public class AuthorizeUserActionTest {
 
                 NiFiUser user = null;
                 switch (dn) {
-                    case USER_DN_7:
+                    case USER_IDENTITY_7:
                         user = new NiFiUser();
                         user.setId(USER_ID_7);
-                        user.setDn(USER_DN_7);
+                        user.setIdentity(USER_IDENTITY_7);
                         user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_MONITOR));
                         break;
-                    case USER_DN_8:
+                    case USER_IDENTITY_8:
                         user = new NiFiUser();
                         user.setId(USER_ID_8);
-                        user.setDn(USER_DN_8);
+                        user.setIdentity(USER_IDENTITY_8);
                         user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_MONITOR));
                         user.setLastVerified(new Date());
                         break;
-                    case USER_DN_9:
+                    case USER_IDENTITY_9:
                         user = new NiFiUser();
                         user.setId(USER_ID_9);
-                        user.setDn(USER_DN_9);
+                        user.setIdentity(USER_IDENTITY_9);
                         user.setStatus(AccountStatus.PENDING);
                         break;
-                    case USER_DN_10:
+                    case USER_IDENTITY_10:
                         user = new NiFiUser();
                         user.setId(USER_ID_10);
-                        user.setDn(USER_DN_10);
+                        user.setIdentity(USER_IDENTITY_10);
                         user.setStatus(AccountStatus.DISABLED);
                         break;
-                    case USER_DN_11:
+                    case USER_IDENTITY_11:
                         user = new NiFiUser();
                         user.setId(USER_ID_11);
-                        user.setDn(USER_DN_11);
+                        user.setIdentity(USER_IDENTITY_11);
                         user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_MONITOR));
                         user.setStatus(AccountStatus.ACTIVE);
                         break;
@@ -154,10 +154,10 @@ public class AuthorizeUserActionTest {
             public Void answer(InvocationOnMock invocation) throws Throwable {
                 Object[] args = invocation.getArguments();
                 NiFiUser user = (NiFiUser) args[0];
-                switch (user.getDn()) {
-                    case USER_DN_5:
+                switch (user.getIdentity()) {
+                    case USER_IDENTITY_5:
                         throw new DataAccessException();
-                    case USER_DN_6:
+                    case USER_IDENTITY_6:
                         user.setId(USER_ID_6);
                         break;
                 }
@@ -215,9 +215,9 @@ public class AuthorizeUserActionTest {
                 Object[] args = invocation.getArguments();
                 String dn = (String) args[0];
                 switch (dn) {
-                    case USER_DN_1:
+                    case USER_IDENTITY_1:
                         throw new AuthorityAccessException(StringUtils.EMPTY);
-                    case USER_DN_2:
+                    case USER_IDENTITY_2:
                         return false;
                 }
 
@@ -231,21 +231,21 @@ public class AuthorizeUserActionTest {
                 String dn = (String) args[0];
                 Set<Authority> authorities = EnumSet.noneOf(Authority.class);
                 switch (dn) {
-                    case USER_DN_3:
+                    case USER_IDENTITY_3:
                         throw new UnknownIdentityException(StringUtils.EMPTY);
-                    case USER_DN_4:
+                    case USER_IDENTITY_4:
                         throw new AuthorityAccessException(StringUtils.EMPTY);
-                    case USER_DN_6:
+                    case USER_IDENTITY_6:
                         authorities.add(Authority.ROLE_MONITOR);
                         break;
-                    case USER_DN_7:
+                    case USER_IDENTITY_7:
                         authorities.add(Authority.ROLE_DFM);
                         break;
-                    case USER_DN_9:
+                    case USER_IDENTITY_9:
                         throw new UnknownIdentityException(StringUtils.EMPTY);
-                    case USER_DN_10:
+                    case USER_IDENTITY_10:
                         throw new UnknownIdentityException(StringUtils.EMPTY);
-                    case USER_DN_11:
+                    case USER_IDENTITY_11:
                         throw new UnknownIdentityException(StringUtils.EMPTY);
                 }
 
@@ -272,7 +272,7 @@ public class AuthorizeUserActionTest {
      */
     @Test(expected = AdministrationException.class)
     public void testAuthorityAccessExceptionInDoesDnExist() throws Exception {
-        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_DN_1, 0);
+        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_IDENTITY_1, 0);
         authorizeUser.execute(daoFactory, authorityProvider);
     }
 
@@ -283,7 +283,7 @@ public class AuthorizeUserActionTest {
      */
     @Test(expected = AccountNotFoundException.class)
     public void testUnknownUser() throws Exception {
-        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_DN_2, 0);
+        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_IDENTITY_2, 0);
         authorizeUser.execute(daoFactory, authorityProvider);
     }
 
@@ -294,7 +294,7 @@ public class AuthorizeUserActionTest {
      */
     @Test(expected = AccountNotFoundException.class)
     public void testUserRemovedAfterCheckingExistence() throws Exception {
-        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_DN_3, 0);
+        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_IDENTITY_3, 0);
         authorizeUser.execute(daoFactory, authorityProvider);
     }
 
@@ -305,7 +305,7 @@ public class AuthorizeUserActionTest {
      */
     @Test(expected = AdministrationException.class)
     public void testAuthorityAccessException() throws Exception {
-        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_DN_4, 0);
+        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_IDENTITY_4, 0);
         authorizeUser.execute(daoFactory, authorityProvider);
     }
 
@@ -316,7 +316,7 @@ public class AuthorizeUserActionTest {
      */
     @Test(expected = DataAccessException.class)
     public void testErrorCreatingUserAccount() throws Exception {
-        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_DN_5, 0);
+        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_IDENTITY_5, 0);
         authorizeUser.execute(daoFactory, authorityProvider);
     }
 
@@ -327,11 +327,11 @@ public class AuthorizeUserActionTest {
      */
     @Test
     public void testAccountCreation() throws Exception {
-        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_DN_6, 0);
+        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_IDENTITY_6, 0);
         NiFiUser user = authorizeUser.execute(daoFactory, authorityProvider);
 
         // verify the user
-        Assert.assertEquals(USER_DN_6, user.getDn());
+        Assert.assertEquals(USER_IDENTITY_6, user.getIdentity());
         Assert.assertEquals(1, user.getAuthorities().size());
         Assert.assertTrue(user.getAuthorities().contains(Authority.ROLE_MONITOR));
 
@@ -347,11 +347,11 @@ public class AuthorizeUserActionTest {
      */
     @Test
     public void testExistingUserRequiresVerification() throws Exception {
-        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_DN_7, 0);
+        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_IDENTITY_7, 0);
         NiFiUser user = authorizeUser.execute(daoFactory, authorityProvider);
 
         // verify the user
-        Assert.assertEquals(USER_DN_7, user.getDn());
+        Assert.assertEquals(USER_IDENTITY_7, user.getIdentity());
         Assert.assertEquals(1, user.getAuthorities().size());
         Assert.assertTrue(user.getAuthorities().contains(Authority.ROLE_DFM));
 
@@ -369,11 +369,11 @@ public class AuthorizeUserActionTest {
     @Test
     public void testExistingUserNoVerification() throws Exception {
         // disabling verification by passing in a large cache duration
-        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_DN_8, Integer.MAX_VALUE);
+        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_IDENTITY_8, Integer.MAX_VALUE);
         NiFiUser user = authorizeUser.execute(daoFactory, authorityProvider);
 
         // verify the user
-        Assert.assertEquals(USER_DN_8, user.getDn());
+        Assert.assertEquals(USER_IDENTITY_8, user.getIdentity());
         Assert.assertEquals(1, user.getAuthorities().size());
         Assert.assertTrue(user.getAuthorities().contains(Authority.ROLE_MONITOR));
 
@@ -391,7 +391,7 @@ public class AuthorizeUserActionTest {
     @Test(expected = AccountPendingException.class)
     public void testExistingPendingUser() throws Exception {
         // disabling verification by passing in a large cache duration
-        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_DN_9, Integer.MAX_VALUE);
+        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_IDENTITY_9, Integer.MAX_VALUE);
         authorizeUser.execute(daoFactory, authorityProvider);
     }
 
@@ -403,7 +403,7 @@ public class AuthorizeUserActionTest {
     @Test(expected = AccountDisabledException.class)
     public void testExistingDisabledUser() throws Exception {
         // disabling verification by passing in a large cache duration
-        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_DN_10, Integer.MAX_VALUE);
+        AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_IDENTITY_10, Integer.MAX_VALUE);
         authorizeUser.execute(daoFactory, authorityProvider);
     }
 
@@ -416,7 +416,7 @@ public class AuthorizeUserActionTest {
     @Test
     public void testExistingActiveUserNotFoundInProvider() throws Exception {
         try {
-            AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_DN_11, 0);
+            AuthorizeUserAction authorizeUser = new AuthorizeUserAction(USER_IDENTITY_11, 0);
             authorizeUser.execute(daoFactory, authorityProvider);
 
             Assert.fail();


[29/51] [abbrv] nifi git commit: NIFI-655: - Refactoring web security to use Spring Security Java Configuration. - Introducing security in Web UI in order to get JWT.

Posted by mc...@apache.org.
http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index bd7482e..e7a3328 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -170,6 +170,7 @@ import org.apache.nifi.web.api.dto.status.ClusterProcessGroupStatusDTO;
 import org.apache.nifi.web.api.dto.status.NodeProcessGroupStatusDTO;
 import org.apache.nifi.web.dao.ControllerServiceDAO;
 import org.apache.nifi.web.dao.ReportingTaskDAO;
+import org.apache.nifi.web.security.user.NewAccountRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.access.AccessDeniedException;
@@ -762,7 +763,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
         if (user == null) {
             throw new WebApplicationException(new Throwable("Unable to access details for current user."));
         }
-        final String userDn = user.getDn();
+        final String userDn = user.getIdentity();
 
         if (Node.Status.CONNECTING.name().equalsIgnoreCase(nodeDTO.getStatus())) {
             clusterManager.requestReconnection(nodeDTO.getNodeId(), userDn);
@@ -1782,7 +1783,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
 
         // create a purge action to record that records are being removed
         FlowChangeAction purgeAction = new FlowChangeAction();
-        purgeAction.setUserIdentity(user.getDn());
+        purgeAction.setUserIdentity(user.getIdentity());
         purgeAction.setUserName(user.getUserName());
         purgeAction.setOperation(Operation.Purge);
         purgeAction.setTimestamp(new Date());
@@ -1820,6 +1821,23 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
+    public UserDTO createUser() {
+        NewAccountRequest newAccountRequest = NiFiUserUtils.getNewAccountRequest();
+
+        // log the new user account request
+        logger.info("Requesting new user account for " + newAccountRequest.getUsername());
+
+        // get the justification
+        String justification = newAccountRequest.getJustification();
+        if (justification == null) {
+            justification = StringUtils.EMPTY;
+        }
+
+        // create the pending user account
+        return dtoFactory.createUserDTO(userService.createPendingUserAccount(newAccountRequest.getUsername(), justification));
+    }
+
+    @Override
     public UserDTO updateUser(UserDTO userDto) {
         NiFiUser user;
 
@@ -2256,7 +2274,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
         }
 
         final Set<String> allowedUsers = port.getUserAccessControl();
-        if (allowedUsers.contains(user.getDn())) {
+        if (allowedUsers.contains(user.getIdentity())) {
             return true;
         }
 
@@ -2632,7 +2650,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
                         final UserDTO groupedUser = groupedUserDTOs.get(user.getUserGroup());
                         groupedUser.setId(groupedUser.getId() + "," + String.valueOf(user.getId()));
                         groupedUser.setUserName(groupedUser.getUserName() + ", " + user.getUserName());
-                        groupedUser.setDn(groupedUser.getDn() + ", " + user.getDn());
+                        groupedUser.setDn(groupedUser.getDn() + ", " + user.getIdentity());
                         groupedUser.setCreation(getOldestDate(groupedUser.getCreation(), user.getCreation()));
                         groupedUser.setLastAccessed(getNewestDate(groupedUser.getLastAccessed(), user.getLastAccessed()));
                         groupedUser.setLastVerified(getNewestDate(groupedUser.getLastVerified(), user.getLastVerified()));
@@ -2731,7 +2749,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
             throw new WebApplicationException(new Throwable("Unable to access details for current user."));
         }
 
-        final String userDn = user.getDn();
+        final String userDn = user.getIdentity();
         clusterManager.deleteNode(nodeId, userDn);
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java
index 8e56fc2..9502bf2 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java
@@ -158,19 +158,19 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
 
     @Override
     public String getCurrentUserDn() {
-        String userDn = NiFiUser.ANONYMOUS_USER_DN;
+        String userIdentity = NiFiUser.ANONYMOUS_USER_IDENTITY;
 
         final NiFiUser user = NiFiUserUtils.getNiFiUser();
         if (user != null) {
-            userDn = user.getDn();
+            userIdentity = user.getIdentity();
         }
 
-        return userDn;
+        return userIdentity;
     }
 
     @Override
     public String getCurrentUserName() {
-        String userName = NiFiUser.ANONYMOUS_USER_DN;
+        String userName = NiFiUser.ANONYMOUS_USER_IDENTITY;
 
         final NiFiUser user = NiFiUserUtils.getNiFiUser();
         if (user != null) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
index 03ce630..7e9e77e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
@@ -129,19 +129,19 @@ public class StandardNiFiWebContext implements NiFiWebContext {
 
     @Override
     public String getCurrentUserDn() {
-        String userDn = NiFiUser.ANONYMOUS_USER_DN;
+        String userIdentity = NiFiUser.ANONYMOUS_USER_IDENTITY;
 
         final NiFiUser user = NiFiUserUtils.getNiFiUser();
         if (user != null) {
-            userDn = user.getDn();
+            userIdentity = user.getIdentity();
         }
 
-        return userDn;
+        return userIdentity;
     }
 
     @Override
     public String getCurrentUserName() {
-        String userName = NiFiUser.ANONYMOUS_USER_DN;
+        String userName = NiFiUser.ANONYMOUS_USER_IDENTITY;
 
         final NiFiUser user = NiFiUserUtils.getNiFiUser();
         if (user != null) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
new file mode 100644
index 0000000..e198438
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -0,0 +1,424 @@
+/*
+ * 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.api;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import io.jsonwebtoken.JwtException;
+import org.apache.nifi.util.NiFiProperties;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+import java.net.URI;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.admin.service.AdministrationException;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.apache.nifi.authentication.LoginCredentials;
+import org.apache.nifi.authentication.LoginIdentityProvider;
+import org.apache.nifi.authentication.exception.IdentityAccessException;
+import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException;
+import org.apache.nifi.security.util.CertificateUtils;
+import org.apache.nifi.web.api.dto.AccessStatusDTO;
+import org.apache.nifi.web.api.dto.AccessConfigurationDTO;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.entity.AccessStatusEntity;
+import org.apache.nifi.web.api.entity.AccessConfigurationEntity;
+import org.apache.nifi.web.api.request.ClientIdParameter;
+import org.apache.nifi.web.security.InvalidAuthenticationException;
+import org.apache.nifi.web.security.ProxiedEntitiesUtils;
+import org.apache.nifi.web.security.UntrustedProxyException;
+import org.apache.nifi.web.security.jwt.JwtService;
+import org.apache.nifi.web.security.token.LoginAuthenticationToken;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.x509.X509CertificateExtractor;
+import org.apache.nifi.web.security.x509.X509IdentityProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.AccountStatusException;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+/**
+ * RESTful endpoint for managing a cluster.
+ */
+@Path("/access")
+@Api(
+        value = "/access",
+        description = "Endpoints for obtaining an access token or checking access status"
+)
+public class AccessResource extends ApplicationResource {
+
+    private static final Logger logger = LoggerFactory.getLogger(AccessResource.class);
+
+    private static final String AUTHORIZATION = "Authorization";
+
+    private NiFiProperties properties;
+
+    private LoginIdentityProvider loginIdentityProvider;
+    private X509CertificateExtractor certificateExtractor;
+    private X509IdentityProvider certificateIdentityProvider;
+    private JwtService jwtService;
+
+    private AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService;
+
+    /**
+     * Retrieves the access configuration for this NiFi.
+     *
+     * @param httpServletRequest the servlet request
+     * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
+     * @return A accessConfigurationEntity
+     */
+    @GET
+    @Consumes(MediaType.WILDCARD)
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/config")
+    @ApiOperation(
+            value = "Retrieves the access configuration for this NiFi",
+            response = AccessConfigurationEntity.class
+    )
+    public Response getLoginConfig(
+            @Context HttpServletRequest httpServletRequest,
+            @ApiParam(
+                    value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+                    required = false
+            )
+            @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) {
+
+        final AccessConfigurationDTO accessConfiguration = new AccessConfigurationDTO();
+
+        // specify whether login should be supported and only support for secure requests
+        accessConfiguration.setSupportsLogin(loginIdentityProvider != null && httpServletRequest.isSecure());
+        accessConfiguration.setSupportsAnonymous(!properties.getAnonymousAuthorities().isEmpty() || !httpServletRequest.isSecure());
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+
+        // create the response entity
+        final AccessConfigurationEntity entity = new AccessConfigurationEntity();
+        entity.setRevision(revision);
+        entity.setConfig(accessConfiguration);
+
+        // generate the response
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+
+    /**
+     * Gets the status the client's access.
+     *
+     * @param httpServletRequest the servlet request
+     * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
+     * @return A accessStatusEntity
+     */
+    @GET
+    @Consumes(MediaType.WILDCARD)
+    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
+    @Path("")
+    @ApiOperation(
+            value = "Gets the status the client's access",
+            response = AccessStatusEntity.class
+    )
+    @ApiResponses(
+            value = {
+                @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
+                @ApiResponse(code = 401, message = "Unable to determine access status because the client could not be authenticated."),
+                @ApiResponse(code = 403, message = "Unable to determine access status because the client is not authorized to make this request."),
+                @ApiResponse(code = 409, message = "Unable to determine access status because NiFi is not in the appropriate state."),
+                @ApiResponse(code = 500, message = "Unable to determine access status because an unexpected error occurred.")
+            }
+    )
+    public Response getAccessStatus(
+            @Context HttpServletRequest httpServletRequest,
+            @ApiParam(
+                    value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+                    required = false
+            )
+            @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) {
+
+        // only consider user specific access over https
+        if (!httpServletRequest.isSecure()) {
+            throw new IllegalStateException("User authentication/authorization is only supported when running over HTTPS.");
+        }
+
+        final AccessStatusDTO accessStatus = new AccessStatusDTO();
+
+        try {
+            final X509Certificate[] certificates = certificateExtractor.extractClientCertificate(httpServletRequest);
+
+            // if there is not certificate, consider a token
+            if (certificates == null) {
+                // look for an authorization token
+                final String authorization = httpServletRequest.getHeader(AUTHORIZATION);
+
+                // if there is no authorization header, we don't know the user
+                if (authorization == null) {
+                    accessStatus.setStatus(AccessStatusDTO.Status.UNKNOWN.name());
+                    accessStatus.setMessage("No credentials supplied, unknown user.");
+                } else {
+                    try {
+                        // Extract the Base64 encoded token from the Authorization header
+                        final String token = StringUtils.substringAfterLast(authorization, " ");
+                        final String principal = jwtService.getAuthenticationFromToken(token);
+
+                        // set the user identity
+                        accessStatus.setIdentity(principal);
+                        accessStatus.setUsername(CertificateUtils.extractUsername(principal));
+
+                        // without a certificate, this is not a proxied request
+                        final List<String> chain = Arrays.asList(principal);
+
+                        // check authorization for this user
+                        checkAuthorization(chain);
+
+                        // no issues with authorization
+                        accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
+                        accessStatus.setMessage("Account is active and authorized");
+                    } catch (JwtException e) {
+                        throw new InvalidAuthenticationException(e.getMessage(), e);
+                    }
+                }
+            } else {
+                try {
+                    final AuthenticationResponse authenticationResponse = certificateIdentityProvider.authenticate(certificates);
+
+                    // get the proxy chain and ensure its populated
+                    final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(httpServletRequest, authenticationResponse.getIdentity());
+                    if (proxyChain.isEmpty()) {
+                        logger.error(String.format("Unable to parse the proxy chain %s from the incoming request.", authenticationResponse.getIdentity()));
+                        throw new IllegalArgumentException("Unable to determine the user from the incoming request.");
+                    }
+
+                    // set the user identity
+                    accessStatus.setIdentity(proxyChain.get(0));
+                    accessStatus.setUsername(CertificateUtils.extractUsername(proxyChain.get(0)));
+
+                    // ensure the proxy chain is authorized
+                    checkAuthorization(proxyChain);
+
+                    // no issues with authorization
+                    accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
+                    accessStatus.setMessage("Account is active and authorized");
+                } catch (final IllegalArgumentException iae) {
+                    throw new InvalidAuthenticationException(iae.getMessage(), iae);
+                }
+            }
+        } catch (final UsernameNotFoundException unfe) {
+            accessStatus.setStatus(AccessStatusDTO.Status.UNREGISTERED.name());
+            accessStatus.setMessage(String.format("Unregistered user %s", accessStatus.getIdentity()));
+        } catch (final AccountStatusException ase) {
+            accessStatus.setStatus(AccessStatusDTO.Status.NOT_ACTIVE.name());
+            accessStatus.setMessage(ase.getMessage());
+        } catch (final UntrustedProxyException upe) {
+            throw new AccessDeniedException(upe.getMessage(), upe);
+        } catch (final AuthenticationServiceException ase) {
+            throw new AdministrationException(ase.getMessage(), ase);
+        }
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+
+        // create the entity
+        final AccessStatusEntity entity = new AccessStatusEntity();
+        entity.setRevision(revision);
+        entity.setAccessStatus(accessStatus);
+
+        return generateOkResponse(entity).build();
+    }
+
+    /**
+     * Checks the status of the proxy.
+     *
+     * @param proxyChain the proxy chain
+     * @throws AuthenticationException if the proxy chain is not authorized
+     */
+    private void checkAuthorization(final List<String> proxyChain) throws AuthenticationException {
+        userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
+    }
+
+    /**
+     * Creates a token for accessing the REST API via username/password.
+     *
+     * @param httpServletRequest the servlet request
+     * @param username the username
+     * @param password the password
+     * @return A JWT (string)
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    @Produces(MediaType.TEXT_PLAIN)
+    @Path("/token")
+    @ApiOperation(
+            value = "Creates a token for accessing the REST API via username/password",
+            response = String.class
+    )
+    @ApiResponses(
+            value = {
+                @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
+                @ApiResponse(code = 403, message = "Client is not authorized to make this request."),
+                @ApiResponse(code = 409, message = "Unable to create access token because NiFi is not in the appropriate state. (i.e. may not be configured to support username/password login."),
+                @ApiResponse(code = 500, message = "Unable to create access token because an unexpected error occurred.")
+            }
+    )
+    public Response createAccessToken(
+            @Context HttpServletRequest httpServletRequest,
+            @FormParam("username") String username,
+            @FormParam("password") String password) {
+
+        // only support access tokens when communicating over HTTPS
+        if (!httpServletRequest.isSecure()) {
+            throw new IllegalStateException("Access tokens are only issued over HTTPS.");
+        }
+
+        // if not configuration for login, don't consider credentials
+        if (loginIdentityProvider == null) {
+            throw new IllegalStateException("Username/Password login not supported by this NiFi.");
+        }
+
+        final LoginAuthenticationToken loginAuthenticationToken;
+
+        final X509Certificate[] certificates = certificateExtractor.extractClientCertificate(httpServletRequest);
+
+        // if there is not certificate, consider login credentials
+        if (certificates == null) {
+            // ensure we have login credentials
+            if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
+                throw new IllegalArgumentException("The username and password must be specified.");
+            }
+
+            try {
+                // attempt to authenticate
+                final AuthenticationResponse authenticationResponse = loginIdentityProvider.authenticate(new LoginCredentials(username, password));
+                final long maxExpiration = TimeUnit.MILLISECONDS.convert(12, TimeUnit.HOURS);
+                final long minExpiration = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES);
+
+                long expiration = authenticationResponse.getExpiration();
+                if (expiration > maxExpiration) {
+                    expiration = maxExpiration;
+
+                    logger.warn(String.format("Max token expiration exceeded. Setting expiration to %s from %s for %s", expiration,
+                            authenticationResponse.getExpiration(), authenticationResponse.getIdentity()));
+                } else if (expiration < minExpiration) {
+                    expiration = minExpiration;
+
+                    logger.warn(String.format("Min token expiration not met. Setting expiration to %s from %s for %s", expiration,
+                            authenticationResponse.getExpiration(), authenticationResponse.getIdentity()));
+                }
+
+                // create the authentication token
+                loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getIdentity(), expiration, authenticationResponse.getIssuer());
+            } catch (final InvalidLoginCredentialsException ilce) {
+                throw new IllegalArgumentException("The supplied username and password are not valid.", ilce);
+            } catch (final IdentityAccessException iae) {
+                throw new AdministrationException(iae.getMessage(), iae);
+            }
+        } else {
+            // consider a certificate
+            final AuthenticationResponse authenticationResponse = certificateIdentityProvider.authenticate(certificates);
+
+            // get the proxy chain and ensure its populated
+            final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(httpServletRequest, authenticationResponse.getIdentity());
+            if (proxyChain.isEmpty()) {
+                logger.error(String.format("Unable to parse the proxy chain %s from the incoming request.", authenticationResponse.getIdentity()));
+                throw new IllegalArgumentException("Unable to determine the user from the incoming request.");
+            }
+
+            // authorize the proxy if necessary
+            authorizeProxyIfNecessary(proxyChain);
+
+            // create the authentication token
+            loginAuthenticationToken = new LoginAuthenticationToken(proxyChain.get(0), authenticationResponse.getExpiration(), authenticationResponse.getIssuer());
+        }
+
+        // generate JWT for response
+        final String token = jwtService.generateSignedToken(loginAuthenticationToken);
+
+        // build the response
+        final URI uri = URI.create(generateResourceUri("access", "token"));
+        return generateCreatedResponse(uri, token).build();
+    }
+
+    /**
+     * Ensures the proxyChain is authorized before allowing the user to be authenticated.
+     *
+     * @param proxyChain the proxy chain
+     * @throws AuthenticationException if the proxy chain is not authorized
+     */
+    private void authorizeProxyIfNecessary(final List<String> proxyChain) throws AuthenticationException {
+        if (proxyChain.size() > 1) {
+            try {
+                userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
+            } catch (final UsernameNotFoundException unfe) {
+                // if a username not found exception was thrown, the proxies were authorized and now
+                // we can issue a new token to the end user which they will use to identify themselves
+                // when they enter a new account request
+            } catch (final AuthenticationServiceException ase) {
+                // throw an administration exception which will return a 500
+                throw new AdministrationException(ase.getMessage(), ase);
+            } catch (final Exception e) {
+                // any other issue we're going to treat as access denied exception which will return 403
+                throw new AccessDeniedException(e.getMessage(), e);
+            }
+        }
+    }
+
+    // setters
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) {
+        this.loginIdentityProvider = loginIdentityProvider;
+    }
+
+    public void setJwtService(JwtService jwtService) {
+        this.jwtService = jwtService;
+    }
+
+    public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
+        this.certificateExtractor = certificateExtractor;
+    }
+
+    public void setCertificateIdentityProvider(X509IdentityProvider certificateIdentityProvider) {
+        this.certificateIdentityProvider = certificateIdentityProvider;
+    }
+
+    public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) {
+        this.userDetailsService = userDetailsService;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
index 3bad5e3..d0c36d4 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
@@ -23,7 +23,6 @@ import com.sun.jersey.server.impl.model.method.dispatch.FormDispatchProvider;
 import java.io.Serializable;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.security.cert.X509Certificate;
 import java.util.Collection;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -45,9 +44,8 @@ import org.apache.nifi.action.Operation;
 import org.apache.nifi.cluster.context.ClusterContext;
 import org.apache.nifi.cluster.context.ClusterContextThreadLocal;
 import org.apache.nifi.cluster.manager.impl.WebClusterManager;
-import org.apache.nifi.web.security.DnUtils;
+import org.apache.nifi.web.security.ProxiedEntitiesUtils;
 import org.apache.nifi.web.security.user.NiFiUserDetails;
-import org.apache.nifi.web.security.x509.X509CertificateExtractor;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.api.entity.Entity;
 import org.apache.nifi.web.api.request.ClientIdParameter;
@@ -55,6 +53,8 @@ import org.apache.nifi.web.util.WebUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.nifi.user.NiFiUser;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.core.Authentication;
@@ -362,14 +362,12 @@ public abstract class ApplicationResource {
             result.put(PROXY_SCHEME_HTTP_HEADER, httpServletRequest.getScheme());
         }
 
-        // if this is a secure request, add the custom headers for proxying user requests
-        final X509Certificate cert = new X509CertificateExtractor().extractClientCertificate(httpServletRequest);
-        if (cert != null) {
+        if (httpServletRequest.isSecure()) {
 
             // add the certificate DN to the proxy chain
-            final String xProxiedEntitiesChain = DnUtils.getXProxiedEntitiesChain(httpServletRequest);
-            if (StringUtils.isNotBlank(xProxiedEntitiesChain)) {
-                result.put(PROXIED_ENTITIES_CHAIN_HTTP_HEADER, xProxiedEntitiesChain);
+            final NiFiUser user = NiFiUserUtils.getNiFiUser();
+            if (user != null) {
+                result.put(PROXIED_ENTITIES_CHAIN_HTTP_HEADER, ProxiedEntitiesUtils.buildProxiedEntitiesChainString(user));
             }
 
             // add the user's authorities (if any) to the headers

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
index 4883721..6e6739d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
@@ -79,6 +79,7 @@ import org.apache.nifi.web.api.request.IntegerParameter;
 import org.apache.nifi.web.api.request.LongParameter;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.web.api.entity.ControllerServiceTypesEntity;
+import org.apache.nifi.web.api.entity.IdentityEntity;
 import org.apache.nifi.web.api.entity.ReportingTaskTypesEntity;
 import org.springframework.security.access.prepost.PreAuthorize;
 
@@ -239,7 +240,7 @@ public class ControllerResource extends ApplicationResource {
     public ProcessGroupResource getGroupResource(
             @ApiParam(
                     value = "The id of the process group that is the parent of the requested resource(s). If the desired process group is "
-                            + "the root group an alias 'root' may be used as the process-group-id.",
+                    + "the root group an alias 'root' may be used as the process-group-id.",
                     required = true
             )
             @PathParam("process-group-id") String groupId) {
@@ -454,13 +455,13 @@ public class ControllerResource extends ApplicationResource {
     @ApiOperation(
             value = "Gets the current revision of this NiFi",
             notes = "NiFi employs an optimistic locking strategy where the client must include a revision in their request when "
-                    + "performing an update. If the specified revision does not match the current base revision a 409 status code "
-                    + "is returned. The revision is comprised of a clientId and a version number. The version is a simple integer "
-                    + "value that is incremented with each change. Including the most recent version tells NiFi that your working "
-                    + "with the most recent flow. In addition to the version the client who is performing the updates is recorded. "
-                    + "This allows the same client to submit multiple requests without having to wait for the previously ones to "
-                    + "return. Invoking this endpoint will return the current base revision. It is also available when retrieving "
-                    + "a process group and in the response of all mutable requests.",
+            + "performing an update. If the specified revision does not match the current base revision a 409 status code "
+            + "is returned. The revision is comprised of a clientId and a version number. The version is a simple integer "
+            + "value that is incremented with each change. Including the most recent version tells NiFi that your working "
+            + "with the most recent flow. In addition to the version the client who is performing the updates is recorded. "
+            + "This allows the same client to submit multiple requests without having to wait for the previously ones to "
+            + "return. Invoking this endpoint will return the current base revision. It is also available when retrieving "
+            + "a process group and in the response of all mutable requests.",
             response = Entity.class,
             authorizations = {
                 @Authorization(value = "Read Only", type = "ROLE_MONITOR"),
@@ -845,6 +846,47 @@ public class ControllerResource extends ApplicationResource {
     }
 
     /**
+     * Retrieves the identity of the user making the request.
+     *
+     * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
+     * @return An identityEntity
+     */
+    @GET
+    @Consumes(MediaType.WILDCARD)
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/identity")
+    @ApiOperation(
+            value = "Retrieves the user identity of the user making the request",
+            response = IdentityEntity.class
+    )
+    public Response getIdentity(
+            @ApiParam(
+                    value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+                    required = false
+            )
+            @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) {
+
+        // note that the cluster manager will handle this request directly
+        final NiFiUser user = NiFiUserUtils.getNiFiUser();
+        if (user == null) {
+            throw new WebApplicationException(new Throwable("Unable to access details for current user."));
+        }
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+
+        // create the response entity
+        IdentityEntity entity = new IdentityEntity();
+        entity.setRevision(revision);
+        entity.setUserId(user.getId());
+        entity.setIdentity(user.getUserName());
+
+        // generate the response
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+
+    /**
      * Retrieves the user details, including the authorities, about the user making the request.
      *
      * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
@@ -854,14 +896,17 @@ public class ControllerResource extends ApplicationResource {
     @Consumes(MediaType.WILDCARD)
     @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
     @Path("/authorities")
-    @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
+    @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN', 'ROLE_PROXY', 'ROLE_NIFI', 'ROLE_PROVENANCE')")
     @ApiOperation(
             value = "Retrieves the user details, including the authorities, about the user making the request",
             response = AuthorityEntity.class,
             authorizations = {
                 @Authorization(value = "Read Only", type = "ROLE_MONITOR"),
                 @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"),
-                @Authorization(value = "Administrator", type = "ROLE_ADMIN")
+                @Authorization(value = "Administrator", type = "ROLE_ADMIN"),
+                @Authorization(value = "Proxy", type = "ROLE_PROXY"),
+                @Authorization(value = "NiFi", type = "ROLE_NIFI"),
+                @Authorization(value = "Provenance", type = "ROLE_PROVENANCE")
             }
     )
     @ApiResponses(

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.java
index 4a61ef4..4b9d2ae 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.java
@@ -16,12 +16,14 @@
  */
 package org.apache.nifi.web.api;
 
+import com.sun.jersey.api.Responses;
 import com.wordnik.swagger.annotations.Api;
 import com.wordnik.swagger.annotations.ApiOperation;
 import com.wordnik.swagger.annotations.ApiParam;
 import com.wordnik.swagger.annotations.ApiResponse;
 import com.wordnik.swagger.annotations.ApiResponses;
 import com.wordnik.swagger.annotations.Authorization;
+import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -39,6 +41,7 @@ import javax.ws.rs.DefaultValue;
 import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
 import javax.ws.rs.HttpMethod;
+import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
@@ -59,9 +62,11 @@ import org.apache.nifi.web.api.entity.UserSearchResultsEntity;
 import org.apache.nifi.web.api.entity.UsersEntity;
 import org.apache.nifi.web.api.request.ClientIdParameter;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.web.NiFiServiceFacade;
 import static org.apache.nifi.web.api.ApplicationResource.CLIENT_ID;
 import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
 import org.springframework.security.access.prepost.PreAuthorize;
 
 /**
@@ -83,12 +88,35 @@ public class UserResource extends ApplicationResource {
     private NiFiProperties properties;
     private NiFiServiceFacade serviceFacade;
 
+    @POST
+    @Consumes(MediaType.WILDCARD)
+    @Produces(MediaType.TEXT_PLAIN)
+    @Path("") // necessary due to a bug in swagger
+    @ApiOperation(
+            value = "Creates a user",
+            response = String.class
+    )
+    public Response createUser() {
+        if (!properties.getSupportNewAccountRequests()) {
+            return Responses.notFound().entity("This NiFi does not support new account requests.").build();
+        }
+
+        final NiFiUser nifiUser = NiFiUserUtils.getNiFiUser();
+        if (nifiUser != null) {
+            throw new IllegalArgumentException("User account already created " + nifiUser.getIdentity());
+        }
+
+        // create an account request for the current user
+        final UserDTO user = serviceFacade.createUser();
+
+        final String uri = generateResourceUri("controller", "templates", user.getId());
+        return generateCreatedResponse(URI.create(uri), "Not authorized. User account created. Authorization pending.").build();
+    }
+
     /**
      * Gets all users that are registered within this Controller.
      *
-     * @param clientId Optional client id. If the client id is not specified, a
-     * new one will be generated. This value (whether specified or generated) is
-     * included in the response.
+     * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
      * @param grouped Whether to return the users in their groups.
      * @return A usersEntity.
      */
@@ -144,9 +172,7 @@ public class UserResource extends ApplicationResource {
     /**
      * Gets the details for the specified user.
      *
-     * @param clientId Optional client id. If the client id is not specified, a
-     * new one will be generated. This value (whether specified or generated) is
-     * included in the response.
+     * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
      * @param id The user id.
      * @return A userEntity.
      */
@@ -315,12 +341,9 @@ public class UserResource extends ApplicationResource {
      * Updates the specified user.
      *
      * @param httpServletRequest request
-     * @param clientId Optional client id. If the client id is not specified, a
-     * new one will be generated. This value (whether specified or generated) is
-     * included in the response.
+     * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
      * @param id The id of the user to update.
-     * @param rawAuthorities Array of authorities to assign to the specified
-     * user.
+     * @param rawAuthorities Array of authorities to assign to the specified user.
      * @param status The status of the specified users account.
      * @param formParams form params
      * @return A userEntity
@@ -491,9 +514,7 @@ public class UserResource extends ApplicationResource {
      *
      * @param httpServletRequest request
      * @param id The user id
-     * @param clientId Optional client id. If the client id is not specified, a
-     * new one will be generated. This value (whether specified or generated) is
-     * included in the response.
+     * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
      * @return A userEntity.
      */
     @DELETE

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/AccessDeniedExceptionMapper.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/AccessDeniedExceptionMapper.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/AccessDeniedExceptionMapper.java
index bc6dded..5d50e70 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/AccessDeniedExceptionMapper.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/AccessDeniedExceptionMapper.java
@@ -39,7 +39,7 @@ public class AccessDeniedExceptionMapper implements ExceptionMapper<AccessDenied
         // get the current user
         NiFiUser user = NiFiUserUtils.getNiFiUser();
         if (user != null) {
-            logger.info(String.format("%s does not have permission to access the requested resource. Returning %s response.", user.getDn(), Response.Status.FORBIDDEN));
+            logger.info(String.format("%s does not have permission to access the requested resource. Returning %s response.", user.getIdentity(), Response.Status.FORBIDDEN));
         } else {
             logger.info(String.format("User does not have permission to access the requested resource. Returning %s response.", Response.Status.FORBIDDEN));
         }

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/InvalidAuthenticationExceptionMapper.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/InvalidAuthenticationExceptionMapper.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/InvalidAuthenticationExceptionMapper.java
new file mode 100644
index 0000000..14d5139
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/InvalidAuthenticationExceptionMapper.java
@@ -0,0 +1,44 @@
+/*
+ * 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.api.config;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.web.security.InvalidAuthenticationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Maps access denied exceptions into a client response.
+ */
+@Provider
+public class InvalidAuthenticationExceptionMapper implements ExceptionMapper<InvalidAuthenticationException> {
+
+    private static final Logger logger = LoggerFactory.getLogger(InvalidAuthenticationExceptionMapper.class);
+
+    @Override
+    public Response toResponse(InvalidAuthenticationException exception) {
+        if (logger.isDebugEnabled()) {
+            logger.debug(StringUtils.EMPTY, exception);
+        }
+
+        return Response.status(Response.Status.UNAUTHORIZED).entity(exception.getMessage()).type("text/plain").build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
index 87277ae..c16653e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
@@ -2382,7 +2382,7 @@ public final class DtoFactory {
         // create the user
         UserDTO userDTO = new UserDTO();
         userDTO.setId(String.valueOf(user.getId()));
-        userDTO.setDn(user.getDn());
+        userDTO.setDn(user.getIdentity());
         userDTO.setUserName(user.getUserName());
         userDTO.setUserGroup(user.getUserGroup());
         userDTO.setJustification(user.getJustification());

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
index 0e3bcac..47ebd30 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
@@ -113,6 +113,7 @@ import org.apache.nifi.web.api.dto.search.SearchResultsDTO;
 import org.apache.nifi.web.api.dto.status.ControllerStatusDTO;
 import org.apache.nifi.web.api.dto.status.ProcessGroupStatusDTO;
 import org.apache.nifi.web.api.dto.status.StatusHistoryDTO;
+import org.apache.nifi.web.security.ProxiedEntitiesUtils;
 import org.apache.nifi.web.security.user.NiFiUserUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -822,17 +823,7 @@ public class ControllerFacade {
             final Map<String, String> attributes = event.getAttributes();
 
             // calculate the dn chain
-            final List<String> dnChain = new ArrayList<>();
-
-            // build the dn chain
-            NiFiUser chainedUser = user;
-            do {
-                // add the entry for this user
-                dnChain.add(chainedUser.getDn());
-
-                // go to the next user in the chain
-                chainedUser = chainedUser.getChain();
-            } while (chainedUser != null);
+            final List<String> dnChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(user);
 
             // ensure the users in this chain are allowed to download this content
             final DownloadAuthorization downloadAuthorization = userService.authorizeDownload(dnChain, attributes);
@@ -850,7 +841,7 @@ public class ControllerFacade {
             final String type = event.getAttributes().get(CoreAttributes.MIME_TYPE.key());
 
             // get the content
-            final InputStream content = flowController.getContent(event, contentDirection, user.getDn(), uri);
+            final InputStream content = flowController.getContent(event, contentDirection, user.getIdentity(), uri);
             return new DownloadableContent(filename, type, content);
         } catch (final ContentNotFoundException cnfe) {
             throw new ResourceNotFoundException("Unable to find the specified content.");
@@ -880,7 +871,7 @@ public class ControllerFacade {
             }
 
             // replay the flow file
-            final ProvenanceEventRecord event = flowController.replayFlowFile(originalEvent, user.getDn());
+            final ProvenanceEventRecord event = flowController.replayFlowFile(originalEvent, user.getIdentity());
 
             // convert the event record
             return createProvenanceEventDto(event);

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
index df271a5..565e5af 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
@@ -321,7 +321,7 @@ public class StandardConnectionDAO extends ComponentDAO implements ConnectionDAO
             throw new WebApplicationException(new Throwable("Unable to access details for current user."));
         }
 
-        return queue.dropFlowFiles(dropRequestId, user.getDn());
+        return queue.dropFlowFiles(dropRequestId, user.getIdentity());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/filter/RequestLogger.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/filter/RequestLogger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/filter/RequestLogger.java
index bfd2df6..9f63611 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/filter/RequestLogger.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/filter/RequestLogger.java
@@ -52,13 +52,13 @@ public class RequestLogger implements Filter {
             final NiFiUser user = NiFiUserUtils.getNiFiUser();
 
             // get the user details for the log message
-            String dn = "<no user found>";
+            String identity = "<no user found>";
             if (user != null) {
-                dn = user.getDn();
+                identity = user.getIdentity();
             }
 
             // log the request attempt - response details will be logged later
-            logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)", dn, request.getMethod(),
+            logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)", identity, request.getMethod(),
                     request.getRequestURL().toString(), request.getRemoteAddr()));
         }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
index e034baa..9f3d2f5 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
@@ -241,12 +241,21 @@
     <bean id="systemDiagnosticsResource" class="org.apache.nifi.web.api.SystemDiagnosticsResource" scope="singleton">
         <property name="serviceFacade" ref="serviceFacade"/>
     </bean>
+    <bean id="accessResource" class="org.apache.nifi.web.api.AccessResource" scope="singleton">
+        <property name="properties" ref="nifiProperties"/>
+        <property name="certificateExtractor" ref="certificateExtractor"/>
+        <property name="certificateIdentityProvider" ref="certificateIdentityProvider"/>
+        <property name="loginIdentityProvider" ref="loginIdentityProvider"/>
+        <property name="jwtService" ref="jwtService"/>
+        <property name="userDetailsService" ref="userDetailsService"/>
+    </bean>
 
     <!-- configuration for jaxb serialization -->
     <bean class="org.apache.nifi.web.util.ObjectMapperResolver" scope="singleton"/>
 
     <!-- exception mapping -->
     <bean class="org.apache.nifi.web.api.config.AccessDeniedExceptionMapper" scope="singleton"/>
+    <bean class="org.apache.nifi.web.api.config.InvalidAuthenticationExceptionMapper" scope="singleton"/>
     <bean class="org.apache.nifi.web.api.config.AuthenticationCredentialsNotFoundExceptionMapper" scope="singleton"/>
     <bean class="org.apache.nifi.web.api.config.AccountNotFoundExceptionMapper" scope="singleton"/>
     <bean class="org.apache.nifi.web.api.config.AdministrationExceptionMapper" scope="singleton"/>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/webapp/WEB-INF/web.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/webapp/WEB-INF/web.xml
index 4ce319e..b57998d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/webapp/WEB-INF/web.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/webapp/WEB-INF/web.xml
@@ -16,15 +16,12 @@
 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
     <display-name>nifi-api</display-name>
     <context-param>
+        <param-name>contextClass</param-name>
+        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
+    </context-param>
+    <context-param>
         <param-name>contextConfigLocation</param-name>
-        <param-value>
-            classpath:nifi-context.xml
-            classpath:nifi-web-api-context.xml
-            classpath:nifi-web-security-context.xml
-            classpath:nifi-administration-context.xml
-            classpath:nifi-cluster-manager-context.xml
-            classpath:nifi-cluster-protocol-context.xml
-        </param-value>
+        <param-value>org.apache.nifi.web</param-value>
     </context-param>
     <listener>
         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java
new file mode 100644
index 0000000..82fe73a
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java
@@ -0,0 +1,292 @@
+/*
+ * 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.integration.accesscontrol;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import javax.net.ssl.SSLContext;
+import org.apache.commons.io.FileUtils;
+import org.apache.nifi.integration.util.NiFiTestServer;
+import org.apache.nifi.integration.util.NiFiTestUser;
+import org.apache.nifi.integration.util.SourceTestProcessor;
+import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.NarClassLoaders;
+import org.apache.nifi.security.util.SslContextFactory;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.api.dto.AccessConfigurationDTO;
+import org.apache.nifi.web.api.dto.AccessStatusDTO;
+import org.apache.nifi.web.api.dto.ProcessorDTO;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.entity.AccessConfigurationEntity;
+import org.apache.nifi.web.api.entity.AccessStatusEntity;
+import org.apache.nifi.web.api.entity.ProcessorEntity;
+import org.apache.nifi.web.util.WebUtils;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Access token endpoint test.
+ */
+public class AccessTokenEndpointTest {
+
+    private static final String CLIENT_ID = "token-endpoint-id";
+    private static final String CONTEXT_PATH = "/nifi-api";
+    private static final String FLOW_XML_PATH = "target/test-classes/access-control/flow-admin.xml";
+
+    private static NiFiTestServer SERVER;
+    private static NiFiTestUser TOKEN_USER;
+    private static String BASE_URL;
+
+    @BeforeClass
+    public static void setup() throws Exception {
+        // configure the location of the nifi properties
+        File nifiPropertiesFile = new File("src/test/resources/access-control/nifi.properties");
+        System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, nifiPropertiesFile.getAbsolutePath());
+
+        // update the flow.xml property
+        NiFiProperties props = NiFiProperties.getInstance();
+        props.setProperty("nifi.flow.configuration.file", FLOW_XML_PATH);
+
+        // delete the database directory to avoid issues with re-registration in testRequestAccessUsingToken
+        FileUtils.deleteDirectory(props.getDatabaseRepositoryPath().toFile());
+
+        // load extensions
+        NarClassLoaders.load(props);
+        ExtensionManager.discoverExtensions();
+
+        // start the server
+        SERVER = new NiFiTestServer("src/main/webapp", CONTEXT_PATH);
+        SERVER.startServer();
+        SERVER.loadFlow();
+
+        // get the base url
+        BASE_URL = SERVER.getBaseUrl() + CONTEXT_PATH;
+
+        // create the user
+        final Client client = WebUtils.createClient(null, createTrustContext(props));
+        TOKEN_USER = new NiFiTestUser(client, null);
+    }
+
+    private static SSLContext createTrustContext(final NiFiProperties props) throws Exception {
+        return SslContextFactory.createTrustSslContext(props.getProperty(NiFiProperties.SECURITY_TRUSTSTORE),
+                props.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD).toCharArray(),
+                props.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE), "TLS");
+    }
+
+    // -----------
+    // LOGIN CONIG
+    // -----------
+    /**
+     * Test getting access configuration.
+     *
+     * @throws Exception ex
+     */
+    @Test
+    public void testGetAccessConfig() throws Exception {
+        String url = BASE_URL + "/access/config";
+
+        ClientResponse response = TOKEN_USER.testGet(url);
+
+        // ensure the request is successful
+        Assert.assertEquals(200, response.getStatus());
+
+        // extract the process group
+        AccessConfigurationEntity accessConfigEntity = response.getEntity(AccessConfigurationEntity.class);
+
+        // ensure there is content
+        Assert.assertNotNull(accessConfigEntity);
+
+        // extract the process group dto
+        AccessConfigurationDTO accessConfig = accessConfigEntity.getConfig();
+
+        // verify config
+        Assert.assertTrue(accessConfig.getSupportsLogin());
+        Assert.assertFalse(accessConfig.getSupportsAnonymous());
+    }
+
+    /**
+     * Obtains a token and creates a processor using it.
+     *
+     * @throws Exception ex
+     */
+    @Test
+    public void testCreateProcessorUsingToken() throws Exception {
+        String url = BASE_URL + "/access/token";
+
+        ClientResponse response = TOKEN_USER.testCreateToken(url, "user@nifi", "whateve");
+
+        // ensure the request is successful
+        Assert.assertEquals(201, response.getStatus());
+
+        // get the token
+        String token = response.getEntity(String.class);
+
+        // attempt to create a processor with it
+        createProcessor(token);
+    }
+
+    private ProcessorDTO createProcessor(final String token) throws Exception {
+        String url = BASE_URL + "/controller/process-groups/root/processors";
+
+        // authorization header
+        Map<String, String> headers = new HashMap<>();
+        headers.put("Authorization", "Bearer " + token);
+
+        // create the processor
+        ProcessorDTO processor = new ProcessorDTO();
+        processor.setName("Copy");
+        processor.setType(SourceTestProcessor.class.getName());
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(CLIENT_ID);
+        revision.setVersion(NiFiTestUser.REVISION);
+
+        // create the entity body
+        ProcessorEntity entity = new ProcessorEntity();
+        entity.setRevision(revision);
+        entity.setProcessor(processor);
+
+        // perform the request
+        ClientResponse response = TOKEN_USER.testPostWithHeaders(url, entity, headers);
+
+        // ensure the request is successful
+        Assert.assertEquals(201, response.getStatus());
+
+        // get the entity body
+        entity = response.getEntity(ProcessorEntity.class);
+
+        // verify creation
+        processor = entity.getProcessor();
+        Assert.assertEquals("Copy", processor.getName());
+        Assert.assertEquals("org.apache.nifi.integration.util.SourceTestProcessor", processor.getType());
+
+        return processor;
+    }
+
+    /**
+     * Verifies the response when bad credentials are specified.
+     *
+     * @throws Exception ex
+     */
+    @Test
+    public void testInvalidCredentials() throws Exception {
+        String url = BASE_URL + "/access/token";
+
+        ClientResponse response = TOKEN_USER.testCreateToken(url, "user@nifi", "not a real password");
+
+        // ensure the request is successful
+        Assert.assertEquals(400, response.getStatus());
+    }
+
+    /**
+     * Verifies the response when the user is known.
+     *
+     * @throws Exception ex
+     */
+    @Test
+    public void testUnkownUser() throws Exception {
+        String url = BASE_URL + "/access/token";
+
+        ClientResponse response = TOKEN_USER.testCreateToken(url, "not a real user", "not a real password");
+
+        // ensure the request is successful
+        Assert.assertEquals(400, response.getStatus());
+    }
+
+    /**
+     * Request access using access token.
+     *
+     * @throws Exception ex
+     */
+    @Test
+    public void testRequestAccessUsingToken() throws Exception {
+        String accessStatusUrl = BASE_URL + "/access";
+        String accessTokenUrl = BASE_URL + "/access/token";
+        String registrationUrl = BASE_URL + "/controller/users";
+
+        ClientResponse response = TOKEN_USER.testGet(accessStatusUrl);
+
+        // ensure the request is successful
+        Assert.assertEquals(200, response.getStatus());
+
+        AccessStatusEntity accessStatusEntity = response.getEntity(AccessStatusEntity.class);
+        AccessStatusDTO accessStatus = accessStatusEntity.getAccessStatus();
+
+        // verify unknown
+        Assert.assertEquals("UNKNOWN", accessStatus.getStatus());
+
+        response = TOKEN_USER.testCreateToken(accessTokenUrl, "unregistered-user@nifi", "password");
+
+        // ensure the request is successful
+        Assert.assertEquals(201, response.getStatus());
+
+        // get the token
+        String token = response.getEntity(String.class);
+
+        // authorization header
+        Map<String, String> headers = new HashMap<>();
+        headers.put("Authorization", "Bearer " + token);
+
+        // check the status with the token
+        response = TOKEN_USER.testGetWithHeaders(accessStatusUrl, null, headers);
+
+        // ensure the request is successful
+        Assert.assertEquals(200, response.getStatus());
+
+        accessStatusEntity = response.getEntity(AccessStatusEntity.class);
+        accessStatus = accessStatusEntity.getAccessStatus();
+
+        // verify unregistered
+        Assert.assertEquals("UNREGISTERED", accessStatus.getStatus());
+
+        response = TOKEN_USER.testRegisterUser(registrationUrl, "Gimme access", headers);
+
+        // ensure the request is successful
+        Assert.assertEquals(201, response.getStatus());
+
+        // check the status with the token
+        response = TOKEN_USER.testGetWithHeaders(accessStatusUrl, null, headers);
+
+        // ensure the request is successful
+        Assert.assertEquals(200, response.getStatus());
+
+        accessStatusEntity = response.getEntity(AccessStatusEntity.class);
+        accessStatus = accessStatusEntity.getAccessStatus();
+
+        // verify unregistered
+        Assert.assertEquals("NOT_ACTIVE", accessStatus.getStatus());
+    }
+
+    @AfterClass
+    public static void cleanup() throws Exception {
+        // shutdown the server
+        SERVER.shutdownServer();
+        SERVER = null;
+
+        // look for the flow.xml
+        File flow = new File(FLOW_XML_PATH);
+        if (flow.exists()) {
+            flow.delete();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizationProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizationProvider.java
index d51b7df..d29be92 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizationProvider.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizationProvider.java
@@ -37,7 +37,7 @@ import org.apache.nifi.authorization.DownloadAuthorization;
  */
 public class NiFiTestAuthorizationProvider implements AuthorityProvider {
 
-    private Map<String, Set<Authority>> users;
+    private final Map<String, Set<Authority>> users;
 
     /**
      * Creates a new FileAuthorizationProvider.
@@ -48,6 +48,7 @@ public class NiFiTestAuthorizationProvider implements AuthorityProvider {
         users.put("CN=Lastname Firstname Middlename monitor, OU=Unknown, OU=Unknown, OU=Unknown, O=Unknown, C=Unknown", EnumSet.of(Authority.ROLE_MONITOR));
         users.put("CN=Lastname Firstname Middlename dfm, OU=Unknown, OU=Unknown, OU=Unknown, O=Unknown, C=Unknown", EnumSet.of(Authority.ROLE_DFM));
         users.put("CN=Lastname Firstname Middlename admin, OU=Unknown, OU=Unknown, OU=Unknown, O=Unknown, C=Unknown", EnumSet.of(Authority.ROLE_ADMIN));
+        users.put("user@nifi", EnumSet.of(Authority.ROLE_DFM));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java
new file mode 100644
index 0000000..c023ce1
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java
@@ -0,0 +1,75 @@
+/*
+ * 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.integration.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import org.apache.nifi.authorization.exception.ProviderCreationException;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.apache.nifi.authentication.LoginCredentials;
+import org.apache.nifi.authentication.LoginIdentityProvider;
+import org.apache.nifi.authentication.LoginIdentityProviderConfigurationContext;
+import org.apache.nifi.authentication.LoginIdentityProviderInitializationContext;
+import org.apache.nifi.authentication.exception.IdentityAccessException;
+import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException;
+
+/**
+ *
+ */
+public class NiFiTestLoginIdentityProvider implements LoginIdentityProvider {
+
+    private final Map<String, String> users;
+
+    /**
+     * Creates a new FileAuthorizationProvider.
+     */
+    public NiFiTestLoginIdentityProvider() {
+        users = new HashMap<>();
+        users.put("user@nifi", "whateve");
+        users.put("unregistered-user@nifi", "password");
+    }
+
+    private void checkUser(final String user, final String password) {
+        if (!users.containsKey(user)) {
+            throw new InvalidLoginCredentialsException("Unknown user");
+        }
+
+        if (!users.get(user).equals(password)) {
+            throw new InvalidLoginCredentialsException("Invalid password");
+        }
+    }
+
+    @Override
+    public AuthenticationResponse authenticate(LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException {
+        checkUser(credentials.getUsername(), credentials.getPassword());
+        return new AuthenticationResponse(credentials.getUsername(), credentials.getUsername(), TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS), getClass().getSimpleName());
+    }
+
+    @Override
+    public void initialize(LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException {
+    }
+
+    @Override
+    public void onConfigured(LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException {
+    }
+
+    @Override
+    public void preDestruction() {
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java
index 42b0aab..38c2d41 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java
@@ -78,8 +78,12 @@ public class NiFiTestServer {
     private void createSecureConnector() {
         org.eclipse.jetty.util.ssl.SslContextFactory contextFactory = new org.eclipse.jetty.util.ssl.SslContextFactory();
 
-        // need client auth
-        contextFactory.setNeedClientAuth(properties.getNeedClientAuth());
+        // require client auth when not supporting login or anonymous access
+        if (StringUtils.isBlank(properties.getProperty(NiFiProperties.SECURITY_USER_LOGIN_IDENTITY_PROVIDER)) && properties.getAnonymousAuthorities().isEmpty()) {
+            contextFactory.setNeedClientAuth(true);
+        } else {
+            contextFactory.setWantClientAuth(true);
+        }
 
         /* below code sets JSSE system properties when values are provided */
         // keystore properties
@@ -163,7 +167,6 @@ public class NiFiTestServer {
     }
 
     public Client getClient() {
-        // create the client
         return WebUtils.createClient(null, SslContextFactory.createSslContext(properties));
     }
 


[44/51] [abbrv] nifi git commit: NIFI-655: - Ensuring the access token is not replicated when the user is already authenticated/authorized.

Posted by mc...@apache.org.
NIFI-655:
- Ensuring the access token is not replicated when the user is already authenticated/authorized.

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

Branch: refs/heads/master
Commit: c722b563351652702db70f1cc1501ff6754ffc70
Parents: a84e505
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 30 14:57:38 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 30 14:57:38 2015 -0500

----------------------------------------------------------------------
 .../java/org/apache/nifi/web/api/ApplicationResource.java     | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/c722b563/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
index e4afd05..399879d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
@@ -370,9 +370,6 @@ public abstract class ApplicationResource {
             if (user != null) {
                 // add the proxied user details
                 result.put(PROXIED_ENTITIES_CHAIN_HTTP_HEADER, ProxiedEntitiesUtils.buildProxiedEntitiesChainString(user));
-
-                // remove the access token if present, since the user is already authenticated/authorized
-                result.remove(JwtAuthenticationFilter.AUTHORIZATION);
             }
 
             // add the user's authorities (if any) to the headers
@@ -385,9 +382,11 @@ public abstract class ApplicationResource {
 
                     // put serialized user details in header
                     result.put(PROXIED_ENTITY_USER_DETAILS_HTTP_HEADER, hexEncodedUserDetails);
+
+                    // remove the access token if present, since the user is already authenticated/authorized
+                    result.remove(JwtAuthenticationFilter.AUTHORIZATION);
                 }
             }
-
         }
         return result;
     }


[18/51] [abbrv] nifi git commit: NIFI-655: - Removing deprecated authentication strategy. - Renaming TLS to START_TLS. - Allowing the protocol to be configured.

Posted by mc...@apache.org.
NIFI-655:
- Removing deprecated authentication strategy.
- Renaming TLS to START_TLS.
- Allowing the protocol to be configured.

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

Branch: refs/heads/master
Commit: 48c65e0498b1288729a99bff6247522c982134f0
Parents: 2a0439c
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 23 08:09:49 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 23 08:09:49 2015 -0500

----------------------------------------------------------------------
 .../nifi/ldap/LdapAuthenticationStrategy.java   |  4 +--
 .../java/org/apache/nifi/ldap/LdapProvider.java | 36 +++++++++++---------
 2 files changed, 22 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/48c65e04/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapAuthenticationStrategy.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapAuthenticationStrategy.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapAuthenticationStrategy.java
index a3c4f09..7124ce1 100644
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapAuthenticationStrategy.java
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapAuthenticationStrategy.java
@@ -20,8 +20,8 @@ package org.apache.nifi.ldap;
  *
  */
 public enum LdapAuthenticationStrategy {
+
     ANONYMOUS,
     SIMPLE,
-    DIGEST_MD5,
-    TLS
+    START_TLS
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/48c65e04/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
index b46c9be..cfa0bf8 100644
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
@@ -37,13 +37,13 @@ import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException
 import org.apache.nifi.authorization.exception.ProviderCreationException;
 import org.apache.nifi.authorization.exception.ProviderDestructionException;
 import org.apache.nifi.security.util.SslContextFactory;
+import org.apache.nifi.security.util.SslContextFactory.ClientAuth;
 import org.apache.nifi.util.FormatUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.ldap.CommunicationException;
 import org.springframework.ldap.core.support.AbstractTlsDirContextAuthenticationStrategy;
 import org.springframework.ldap.core.support.DefaultTlsDirContextAuthenticationStrategy;
-import org.springframework.ldap.core.support.DigestMd5DirContextAuthenticationStrategy;
 import org.springframework.ldap.core.support.LdapContextSource;
 import org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy;
 import org.springframework.security.authentication.AuthenticationServiceException;
@@ -63,7 +63,6 @@ import org.springframework.security.ldap.userdetails.LdapUserDetails;
 public class LdapProvider implements LoginIdentityProvider {
 
     private static final Logger logger = LoggerFactory.getLogger(LdapProvider.class);
-    private static final String TLS = "TLS";
 
     private AbstractLdapAuthenticationProvider provider;
     private String issuer;
@@ -99,7 +98,7 @@ public class LdapProvider implements LoginIdentityProvider {
         if (!baseEnvironment.isEmpty()) {
             context.setBaseEnvironmentProperties(baseEnvironment);
         }
-
+        
         // authentication strategy
         final String rawAuthenticationStrategy = configurationContext.getProperty("Authentication Strategy");
         final LdapAuthenticationStrategy authenticationStrategy;
@@ -125,10 +124,7 @@ public class LdapProvider implements LoginIdentityProvider {
                     case SIMPLE:
                         context.setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy());
                         break;
-                    case DIGEST_MD5:
-                        context.setAuthenticationStrategy(new DigestMd5DirContextAuthenticationStrategy());
-                        break;
-                    case TLS:
+                    case START_TLS:
                         final AbstractTlsDirContextAuthenticationStrategy tlsAuthenticationStrategy = new DefaultTlsDirContextAuthenticationStrategy();
 
                         // shutdown gracefully
@@ -145,22 +141,30 @@ public class LdapProvider implements LoginIdentityProvider {
                         final String rawTruststorePassword = configurationContext.getProperty("TLS - Truststore Password");
                         final String rawTruststoreType = configurationContext.getProperty("TLS - Truststore Type");
                         final String rawClientAuth = configurationContext.getProperty("TLS - Client Auth");
+                        final String rawProtocol = configurationContext.getProperty("TLS - Protocol");
+
+                        final ClientAuth clientAuth;
+                        if (StringUtils.isBlank(rawClientAuth)) {
+                            clientAuth = ClientAuth.NONE;
+                        } else {
+                            try {
+                                clientAuth = ClientAuth.valueOf(rawClientAuth);
+                            } catch (final IllegalArgumentException iae) {
+                                throw new ProviderCreationException(String.format("Unrecognized client auth '%s'. Possible values are [%s]",
+                                        rawClientAuth, StringUtils.join(ClientAuth.values(), ", ")));
+                            }
+                        }
 
                         try {
                             final SSLContext sslContext;
                             if (StringUtils.isBlank(rawKeystore)) {
-                                sslContext = SslContextFactory.createTrustSslContext(rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, TLS);
+                                sslContext = SslContextFactory.createTrustSslContext(rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, rawProtocol);
                             } else {
                                 if (StringUtils.isBlank(rawTruststore)) {
-                                    sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType, TLS);
+                                    sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType, rawProtocol);
                                 } else {
-                                    try {
-                                        final SslContextFactory.ClientAuth clientAuth = SslContextFactory.ClientAuth.valueOf(rawClientAuth);
-                                        sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType,
-                                                rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, clientAuth, TLS);
-                                    } catch (final IllegalArgumentException iae) {
-                                        throw new ProviderCreationException(String.format("Unrecognized client auth '%s'", rawClientAuth));
-                                    }
+                                    sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType,
+                                            rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, clientAuth, rawProtocol);
                                 }
                             }
                             tlsAuthenticationStrategy.setSslSocketFactory(sslContext.getSocketFactory());


[15/51] [abbrv] nifi git commit: NIFI-655: - Showing a logging in notification during the log in process.

Posted by mc...@apache.org.
NIFI-655:
- Showing a logging in notification during the log in process.

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

Branch: refs/heads/master
Commit: 242949ee98836794fd18d2503c275c84bfd41a9f
Parents: 3da1981
Author: Matt Gilman <ma...@gmail.com>
Authored: Wed Nov 18 18:23:59 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Nov 18 18:23:59 2015 -0500

----------------------------------------------------------------------
 .../src/main/webapp/WEB-INF/pages/login.jsp     |  1 +
 .../WEB-INF/partials/login/login-progress.jsp   | 22 ++++++++++++++++++
 .../WEB-INF/partials/login/login-submission.jsp |  2 +-
 .../partials/login/nifi-registration-form.jsp   |  2 +-
 .../nifi-web-ui/src/main/webapp/css/login.css   | 24 ++++++++++++--------
 .../src/main/webapp/js/nf/login/nf-login.js     | 24 ++++++++++++++++----
 .../src/main/webapp/js/nf/nf-common.js          | 17 ++++++++++----
 7 files changed, 71 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/242949ee/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 da2ae00..978d019 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
@@ -45,6 +45,7 @@
             <jsp:include page="/WEB-INF/partials/login/login-form.jsp"/>
             <jsp:include page="/WEB-INF/partials/login/nifi-registration-form.jsp"/>
             <jsp:include page="/WEB-INF/partials/login/login-submission.jsp"/>
+            <jsp:include page="/WEB-INF/partials/login/login-progress.jsp"/>
         </div>
         <jsp:include page="/WEB-INF/partials/ok-dialog.jsp"/>
         <div id="faded-background"></div>

http://git-wip-us.apache.org/repos/asf/nifi/blob/242949ee/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-progress.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-progress.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-progress.jsp
new file mode 100644
index 0000000..874a0cb
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-progress.jsp
@@ -0,0 +1,22 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="login-progress-container" class="login-container hidden">
+    <div id="login-progress-spinner" class="loading-container ajax-loading"></div>
+    <div id="login-progress-label">Logging in...</div>
+    <div class="clear"></div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/242949ee/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-submission.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-submission.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-submission.jsp
index 65c6077..508ead3 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-submission.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-submission.jsp
@@ -15,6 +15,6 @@
   limitations under the License.
 --%>
 <%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
-<div id="login-submission-container" class="hidden">
+<div id="login-submission-container" class="login-container hidden">
     <div id="login-submission-button" class="button">Log in</div>
 </div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/242949ee/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 f1b73c0..3806bd5 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
@@ -15,7 +15,7 @@
   limitations under the License.
 --%>
 <%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
-<div id="nifi-registration-container" class="hidden">
+<div id="nifi-registration-container" class="login-container hidden">
     <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">

http://git-wip-us.apache.org/repos/asf/nifi/blob/242949ee/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 62f6118..68086a7 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
@@ -56,14 +56,14 @@ body.login-body input, body.login-body textarea {
     width: 400px;
 }
 
+div.login-container {
+    width: 412px;
+}
+
 /*
     NiFi Registration
 */
 
-#nifi-registration-container {
-    width: 412px;
-}
-
 #nifi-user-submit-justification-container {
     margin-bottom: 10px;
 }
@@ -77,13 +77,19 @@ body.login-body input, body.login-body textarea {
 }
 
 /*
-    Submission
+    Login Progress
 */
 
-#login-submission-container {
-    width: 412px;
+#login-progress-label {
+    float: right;
+    font-weight: bold;
+    line-height: 16px;
 }
 
-#login-submission-button {
-    
+#login-progress-spinner {
+    float: right;
+    width: 16px;
+    height: 16px;
+    background-color: transparent;
+    margin-left: 3px;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/242949ee/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 bd39a44..7be8e46 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
@@ -87,6 +87,10 @@ nf.Login = (function () {
     };
 
     var login = function () {
+        // show the logging message...
+        $('#login-progress-container').show();
+        $('#login-submission-container').hide();
+        
         // login submit
         $.ajax({
             type: 'POST',
@@ -119,6 +123,10 @@ nf.Login = (function () {
                     // show the registration form
                     initializeNiFiRegistration();
                     showNiFiRegistration();
+                    
+                    // update the form visibility
+                    $('#login-submission-container').show();
+                    $('#login-progress-container').hide();
                 } else {
                     // reload as appropriate - no need to schedule token refresh as the page is reloading
                     if (top !== window) {
@@ -139,6 +147,10 @@ nf.Login = (function () {
                 if (xhr.status === 401) {
                     initializeNiFiRegistration();
                     showNiFiRegistration();
+                    
+                    // update the form visibility
+                    $('#login-submission-container').show();
+                    $('#login-progress-container').hide();
                 } else {
                     $('#login-message-title').text('Unable to log in');
                     $('#login-message').text(xhr.responseText);
@@ -146,6 +158,7 @@ nf.Login = (function () {
                     // update visibility
                     $('#login-container').hide();
                     $('#login-submission-container').hide();
+                    $('#login-progress-container').hide();
                     $('#login-message-container').show();
                 }
             });
@@ -155,6 +168,10 @@ nf.Login = (function () {
                     dialogContent: nf.Common.escapeHtml(xhr.responseText),
                     overlayBackground: false
                 });
+                
+                // update the form visibility
+                $('#login-submission-container').show();
+                $('#login-progress-container').hide();
             } else {
                 $('#login-message-title').text('Unable to log in');
                 $('#login-message').text(xhr.responseText);
@@ -162,6 +179,7 @@ nf.Login = (function () {
                 // update visibility
                 $('#login-container').hide();
                 $('#login-submission-container').hide();
+                $('#login-progress-container').hide();
                 $('#login-message-container').show();
             }
         });
@@ -194,12 +212,8 @@ nf.Login = (function () {
         });
     };
 
-    var logout = function () {
-        nf.Storage.removeItem('jwt');
-    };
-
     var showLogoutLink = function () {
-        $('#user-logout-container').show();
+        nf.Common.showLogoutLink();
     };
 
     return {

http://git-wip-us.apache.org/repos/asf/nifi/blob/242949ee/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 d71c8ef..321044f 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
@@ -424,11 +424,7 @@ nf.Common = (function () {
          * Closes the canvas by removing the splash screen and stats poller.
          */
         closeCanvas: function () {
-            if (nf.Storage.getItem('jwt') === null) {
-                $('#user-logout-container').hide();
-            } else {
-                $('#user-logout-container').show();
-            }
+            nf.Common.showLogoutLink();
             
             // ensure this javascript has been loaded in the nf canvas page
             if (nf.Common.isDefinedAndNotNull(nf.Canvas)) {
@@ -447,6 +443,17 @@ nf.Common = (function () {
         },
 
         /**
+         * Shows the logout link if appropriate.
+         */
+        showLogoutLink: function () {
+            if (nf.Storage.getItem('jwt') === null) {
+                $('#user-logout-container').hide();
+            } else {
+                $('#user-logout-container').show();
+            }
+        },
+
+        /**
          * Populates the specified field with the specified value. If the value is 
          * undefined, the field will read 'No value set.' If the value is an empty
          * string, the field will read 'Empty string set.'


[17/51] [abbrv] nifi git commit: NIFI-655: - Fixing checkstyle issues. - Showing the progress spinner while submitting account justification.

Posted by mc...@apache.org.
NIFI-655:
- Fixing checkstyle issues.
- Showing the progress spinner while submitting account justification.

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

Branch: refs/heads/master
Commit: 2a0439ca06b81b27cbbed2058307af778169d9e6
Parents: 9f60411
Author: Matt Gilman <ma...@gmail.com>
Authored: Thu Nov 19 08:29:39 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Thu Nov 19 08:29:39 2015 -0500

----------------------------------------------------------------------
 .../nifi/security/util/CertificateUtils.java    |  22 +---
 .../src/main/java/org/apache/nifi/key/Key.java  |   6 +-
 .../web/security/x509/X509IdentityProvider.java |   4 +-
 .../nifi/web/security/jwt/JwtServiceTest.java   | 128 ++++++++-----------
 .../WEB-INF/partials/login/login-progress.jsp   |   2 +-
 .../src/main/webapp/js/nf/login/nf-login.js     |   7 +
 .../java/org/apache/nifi/ldap/LdapProvider.java |   4 +-
 7 files changed, 77 insertions(+), 96 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/2a0439ca/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
index ea3a6c6..6236d8e 100644
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
+++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
@@ -34,8 +34,7 @@ public final class CertificateUtils {
     private static final Logger logger = LoggerFactory.getLogger(CertificateUtils.class);
 
     /**
-     * Returns true if the given keystore can be loaded using the given keystore
-     * type and password. Returns false otherwise.
+     * Returns true if the given keystore can be loaded using the given keystore type and password. Returns false otherwise.
      *
      * @param keystore the keystore to validate
      * @param keystoreType the type of the keystore
@@ -77,10 +76,8 @@ public final class CertificateUtils {
     }
 
     /**
-     * Extracts the username from the specified DN. If the username cannot be
-     * extracted because the CN is in an unrecognized format, the entire CN is
-     * returned. If the CN cannot be extracted because the DN is in an
-     * unrecognized format, the entire DN is returned.
+     * Extracts the username from the specified DN. If the username cannot be extracted because the CN is in an unrecognized format, the entire CN is returned. If the CN cannot be extracted because
+     * the DN is in an unrecognized format, the entire DN is returned.
      *
      * @param dn the dn to extract the username from
      * @return the exatracted username
@@ -92,7 +89,7 @@ public final class CertificateUtils {
         if (StringUtils.isNotBlank(dn)) {
             // determine the separate
             final String separator = StringUtils.indexOfIgnoreCase(dn, "/cn=") > 0 ? "/" : ",";
-            
+
             // attempt to locate the cd
             final String cnPattern = "cn=";
             final int cnIndex = StringUtils.indexOfIgnoreCase(dn, cnPattern);
@@ -110,9 +107,7 @@ public final class CertificateUtils {
     }
 
     /**
-     * Returns a list of subject alternative names. Any name that is represented
-     * as a String by X509Certificate.getSubjectAlternativeNames() is converted
-     * to lowercase and returned.
+     * Returns a list of subject alternative names. Any name that is represented as a String by X509Certificate.getSubjectAlternativeNames() is converted to lowercase and returned.
      *
      * @param certificate a certificate
      * @return a list of subject alternative names; list is never null
@@ -128,12 +123,9 @@ public final class CertificateUtils {
         final List<String> result = new ArrayList<>();
         for (final List<?> generalName : altNames) {
             /**
-             * generalName has the name type as the first element a String or
-             * byte array for the second element.  We return any general names
-             * that are String types.
+             * generalName has the name type as the first element a String or byte array for the second element. We return any general names that are String types.
              *
-             * We don't inspect the numeric name type because some certificates
-             * incorrectly put IPs and DNS names under the wrong name types.
+             * We don't inspect the numeric name type because some certificates incorrectly put IPs and DNS names under the wrong name types.
              */
             final Object value = generalName.get(1);
             if (value instanceof String) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/2a0439ca/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java
index b7158c2..9ce7a9a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java
@@ -29,7 +29,7 @@ public class Key implements Serializable {
 
     /**
      * The key id.
-     * 
+     *
      * @return the id
      */
     public int getId() {
@@ -42,7 +42,7 @@ public class Key implements Serializable {
 
     /**
      * The identity of the user this key is associated with.
-     * 
+     *
      * @return the identity
      */
     public String getIdentity() {
@@ -55,7 +55,7 @@ public class Key implements Serializable {
 
     /**
      * The signing key.
-     * 
+     *
      * @return the signing key
      */
     public String getKey() {

http://git-wip-us.apache.org/repos/asf/nifi/blob/2a0439ca/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.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/x509/X509IdentityProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
index cae1134..db0b529 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
@@ -31,9 +31,9 @@ import org.springframework.security.web.authentication.preauth.x509.X509Principa
 public class X509IdentityProvider {
 
     private static final Logger logger = LoggerFactory.getLogger(X509IdentityProvider.class);
-    
+
     private final String issuer = getClass().getSimpleName();
-    
+
     private X509CertificateValidator certificateValidator;
     private X509PrincipalExtractor principalExtractor;
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/2a0439ca/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
index 6009c1f..59c66ef 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
@@ -53,77 +53,76 @@ public class JwtServiceTest {
     /**
      * These constant strings were generated using the tool at http://jwt.io
      */
-
-    private static final String VALID_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
-            ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRl" +
-            "ciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZ" +
-            "XJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoxLCJleHAiOjI0NDc4MDg3NjEsIm" +
-            "lhdCI6MTQ0NzgwODcwMX0.r6aGZ6FNNYMOpcXW8BK2VYaQeX1uO0Aw1KJfjB3Q1DU";
+    private static final String VALID_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRl"
+            + "ciIsImF1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZ"
+            + "XJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoxLCJleHAiOjI0NDc4MDg3NjEsIm"
+            + "lhdCI6MTQ0NzgwODcwMX0.r6aGZ6FNNYMOpcXW8BK2VYaQeX1uO0Aw1KJfjB3Q1DU";
 
     // This token has an empty subject field
-    private static final String INVALID_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
-            ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZG" +
-            "VudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvI" +
-            "iwia2lkIjoxLCJleHAiOjI0NDc4MDg3NjEsImlhdCI6MTQ0NzgwODcwMX0" +
-            ".x_1p2M6E0vwWHWMujIUnSL3GkFoDqqICllRxo2SMNaw";
+    private static final String INVALID_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZG"
+            + "VudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvI"
+            + "iwia2lkIjoxLCJleHAiOjI0NDc4MDg3NjEsImlhdCI6MTQ0NzgwODcwMX0"
+            + ".x_1p2M6E0vwWHWMujIUnSL3GkFoDqqICllRxo2SMNaw";
 
-    private static final String VALID_UNSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
-            ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZC" +
-            "I6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJl" +
-            "c3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9";
+    private static final String VALID_UNSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZC"
+            + "I6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJl"
+            + "c3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9";
 
     // This token has an empty subject field
-    private static final String INVALID_UNSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
-            ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVu" +
-            "dGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoi" +
-            "YWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9";
+    private static final String INVALID_UNSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVu"
+            + "dGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoi"
+            + "YWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9";
 
     // Algorithm field is "none"
-    private static final String VALID_MALSIGNED_TOKEN = "eyJhbGciOiJub25lIn0" +
-            ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZC" +
-            "I6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJl" +
-            "c3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoxNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9" +
-            ".mPO_wMNMl_zjMNevhNvUoXbSJ9Kx6jAe5OxDIAzKQbI";
+    private static final String VALID_MALSIGNED_TOKEN = "eyJhbGciOiJub25lIn0"
+            + ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZC"
+            + "I6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJl"
+            + "c3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoxNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9"
+            + ".mPO_wMNMl_zjMNevhNvUoXbSJ9Kx6jAe5OxDIAzKQbI";
 
     // Algorithm field is "none" and no signature is present
-    private static final String VALID_MALSIGNED_NO_SIG_TOKEN = "eyJhbGciOiJub25lIn0" +
-            ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY" +
-            "2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIj" +
-            "oiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.";
+    private static final String VALID_MALSIGNED_NO_SIG_TOKEN = "eyJhbGciOiJub25lIn0"
+            + ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY"
+            + "2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIj"
+            + "oiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.";
 
     // This token has an empty subject field
-    private static final String INVALID_MALSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
-            ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVud" +
-            "Gl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYW" +
-            "xvcHJlc3RvIiwiZXhwIjoxNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.WAwmUY4KHKV2oARNodkqDkbZsfRXGZfD2Ccy64GX9QF";
+    private static final String INVALID_MALSIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik1vY2tJZGVud"
+            + "Gl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvcHJlc3RvIiwia2lkIjoiYW"
+            + "xvcHJlc3RvIiwiZXhwIjoxNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.WAwmUY4KHKV2oARNodkqDkbZsfRXGZfD2Ccy64GX9QF";
 
     // This token is signed but expired
-    private static final String EXPIRED_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
-            ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik" +
-            "1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvc" +
-            "HJlc3RvIiwia2lkIjoxLCJleHAiOjE0NDc4MDg3NjEsImlhdCI6MTQ0NzgwODcw" +
-            "MX0.ZPDIhNKuL89vTGXcuztOYaGifwcrQy_gid4j8Sspmto";
+    private static final String EXPIRED_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiIiLCJpc3MiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsImF1ZCI6Ik"
+            + "1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxvc"
+            + "HJlc3RvIiwia2lkIjoxLCJleHAiOjE0NDc4MDg3NjEsImlhdCI6MTQ0NzgwODcw"
+            + "MX0.ZPDIhNKuL89vTGXcuztOYaGifwcrQy_gid4j8Sspmto";
 
     // Subject is "mgilman" but signed with "alopresto" key
-    private static final String IMPOSTER_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
-            ".eyJzdWIiOiJtZ2lsbWFuIiwiaXNzIjoiTW9ja0lkZW50aXR5UHJvdmlkZXIiLCJ" +
-            "hdWQiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsInByZWZlcnJlZF91c2VybmFtZSI" +
-            "6ImFsb3ByZXN0byIsImtpZCI6MSwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc" +
-            "4MDg3MDF9.aw5OAvLTnb_sHmSQOQzW-A7NImiZgXJ2ngbbNL2Ymkc";
+    private static final String IMPOSTER_SIGNED_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiJtZ2lsbWFuIiwiaXNzIjoiTW9ja0lkZW50aXR5UHJvdmlkZXIiLCJ"
+            + "hdWQiOiJNb2NrSWRlbnRpdHlQcm92aWRlciIsInByZWZlcnJlZF91c2VybmFtZSI"
+            + "6ImFsb3ByZXN0byIsImtpZCI6MSwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc"
+            + "4MDg3MDF9.aw5OAvLTnb_sHmSQOQzW-A7NImiZgXJ2ngbbNL2Ymkc";
 
     // Issuer field is set to unknown provider
-    private static final String UNKNOWN_ISSUER_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
-            ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJVbmtub3duSWRlbnRpdHlQcm92aWRlciIsIm" +
-            "F1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxv" +
-            "cHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9" +
-            ".SAd9tyNwSaijWet9wvAWSNmpxmPSK4XQuLx7h3ARqBo";
+    private static final String UNKNOWN_ISSUER_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiJhbG9wcmVzdG8iLCJpc3MiOiJVbmtub3duSWRlbnRpdHlQcm92aWRlciIsIm"
+            + "F1ZCI6Ik1vY2tJZGVudGl0eVByb3ZpZGVyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxv"
+            + "cHJlc3RvIiwia2lkIjoiYWxvcHJlc3RvIiwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9"
+            + ".SAd9tyNwSaijWet9wvAWSNmpxmPSK4XQuLx7h3ARqBo";
 
     // Issuer field is absent
-    private static final String NO_ISSUER_TOKEN = "eyJhbGciOiJIUzI1NiJ9" +
-            ".eyJzdWIiOiJhbG9wcmVzdG8iLCJhdWQiOiJNb2NrSWRlbnRpdHlQcm92a" +
-            "WRlciIsInByZWZlcnJlZF91c2VybmFtZSI6ImFsb3ByZXN0byIsImtpZCI" +
-            "6MSwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.6kDjDanA" +
-            "g0NQDb3C8FmgbBAYDoIfMAEkF4WMVALsbJA";
+    private static final String NO_ISSUER_TOKEN = "eyJhbGciOiJIUzI1NiJ9"
+            + ".eyJzdWIiOiJhbG9wcmVzdG8iLCJhdWQiOiJNb2NrSWRlbnRpdHlQcm92a"
+            + "WRlciIsInByZWZlcnJlZF91c2VybmFtZSI6ImFsb3ByZXN0byIsImtpZCI"
+            + "6MSwiZXhwIjoyNDQ3ODA4NzYxLCJpYXQiOjE0NDc4MDg3MDF9.6kDjDanA"
+            + "g0NQDb3C8FmgbBAYDoIfMAEkF4WMVALsbJA";
 
     private static final String DEFAULT_HEADER = "{\"alg\":\"HS256\"}";
     private static final String DEFAULT_IDENTITY = "alopresto";
@@ -137,7 +136,6 @@ public class JwtServiceTest {
     // Class under test
     private JwtService jwtService;
 
-
     private String generateHS256Token(String rawHeader, String rawPayload, boolean isValid, boolean isSigned) {
         return generateHS256Token(rawHeader, rawPayload, HMAC_SECRET, isValid, isSigned);
     }
@@ -172,14 +170,13 @@ public class JwtServiceTest {
         return Base64.encodeBase64URLSafeString(hmacSHA256.doFinal(body.getBytes("UTF-8")));
     }
 
-
     @Before
     public void setUp() throws Exception {
         final Key key = new Key();
         key.setId(1);
         key.setIdentity(DEFAULT_IDENTITY);
         key.setKey(HMAC_SECRET);
-        
+
         mockKeyService = Mockito.mock(KeyService.class);
         when(mockKeyService.getKey(anyInt())).thenReturn(key);
         when(mockKeyService.getOrCreateKey(anyString())).thenReturn(key);
@@ -214,7 +211,6 @@ public class JwtServiceTest {
         logger.debug("Extracted identity: " + identity);
 
         // Assert
-
         // Should fail
     }
 
@@ -228,7 +224,6 @@ public class JwtServiceTest {
         logger.debug("Extracted identity: " + identity);
 
         // Assert
-
         // Should fail
     }
 
@@ -242,7 +237,6 @@ public class JwtServiceTest {
         logger.debug("Extracted identity: " + identity);
 
         // Assert
-
         // Should fail
     }
 
@@ -256,7 +250,6 @@ public class JwtServiceTest {
         logger.debug("Extracted identity: " + identity);
 
         // Assert
-
         // Should fail
     }
 
@@ -270,7 +263,6 @@ public class JwtServiceTest {
         logger.debug("Extracted identity: " + identity);
 
         // Assert
-
         // Should fail
     }
 
@@ -284,7 +276,6 @@ public class JwtServiceTest {
         logger.debug("Extracted identity: " + identity);
 
         // Assert
-
         // Should fail
     }
 
@@ -299,7 +290,6 @@ public class JwtServiceTest {
         logger.debug("Extracted identity: " + identity);
 
         // Assert
-
         // Should fail
     }
 
@@ -313,7 +303,6 @@ public class JwtServiceTest {
         logger.debug("Extracted identity: " + identity);
 
         // Assert
-
         // Should fail
     }
 
@@ -327,7 +316,6 @@ public class JwtServiceTest {
         logger.debug("Extracted identity: " + identity);
 
         // Assert
-
         // Should fail
     }
 
@@ -341,7 +329,6 @@ public class JwtServiceTest {
         logger.debug("Extracted identity: " + identity);
 
         // Assert
-
         // Should fail
     }
 
@@ -366,7 +353,6 @@ public class JwtServiceTest {
         logger.debug("Generated JWT: " + token);
 
         // Run after the SUT generates the token to ensure the same issued at time
-
         // Split the token, decode the middle section, and form a new String
         final String DECODED_PAYLOAD = new String(Base64.decodeBase64(token.split("\\.")[1].getBytes()));
         final long ISSUED_AT_SEC = Long.valueOf(DECODED_PAYLOAD.substring(DECODED_PAYLOAD.lastIndexOf(":") + 1,
@@ -402,7 +388,6 @@ public class JwtServiceTest {
         jwtService.generateSignedToken(nullLoginAuthenticationToken);
 
         // Assert
-
         // Should throw exception
     }
 
@@ -418,7 +403,6 @@ public class JwtServiceTest {
         jwtService.generateSignedToken(emptyIdentityLoginAuthenticationToken);
 
         // Assert
-
         // Should throw exception
     }
 
@@ -434,7 +418,6 @@ public class JwtServiceTest {
         jwtService.generateSignedToken(nullIdentityLoginAuthenticationToken);
 
         // Assert
-
         // Should throw exception
     }
 
@@ -449,15 +432,14 @@ public class JwtServiceTest {
 
         // Set up the bad key service
         KeyService missingKeyService = Mockito.mock(KeyService.class);
-        when(missingKeyService.getOrCreateKey(anyString())).thenThrow(new AdministrationException("Could not find a " +
-                "key for that user"));
+        when(missingKeyService.getOrCreateKey(anyString())).thenThrow(new AdministrationException("Could not find a "
+                + "key for that user"));
         jwtService = new JwtService(missingKeyService);
 
         // Act
         jwtService.generateSignedToken(loginAuthenticationToken);
 
         // Assert
-
         // Should throw exception
     }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/2a0439ca/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-progress.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-progress.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-progress.jsp
index cefc73c..6b08e54 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-progress.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-progress.jsp
@@ -17,6 +17,6 @@
 <%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
 <div id="login-progress-container" class="login-container hidden">
     <div id="login-progress-spinner" class="loading-container"></div>
-    <div id="login-progress-label">Logging in...</div>
+    <div id="login-progress-label"></div>
     <div class="clear"></div>
 </div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/2a0439ca/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 7be8e46..002136c 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
@@ -88,6 +88,7 @@ nf.Login = (function () {
 
     var login = function () {
         // show the logging message...
+        $('#login-progress-label').text('Logging in...');
         $('#login-progress-container').show();
         $('#login-submission-container').hide();
         
@@ -186,6 +187,11 @@ nf.Login = (function () {
     };
 
     var submitJustification = function () {
+        // show the logging message...
+        $('#login-progress-label').text('Submitting...');
+        $('#login-progress-container').show();
+        $('#login-submission-container').hide();
+        
         // attempt to create the nifi account registration
         $.ajax({
             type: 'POST',
@@ -208,6 +214,7 @@ nf.Login = (function () {
             // update form visibility
             $('#nifi-registration-container').hide();
             $('#login-submission-container').hide();
+            $('#login-progress-container').hide();
             $('#login-message-container').show();
         });
     };

http://git-wip-us.apache.org/repos/asf/nifi/blob/2a0439ca/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
index f3abdb0..b46c9be 100644
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
@@ -227,7 +227,7 @@ public class LdapProvider implements LoginIdentityProvider {
             final Map<String, Object> baseEnvironment,
             final String configurationProperty,
             final String environmentKey) {
-        
+
         final String rawTimeout = configurationContext.getProperty(configurationProperty);
         if (StringUtils.isNotBlank(rawTimeout)) {
             try {
@@ -238,7 +238,7 @@ public class LdapProvider implements LoginIdentityProvider {
             }
         }
     }
-    
+
     @Override
     public final AuthenticationResponse authenticate(final LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException {
         if (provider == null) {


[12/51] [abbrv] nifi git commit: NIFI-655: - Refactoring key service to expose the key id. - Handling client side expiration better. - Removing specialized active directory provider and abstract ldap provider.

Posted by mc...@apache.org.
NIFI-655:
- Refactoring key service to expose the key id.
- Handling client side expiration better.
- Removing specialized active directory provider and abstract ldap provider.

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

Branch: refs/heads/master
Commit: c94d0271d990df6d6cd9a1dfee7b1706f451483c
Parents: 7d04dfe
Author: Matt Gilman <ma...@gmail.com>
Authored: Wed Nov 18 14:01:45 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Nov 18 14:01:45 2015 -0500

----------------------------------------------------------------------
 .../nifi/security/util/CertificateUtils.java    |  31 ++---
 .../java/org/apache/nifi/admin/dao/KeyDAO.java  |  18 ++-
 .../nifi/admin/dao/impl/StandardKeyDAO.java     |  67 ++++++++--
 .../apache/nifi/admin/service/KeyService.java   |   8 +-
 .../nifi/admin/service/action/GetKeyAction.java |  41 -------
 .../admin/service/action/GetKeyByIdAction.java  |  42 +++++++
 .../service/action/GetKeyByIdentityAction.java  |  42 +++++++
 .../service/action/GetOrCreateKeyAction.java    |   7 +-
 .../admin/service/impl/StandardKeyService.java  |  19 ++-
 .../src/main/java/org/apache/nifi/key/Key.java  |  69 +++++++++++
 .../org/apache/nifi/web/api/AccessResource.java |  82 ++++++-------
 .../InvalidAuthenticationExceptionMapper.java   |  44 +++++++
 .../src/main/resources/nifi-web-api-context.xml |   1 +
 .../InvalidAuthenticationException.java         |  35 ++++++
 .../web/security/NiFiAuthenticationFilter.java  |   5 +-
 .../security/jwt/JwtAuthenticationFilter.java   |   9 +-
 .../nifi/web/security/jwt/JwtService.java       |  20 ++-
 .../security/x509/X509AuthenticationFilter.java |   4 +-
 .../nifi/web/security/jwt/JwtServiceTest.java   |  11 +-
 .../webapp/js/nf/canvas/nf-canvas-header.js     |   2 +-
 .../src/main/webapp/js/nf/canvas/nf-canvas.js   |  14 +--
 .../src/main/webapp/js/nf/login/nf-login.js     |  12 +-
 .../src/main/webapp/js/nf/nf-common.js          | 112 ++++++++++-------
 .../src/main/webapp/js/nf/nf-dialog.js          |  23 ++--
 .../src/main/webapp/js/nf/nf-storage.js         |  75 ++++++++----
 .../apache/nifi/ldap/AbstractLdapProvider.java  | 106 ----------------
 .../nifi/ldap/ActiveDirectoryProvider.java      |  51 --------
 .../java/org/apache/nifi/ldap/LdapProvider.java | 122 ++++++++++++++-----
 ...he.nifi.authentication.LoginIdentityProvider |   3 +-
 29 files changed, 627 insertions(+), 448 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
index 5126933..ea3a6c6 100644
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
+++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
@@ -87,30 +87,21 @@ public final class CertificateUtils {
      */
     public static String extractUsername(String dn) {
         String username = dn;
-        String cn = "";
 
         // ensure the dn is specified
         if (StringUtils.isNotBlank(dn)) {
-
-            // attempt to locate the cn
-            if (dn.startsWith("CN=")) {
-                cn = StringUtils.substringBetween(dn, "CN=", ",");
-            } else if (dn.startsWith("/CN=")) {
-                cn = StringUtils.substringBetween(dn, "CN=", "/");
-            } else if (dn.startsWith("C=") || dn.startsWith("/C=")) {
-                cn = StringUtils.substringAfter(dn, "CN=");
-            } else if (dn.startsWith("/") && StringUtils.contains(dn, "CN=")) {
-                cn = StringUtils.substringAfter(dn, "CN=");
-            }
-
-            // attempt to get the username from the cn
-            if (StringUtils.isNotBlank(cn)) {
-                if (cn.endsWith(")")) {
-                    username = StringUtils.substringBetween(cn, "(", ")");
-                } else if (cn.contains(" ")) {
-                    username = StringUtils.substringAfterLast(cn, " ");
+            // determine the separate
+            final String separator = StringUtils.indexOfIgnoreCase(dn, "/cn=") > 0 ? "/" : ",";
+            
+            // attempt to locate the cd
+            final String cnPattern = "cn=";
+            final int cnIndex = StringUtils.indexOfIgnoreCase(dn, cnPattern);
+            if (cnIndex >= 0) {
+                int separatorIndex = StringUtils.indexOf(dn, separator, cnIndex);
+                if (separatorIndex > 0) {
+                    username = StringUtils.substring(dn, cnIndex + cnPattern.length(), separatorIndex);
                 } else {
-                    username = cn;
+                    username = StringUtils.substring(dn, cnIndex + cnPattern.length());
                 }
             }
         }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java
index 0bc6e99..2a24e0b 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/KeyDAO.java
@@ -16,18 +16,28 @@
  */
 package org.apache.nifi.admin.dao;
 
+import org.apache.nifi.key.Key;
+
 /**
  * Key data access.
  */
 public interface KeyDAO {
 
     /**
-     * Gets the key for the specified user identity. Returns null if no key exists for the user identity.
+     * Gets the key for the specified user identity. Returns null if no key exists for the key id.
      *
-     * @param identity The user identity
+     * @param id The key id
+     * @return The key or null
+     */
+    Key findKeyById(int id);
+
+    /**
+     * Gets the latest key for the specified identity. Returns null if no key exists for the user identity.
+     *
+     * @param identity The identity
      * @return The key or null
      */
-    String getKey(String identity);
+    Key findLatestKeyByIdentity(String identity);
 
     /**
      * Creates a key for the specified user identity.
@@ -35,5 +45,5 @@ public interface KeyDAO {
      * @param identity The user identity
      * @return The key
      */
-    String createKey(String identity);
+    Key createKey(String identity);
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java
index 994e95b..f4bdc1d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java
@@ -20,17 +20,23 @@ import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
 import java.util.UUID;
 import org.apache.nifi.admin.RepositoryUtils;
 import org.apache.nifi.admin.dao.DataAccessException;
 import org.apache.nifi.admin.dao.KeyDAO;
+import org.apache.nifi.key.Key;
 
 /**
  *
  */
 public class StandardKeyDAO implements KeyDAO {
 
-    private static final String SELECT_KEY_FOR_USER = "SELECT KEY "
+    private static final String SELECT_KEY_FOR_USER_BY_ID = "SELECT ID, IDENTITY, KEY "
+            + "FROM KEY "
+            + "WHERE ID = ?";
+
+    private static final String SELECT_KEY_FOR_USER_BY_IDENTITY = "SELECT ID, IDENTITY, KEY "
             + "FROM KEY "
             + "WHERE IDENTITY = ?";
 
@@ -47,18 +53,49 @@ public class StandardKeyDAO implements KeyDAO {
     }
 
     @Override
-    public String getKey(String identity) {
+    public Key findKeyById(int id) {
+        Key key = null;
+
+        PreparedStatement statement = null;
+        ResultSet rs = null;
+        try {
+            // add each authority for the specified user
+            statement = connection.prepareStatement(SELECT_KEY_FOR_USER_BY_ID);
+            statement.setInt(1, id);
+
+            // execute the query
+            rs = statement.executeQuery();
+
+            // if the key was found, add it
+            if (rs.next()) {
+                key = new Key();
+                key.setId(rs.getInt("ID"));
+                key.setIdentity(rs.getString("IDENTITY"));
+                key.setKey(rs.getString("KEY"));
+            }
+        } catch (SQLException sqle) {
+            throw new DataAccessException(sqle);
+        } finally {
+            RepositoryUtils.closeQuietly(rs);
+            RepositoryUtils.closeQuietly(statement);
+        }
+
+        return key;
+    }
+
+    @Override
+    public Key findLatestKeyByIdentity(String identity) {
         if (identity == null) {
             throw new IllegalArgumentException("Specified identity cannot be null.");
         }
 
-        String key = null;
+        Key key = null;
 
         PreparedStatement statement = null;
         ResultSet rs = null;
         try {
             // add each authority for the specified user
-            statement = connection.prepareStatement(SELECT_KEY_FOR_USER);
+            statement = connection.prepareStatement(SELECT_KEY_FOR_USER_BY_IDENTITY);
             statement.setString(1, identity);
 
             // execute the query
@@ -66,7 +103,10 @@ public class StandardKeyDAO implements KeyDAO {
 
             // if the key was found, add it
             if (rs.next()) {
-                key = rs.getString("KEY");
+                key = new Key();
+                key.setId(rs.getInt("ID"));
+                key.setIdentity(rs.getString("IDENTITY"));
+                key.setKey(rs.getString("KEY"));
             }
         } catch (SQLException sqle) {
             throw new DataAccessException(sqle);
@@ -79,20 +119,27 @@ public class StandardKeyDAO implements KeyDAO {
     }
 
     @Override
-    public String createKey(final String identity) {
+    public Key createKey(final String identity) {
         PreparedStatement statement = null;
         ResultSet rs = null;
         try {
-            final String key = UUID.randomUUID().toString();
+            final String keyValue = UUID.randomUUID().toString();
 
             // add each authority for the specified user
-            statement = connection.prepareStatement(INSERT_KEY);
+            statement = connection.prepareStatement(INSERT_KEY, Statement.RETURN_GENERATED_KEYS);
             statement.setString(1, identity);
-            statement.setString(2, key);
+            statement.setString(2, keyValue);
 
             // insert the key
             int updateCount = statement.executeUpdate();
-            if (updateCount == 1) {
+            rs = statement.getGeneratedKeys();
+
+            // verify the results
+            if (updateCount == 1 && rs.next()) {
+                final Key key = new Key();
+                key.setId(rs.getInt(1));
+                key.setIdentity(identity);
+                key.setKey(keyValue);
                 return key;
             } else {
                 throw new DataAccessException("Unable to add key for user.");

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java
index 9346625..ae64c41 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/KeyService.java
@@ -16,6 +16,8 @@
  */
 package org.apache.nifi.admin.service;
 
+import org.apache.nifi.key.Key;
+
 /**
  * Supports retrieving and issues keys for signing user tokens.
  */
@@ -24,10 +26,10 @@ public interface KeyService {
     /**
      * Gets a key for the specified user identity. Returns null if the user has not had a key issued
      *
-     * @param identity The user identity
+     * @param id The key id
      * @return The key or null
      */
-    String getKey(String identity);
+    Key getKey(int id);
 
     /**
      * Gets a key for the specified user identity. If a key does not exist, one will be created.
@@ -36,5 +38,5 @@ public interface KeyService {
      * @return The key
      * @throws AdministrationException if it failed to get/create the key
      */
-    String getOrCreateKey(String identity);
+    Key getOrCreateKey(String identity);
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyAction.java
deleted file mode 100644
index f12b1ef..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyAction.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.admin.service.action;
-
-import org.apache.nifi.admin.dao.DAOFactory;
-import org.apache.nifi.authorization.AuthorityProvider;
-
-import org.apache.nifi.admin.dao.KeyDAO;
-
-/**
- * Gets a key for the specified user identity.
- */
-public class GetKeyAction implements AdministrationAction<String> {
-
-    private final String identity;
-
-    public GetKeyAction(String identity) {
-        this.identity = identity;
-    }
-
-    @Override
-    public String execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) {
-        final KeyDAO keyDao = daoFactory.getKeyDAO();
-        return keyDao.getKey(identity);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdAction.java
new file mode 100644
index 0000000..8763b9d
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdAction.java
@@ -0,0 +1,42 @@
+/*
+ * 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.admin.service.action;
+
+import org.apache.nifi.admin.dao.DAOFactory;
+import org.apache.nifi.authorization.AuthorityProvider;
+
+import org.apache.nifi.admin.dao.KeyDAO;
+import org.apache.nifi.key.Key;
+
+/**
+ * Gets a key for the specified key id.
+ */
+public class GetKeyByIdAction implements AdministrationAction<Key> {
+
+    private final int id;
+
+    public GetKeyByIdAction(int id) {
+        this.id = id;
+    }
+
+    @Override
+    public Key execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) {
+        final KeyDAO keyDao = daoFactory.getKeyDAO();
+        return keyDao.findKeyById(id);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdentityAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdentityAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdentityAction.java
new file mode 100644
index 0000000..9bcb0b3
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetKeyByIdentityAction.java
@@ -0,0 +1,42 @@
+/*
+ * 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.admin.service.action;
+
+import org.apache.nifi.admin.dao.DAOFactory;
+import org.apache.nifi.authorization.AuthorityProvider;
+
+import org.apache.nifi.admin.dao.KeyDAO;
+import org.apache.nifi.key.Key;
+
+/**
+ * Gets a key for the specified key id.
+ */
+public class GetKeyByIdentityAction implements AdministrationAction<Key> {
+
+    private final String identity;
+
+    public GetKeyByIdentityAction(String identity) {
+        this.identity = identity;
+    }
+
+    @Override
+    public Key execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) {
+        final KeyDAO keyDao = daoFactory.getKeyDAO();
+        return keyDao.findLatestKeyByIdentity(identity);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetOrCreateKeyAction.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetOrCreateKeyAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetOrCreateKeyAction.java
index 209cbcd..bb85b6f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetOrCreateKeyAction.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetOrCreateKeyAction.java
@@ -20,11 +20,12 @@ import org.apache.nifi.admin.dao.DAOFactory;
 import org.apache.nifi.authorization.AuthorityProvider;
 
 import org.apache.nifi.admin.dao.KeyDAO;
+import org.apache.nifi.key.Key;
 
 /**
  * Gets a key for the specified user identity.
  */
-public class GetOrCreateKeyAction implements AdministrationAction<String> {
+public class GetOrCreateKeyAction implements AdministrationAction<Key> {
 
     private final String identity;
 
@@ -33,10 +34,10 @@ public class GetOrCreateKeyAction implements AdministrationAction<String> {
     }
 
     @Override
-    public String execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) {
+    public Key execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) {
         final KeyDAO keyDao = daoFactory.getKeyDAO();
 
-        String key = keyDao.getKey(identity);
+        Key key = keyDao.findLatestKeyByIdentity(identity);
         if (key == null) {
             key = keyDao.createKey(identity);
         }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java
index 7dff9d8..ca0a124 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardKeyService.java
@@ -19,7 +19,7 @@ package org.apache.nifi.admin.service.impl;
 import org.apache.nifi.admin.dao.DataAccessException;
 import org.apache.nifi.admin.service.AdministrationException;
 import org.apache.nifi.admin.service.KeyService;
-import org.apache.nifi.admin.service.action.GetKeyAction;
+import org.apache.nifi.admin.service.action.GetKeyByIdAction;
 import org.apache.nifi.admin.service.action.GetOrCreateKeyAction;
 import org.apache.nifi.admin.service.transaction.Transaction;
 import org.apache.nifi.admin.service.transaction.TransactionBuilder;
@@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
+import org.apache.nifi.key.Key;
 
 /**
  *
@@ -44,19 +45,17 @@ public class StandardKeyService implements KeyService {
     private TransactionBuilder transactionBuilder;
 
     @Override
-    public String getKey(String identity) {
-        // TODO: Change this service to look up by "key ID" instead of identity
-        // TODO: Change the return type to a Key POJO to support key rotation
+    public Key getKey(int id) {
         Transaction transaction = null;
-        String key = null;
+        Key key = null;
 
         readLock.lock();
         try {
             // start the transaction
             transaction = transactionBuilder.start();
 
-            // seed the accounts
-            GetKeyAction addActions = new GetKeyAction(identity);
+            // get the key
+            GetKeyByIdAction addActions = new GetKeyByIdAction(id);
             key = transaction.execute(addActions);
 
             // commit the transaction
@@ -76,11 +75,9 @@ public class StandardKeyService implements KeyService {
     }
 
     @Override
-    public String getOrCreateKey(String identity) {
-        // TODO: Change this service to look up by "key ID" instead of identity
-        // TODO: Change the return type to a Key POJO to support key rotation
+    public Key getOrCreateKey(String identity) {
         Transaction transaction = null;
-        String key = null;
+        Key key = null;
 
         writeLock.lock();
         try {

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java
new file mode 100644
index 0000000..b7158c2
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/key/Key.java
@@ -0,0 +1,69 @@
+/*
+ * 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.key;
+
+import java.io.Serializable;
+
+/**
+ * An signing key for a NiFi user.
+ */
+public class Key implements Serializable {
+
+    private int id;
+    private String identity;
+    private String key;
+
+    /**
+     * The key id.
+     * 
+     * @return the id
+     */
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    /**
+     * The identity of the user this key is associated with.
+     * 
+     * @return the identity
+     */
+    public String getIdentity() {
+        return identity;
+    }
+
+    public void setIdentity(String identity) {
+        this.identity = identity;
+    }
+
+    /**
+     * The signing key.
+     * 
+     * @return the signing key
+     */
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
index 2e1c44e..27d7d29 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -55,6 +55,7 @@ import org.apache.nifi.web.api.dto.RevisionDTO;
 import org.apache.nifi.web.api.entity.AccessStatusEntity;
 import org.apache.nifi.web.api.entity.AccessConfigurationEntity;
 import org.apache.nifi.web.api.request.ClientIdParameter;
+import org.apache.nifi.web.security.InvalidAuthenticationException;
 import org.apache.nifi.web.security.ProxiedEntitiesUtils;
 import org.apache.nifi.web.security.UntrustedProxyException;
 import org.apache.nifi.web.security.jwt.JwtService;
@@ -185,55 +186,52 @@ public class AccessResource extends ApplicationResource {
                     accessStatus.setStatus(AccessStatusDTO.Status.UNKNOWN.name());
                     accessStatus.setMessage("No credentials supplied, unknown user.");
                 } else {
-                    // Extract the Base64 encoded token from the Authorization header
-                    final String token = StringUtils.substringAfterLast(authorization, " ");
-
                     try {
+                        // Extract the Base64 encoded token from the Authorization header
+                        final String token = StringUtils.substringAfterLast(authorization, " ");
                         final String principal = jwtService.getAuthenticationFromToken(token);
 
-                        // ensure we have something we can work with (certificate or credentials)
-                        if (principal == null) {
-                            throw new IllegalArgumentException("The specific token is not valid.");
-                        } else {
-                            // set the user identity
-                            accessStatus.setIdentity(principal);
-                            accessStatus.setUsername(CertificateUtils.extractUsername(principal));
+                        // set the user identity
+                        accessStatus.setIdentity(principal);
+                        accessStatus.setUsername(CertificateUtils.extractUsername(principal));
 
-                            // without a certificate, this is not a proxied request
-                            final List<String> chain = Arrays.asList(principal);
+                        // without a certificate, this is not a proxied request
+                        final List<String> chain = Arrays.asList(principal);
 
-                            // check authorization for this user
-                            checkAuthorization(chain);
+                        // check authorization for this user
+                        checkAuthorization(chain);
 
-                            // no issues with authorization
-                            accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
-                            accessStatus.setMessage("Account is active and authorized");
-                        }
+                        // no issues with authorization
+                        accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
+                        accessStatus.setMessage("Account is active and authorized");
                     } catch (JwtException e) {
-                        // TODO: Handle the exception from a failed JWT verification
-                        throw new AccessDeniedException("The JWT could not be verified", e);
+                        throw new InvalidAuthenticationException(e.getMessage(), e);
                     }
                 }
             } else {
-                final AuthenticationResponse authenticationResponse = certificateIdentityProvider.authenticate(certificates);
-
-                // get the proxy chain and ensure its populated
-                final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(httpServletRequest, authenticationResponse.getIdentity());
-                if (proxyChain.isEmpty()) {
-                    logger.error(String.format("Unable to parse the proxy chain %s from the incoming request.", authenticationResponse.getIdentity()));
-                    throw new IllegalArgumentException("Unable to determine the user from the incoming request.");
-                }
+                try {
+                    final AuthenticationResponse authenticationResponse = certificateIdentityProvider.authenticate(certificates);
+
+                    // get the proxy chain and ensure its populated
+                    final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(httpServletRequest, authenticationResponse.getIdentity());
+                    if (proxyChain.isEmpty()) {
+                        logger.error(String.format("Unable to parse the proxy chain %s from the incoming request.", authenticationResponse.getIdentity()));
+                        throw new IllegalArgumentException("Unable to determine the user from the incoming request.");
+                    }
 
-                // ensure the proxy chain is authorized
-                checkAuthorization(proxyChain);
+                    // set the user identity
+                    accessStatus.setIdentity(proxyChain.get(0));
+                    accessStatus.setUsername(CertificateUtils.extractUsername(proxyChain.get(0)));
 
-                // set the user identity
-                accessStatus.setIdentity(proxyChain.get(0));
-                accessStatus.setUsername(CertificateUtils.extractUsername(proxyChain.get(0)));
+                    // ensure the proxy chain is authorized
+                    checkAuthorization(proxyChain);
 
-                // no issues with authorization
-                accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
-                accessStatus.setMessage("Account is active and authorized");
+                    // no issues with authorization
+                    accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
+                    accessStatus.setMessage("Account is active and authorized");
+                } catch (final IllegalArgumentException iae) {
+                    throw new InvalidAuthenticationException(iae.getMessage(), iae);
+                }
             }
         } catch (final UsernameNotFoundException unfe) {
             accessStatus.setStatus(AccessStatusDTO.Status.UNREGISTERED.name());
@@ -323,20 +321,20 @@ public class AccessResource extends ApplicationResource {
                 final AuthenticationResponse authenticationResponse = loginIdentityProvider.authenticate(new LoginCredentials(username, password));
                 final long maxExpiration = TimeUnit.MILLISECONDS.convert(12, TimeUnit.HOURS);
                 final long minExpiration = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES);
-                
+
                 long expiration = authenticationResponse.getExpiration();
                 if (expiration > maxExpiration) {
                     expiration = maxExpiration;
-                    
-                    logger.warn(String.format("Max token expiration exceeded. Setting expiration to %s from %s for %s", expiration, 
+
+                    logger.warn(String.format("Max token expiration exceeded. Setting expiration to %s from %s for %s", expiration,
                             authenticationResponse.getExpiration(), authenticationResponse.getIdentity()));
                 } else if (expiration < minExpiration) {
                     expiration = minExpiration;
-                    
-                    logger.warn(String.format("Min token expiration not met. Setting expiration to %s from %s for %s", expiration, 
+
+                    logger.warn(String.format("Min token expiration not met. Setting expiration to %s from %s for %s", expiration,
                             authenticationResponse.getExpiration(), authenticationResponse.getIdentity()));
                 }
-                
+
                 // create the authentication token
                 // TODO: Some Spring beans return "" for getClass().getSimpleName(). Using getName() temporarily
                 loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getIdentity(), expiration, loginIdentityProvider.getClass().getName());

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/InvalidAuthenticationExceptionMapper.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/InvalidAuthenticationExceptionMapper.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/InvalidAuthenticationExceptionMapper.java
new file mode 100644
index 0000000..14d5139
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/InvalidAuthenticationExceptionMapper.java
@@ -0,0 +1,44 @@
+/*
+ * 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.api.config;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.web.security.InvalidAuthenticationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Maps access denied exceptions into a client response.
+ */
+@Provider
+public class InvalidAuthenticationExceptionMapper implements ExceptionMapper<InvalidAuthenticationException> {
+
+    private static final Logger logger = LoggerFactory.getLogger(InvalidAuthenticationExceptionMapper.class);
+
+    @Override
+    public Response toResponse(InvalidAuthenticationException exception) {
+        if (logger.isDebugEnabled()) {
+            logger.debug(StringUtils.EMPTY, exception);
+        }
+
+        return Response.status(Response.Status.UNAUTHORIZED).entity(exception.getMessage()).type("text/plain").build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
index 73929d8..9f3d2f5 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
@@ -255,6 +255,7 @@
 
     <!-- exception mapping -->
     <bean class="org.apache.nifi.web.api.config.AccessDeniedExceptionMapper" scope="singleton"/>
+    <bean class="org.apache.nifi.web.api.config.InvalidAuthenticationExceptionMapper" scope="singleton"/>
     <bean class="org.apache.nifi.web.api.config.AuthenticationCredentialsNotFoundExceptionMapper" scope="singleton"/>
     <bean class="org.apache.nifi.web.api.config.AccountNotFoundExceptionMapper" scope="singleton"/>
     <bean class="org.apache.nifi.web.api.config.AdministrationExceptionMapper" scope="singleton"/>

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/InvalidAuthenticationException.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/InvalidAuthenticationException.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/InvalidAuthenticationException.java
new file mode 100644
index 0000000..1065152
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/InvalidAuthenticationException.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ * Thrown if the authentication of a given request is invalid. For instance,
+ * an expired certificate or token.
+ */
+public class InvalidAuthenticationException extends AuthenticationException {
+
+    public InvalidAuthenticationException(String msg) {
+        super(msg);
+    }
+
+    public InvalidAuthenticationException(String msg, Throwable t) {
+        super(msg, t);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.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/NiFiAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
index ec34ace..f09d610 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
@@ -36,7 +36,6 @@ import org.slf4j.LoggerFactory;
 import org.springframework.security.authentication.AccountStatusException;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.AuthenticationServiceException;
-import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -134,8 +133,8 @@ public abstract class NiFiAuthenticationFilter implements Filter {
                 response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                 out.println("Access is denied.");
             }
-        } else if (ae instanceof BadCredentialsException) {
-            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+        } else if (ae instanceof InvalidAuthenticationException) {
+            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
             out.println(ae.getMessage());
         } else if (ae instanceof AccountStatusException) {
             response.setStatus(HttpServletResponse.SC_FORBIDDEN);

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.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/JwtAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
index dea5bba..20675fb 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
@@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.util.Arrays;
+import org.apache.nifi.web.security.InvalidAuthenticationException;
 
 /**
  */
@@ -49,7 +50,6 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
         // TODO: Refactor request header extraction logic to shared utility as it is duplicated in AccessResource
 
         // get the principal out of the user token
-        // look for an authorization token
         final String authorization = request.getHeader(AUTHORIZATION);
 
         // if there is no authorization header, we don't know the user
@@ -61,9 +61,6 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
 
             try {
                 final String jwtPrincipal = jwtService.getAuthenticationFromToken(token);
-                if (jwtPrincipal == null) {
-                    return null;
-                }
 
                 if (isNewAccountRequest(request)) {
                     return new NewAccountAuthenticationRequestToken(new NewAccountRequest(Arrays.asList(jwtPrincipal), getJustification(request)));
@@ -71,9 +68,7 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
                     return new NiFiAuthenticationRequestToken(Arrays.asList(jwtPrincipal));
                 }
             } catch (JwtException e) {
-                // TODO: Is this the correct way to handle an unverified token?
-                logger.error("Could not verify JWT", e);
-                return null;
+                throw new InvalidAuthenticationException(e.getMessage(), e);
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/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 f006e5b..9635354 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
@@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory;
 
 import java.nio.charset.StandardCharsets;
 import java.util.Calendar;
+import org.apache.nifi.key.Key;
 
 /**
  *
@@ -88,17 +89,16 @@ public class JwtService {
                 public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {
                     final String identity = claims.getSubject();
 
-                    // TODO: Currently the kid field is identical to identity, but will be a unique key ID when key rotation is implemented
-                    final String keyId = claims.get(KEY_ID_CLAIM, String.class);
-                    // The key is unique per identity and should be retrieved from the key service
-                    final String key = keyService.getKey(keyId);
+                    // Get the key based on the key id in the claims
+                    final Integer keyId = claims.get(KEY_ID_CLAIM, Integer.class);
+                    final Key key = keyService.getKey(keyId);
 
                     // Ensure we were able to find a key that was previously issued by this key service for this user
-                    if (key == null) {
+                    if (key == null || key.getKey() == null) {
                         throw new UnsupportedJwtException("Unable to determine signing key for " + identity + " [kid: " + keyId + "]");
                     }
 
-                    return key.getBytes(StandardCharsets.UTF_8);
+                    return key.getKey().getBytes(StandardCharsets.UTF_8);
                 }
             }).parseClaimsJws(base64EncodedToken);
         } catch (final MalformedJwtException | UnsupportedJwtException | SignatureException | ExpiredJwtException | IllegalArgumentException | AdministrationException e) {
@@ -137,21 +137,19 @@ public class JwtService {
 
         try {
             // Get/create the key for this user
-            final String key = keyService.getOrCreateKey(identity);
-            final byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
+            final Key key = keyService.getOrCreateKey(identity);
+            final byte[] keyBytes = key.getKey().getBytes(StandardCharsets.UTF_8);
 
             logger.trace("Generating JWT for " + authenticationToken);
 
             // TODO: Implement "jti" claim with nonce to prevent replay attacks and allow blacklisting of revoked tokens
 
-            // TODO: Change kid field to key ID when KeyService is refactored
-
             // Build the token
             return Jwts.builder().setSubject(identity)
                     .setIssuer(authenticationToken.getIssuer())
                     .setAudience(authenticationToken.getIssuer())
                     .claim(USERNAME_CLAIM, username)
-                    .claim(KEY_ID_CLAIM, identity)
+                    .claim(KEY_ID_CLAIM, key.getId())
                     .setExpiration(expiration.getTime())
                     .setIssuedAt(Calendar.getInstance().getTime())
                     .signWith(SIGNATURE_ALGORITHM, keyBytes).compact();

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.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/x509/X509AuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
index e626f74..dd7d47e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
@@ -21,6 +21,7 @@ import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import org.apache.nifi.authentication.AuthenticationResponse;
+import org.apache.nifi.web.security.InvalidAuthenticationException;
 import org.apache.nifi.web.security.NiFiAuthenticationFilter;
 import org.apache.nifi.web.security.ProxiedEntitiesUtils;
 import org.apache.nifi.web.security.token.NewAccountAuthenticationRequestToken;
@@ -28,7 +29,6 @@ import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
 import org.apache.nifi.web.security.user.NewAccountRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.security.authentication.BadCredentialsException;
 
 /**
  * Custom X509 filter that will inspect the HTTP headers for a proxied user before extracting the user details from the client certificate.
@@ -58,7 +58,7 @@ public class X509AuthenticationFilter extends NiFiAuthenticationFilter {
         try {
             authenticationResponse = certificateIdentityProvider.authenticate(certificates);
         } catch (final IllegalArgumentException iae) {
-            throw new BadCredentialsException(iae.getMessage(), iae);
+            throw new InvalidAuthenticationException(iae.getMessage(), iae);
         }
 
         final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(request, authenticationResponse.getIdentity());

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
index c9107b9..ea3e122 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
@@ -22,10 +22,12 @@ import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import org.apache.nifi.key.Key;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.when;
 
 /**
@@ -117,9 +119,14 @@ public class JwtServiceTest {
 
     @Before
     public void setUp() throws Exception {
+        final Key key = new Key();
+        key.setId(0);
+        key.setIdentity(HMAC_SECRET);
+        key.setKey(HMAC_SECRET);
+        
         mockKeyService = Mockito.mock(KeyService.class);
-        when(mockKeyService.getKey(anyString())).thenReturn(HMAC_SECRET);
-        when(mockKeyService.getOrCreateKey(anyString())).thenReturn(HMAC_SECRET);
+        when(mockKeyService.getKey(anyInt())).thenReturn(key);
+        when(mockKeyService.getOrCreateKey(anyString())).thenReturn(key);
         jwtService = new JwtService(mockKeyService);
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.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-header.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js
index eab5297..7d63534 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js
@@ -142,7 +142,7 @@ nf.CanvasHeader = (function () {
             });
 
             // show the login link if supported and user is currently anonymous
-            var isAnonymous = $('#current-user').text() === nf.Canvas.ANONYMOUS_USER_TEXT;
+            var isAnonymous = $('#current-user').text() === nf.Common.ANONYMOUS_USER_TEXT;
             if (supportsLogin === true && isAnonymous) {
                 // login link
                 $('#login-link').click(function () {

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/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 09a8212..c9498fe 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
@@ -936,7 +936,6 @@ nf.Canvas = (function () {
     };
 
     return {
-        ANONYMOUS_USER_TEXT: 'Anonymous user',
         CANVAS_OFFSET: 0,
         /**
          * Determines if the current broswer supports SVG.
@@ -1056,17 +1055,8 @@ 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();
+                        // set the anonymous user label
+                        nf.Common.setAnonymousUserLabel();
                     }
                     deferred.resolve();
                 }).fail(function (xhr, status, error) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/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 89db6ce..bd39a44 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
@@ -96,8 +96,10 @@ nf.Login = (function () {
                 'password': $('#password').val()
             }
         }).done(function (jwt) {
-            // store the jwt and reload the page
-            nf.Storage.setItem('jwt', jwt, nf.Common.getJwtExpiration(jwt));
+            // get the payload and store the token with the appropirate expiration
+            var token = nf.Common.getJwtPayload(jwt);
+            var expiration = parseInt(token['exp'], 10) * nf.Common.MILLIS_PER_SECOND;
+            nf.Storage.setItem('jwt', jwt, expiration);
 
             // check to see if they actually have access now
             $.ajax({
@@ -112,8 +114,7 @@ nf.Login = (function () {
                     nf.Common.scheduleTokenRefresh();
             
                     // show the user
-                    var user = nf.Common.getJwtSubject(jwt);
-                    $('#nifi-user-submit-justification').text(user);
+                    $('#nifi-user-submit-justification').text(token['preferred_username']);
 
                     // show the registration form
                     initializeNiFiRegistration();
@@ -133,8 +134,7 @@ nf.Login = (function () {
                 nf.Common.scheduleTokenRefresh();
 
                 // show the user
-                var user = nf.Common.getJwtSubject(jwt);
-                $('#nifi-user-submit-justification').text(user);
+                $('#nifi-user-submit-justification').text(token['preferred_username']);
 
                 if (xhr.status === 401) {
                     initializeNiFiRegistration();

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/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 3be6b91..9202819 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
@@ -54,9 +54,17 @@ $(document).ready(function () {
     // include jwt when possible
     $.ajaxSetup({
         'beforeSend': function(xhr) {
+            var hadToken = nf.Storage.hasItem('jwt');
+            
+            // get the token to include in all requests
             var token = nf.Storage.getItem('jwt');
-            if (token) {
+            if (token !== null) {
                 xhr.setRequestHeader('Authorization', 'Bearer ' + token);
+            } else {
+                // if the current user was logged in with a token and the token just expired, reload
+                if (hadToken === true) {
+                    return false;
+                }
             }
         }
     });
@@ -83,6 +91,8 @@ nf.Common = (function () {
     var tokenRefreshInterval = null;
     
     return {
+        ANONYMOUS_USER_TEXT: 'Anonymous user',
+        
         config: {
             sensitiveText: 'Sensitive value set',
             tooltipConfig: {
@@ -100,9 +110,6 @@ nf.Common = (function () {
                     at: 'top right',
                     my: 'bottom left'
                 }
-            },
-            urls: {
-                token: '../nifi-api/access/token'
             }
         },
 
@@ -148,7 +155,7 @@ nf.Common = (function () {
             }
             
             // set the interval to one hour
-            var interval = nf.Common.MILLIS_PER_HOUR;
+            var interval = 10 * nf.Common.MILLIS_PER_MINUTE;
             
             var checkExpiration = function () {
                 var expiration = nf.Storage.getItemExpiration('jwt');
@@ -161,13 +168,16 @@ nf.Common = (function () {
                     // get the time remainging plus a little bonus time to reload the token
                     var timeRemaining = expirationDate.valueOf() - now.valueOf() - nf.Common.MILLIS_PER_MINUTE;
                     if (timeRemaining < interval) {
-                        // if the token will expire before the next interval minus some bonus time, refresh now
-                        $.ajax({
-                            type: 'POST',
-                            url: nf.Common.config.urls.token
-                        }).done(function (jwt) {
-                            nf.Storage.setItem('jwt', jwt, nf.Common.getJwtExpiration(jwt));
-                        });
+                        if ($('#current-user').text() !== nf.Common.ANONYMOUS_USER_TEXT && !$('#anonymous-user-alert').is(':visible')) {
+                            // if the token will expire before the next interval minus some bonus time, notify the user to re-login
+                            $('#anonymous-user-alert').show().qtip($.extend({}, nf.Common.config.tooltipConfig, {
+                                content: 'Your session will expire soon. Please log in again to avoid being automatically logged out.',
+                                position: {
+                                    my: 'top right',
+                                    at: 'bottom left'
+                                }
+                            }));
+                        }
                     }
                 }
             };
@@ -180,56 +190,46 @@ nf.Common = (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}
+         * Sets the anonymous user label.
          */
-        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 {
-                    '';
-                }
+        setAnonymousUserLabel: function () {
+            var anonymousUserAlert = $('#anonymous-user-alert');
+            if (anonymousUserAlert.data('qtip')) {
+                anonymousUserAlert.qtip('api').destroy(true);
             }
+                        
+            // alert user's of anonymous access
+            anonymousUserAlert.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'
+                }
+            }));
 
-            return '';
+            // render the anonymous user text
+            $('#current-user').text(nf.Common.ANONYMOUS_USER_TEXT).show();  
         },
 
         /**
-         * Extracts the expiration from the specified jwt. If the jwt is not as expected
-         * a null value is returned.
+         * Extracts the subject from the specified jwt. If the jwt is not as expected
+         * an empty string is returned.
          * 
          * @param {string} jwt
-         * @returns {integer}
+         * @returns {string}
          */
-        getJwtExpiration: function (jwt) {
+        getJwtPayload: function (jwt) {
             if (nf.Common.isDefinedAndNotNull(jwt)) {
                 var segments = jwt.split(/\./);
                 if (segments.length !== 3) {
-                    return null;
+                    return '';
                 }
 
                 var rawPayload = $.base64.atob(segments[1]);
                 var payload = JSON.parse(rawPayload);
 
-                if (nf.Common.isDefinedAndNotNull(payload['exp'])) {
-                    try {
-                        // jwt exp is in seconds
-                        return parseInt(payload['exp'], 10) * nf.Common.MILLIS_PER_SECOND;
-                    } catch (e) {
-                        return null;
-                    }
+                if (nf.Common.isDefinedAndNotNull(payload)) {
+                    return payload;
                 } else {
                     return null;
                 }
@@ -313,6 +313,28 @@ nf.Common = (function () {
          * @argument {string} error     The error
          */
         handleAjaxError: function (xhr, status, error) {
+            if (status === 'canceled') {
+                if ($('#splash').is(':visible')) {
+                    $('#message-title').text('Session Expired');
+                    $('#message-content').text('Your session has expired. Please reload to log in again.');
+
+                    // show the error pane
+                    $('#message-pane').show();
+
+                    // close the canvas
+                    nf.Common.closeCanvas();
+                } else {
+                    nf.Dialog.showOkDialog({
+                        dialogContent: 'Your session has expired. Please press Ok to log in again.',
+                        overlayBackground: false,
+                        okHandler: function () {
+                            window.location = '/nifi';
+                        }
+                    });
+                }
+                return;
+            }
+            
             // if an error occurs while the splash screen is visible close the canvas show the error message
             if ($('#splash').is(':visible')) {
                 if (xhr.status === 401) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
index 2d1183f..d305e4d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
@@ -23,15 +23,6 @@ $(document).ready(function () {
 
     // configure the ok dialog
     $('#nf-ok-dialog').modal({
-        buttons: [{
-                buttonText: 'Ok',
-                handler: {
-                    click: function () {
-                        // close the dialog
-                        $('#nf-ok-dialog').modal('hide');
-                    }
-                }
-            }],
         handler: {
             close: function () {
                 // clear the content
@@ -77,6 +68,20 @@ nf.Dialog = (function () {
             var content = $('<p></p>').append(options.dialogContent);
             $('#nf-ok-dialog-content').append(content);
 
+            // update the button model
+            $('#nf-ok-dialog').modal('setButtonModel', [{
+                buttonText: 'Ok',
+                handler: {
+                    click: function () {
+                        // close the dialog
+                        $('#nf-ok-dialog').modal('hide');
+                        if (typeof options.okHandler === 'function') {
+                            options.okHandler.call(this);
+                        }
+                    }
+                }
+            }]);
+
             // show the dialog
             $('#nf-ok-dialog').modal('setHeaderText', options.headerText).modal('setOverlayBackground', options.overlayBackground).modal('show');
         },

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js
index cdb2e9e..7b0c3f6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js
@@ -42,32 +42,18 @@ nf.Storage = (function () {
     };
     
     /**
-     * If the item at key is not expired, the value of field is returned. Otherwise, null.
+     * Gets an enty for the key. The entry expiration is not checked.
      * 
      * @param {string} key
-     * @param {string} field
-     * @return {object} the value
      */
-    var getEntryField = function (key, field) {
+    var getEntry = function (key) {
         try {
             // parse the entry
             var entry = JSON.parse(localStorage.getItem(key));
 
             // ensure the entry and item are present
             if (nf.Common.isDefinedAndNotNull(entry)) {
-                
-                // if the entry is expired, drop it and return null
-                if (checkExpiration(entry)) {
-                    nf.Storage.removeItem(key);
-                    return null;
-                }
-
-                // if the entry has the specified field return its value
-                if (nf.Common.isDefinedAndNotNull(entry[field])) {
-                    return entry[field];
-                } else {
-                    return null;
-                }
+                return entry;
             } else {
                 return null;
             }
@@ -75,7 +61,7 @@ nf.Storage = (function () {
             return null;
         }
     };
-
+    
     return {
         /**
          * Initializes the storage. Items will be persisted for two days. Once the scripts runs
@@ -86,11 +72,9 @@ nf.Storage = (function () {
                 try {
                     // get the next item
                     var key = localStorage.key(i);
-                    var entry = JSON.parse(localStorage.getItem(key));
-
-                    if (checkExpiration(entry)) {
-                        nf.Storage.removeItem(key);
-                    }
+                    
+                    // attempt to get the item which will expire if necessary
+                    nf.Storage.getItem(key);
                 } catch (e) {
                 }
             }
@@ -118,6 +102,17 @@ nf.Storage = (function () {
         },
         
         /**
+         * Returns whether there is an entry for this key. This will not check the expiration. If
+         * the entry is expired, it will return null on a subsequent getItem invocation.
+         * 
+         * @param {string} key
+         * @returns {boolean}
+         */
+        hasItem: function (key) {
+            return getEntry(key) !== null;
+        },
+        
+        /**
          * Gets the item with the specified key. If an item with this key does
          * not exist, null is returned. If an item exists but cannot be parsed
          * or is malformed/unrecognized, null is returned.
@@ -125,18 +120,44 @@ nf.Storage = (function () {
          * @param {type} key
          */
         getItem: function (key) {
-            return getEntryField(key, 'item');
+            var entry = getEntry(key);
+            if (entry === null) {
+                return null;
+            }
+
+            // if the entry is expired, drop it and return null
+            if (checkExpiration(entry)) {
+                nf.Storage.removeItem(key);
+                return null;
+            }
+
+            // if the entry has the specified field return its value
+            if (nf.Common.isDefinedAndNotNull(entry['item'])) {
+                return entry['item'];
+            } else {
+                return null;
+            }
         },
         
         /**
-         * Gets the expiration for the specified item. If the item does not exists our could 
-         * not be parsed, returns null.
+         * Gets the expiration for the specified item. This will not check the expiration. If
+         * the entry is expired, it will return null on a subsequent getItem invocation.
          * 
          * @param {string} key
          * @returns {integer}
          */
         getItemExpiration: function (key) {
-            return getEntryField(key, 'expires');
+            var entry = getEntry(key);
+            if (entry === null) {
+                return null;
+            }
+
+            // if the entry has the specified field return its value
+            if (nf.Common.isDefinedAndNotNull(entry['expires'])) {
+                return entry['expires'];
+            } else {
+                return null;
+            }
         },
         
         /**

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java
deleted file mode 100644
index 64c48bf..0000000
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.ldap;
-
-import java.util.concurrent.TimeUnit;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.authentication.AuthenticationResponse;
-import org.apache.nifi.authentication.LoginCredentials;
-import org.apache.nifi.authentication.LoginIdentityProvider;
-import org.apache.nifi.authentication.LoginIdentityProviderConfigurationContext;
-import org.apache.nifi.authentication.LoginIdentityProviderInitializationContext;
-import org.apache.nifi.authentication.exception.IdentityAccessException;
-import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException;
-import org.apache.nifi.authorization.exception.ProviderCreationException;
-import org.apache.nifi.authorization.exception.ProviderDestructionException;
-import org.apache.nifi.util.FormatUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.ldap.CommunicationException;
-import org.springframework.security.authentication.AuthenticationServiceException;
-import org.springframework.security.authentication.BadCredentialsException;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider;
-import org.springframework.security.ldap.userdetails.LdapUserDetails;
-
-/**
- * Abstract LDAP based implementation of a login identity provider.
- */
-public abstract class AbstractLdapProvider implements LoginIdentityProvider {
-
-    private static final Logger logger = LoggerFactory.getLogger(AbstractLdapProvider.class);
-
-    private AbstractLdapAuthenticationProvider provider;
-    private long expiration;
-
-    @Override
-    public final void initialize(final LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException {
-    }
-
-    @Override
-    public final void onConfigured(final LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException {
-        final String rawExpiration = configurationContext.getProperty("Expiration Duration");
-        if (StringUtils.isBlank(rawExpiration)) {
-            throw new ProviderCreationException("The Expiration Duration must be specified.");
-        }
-
-        try {
-            expiration = FormatUtils.getTimeDuration(rawExpiration, TimeUnit.MILLISECONDS);
-        } catch (final IllegalArgumentException iae) {
-            throw new ProviderCreationException(String.format("The Expiration Duration '%s' is not a valid time duration", rawExpiration));
-        }
-
-        provider = getLdapAuthenticationProvider(configurationContext);
-    }
-
-    protected abstract AbstractLdapAuthenticationProvider getLdapAuthenticationProvider(LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException;
-
-    @Override
-    public final AuthenticationResponse authenticate(final LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException {
-        if (provider == null) {
-            throw new IdentityAccessException("The LDAP authentication provider is not initialized.");
-        }
-
-        try {
-            // perform the authentication
-            final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword());
-            final Authentication authentication = provider.authenticate(token);
-
-            // attempt to get the ldap user details to get the DN
-            if (authentication.getPrincipal() instanceof LdapUserDetails) {
-                final LdapUserDetails userDetails = (LdapUserDetails) authentication.getPrincipal();
-                return new AuthenticationResponse(userDetails.getDn(), credentials.getUsername(), expiration);
-            } else {
-                return new AuthenticationResponse(authentication.getName(), credentials.getUsername(), expiration);
-            }
-        } catch (final CommunicationException | AuthenticationServiceException e) {
-            logger.error(e.getMessage());
-            if (logger.isDebugEnabled()) {
-                logger.debug(StringUtils.EMPTY, e);
-            }
-            throw new IdentityAccessException("Unable to query the configured directory server. See the logs for additional details.", e);
-        } catch (final BadCredentialsException bce) {
-            throw new InvalidLoginCredentialsException(bce.getMessage(), bce);
-        }
-    }
-
-    @Override
-    public final void preDestruction() throws ProviderDestructionException {
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/c94d0271/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/ActiveDirectoryProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/ActiveDirectoryProvider.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/ActiveDirectoryProvider.java
deleted file mode 100644
index e43c1ee..0000000
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/ActiveDirectoryProvider.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.ldap;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.authentication.LoginIdentityProviderConfigurationContext;
-import org.apache.nifi.authorization.exception.ProviderCreationException;
-import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider;
-import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
-
-/**
- * Active Directory based implementation of a login identity provider.
- */
-public class ActiveDirectoryProvider extends AbstractLdapProvider {
-
-    @Override
-    protected AbstractLdapAuthenticationProvider getLdapAuthenticationProvider(LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException {
-
-        final String url = configurationContext.getProperty("Url");
-        if (StringUtils.isBlank(url)) {
-            throw new ProviderCreationException("The Active Directory 'Url' must be specified.");
-        }
-
-        final String domain = configurationContext.getProperty("Domain");
-        final String userSearchBase = configurationContext.getProperty("User Search Base");
-
-        final ActiveDirectoryLdapAuthenticationProvider activeDirectoryAuthenticationProvider
-                = new ActiveDirectoryLdapAuthenticationProvider(StringUtils.isBlank(domain) ? null : domain, url, StringUtils.isBlank(userSearchBase) ? null : userSearchBase);
-
-        final String userSearchFilter = configurationContext.getProperty("User Search Filter");
-        if (StringUtils.isNotBlank(userSearchFilter)) {
-            activeDirectoryAuthenticationProvider.setSearchFilter(userSearchFilter);
-        }
-
-        return activeDirectoryAuthenticationProvider;
-    }
-}


[43/51] [abbrv] nifi git commit: NIFI-655: - Ensuring the access token is not replicated when the user is already authenticated/authorized.

Posted by mc...@apache.org.
NIFI-655:
- Ensuring the access token is not replicated when the user is already authenticated/authorized.

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

Branch: refs/heads/master
Commit: a84e505bcd97c79a4c91ed7f34f18988c11bc267
Parents: 99016a8
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 30 14:47:30 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 30 14:47:30 2015 -0500

----------------------------------------------------------------------
 .../src/main/java/org/apache/nifi/web/api/AccessResource.java   | 5 ++---
 .../main/java/org/apache/nifi/web/api/ApplicationResource.java  | 5 +++++
 .../apache/nifi/web/security/jwt/JwtAuthenticationFilter.java   | 2 +-
 3 files changed, 8 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/a84e505b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
index f2b23c2..c67a314 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -58,6 +58,7 @@ import org.apache.nifi.web.api.request.ClientIdParameter;
 import org.apache.nifi.web.security.InvalidAuthenticationException;
 import org.apache.nifi.web.security.ProxiedEntitiesUtils;
 import org.apache.nifi.web.security.UntrustedProxyException;
+import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
 import org.apache.nifi.web.security.jwt.JwtService;
 import org.apache.nifi.web.security.token.LoginAuthenticationToken;
 import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
@@ -85,8 +86,6 @@ public class AccessResource extends ApplicationResource {
 
     private static final Logger logger = LoggerFactory.getLogger(AccessResource.class);
 
-    private static final String AUTHORIZATION = "Authorization";
-
     private NiFiProperties properties;
 
     private LoginIdentityProvider loginIdentityProvider;
@@ -183,7 +182,7 @@ public class AccessResource extends ApplicationResource {
             // if there is not certificate, consider a token
             if (certificates == null) {
                 // look for an authorization token
-                final String authorization = httpServletRequest.getHeader(AUTHORIZATION);
+                final String authorization = httpServletRequest.getHeader(JwtAuthenticationFilter.AUTHORIZATION);
 
                 // if there is no authorization header, we don't know the user
                 if (authorization == null) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/a84e505b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
index d0c36d4..e4afd05 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
@@ -54,6 +54,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 import org.apache.nifi.user.NiFiUser;
+import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
 import org.apache.nifi.web.security.user.NiFiUserUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -367,7 +368,11 @@ public abstract class ApplicationResource {
             // add the certificate DN to the proxy chain
             final NiFiUser user = NiFiUserUtils.getNiFiUser();
             if (user != null) {
+                // add the proxied user details
                 result.put(PROXIED_ENTITIES_CHAIN_HTTP_HEADER, ProxiedEntitiesUtils.buildProxiedEntitiesChainString(user));
+
+                // remove the access token if present, since the user is already authenticated/authorized
+                result.remove(JwtAuthenticationFilter.AUTHORIZATION);
             }
 
             // add the user's authorities (if any) to the headers

http://git-wip-us.apache.org/repos/asf/nifi/blob/a84e505b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.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/JwtAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
index 246cbd7..2f18406 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
@@ -36,7 +36,7 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
 
     private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
 
-    private static final String AUTHORIZATION = "Authorization";
+    public static final String AUTHORIZATION = "Authorization";
 
     private JwtService jwtService;
 


[02/51] [abbrv] nifi git commit: NIFI-655: - Added an endpoint for access details including configuration, creating tokens, and checking status. - Updated DTOs and client side to utilize new endpoints.

Posted by mc...@apache.org.
NIFI-655:
- Added an endpoint for access details including configuration, creating tokens, and checking status.
- Updated DTOs and client side to utilize new endpoints.

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

Branch: refs/heads/master
Commit: 7529694f23cf46f977f7f1210f91ec4636f35e3e
Parents: 9ccf61a
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 16 21:18:04 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 16 21:18:04 2015 -0500

----------------------------------------------------------------------
 .../web/api/dto/AccessConfigurationDTO.java     |  61 +++
 .../nifi/web/api/dto/AccessStatusDTO.java       | 101 +++++
 .../nifi/web/api/dto/LoginConfigurationDTO.java |  44 --
 .../api/entity/AccessConfigurationEntity.java   |  43 ++
 .../nifi/web/api/entity/AccessStatusEntity.java |  43 ++
 .../api/entity/LoginConfigurationEntity.java    |  43 --
 .../org/apache/nifi/web/NiFiServiceFacade.java  |   8 -
 .../web/NiFiWebApiSecurityConfiguration.java    |  47 +--
 .../nifi/web/StandardNiFiServiceFacade.java     |  17 -
 .../org/apache/nifi/web/api/AccessResource.java | 412 +++++++++++++++++++
 .../apache/nifi/web/api/ControllerResource.java |  48 ---
 .../src/main/resources/nifi-web-api-context.xml |  10 +-
 .../web/security/RegistrationStatusFilter.java  | 251 -----------
 .../nifi/web/security/jwt/JwtService.java       |  29 +-
 .../login/LoginAuthenticationFilter.java        | 243 -----------
 .../resources/nifi-web-security-context.xml     |   2 +-
 .../src/main/webapp/js/nf/canvas/nf-canvas.js   |   4 +-
 .../src/main/webapp/js/nf/login/nf-login.js     | 197 +++------
 .../src/main/webapp/js/nf/nf-common.js          |   4 +-
 19 files changed, 741 insertions(+), 866 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessConfigurationDTO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessConfigurationDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessConfigurationDTO.java
new file mode 100644
index 0000000..d9719b3
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessConfigurationDTO.java
@@ -0,0 +1,61 @@
+/*
+ * 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.api.dto;
+
+import com.wordnik.swagger.annotations.ApiModelProperty;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * Details for the access configuration.
+ */
+@XmlType(name = "accessConfig")
+public class AccessConfigurationDTO {
+
+    private Boolean supportsLogin;
+    private Boolean supportsAnonymous;
+
+    /**
+     * @return Indicates whether or not this NiFi supports user login.
+     */
+    @ApiModelProperty(
+            value = "Indicates whether or not this NiFi supports user login.",
+            readOnly = true
+    )
+    public Boolean getSupportsLogin() {
+        return supportsLogin;
+    }
+
+    public void setSupportsLogin(Boolean supportsLogin) {
+        this.supportsLogin = supportsLogin;
+    }
+
+    /**
+     * @return Indicates whether or not this NiFi supports anonymous access.
+     */
+    @ApiModelProperty(
+            value = "Indicates whether or not this NiFi supports anonymous.",
+            readOnly = true
+    )
+    public Boolean getSupportsAnonymous() {
+        return supportsAnonymous;
+    }
+
+    public void setSupportsAnonymous(Boolean supportsAnonymous) {
+        this.supportsAnonymous = supportsAnonymous;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java
new file mode 100644
index 0000000..712da0e
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java
@@ -0,0 +1,101 @@
+/*
+ * 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.api.dto;
+
+import com.wordnik.swagger.annotations.ApiModelProperty;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of a response to the API. This particular entity holds the users access status.
+ */
+@XmlRootElement(name = "accessStatus")
+public class AccessStatusDTO {
+
+    public static enum Status {
+
+        UNKNOWN,
+        UNREGISTERED,
+        NOT_ACTIVE,
+        ACTIVE
+    }
+
+    private String identity;
+    private String username;
+    private String status;
+    private String message;
+
+    /**
+     * @return the user identity
+     */
+    @ApiModelProperty(
+            value = "The user identity.",
+            readOnly = true
+    )
+    public String getIdentity() {
+        return identity;
+    }
+
+    public void setIdentity(String identity) {
+        this.identity = identity;
+    }
+
+    /**
+     * @return the username
+     */
+    @ApiModelProperty(
+            value = "The username.",
+            readOnly = true
+    )
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    /**
+     * @return the user access status
+     */
+    @ApiModelProperty(
+            value = "The user access status.",
+            readOnly = true
+    )
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    /**
+     * @return additional details about the user access status
+     */
+    @ApiModelProperty(
+            value = "Additional details about the user access status.",
+            readOnly = true
+    )
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/LoginConfigurationDTO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/LoginConfigurationDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/LoginConfigurationDTO.java
deleted file mode 100644
index 60f644b..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/LoginConfigurationDTO.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web.api.dto;
-
-import com.wordnik.swagger.annotations.ApiModelProperty;
-import javax.xml.bind.annotation.XmlType;
-
-/**
- * Details for the login configuration.
- */
-@XmlType(name = "loginConfig")
-public class LoginConfigurationDTO {
-
-    private Boolean supportsLogin;
-
-    /**
-     * @return Indicates whether or not this NiFi supports user login.
-     */
-    @ApiModelProperty(
-            value = "Indicates whether or not this NiFi supports user login.",
-            readOnly = true
-    )
-    public Boolean getSupportsLogin() {
-        return supportsLogin;
-    }
-
-    public void setSupportsLogin(Boolean supportsLogin) {
-        this.supportsLogin = supportsLogin;
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessConfigurationEntity.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessConfigurationEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessConfigurationEntity.java
new file mode 100644
index 0000000..3af0e49
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessConfigurationEntity.java
@@ -0,0 +1,43 @@
+/*
+ * 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.api.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.web.api.dto.AccessConfigurationDTO;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a AccessConfigurationDTO.
+ */
+@XmlRootElement(name = "accessConfigurationEntity")
+public class AccessConfigurationEntity extends Entity {
+
+    private AccessConfigurationDTO config;
+
+    /**
+     * The AccessConfigurationDTO that is being serialized.
+     *
+     * @return The AccessConfigurationDTO object
+     */
+    public AccessConfigurationDTO getConfig() {
+        return config;
+    }
+
+    public void setConfig(AccessConfigurationDTO config) {
+        this.config = config;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessStatusEntity.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessStatusEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessStatusEntity.java
new file mode 100644
index 0000000..f19a268
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessStatusEntity.java
@@ -0,0 +1,43 @@
+/*
+ * 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.api.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.web.api.dto.AccessStatusDTO;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a AccessStatusDTO.
+ */
+@XmlRootElement(name = "accessStatusEntity")
+public class AccessStatusEntity extends Entity {
+
+    private AccessStatusDTO accessStatus;
+
+    /**
+     * The AccessStatusDTO that is being serialized.
+     *
+     * @return The AccessStatusDTO object
+     */
+    public AccessStatusDTO getAccessStatus() {
+        return accessStatus;
+    }
+
+    public void setAccessStatus(AccessStatusDTO accessStatus) {
+        this.accessStatus = accessStatus;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/LoginConfigurationEntity.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/LoginConfigurationEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/LoginConfigurationEntity.java
deleted file mode 100644
index da62d6f..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/LoginConfigurationEntity.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web.api.entity;
-
-import javax.xml.bind.annotation.XmlRootElement;
-import org.apache.nifi.web.api.dto.LoginConfigurationDTO;
-
-/**
- * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a LoginConfigurationDTO.
- */
-@XmlRootElement(name = "loginConfigurationEntity")
-public class LoginConfigurationEntity extends Entity {
-
-    private LoginConfigurationDTO config;
-
-    /**
-     * The LoginConfigurationDTO that is being serialized.
-     *
-     * @return The LoginConfigurationDTO object
-     */
-    public LoginConfigurationDTO getConfig() {
-        return config;
-    }
-
-    public void setConfig(LoginConfigurationDTO config) {
-        this.config = config;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
index 3c64a02..f4d5821 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
@@ -43,7 +43,6 @@ import org.apache.nifi.web.api.dto.ProcessGroupDTO;
 import org.apache.nifi.web.api.dto.ProcessorDTO;
 import org.apache.nifi.web.api.dto.ComponentHistoryDTO;
 import org.apache.nifi.web.api.dto.ControllerServiceReferencingComponentDTO;
-import org.apache.nifi.web.api.dto.LoginConfigurationDTO;
 import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO;
@@ -179,13 +178,6 @@ public interface NiFiServiceFacade {
     ControllerConfigurationDTO getControllerConfiguration();
 
     /**
-     * Gets the login configuration for this controller.
-     *
-     * @return The login configuration
-     */
-    LoginConfigurationDTO getLoginConfiguration();
-
-    /**
      * Updates the configuration for this controller.
      *
      * @param revision Revision to compare with current base revision

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/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 20bbc55..216e311 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
@@ -16,15 +16,12 @@
  */
 package org.apache.nifi.web;
 
-import javax.servlet.Filter;
 import org.apache.nifi.admin.service.UserService;
 import org.apache.nifi.authentication.LoginIdentityProvider;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.security.NiFiAuthenticationProvider;
 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.login.LoginAuthenticationFilter;
 import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
 import org.apache.nifi.web.security.jwt.JwtService;
 import org.apache.nifi.web.security.node.NodeAuthorizedUserFilter;
@@ -35,7 +32,6 @@ import org.apache.nifi.web.security.x509.X509CertificateValidator;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.http.HttpMethod;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@@ -46,7 +42,6 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
 import org.springframework.security.config.http.SessionCreationPolicy;
 import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
 import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
-import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
 
 /**
@@ -72,7 +67,9 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
 
     @Override
     public void configure(WebSecurity webSecurity) throws Exception {
-        webSecurity.ignoring().antMatchers(HttpMethod.GET, "/controller/login/config");
+        webSecurity
+                .ignoring()
+                    .antMatchers("/access/**");
     }
 
     @Override
@@ -80,19 +77,13 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
         http
                 .rememberMe().disable()
                 .exceptionHandling()
-                .authenticationEntryPoint(new NiFiAuthenticationEntryPoint(properties))
-                .and()
+                    .authenticationEntryPoint(new NiFiAuthenticationEntryPoint(properties))
+                    .and()
                 .authorizeRequests()
-                .anyRequest().fullyAuthenticated()
-                .and()
+                    .anyRequest().fullyAuthenticated()
+                    .and()
                 .sessionManagement()
-                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
-
-        // login authentication for /token - exchanges for JWT for subsequent API usage
-        http.addFilterBefore(buildLoginFilter("/token"), UsernamePasswordAuthenticationFilter.class);
-
-        // registration status - will check the status of a user's account registration (regardless if its based on login or not)
-        http.addFilterBefore(buildRegistrationStatusFilter("/registration/status"), UsernamePasswordAuthenticationFilter.class);
+                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
 
         // cluster authorized user
         http.addFilterBefore(buildNodeAuthorizedUserFilter(), AnonymousAuthenticationFilter.class);
@@ -121,28 +112,6 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
         auth.authenticationProvider(new NiFiAuthenticationProvider(userDetailsService));
     }
 
-    private LoginAuthenticationFilter buildLoginFilter(final String url) {
-        final LoginAuthenticationFilter loginFilter = new LoginAuthenticationFilter(url);
-        loginFilter.setJwtService(jwtService);
-        loginFilter.setLoginIdentityProvider(loginIdentityProvider);
-        loginFilter.setUserDetailsService(userDetailsService);
-        loginFilter.setCertificateExtractor(certificateExtractor);
-        loginFilter.setPrincipalExtractor(principalExtractor);
-        loginFilter.setCertificateValidator(certificateValidator);
-        return loginFilter;
-    }
-
-    private Filter buildRegistrationStatusFilter(final String url) {
-        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() {
         return new NodeAuthorizedUserFilter(properties);
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index 324be87..b13077d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -153,7 +153,6 @@ import org.apache.nifi.web.util.SnippetUtils;
 
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.authentication.LoginIdentityProvider;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.Validator;
 import org.apache.nifi.controller.ReportingTaskNode;
@@ -164,7 +163,6 @@ import org.apache.nifi.controller.service.ControllerServiceState;
 import org.apache.nifi.reporting.ComponentType;
 import org.apache.nifi.web.api.dto.ControllerServiceDTO;
 import org.apache.nifi.web.api.dto.ControllerServiceReferencingComponentDTO;
-import org.apache.nifi.web.api.dto.LoginConfigurationDTO;
 import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
 import org.apache.nifi.web.api.dto.ReportingTaskDTO;
 import org.apache.nifi.web.api.dto.status.ClusterProcessGroupStatusDTO;
@@ -207,7 +205,6 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     // administrative services
     private AuditService auditService;
     private UserService userService;
-    private LoginIdentityProvider loginIdentityProvider;
 
     // cluster manager
     private WebClusterManager clusterManager;
@@ -2351,16 +2348,6 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
-    public LoginConfigurationDTO getLoginConfiguration() {
-        final LoginConfigurationDTO loginConfiguration = new LoginConfigurationDTO();
-
-        // specify whether login should be supported
-        loginConfiguration.setSupportsLogin(loginIdentityProvider != null);
-
-        return loginConfiguration;
-    }
-
-    @Override
     public ControllerConfigurationDTO getControllerConfiguration() {
         ControllerConfigurationDTO controllerConfig = new ControllerConfigurationDTO();
         controllerConfig.setName(controllerFacade.getName());
@@ -3420,10 +3407,6 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
         this.snippetUtils = snippetUtils;
     }
 
-    public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) {
-        this.loginIdentityProvider = loginIdentityProvider;
-    }
-
     private boolean isPrimaryNode(String nodeId) {
         final Node primaryNode = clusterManager.getPrimaryNode();
         return (primaryNode != null && primaryNode.getNodeId().getId().equals(nodeId));

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
new file mode 100644
index 0000000..9cb4141
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -0,0 +1,412 @@
+/*
+ * 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.api;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.nifi.util.NiFiProperties;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+import java.net.URI;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import org.apache.nifi.admin.service.AdministrationException;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.apache.nifi.authentication.LoginCredentials;
+import org.apache.nifi.authentication.LoginIdentityProvider;
+import org.apache.nifi.authentication.exception.IdentityAccessException;
+import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException;
+import org.apache.nifi.security.util.CertificateUtils;
+import org.apache.nifi.util.StringUtils;
+import static org.apache.nifi.web.api.ApplicationResource.CLIENT_ID;
+import org.apache.nifi.web.api.dto.AccessStatusDTO;
+import org.apache.nifi.web.api.dto.AccessConfigurationDTO;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.entity.AccessStatusEntity;
+import org.apache.nifi.web.api.entity.AccessConfigurationEntity;
+import org.apache.nifi.web.api.request.ClientIdParameter;
+import org.apache.nifi.web.security.ProxiedEntitiesUtils;
+import org.apache.nifi.web.security.UntrustedProxyException;
+import org.apache.nifi.web.security.jwt.JwtService;
+import org.apache.nifi.web.security.token.LoginAuthenticationToken;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.x509.X509CertificateExtractor;
+import org.apache.nifi.web.security.x509.X509CertificateValidator;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.AccountStatusException;
+import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
+import org.springframework.security.authentication.AuthenticationServiceException;
+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.preauth.x509.X509PrincipalExtractor;
+
+/**
+ * RESTful endpoint for managing a cluster.
+ */
+@Path("/access")
+@Api(
+        value = "/access",
+        description = "Endpoints for obtaining an access token or checking access status"
+)
+public class AccessResource extends ApplicationResource {
+
+    private NiFiProperties properties;
+
+    private X509CertificateValidator certificateValidator;
+    private X509CertificateExtractor certificateExtractor;
+    private X509PrincipalExtractor principalExtractor;
+
+    private LoginIdentityProvider loginIdentityProvider;
+    private JwtService jwtService;
+
+    private AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService;
+
+    /**
+     * Retrieves the access configuration for this NiFi.
+     *
+     * @param httpServletRequest the servlet request
+     * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
+     * @return A accessConfigurationEntity
+     */
+    @GET
+    @Consumes(MediaType.WILDCARD)
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/config")
+    @ApiOperation(
+            value = "Retrieves the access configuration for this NiFi",
+            response = AccessConfigurationEntity.class
+    )
+    public Response getLoginConfig(
+            @Context HttpServletRequest httpServletRequest,
+            @ApiParam(
+                    value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+                    required = false
+            )
+            @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) {
+
+        final AccessConfigurationDTO accessConfiguration = new AccessConfigurationDTO();
+
+        // specify whether login should be supported and only support for secure requests
+        accessConfiguration.setSupportsLogin(loginIdentityProvider != null && httpServletRequest.isSecure());
+        accessConfiguration.setSupportsAnonymous(!properties.getAnonymousAuthorities().isEmpty() || !httpServletRequest.isSecure());
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+
+        // create the response entity
+        final AccessConfigurationEntity entity = new AccessConfigurationEntity();
+        entity.setRevision(revision);
+        entity.setConfig(accessConfiguration);
+
+        // generate the response
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+
+    /**
+     * Gets the status the client's access.
+     *
+     * @param httpServletRequest the servlet request
+     * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
+     * @return A accessStatusEntity
+     */
+    @GET
+    @Consumes(MediaType.WILDCARD)
+    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
+    @Path("")
+    @ApiOperation(
+            value = "Gets the status the client's access",
+            response = AccessStatusEntity.class
+    )
+    @ApiResponses(
+            value = {
+                @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
+                @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
+            }
+    )
+    public Response getAccessStatus(
+            @Context HttpServletRequest httpServletRequest,
+            @ApiParam(
+                    value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+                    required = false
+            )
+            @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) {
+
+        // only consider user specific access over https
+        if (!httpServletRequest.isSecure()) {
+            throw new IllegalStateException("User authentication/authorization is only supported when running over HTTPS.");
+        }
+
+        final AccessStatusDTO accessStatus = new AccessStatusDTO();
+
+        try {
+            // look for a certificate
+            final X509Certificate certificate = certificateExtractor.extractClientCertificate(httpServletRequest);
+
+            // if no certificate, just check the credentials
+            if (certificate == null) {
+                final String principal = jwtService.getAuthentication(httpServletRequest);
+
+                // ensure we have something we can work with (certificate or crendentials)
+                if (principal == null) {
+                    accessStatus.setStatus(AccessStatusDTO.Status.UNKNOWN.name());
+                    accessStatus.setMessage("No credentials supplied, unknown user.");
+                } else {
+                    // set the user identity
+                    accessStatus.setIdentity(principal);
+                    accessStatus.setUsername(CertificateUtils.extractUsername(principal));
+
+                    // without a certificate, this is not a proxied request
+                    final List<String> chain = Arrays.asList(principal);
+
+                    // check authorization for this user
+                    checkAuthorization(chain);
+
+                    // no issues with authorization
+                    accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
+                    accessStatus.setStatus("Account is active and authorized");
+                }
+            } else {
+                // we have a certificate so let's consider a proxy chain
+                final String principal = principalExtractor.extractPrincipal(certificate).toString();
+
+                try {
+                    // validate the certificate
+                    certificateValidator.validateClientCertificate(httpServletRequest, certificate);
+                } catch (CertificateExpiredException cee) {
+                    throw new IllegalArgumentException(String.format("Client certificate for (%s) is expired.", principal), cee);
+                } catch (CertificateNotYetValidException cnyve) {
+                    throw new IllegalArgumentException(String.format("Client certificate for (%s) is not yet valid.", principal), cnyve);
+                } catch (final Exception e) {
+                    throw new IllegalArgumentException(e.getMessage(), e);
+                }
+
+                // set the user identity
+                accessStatus.setIdentity(principal);
+                accessStatus.setUsername(CertificateUtils.extractUsername(principal));
+
+                // ensure the proxy chain is authorized
+                checkAuthorization(ProxiedEntitiesUtils.buildProxyChain(httpServletRequest, principal));
+
+                // no issues with authorization
+                accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
+                accessStatus.setStatus("Account is active and authorized");
+            }
+        } catch (final UsernameNotFoundException unfe) {
+            accessStatus.setStatus(AccessStatusDTO.Status.UNREGISTERED.name());
+            accessStatus.setMessage(String.format("Unregistered user %s", accessStatus.getIdentity()));
+        } catch (final AccountStatusException ase) {
+            accessStatus.setStatus(AccessStatusDTO.Status.NOT_ACTIVE.name());
+            accessStatus.setMessage(ase.getMessage());
+        } catch (final UntrustedProxyException upe) {
+            throw new AccessDeniedException(upe.getMessage(), upe);
+        } catch (final AuthenticationServiceException ase) {
+            throw new AdministrationException(ase.getMessage(), ase);
+        }
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+
+        // create the entity
+        final AccessStatusEntity entity = new AccessStatusEntity();
+        entity.setRevision(revision);
+        entity.setAccessStatus(accessStatus);
+
+        return generateOkResponse(entity).build();
+    }
+
+    /**
+     * Checks the status of the proxy.
+     *
+     * @param proxyChain the proxy chain
+     * @throws AuthenticationException if the proxy chain is not authorized
+     */
+    private void checkAuthorization(final List<String> proxyChain) throws AuthenticationException {
+        userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
+    }
+
+    /**
+     * Creates a token for accessing the REST API via username/password.
+     *
+     * @param httpServletRequest the servlet request
+     * @param username the username
+     * @param password the password
+     * @return A JWT (string)
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    @Produces(MediaType.TEXT_PLAIN)
+    @Path("/token")
+    @ApiOperation(
+            value = "Creates a token for accessing the REST API via username/password",
+            response = String.class
+    )
+    @ApiResponses(
+            value = {
+                @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
+                @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
+            }
+    )
+    public Response createAccessToken(
+            @Context HttpServletRequest httpServletRequest,
+            @FormParam("username") String username,
+            @FormParam("password") String password) {
+
+        // only support access tokens when communicating over HTTPS
+        if (!httpServletRequest.isSecure()) {
+            throw new IllegalStateException("Access tokens are only issued over HTTPS.");
+        }
+
+        // if not configuration for login, don't consider credentials
+        if (loginIdentityProvider == null) {
+            throw new IllegalStateException("Username/Password login not supported by this NiFi.");
+        }
+
+        final LoginAuthenticationToken loginAuthenticationToken;
+
+        // if we don't have username/password, consider JWT or x509
+        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
+            // look for a certificate
+            final X509Certificate certificate = certificateExtractor.extractClientCertificate(httpServletRequest);
+
+            // if there is no certificate, look for an existing token
+            if (certificate == null) {
+                // if not configured for login, don't consider existing tokens
+                if (loginIdentityProvider == null) {
+                    throw new IllegalStateException("Login not supported.");
+                }
+
+                // look for the principal
+                final String principal = jwtService.getAuthentication(httpServletRequest);
+                if (principal == null) {
+                    throw new AuthenticationCredentialsNotFoundException("Unable to issue token as issue token as no credentials were found in the request.");
+                }
+
+                // create the authentication token
+                loginAuthenticationToken = new LoginAuthenticationToken(principal, loginIdentityProvider.getExpiration());
+            } else {
+                // extract the principal
+                final String principal = principalExtractor.extractPrincipal(certificate).toString();
+
+                try {
+                    certificateValidator.validateClientCertificate(httpServletRequest, certificate);
+                } catch (CertificateExpiredException cee) {
+                    throw new IllegalArgumentException(String.format("Client certificate for (%s) is expired.", principal), cee);
+                } catch (CertificateNotYetValidException cnyve) {
+                    throw new IllegalArgumentException(String.format("Client certificate for (%s) is not yet valid.", principal), cnyve);
+                } catch (final Exception e) {
+                    throw new IllegalArgumentException(e.getMessage(), e);
+                }
+
+                // authorize the proxy if necessary
+                authorizeProxyIfNecessary(ProxiedEntitiesUtils.buildProxyChain(httpServletRequest, principal));
+
+                // create the authentication token
+                loginAuthenticationToken = new LoginAuthenticationToken(principal, loginIdentityProvider.getExpiration());
+            }
+        } else {
+            try {
+                // attempt to authenticate
+                final AuthenticationResponse authenticationResponse = loginIdentityProvider.authenticate(new LoginCredentials(username, password));
+
+                // create the authentication token
+                loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getUsername(), loginIdentityProvider.getExpiration());
+            } catch (final InvalidLoginCredentialsException ilce) {
+                throw new IllegalArgumentException("The supplied username and password are not valid.", ilce);
+            } catch (final IdentityAccessException iae) {
+                throw new AdministrationException(iae.getMessage(), iae);
+            }
+        }
+
+        // generate JWT for response
+        final String token = jwtService.generateSignedToken(loginAuthenticationToken);
+
+        // build the response
+        final URI uri = URI.create(generateResourceUri("access", "token"));
+        return generateCreatedResponse(uri, token).build();
+    }
+
+    /**
+     * Ensures the proxyChain is authorized before allowing the user to be authenticated.
+     *
+     * @param proxyChain the proxy chain
+     * @throws AuthenticationException if the proxy chain is not authorized
+     */
+    private void authorizeProxyIfNecessary(final List<String> proxyChain) throws AuthenticationException {
+        if (proxyChain.size() > 1) {
+            try {
+                userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
+            } 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 AdministrationException(e.getMessage(), e) {
+                };
+            }
+        }
+    }
+
+    // setters
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) {
+        this.loginIdentityProvider = loginIdentityProvider;
+    }
+
+    public void setJwtService(JwtService jwtService) {
+        this.jwtService = jwtService;
+    }
+
+    public void setCertificateValidator(X509CertificateValidator certificateValidator) {
+        this.certificateValidator = certificateValidator;
+    }
+
+    public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
+        this.certificateExtractor = certificateExtractor;
+    }
+
+    public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) {
+        this.principalExtractor = principalExtractor;
+    }
+
+    public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) {
+        this.userDetailsService = userDetailsService;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
index 93f21b2..6e6739d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
@@ -78,10 +78,8 @@ import org.apache.nifi.web.api.request.ClientIdParameter;
 import org.apache.nifi.web.api.request.IntegerParameter;
 import org.apache.nifi.web.api.request.LongParameter;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.web.api.dto.LoginConfigurationDTO;
 import org.apache.nifi.web.api.entity.ControllerServiceTypesEntity;
 import org.apache.nifi.web.api.entity.IdentityEntity;
-import org.apache.nifi.web.api.entity.LoginConfigurationEntity;
 import org.apache.nifi.web.api.entity.ReportingTaskTypesEntity;
 import org.springframework.security.access.prepost.PreAuthorize;
 
@@ -657,52 +655,6 @@ public class ControllerResource extends ApplicationResource {
     }
 
     /**
-     * Retrieves the login configuration for this NiFi.
-     *
-     * @param httpServletRequest the servlet request
-     * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
-     * @return A loginConfigurationEntity
-     */
-    @GET
-    @Consumes(MediaType.WILDCARD)
-    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
-    @Path("/login/config")
-    @ApiOperation(
-            value = "Retrieves the login configuration for this NiFi",
-            response = LoginConfigurationEntity.class
-    )
-    public Response getLoginConfig(
-            @Context HttpServletRequest httpServletRequest,
-            @ApiParam(
-                    value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
-                    required = false
-            )
-            @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) {
-
-        // replicate if cluster manager
-        if (properties.isClusterManager()) {
-            return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
-        }
-
-        final LoginConfigurationDTO loginConfig = serviceFacade.getLoginConfiguration();
-
-        // only support login/registration when running securely
-        loginConfig.setSupportsLogin(loginConfig.getSupportsLogin() && httpServletRequest.isSecure());
-
-        // create the revision
-        final RevisionDTO revision = new RevisionDTO();
-        revision.setClientId(clientId.getClientId());
-
-        // create the response entity
-        final LoginConfigurationEntity entity = new LoginConfigurationEntity();
-        entity.setRevision(revision);
-        entity.setConfig(loginConfig);
-
-        // generate the response
-        return clusterContext(generateOkResponse(entity)).build();
-    }
-
-    /**
      * Retrieves the configuration for this NiFi.
      *
      * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
index 96c3f2b..e992dc9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
@@ -122,7 +122,6 @@
         <property name="optimisticLockingManager" ref="webOptimisticLockingManager"/>
         <property name="dtoFactory" ref="dtoFactory"/>
         <property name="clusterManager" ref="clusterManager"/>
-        <property name="loginIdentityProvider" ref="loginIdentityProvider"/>
     </bean>
 
     <!-- depecrated -->
@@ -242,6 +241,15 @@
     <bean id="systemDiagnosticsResource" class="org.apache.nifi.web.api.SystemDiagnosticsResource" scope="singleton">
         <property name="serviceFacade" ref="serviceFacade"/>
     </bean>
+    <bean id="accessResource" class="org.apache.nifi.web.api.AccessResource" scope="singleton">
+        <property name="properties" ref="nifiProperties"/>
+        <property name="certificateValidator" ref="certificateValidator"/>
+        <property name="certificateExtractor" ref="certificateExtractor"/>
+        <property name="principalExtractor" ref="principalExtractor"/>
+        <property name="loginIdentityProvider" ref="loginIdentityProvider"/>
+        <property name="jwtService" ref="jwtService"/>
+        <property name="userDetailsService" ref="userDetailsService"/>
+    </bean>
 
     <!-- configuration for jaxb serialization -->
     <bean class="org.apache.nifi.web.util.ObjectMapperResolver" scope="singleton"/>

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/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
deleted file mode 100644
index 606d2e3..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/RegistrationStatusFilter.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web.security;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import java.util.List;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-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;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.security.authentication.AbstractAuthenticationToken;
-import org.springframework.security.authentication.AccountStatusException;
-import org.springframework.security.authentication.AuthenticationServiceException;
-import org.springframework.security.authentication.BadCredentialsException;
-import org.springframework.security.core.Authentication;
-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.x509.X509PrincipalExtractor;
-
-/**
- * Exchanges a successful login with the configured provider for a ID token for accessing the API.
- */
-public class RegistrationStatusFilter extends AbstractAuthenticationProcessingFilter {
-
-    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;
-    private X509PrincipalExtractor principalExtractor;
-
-    public RegistrationStatusFilter(final String defaultFilterProcessesUrl) {
-        super(defaultFilterProcessesUrl);
-
-        // do not continue filter chain... simply exchaning authentication for token
-        setContinueChainBeforeSuccessfulAuthentication(false);
-    }
-
-    @Override
-    public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
-        // only suppport login when running securely
-        if (!request.isSecure()) {
-            return null;
-        }
-
-        // look for a certificate
-        final X509Certificate certificate = certificateExtractor.extractClientCertificate(request);
-
-        // if no certificate, just check the credentials
-        if (certificate == null) {
-            final String principal = jwtService.getAuthentication(request);
-
-            // ensure we have something we can work with (certificate or crendentials)
-            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(principal);
-
-            // check authorization for this user
-            checkAuthorization(chain);
-
-            // no issues with authorization
-            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 = principalExtractor.extractPrincipal(certificate).toString();
-
-            try {
-                // validate the certificate
-                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;
-            }
-
-            // ensure the proxy chain is authorized
-            checkAuthorization(ProxiedEntitiesUtils.buildProxyChain(request, principal));
-
-            // no issues with authorization
-            final LoginCredentials preAuthenticatedCredentials = new LoginCredentials(principal, null);
-            return new RegistrationStatusAuthenticationToken(preAuthenticatedCredentials);
-        }
-    }
-
-    /**
-     * Checks the status of the proxy.
-     *
-     * @param proxyChain the proxy chain
-     * @throws AuthenticationException if the proxy chain is not authorized
-     */
-    private void checkAuthorization(final List<String> proxyChain) throws AuthenticationException {
-        userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
-    }
-
-    @Override
-    protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain, final Authentication authentication)
-            throws IOException, ServletException {
-
-        // mark as successful
-        response.setStatus(HttpServletResponse.SC_OK);
-        response.setContentType("text/plain");
-        response.setContentLength(0);
-    }
-
-    @Override
-    protected void unsuccessfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException ae) throws IOException, ServletException {
-        // set the response status
-        response.setContentType("text/plain");
-
-        // write the response message
-        PrintWriter out = response.getWriter();
-
-        // use the type of authentication exception to determine the response code
-        if (ae instanceof UsernameNotFoundException) {
-            if (properties.getSupportNewAccountRequests()) {
-                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-                out.println("Not authorized.");
-            } else {
-                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-                out.println("Access is denied.");
-            }
-        } else if (ae instanceof AccountStatusException) {
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println(ae.getMessage());
-        } else if (ae instanceof UntrustedProxyException) {
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println(ae.getMessage());
-        } else if (ae instanceof AuthenticationServiceException) {
-            logger.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
-            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-            out.println(String.format("Unable to authorize: %s", ae.getMessage()));
-        } else {
-            logger.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println("Access is denied.");
-        }
-
-        // log the failure
-        logger.info(String.format("Rejecting access to web api: %s", ae.getMessage()));
-
-        // optionally log the stack trace
-        if (logger.isDebugEnabled()) {
-            logger.debug(StringUtils.EMPTY, ae);
-        }
-    }
-
-    /**
-     * This is an Authentication Token for logging in. Once a user is authenticated, they can be issues an ID token.
-     */
-    public static class RegistrationStatusAuthenticationToken extends AbstractAuthenticationToken {
-
-        final LoginCredentials credentials;
-
-        public RegistrationStatusAuthenticationToken(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();
-        }
-    }
-
-    public void setJwtService(JwtService jwtService) {
-        this.jwtService = jwtService;
-    }
-
-    public void setCertificateValidator(X509CertificateValidator certificateValidator) {
-        this.certificateValidator = certificateValidator;
-    }
-
-    public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
-        this.certificateExtractor = certificateExtractor;
-    }
-
-    public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) {
-        this.principalExtractor = principalExtractor;
-    }
-
-    public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) {
-        this.userDetailsService = userDetailsService;
-    }
-
-    public void setProperties(NiFiProperties properties) {
-        this.properties = properties;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/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 5dfbdfe..2f6c726 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
@@ -26,12 +26,9 @@ import io.jsonwebtoken.SignatureAlgorithm;
 import io.jsonwebtoken.SignatureException;
 import io.jsonwebtoken.SigningKeyResolverAdapter;
 import io.jsonwebtoken.UnsupportedJwtException;
-import java.io.IOException;
-import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.Calendar;
 import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.admin.service.AdministrationException;
 import org.apache.nifi.admin.service.KeyService;
@@ -83,35 +80,25 @@ public class JwtService {
     }
 
     /**
-     * Adds a token for the specified authentication in the specified response.
+     * Generates a signed JWT token from the provided (Spring Security) login authentication token.
      *
-     * @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
+     * @param authenticationToken
+     * @return a signed JWT containing the user identity and the identity provider, Base64-encoded
      */
-    public void addToken(final HttpServletResponse response, final LoginAuthenticationToken authentication) throws IOException {
+    public String generateSignedToken(final LoginAuthenticationToken authenticationToken) {
         // set expiration to one day from now
         final Calendar calendar = Calendar.getInstance();
-        calendar.setTimeInMillis(calendar.getTimeInMillis() + authentication.getExpiration());
+        calendar.setTimeInMillis(calendar.getTimeInMillis() + authenticationToken.getExpiration());
 
         // create a token the specified authentication
-        final String identity = authentication.getPrincipal().toString();
-        final String username = authentication.getName();
+        final String identity = authenticationToken.getPrincipal().toString();
+        final String username = authenticationToken.getName();
 
         // get/create the key for this user
         final String key = keyService.getOrCreateKey(identity);
         final byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
 
         // build the token
-        final String token = Jwts.builder().setSubject(identity).claim("preferred_username", username).setExpiration(calendar.getTime()).signWith(SignatureAlgorithm.HS512, keyBytes).compact();
-
-        // add the token as a response header
-        final PrintWriter out = response.getWriter();
-        out.print(token);
-
-        // mark the response as successful
-        response.setStatus(HttpServletResponse.SC_CREATED);
-        response.setContentType("text/plain");
+        return Jwts.builder().setSubject(identity).claim("preferred_username", username).setExpiration(calendar.getTime()).signWith(SignatureAlgorithm.HS512, keyBytes).compact();
     }
-
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/login/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/login/LoginAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/login/LoginAuthenticationFilter.java
deleted file mode 100644
index c90364a..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/login/LoginAuthenticationFilter.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web.security.login;
-
-import org.apache.nifi.web.security.token.LoginAuthenticationToken;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.X509Certificate;
-import java.util.List;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.apache.nifi.admin.service.AdministrationException;
-import org.apache.nifi.authentication.AuthenticationResponse;
-import org.apache.nifi.authentication.LoginCredentials;
-import org.apache.nifi.authentication.LoginIdentityProvider;
-import org.apache.nifi.authentication.exception.IdentityAccessException;
-import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException;
-import org.apache.nifi.util.StringUtils;
-import org.apache.nifi.web.security.ProxiedEntitiesUtils;
-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;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
-import org.springframework.security.authentication.AuthenticationServiceException;
-import org.springframework.security.authentication.BadCredentialsException;
-import org.springframework.security.core.Authentication;
-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.x509.X509PrincipalExtractor;
-
-/**
- * Exchanges a successful login with the configured provider for a ID token for accessing the API.
- */
-public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
-
-    private static final Logger logger = LoggerFactory.getLogger(LoginAuthenticationFilter.class);
-
-    private AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService;
-
-    private X509CertificateValidator certificateValidator;
-    private X509CertificateExtractor certificateExtractor;
-    private X509PrincipalExtractor principalExtractor;
-
-    private LoginIdentityProvider loginIdentityProvider;
-    private JwtService jwtService;
-
-    public LoginAuthenticationFilter(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 login 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) {
-            // look for a certificate
-            final X509Certificate certificate = certificateExtractor.extractClientCertificate(request);
-
-            // if there is no certificate, look for an existing token
-            if (certificate == null) {
-                // if not configured for login, don't consider existing tokens
-                if (loginIdentityProvider == null) {
-                    throw new BadCredentialsException("Login not supported.");
-                }
-
-                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 new LoginAuthenticationToken(principal, loginIdentityProvider.getExpiration());
-            } else {
-                // extract the principal
-                final String principal = principalExtractor.extractPrincipal(certificate).toString();
-
-                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;
-                }
-
-                // authorize the proxy if necessary
-                authorizeProxyIfNecessary(ProxiedEntitiesUtils.buildProxyChain(request, principal));
-
-                return new LoginAuthenticationToken(principal, loginIdentityProvider.getExpiration());
-            }
-        } else {
-            // if not configuration for login, don't consider credentials
-            if (loginIdentityProvider == null) {
-                throw new BadCredentialsException("Login not supported.");
-            }
-
-            try {
-                // attempt to authenticate
-                final AuthenticationResponse authenticationResponse = loginIdentityProvider.authenticate(credentials);
-
-                // create the authentication token
-                return new LoginAuthenticationToken(authenticationResponse.getUsername(), loginIdentityProvider.getExpiration());
-            } catch (final InvalidLoginCredentialsException ilce) {
-                throw new BadCredentialsException("The supplied username and password are not valid.", ilce);
-            } catch (final IdentityAccessException iae) {
-                throw new AuthenticationServiceException(iae.getMessage(), iae);
-            }
-        }
-    }
-
-    /**
-     * Ensures the proxyChain is authorized before allowing the user to be authenticated.
-     *
-     * @param proxyChain the proxy chain
-     * @throws AuthenticationException if the proxy chain is not authorized
-     */
-    private void authorizeProxyIfNecessary(final List<String> proxyChain) throws AuthenticationException {
-        if (proxyChain.size() > 1) {
-            try {
-                userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
-            } 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 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 {
-
-        try {
-            // generate JWT for response
-            jwtService.addToken(response, (LoginAuthenticationToken) authentication);
-        } catch (final AdministrationException ae) {
-            unsuccessfulAuthentication(request, response, new AuthenticationServiceException(ae.getMessage(), ae));
-        }
-    }
-
-    @Override
-    protected void unsuccessfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException failed) throws IOException, ServletException {
-        if (failed instanceof BadCredentialsException || failed instanceof AuthenticationCredentialsNotFoundException) {
-            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-        } else if (failed instanceof AuthenticationServiceException) {
-            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-        } else {
-            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-        }
-
-        response.setContentType("text/plain");
-
-        final PrintWriter out = response.getWriter();
-        out.println(failed.getMessage());
-    }
-
-    public void setJwtService(JwtService jwtService) {
-        this.jwtService = jwtService;
-    }
-
-    public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) {
-        this.loginIdentityProvider = loginIdentityProvider;
-    }
-
-    public void setCertificateValidator(X509CertificateValidator certificateValidator) {
-        this.certificateValidator = certificateValidator;
-    }
-
-    public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
-        this.certificateExtractor = certificateExtractor;
-    }
-
-    public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) {
-        this.principalExtractor = principalExtractor;
-    }
-
-    public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) {
-        this.userDetailsService = userDetailsService;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/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 0ffd46c..c88d303 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
@@ -29,7 +29,7 @@
     </bean>
         
     <!-- x509 validator -->
-    <bean id="x509Validator" class="org.apache.nifi.web.security.x509.X509CertificateValidator">
+    <bean id="certificateValidator" class="org.apache.nifi.web.security.x509.X509CertificateValidator">
         <property name="ocspValidator" ref="ocspValidator"/> 
     </bean>
     

http://git-wip-us.apache.org/repos/asf/nifi/blob/7529694f/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 894ade8..09a8212 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
@@ -65,7 +65,7 @@ nf.Canvas = (function () {
             banners: '../nifi-api/controller/banners',
             controller: '../nifi-api/controller',
             controllerConfig: '../nifi-api/controller/config',
-            loginConfig: '../nifi-api/controller/login/config',
+            accessConfig: '../nifi-api/access/config',
             cluster: '../nifi-api/cluster',
             d3Script: 'js/d3/d3.min.js'
         }
@@ -1090,7 +1090,7 @@ nf.Canvas = (function () {
                 // get the login config
                 var loginXhr = $.ajax({
                     type: 'GET',
-                    url: config.urls.loginConfig,
+                    url: config.urls.accessConfig,
                     dataType: 'json'
                 });
 


[07/51] [abbrv] nifi git commit: NIFI-655: - Fixed typo in error message for unrecognized authentication strategy.

Posted by mc...@apache.org.
NIFI-655: - Fixed typo in error message for unrecognized authentication strategy.

Signed-off-by: Matt Gilman <ma...@gmail.com>


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

Branch: refs/heads/master
Commit: 45b24a4b6015e14525da0feb93d0a3dbc98180f1
Parents: 16608aa
Author: Andy LoPresto <an...@andylopresto.com>
Authored: Tue Nov 17 17:26:39 2015 -0800
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Nov 18 08:30:44 2015 -0500

----------------------------------------------------------------------
 .../src/main/java/org/apache/nifi/ldap/LdapProvider.java       | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/45b24a4b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
index 3c26d9f..628fc02 100644
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
@@ -58,6 +58,8 @@ public class LdapProvider extends AbstractLdapProvider {
 
         // connection time out
         final String rawConnectTimeout = configurationContext.getProperty("Connect Timeout");
+
+        // TODO: Refactor to utility method to remove duplicate code
         if (StringUtils.isNotBlank(rawConnectTimeout)) {
             try {
                 final Long connectTimeout = FormatUtils.getTimeDuration(rawConnectTimeout, TimeUnit.MILLISECONDS);
@@ -89,7 +91,7 @@ public class LdapProvider extends AbstractLdapProvider {
         try {
             authenticationStrategy = LdapAuthenticationStrategy.valueOf(rawAuthenticationStrategy);
         } catch (final IllegalArgumentException iae) {
-            throw new ProviderCreationException(String.format("Unrecgonized authentication strategy '%s'. Possible values are [%s]",
+            throw new ProviderCreationException(String.format("Unrecognized authentication strategy '%s'. Possible values are [%s]",
                     rawAuthenticationStrategy, StringUtils.join(LdapAuthenticationStrategy.values(), ", ")));
         }
 
@@ -142,7 +144,7 @@ public class LdapProvider extends AbstractLdapProvider {
                                         sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType,
                                                 rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, clientAuth, TLS);
                                     } catch (final IllegalArgumentException iae) {
-                                        throw new ProviderCreationException(String.format("Unrecgonized client auth '%s'", rawClientAuth));
+                                        throw new ProviderCreationException(String.format("Unrecognized client auth '%s'", rawClientAuth));
                                     }
                                 }
                             }


[09/51] [abbrv] nifi git commit: NIFI-655. - Added issuer field to LoginAuthenticationToken. - Updated AccessResource to pass identity provider class name when creating LoginAuthenticationTokens. - Began refactoring JWT logic from request parsing logic i

Posted by mc...@apache.org.
NIFI-655. - Added issuer field to LoginAuthenticationToken. - Updated AccessResource to pass identity provider class name when creating LoginAuthenticationTokens. - Began refactoring JWT logic from request parsing logic in JwtService. - Added unit tests for JWT logic.

Signed-off-by: Matt Gilman <ma...@gmail.com>


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

Branch: refs/heads/master
Commit: 3bc11e13d7f7dd4242881cde03c9b2ff6fc7da02
Parents: caeede5
Author: Andy LoPresto <an...@andylopresto.com>
Authored: Tue Nov 17 18:22:39 2015 -0800
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Nov 18 08:31:23 2015 -0500

----------------------------------------------------------------------
 .../org/apache/nifi/web/api/AccessResource.java |   6 +-
 .../nifi/web/security/jwt/JwtService.java       | 100 ++++++--
 .../token/LoginAuthenticationToken.java         |  64 ++++-
 .../nifi/web/security/jwt/JwtServiceTest.java   | 254 +++++++++++++++++++
 4 files changed, 395 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/3bc11e13/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
index 8f4cd5a..b4778ad 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -191,7 +191,7 @@ public class AccessResource extends ApplicationResource {
                     final String principal = jwtService.getAuthentication(httpServletRequest);
 
                     // TODO - catch jwt exception?
-                    // ensure we have something we can work with (certificate or crendentials)
+                    // ensure we have something we can work with (certificate or credentials)
                     if (principal == null) {
                         throw new IllegalArgumentException("The specific token is not valid.");
                     } else {
@@ -334,7 +334,7 @@ public class AccessResource extends ApplicationResource {
                 }
                 
                 // create the authentication token
-                loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getIdentity(), expiration);
+                loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getIdentity(), expiration, loginIdentityProvider.getClass().getSimpleName());
             } catch (final InvalidLoginCredentialsException ilce) {
                 throw new IllegalArgumentException("The supplied username and password are not valid.", ilce);
             } catch (final IdentityAccessException iae) {
@@ -355,7 +355,7 @@ public class AccessResource extends ApplicationResource {
             authorizeProxyIfNecessary(proxyChain);
 
             // create the authentication token
-            loginAuthenticationToken = new LoginAuthenticationToken(proxyChain.get(0), authenticationResponse.getExpiration());
+            loginAuthenticationToken = new LoginAuthenticationToken(proxyChain.get(0), authenticationResponse.getExpiration(), certificateIdentityProvider.getClass().getSimpleName());
         }
 
         // generate JWT for response

http://git-wip-us.apache.org/repos/asf/nifi/blob/3bc11e13/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 25e52d6..acbbcfe 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
@@ -20,25 +20,30 @@ import io.jsonwebtoken.Claims;
 import io.jsonwebtoken.ExpiredJwtException;
 import io.jsonwebtoken.Jws;
 import io.jsonwebtoken.JwsHeader;
+import io.jsonwebtoken.JwtException;
 import io.jsonwebtoken.Jwts;
 import io.jsonwebtoken.MalformedJwtException;
 import io.jsonwebtoken.SignatureAlgorithm;
 import io.jsonwebtoken.SignatureException;
 import io.jsonwebtoken.SigningKeyResolverAdapter;
 import io.jsonwebtoken.UnsupportedJwtException;
-import java.nio.charset.StandardCharsets;
-import java.util.Calendar;
-import javax.servlet.http.HttpServletRequest;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.admin.service.AdministrationException;
 import org.apache.nifi.admin.service.KeyService;
 import org.apache.nifi.web.security.token.LoginAuthenticationToken;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+import java.nio.charset.StandardCharsets;
+import java.util.Calendar;
 
 /**
  *
  */
 public class JwtService {
+    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(JwtService.class);
 
+    private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
     private final static String AUTHORIZATION = "Authorization";
 
     private final KeyService keyService;
@@ -54,51 +59,104 @@ public class JwtService {
      * @return The user identifier from the token
      */
     public String getAuthentication(final HttpServletRequest request) {
+        // TODO: Refactor request token extraction out of this service
         // extract/verify token from incoming request
         final String authorization = request.getHeader(AUTHORIZATION);
-        final String token = StringUtils.substringAfterLast(authorization, " ");
+        final String base64EncodedToken = StringUtils.substringAfterLast(authorization, " ");
+
+        return getAuthenticationFromToken(base64EncodedToken);
+    }
+
+    public String getAuthenticationFromToken(final String base64EncodedToken) {
+        // The library representations of the JWT should be kept internal to this service.
+        try {
+            final Jws<Claims> jws = parseTokenFromBase64EncodedString(base64EncodedToken);
+            return jws.getBody().getSubject();
+        } catch (JwtException e) {
+            logger.debug("The Base64 encoded JWT: " + base64EncodedToken);
+            final String errorMessage = "There was an error parsing the Base64-encoded JWT";
+            logger.error(errorMessage, e);
+            throw new JwtException(errorMessage, e);
+        }
+    }
 
+    private Jws<Claims> parseTokenFromBase64EncodedString(final String base64EncodedToken) throws JwtException {
         try {
-            final Jws<Claims> jwt = Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() {
+            // TODO: Check algorithm for validity
+            // TODO: Ensure signature verification occurs
+            return Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() {
                 @Override
                 public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {
                     final String identity = claims.getSubject();
+
+                    // The key is unique per identity and should be retrieved from the key service
                     final String key = keyService.getKey(identity);
 
-                    // ensure we were able to find a key that was previously issued by this key service for this user
+                    // Ensure we were able to find a key that was previously issued by this key service for this user
                     if (key == null) {
                         throw new UnsupportedJwtException("Unable to determine signing key for " + identity);
                     }
 
                     return key.getBytes(StandardCharsets.UTF_8);
                 }
-            }).parseClaimsJws(token);
-            return jwt.getBody().getSubject();
+            }).parseClaimsJws(base64EncodedToken);
         } catch (final MalformedJwtException | UnsupportedJwtException | SignatureException | ExpiredJwtException | IllegalArgumentException | AdministrationException e) {
-            return null;
+            // TODO: Exercise all exceptions to ensure none leak key material to logs
+            final String errorMessage = "There was an error parsing the Base64-encoded JWT";
+            logger.error(errorMessage, e);
+            throw new JwtException(errorMessage, e);
         }
     }
 
     /**
      * Generates a signed JWT token from the provided (Spring Security) login authentication token.
      *
-     * @param authenticationToken the authentication token
+     * @param authenticationToken
      * @return a signed JWT containing the user identity and the identity provider, Base64-encoded
+     * @throws JwtException
      */
-    public String generateSignedToken(final LoginAuthenticationToken authenticationToken) {
-        // set expiration to one day from now
-        final Calendar calendar = Calendar.getInstance();
-        calendar.setTimeInMillis(calendar.getTimeInMillis() + authenticationToken.getExpiration());
+    public String generateSignedToken(final LoginAuthenticationToken authenticationToken) throws JwtException {
+        if (authenticationToken == null) {
+            throw new IllegalArgumentException("Cannot generate a JWT for a null authentication token");
+        }
+
+        // Set expiration from the token
+        final Calendar expiration = Calendar.getInstance();
+        expiration.setTimeInMillis(authenticationToken.getExpiration());
 
-        // create a token the specified authentication
-        final String identity = authenticationToken.getPrincipal().toString();
+        final Object principal = authenticationToken.getPrincipal();
+        if (principal == null || StringUtils.isEmpty(principal.toString())) {
+            final String errorMessage = "Cannot generate a JWT for a token with an empty identity issued by " + authenticationToken.getIssuer();
+            logger.error(errorMessage);
+            throw new JwtException(errorMessage);
+        }
+
+        // Create a JWT with the specified authentication
+        final String identity = principal.toString();
         final String username = authenticationToken.getName();
 
-        // get/create the key for this user
-        final String key = keyService.getOrCreateKey(identity);
-        final byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
+        try {
+            // Get/create the key for this user
+            final String key = keyService.getOrCreateKey(identity);
+            final byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
+
+            logger.trace("Generating JWT for " + authenticationToken);
 
-        // build the token
-        return Jwts.builder().setSubject(identity).claim("preferred_username", username).setExpiration(calendar.getTime()).signWith(SignatureAlgorithm.HS512, keyBytes).compact();
+            // TODO: Implement "jti" claim with nonce to prevent replay attacks and allow blacklisting of revoked tokens
+
+            // Build the token
+            return Jwts.builder().setSubject(identity)
+                    .setIssuer(authenticationToken.getIssuer())
+                    .setAudience(authenticationToken.getIssuer())
+                    .claim("preferred_username", username)
+                    .setExpiration(expiration.getTime())
+                    .setIssuedAt(Calendar.getInstance().getTime())
+                    .signWith(SIGNATURE_ALGORITHM, keyBytes).compact();
+        } catch (NullPointerException | AdministrationException e) {
+            // TODO: Remove exception handling and pass through
+            final String errorMessage = "Could not retrieve the signing key for JWT";
+            logger.error(errorMessage, e);
+            throw new JwtException(errorMessage, e);
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/3bc11e13/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 0bb0932..a06ee89 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
@@ -19,25 +19,46 @@ package org.apache.nifi.web.security.token;
 import org.apache.nifi.security.util.CertificateUtils;
 import org.springframework.security.authentication.AbstractAuthenticationToken;
 
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
 /**
- * This is an Authentication Token for logging in. Once a user is authenticated, they can be issues an ID token.
+ * This is an Authentication Token for logging in. Once a user is authenticated, they can be issued an ID token.
  */
 public class LoginAuthenticationToken extends AbstractAuthenticationToken {
 
     private final String identity;
     private final String username;
     private final long expiration;
+    private final String issuer;
 
-    public LoginAuthenticationToken(final String identity, final long expiration) {
-        this(identity, null, expiration);
+    /**
+     * Creates a representation of the authentication token for a user.
+     *
+     * @param identity   The unique identifier for this user
+     * @param expiration The relative time to expiration in milliseconds
+     * @param issuer     The IdentityProvider implementation that generated this token
+     */
+    public LoginAuthenticationToken(final String identity, final long expiration, final String issuer) {
+        this(identity, null, expiration, issuer);
     }
 
-    public LoginAuthenticationToken(final String identity, final String username, final long expiration) {
+    /**
+     * Creates a representation of the authentication token for a user.
+     *
+     * @param identity   The unique identifier for this user (cannot be null or empty)
+     * @param username   The preferred username for this user
+     * @param expiration The relative time to expiration in milliseconds
+     * @param issuer     The IdentityProvider implementation that generated this token
+     */
+    public LoginAuthenticationToken(final String identity, final String username, final long expiration, final String issuer) {
         super(null);
         setAuthenticated(true);
         this.identity = identity;
         this.username = username;
-        this.expiration = expiration;
+        this.issuer = issuer;
+        Calendar now = Calendar.getInstance();
+        this.expiration = now.getTimeInMillis() + expiration;
     }
 
     @Override
@@ -50,10 +71,19 @@ public class LoginAuthenticationToken extends AbstractAuthenticationToken {
         return identity;
     }
 
+    /**
+     * Returns the expiration instant in milliseconds. This value is an absolute point in time (i.e. Nov 16, 2015 11:30:00.000 GMT), not a relative time (i.e. 60 minutes). It is calculated by adding the relative expiration from the constructor to the timestamp at object creation.
+     *
+     * @return the expiration in millis
+     */
     public long getExpiration() {
         return expiration;
     }
 
+    public String getIssuer() {
+        return issuer;
+    }
+
     @Override
     public String getName() {
         if (username == null) {
@@ -64,4 +94,28 @@ public class LoginAuthenticationToken extends AbstractAuthenticationToken {
         }
     }
 
+    @Override
+    public String toString() {
+        Calendar expirationTime = Calendar.getInstance();
+        expirationTime.setTimeInMillis(getExpiration());
+        long remainingTime = expirationTime.getTimeInMillis() - Calendar.getInstance().getTimeInMillis();
+
+        SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss.SSS");
+        dateFormat.setTimeZone(expirationTime.getTimeZone());
+        String expirationTimeString = dateFormat.format(expirationTime.getTime());
+
+        return new StringBuilder("LoginAuthenticationToken for ")
+                .append(getName())
+                .append(" issued by ")
+                .append(getIssuer())
+                .append(" expiring at ")
+                .append(expirationTimeString)
+                .append(" [")
+                .append(getExpiration())
+                .append(" ms, ")
+                .append(remainingTime)
+                .append(" ms remaining]")
+                .toString();
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/3bc11e13/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
new file mode 100644
index 0000000..a7e763e
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
@@ -0,0 +1,254 @@
+package org.apache.nifi.web.security.jwt;
+
+import io.jsonwebtoken.JwtException;
+import org.apache.commons.codec.CharEncoding;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.nifi.admin.service.AdministrationException;
+import org.apache.nifi.admin.service.KeyService;
+import org.apache.nifi.web.security.token.LoginAuthenticationToken;
+import org.codehaus.jettison.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
+
+/**
+ * Created by alopresto on 11/11/15.
+ */
+public class JwtServiceTest {
+
+    private static final Logger logger = LoggerFactory.getLogger(JwtServiceTest.class);
+
+    /**
+     * These constant strings were generated using the tool at http://jwt.io
+     */
+
+    private static final String VALID_SIGNED_TOKEN = "";
+    private static final String INVALID_SIGNED_TOKEN = "";
+    private static final String VALID_UNSIGNED_TOKEN = "";
+    private static final String INVALID_UNSIGNED_TOKEN = "";
+    private static final String VALID_MALSIGNED_TOKEN = "";
+    private static final String INVALID_MALSIGNED_TOKEN = "";
+
+    private static final String DEFAULT_HEADER = "{\"alg\":\"HS256\"}";
+
+    private static final String TOKEN_DELIMITER = ".";
+
+    private static final String HMAC_SECRET = "test_hmac_shared_secret";
+
+    private KeyService mockKeyService;
+
+    // Class under test
+    private JwtService jwtService;
+
+
+    private String generateHS256Token(String rawHeader, String rawPayload, boolean isValid, boolean isSigned) {
+        return generateHS256Token(rawHeader, rawPayload, HMAC_SECRET, isValid, isSigned);
+    }
+
+    private String generateHS256Token(String rawHeader, String rawPayload, String hmacSecret, boolean isValid, boolean isSigned) {
+        try {
+            logger.info("Generating token for " + rawHeader + " + " + rawPayload);
+
+            String base64Header = Base64.encodeBase64URLSafeString(rawHeader.getBytes(CharEncoding.UTF_8));
+            String base64Payload = Base64.encodeBase64URLSafeString(rawPayload.getBytes(CharEncoding.UTF_8));
+            // TODO: Support valid/invalid manipulation
+
+            final String body = base64Header + TOKEN_DELIMITER + base64Payload;
+
+            String signature = generateHMAC(hmacSecret, body);
+
+            return body + TOKEN_DELIMITER + signature;
+        } catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
+            final String errorMessage = "Could not generate the token";
+            logger.error(errorMessage, e);
+            fail(errorMessage);
+            return null;
+        }
+    }
+
+    private String generateHMAC(String hmacSecret, String body) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
+        Mac hmacSHA256 = Mac.getInstance("HmacSHA256");
+        SecretKeySpec secret_key = new SecretKeySpec(hmacSecret.getBytes("UTF-8"), "HmacSHA256");
+        hmacSHA256.init(secret_key);
+        return Base64.encodeBase64URLSafeString(hmacSHA256.doFinal(body.getBytes("UTF-8")));
+    }
+
+
+    @Before
+    public void setUp() throws Exception {
+        mockKeyService = Mockito.mock(KeyService.class);
+        when(mockKeyService.getOrCreateKey(anyString())).thenReturn(HMAC_SECRET);
+        jwtService = new JwtService(mockKeyService);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+
+    }
+
+    @Test
+    public void testShouldGetAuthenticationForValidToken() throws Exception {
+
+    }
+
+    @Test
+    public void testShouldNotGetAuthenticationForInvalidToken() throws Exception {
+        // Arrange
+        String token = INVALID_SIGNED_TOKEN;
+
+        String header = "{" +
+                "  \"alg\":\"HS256\"" +
+                "}";
+        String payload = "{" +
+                "  \"sub\":\"alopresto\"," +
+                "  \"preferred_username\":\"alopresto\"," +
+                "  \"exp\":2895419760" +
+                "}";
+
+        // Act
+        logger.info("Test token: " + generateHS256Token(header, payload, true, true));
+
+
+        // Assert
+
+
+    }
+
+    @Test
+    public void testShouldNotGetAuthenticationForEmptyToken() throws Exception {
+
+    }
+
+    @Test
+    public void testShouldNotGetAuthenticationForUnsignedToken() throws Exception {
+
+    }
+
+    @Test
+    public void testShouldNotGetAuthenticationForTokenWithWrongAlgorithm() throws Exception {
+
+    }
+
+    @Test
+    public void testShouldGenerateSignedToken() throws Exception {
+        // Arrange
+
+        // Token expires in 60 seconds
+        final int EXPIRATION_MILLIS = 60000;
+        LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken("alopresto", EXPIRATION_MILLIS, "MockIdentityProvider");
+        logger.debug("Generating token for " + loginAuthenticationToken);
+
+        final String EXPECTED_HEADER = DEFAULT_HEADER;
+
+        // Convert the expiration time from ms to s
+        final long TOKEN_EXPIRATION_SEC = (long) (loginAuthenticationToken.getExpiration() / 1000.0);
+
+        // Act
+        String token = jwtService.generateSignedToken(loginAuthenticationToken);
+        logger.debug("Generated JWT: " + token);
+
+        // Run after the SUT generates the token to ensure the same issued at time
+
+        // Split the token, decode the middle section, and form a new String
+        final String DECODED_PAYLOAD = new String(Base64.decodeBase64(token.split("\\.")[1].getBytes()));
+        final long ISSUED_AT_SEC = Long.valueOf(DECODED_PAYLOAD.substring(DECODED_PAYLOAD.lastIndexOf(":") + 1, DECODED_PAYLOAD.length() - 1));
+        logger.trace("Actual token was issued at " + ISSUED_AT_SEC);
+
+        // Always use LinkedHashMap to enforce order of the keys because the signature depends on order
+        Map<String, Object> claims = new LinkedHashMap<>();
+        claims.put("sub", "alopresto");
+        claims.put("iss", "MockIdentityProvider");
+        claims.put("aud", "MockIdentityProvider");
+        claims.put("preferred_username", "alopresto");
+        claims.put("exp", TOKEN_EXPIRATION_SEC);
+        claims.put("iat", ISSUED_AT_SEC);
+        logger.trace("JSON Object to String: " + new JSONObject(claims).toString());
+
+        final String EXPECTED_PAYLOAD = new JSONObject(claims).toString();
+        final String EXPECTED_TOKEN_STRING = generateHS256Token(EXPECTED_HEADER, EXPECTED_PAYLOAD, true, true);
+        logger.debug("Expected JWT: " + EXPECTED_TOKEN_STRING);
+
+        // Assert
+        assertEquals("JWT token", EXPECTED_TOKEN_STRING, token);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testShouldNotGenerateTokenWithNullAuthenticationToken() throws Exception {
+        // Arrange
+        LoginAuthenticationToken nullLoginAuthenticationToken = null;
+        logger.debug("Generating token for " + nullLoginAuthenticationToken);
+
+        // Act
+        jwtService.generateSignedToken(nullLoginAuthenticationToken);
+
+        // Assert
+
+        // Should throw exception
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGenerateTokenWithEmptyIdentity() throws Exception {
+        // Arrange
+        final int EXPIRATION_MILLIS = 60000;
+        LoginAuthenticationToken emptyIdentityLoginAuthenticationToken = new LoginAuthenticationToken("", EXPIRATION_MILLIS, "MockIdentityProvider");
+        logger.debug("Generating token for " + emptyIdentityLoginAuthenticationToken);
+
+        // Act
+        jwtService.generateSignedToken(emptyIdentityLoginAuthenticationToken);
+
+        // Assert
+
+        // Should throw exception
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGenerateTokenWithNullIdentity() throws Exception {
+        // Arrange
+        final int EXPIRATION_MILLIS = 60000;
+        LoginAuthenticationToken nullIdentityLoginAuthenticationToken = new LoginAuthenticationToken(null, EXPIRATION_MILLIS, "MockIdentityProvider");
+        logger.debug("Generating token for " + nullIdentityLoginAuthenticationToken);
+
+        // Act
+        jwtService.generateSignedToken(nullIdentityLoginAuthenticationToken);
+
+        // Assert
+
+        // Should throw exception
+    }
+
+    @Test(expected = JwtException.class)
+    public void testShouldNotGenerateTokenWithMissingKey() throws Exception {
+        // Arrange
+        final int EXPIRATION_MILLIS = 60000;
+        LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken("alopresto", EXPIRATION_MILLIS, "MockIdentityProvider");
+        logger.debug("Generating token for " + loginAuthenticationToken);
+
+        // Set up the bad key service
+        KeyService missingKeyService = Mockito.mock(KeyService.class);
+        when(missingKeyService.getOrCreateKey(anyString())).thenThrow(new AdministrationException("Could not find a key for that user"));
+        jwtService = new JwtService(missingKeyService);
+
+        // Act
+        jwtService.generateSignedToken(loginAuthenticationToken);
+
+        // Assert
+
+        // Should throw exception
+    }
+}
\ No newline at end of file


[06/51] [abbrv] nifi git commit: NIFI-655: - Using the user identity provided by the login identity provider.

Posted by mc...@apache.org.
NIFI-655:
- Using the user identity provided by the login identity provider.

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

Branch: refs/heads/master
Commit: 16608aa850c7e2e4b85342cb749f8a83bed54041
Parents: 4bb8b13
Author: Matt Gilman <ma...@gmail.com>
Authored: Tue Nov 17 19:01:07 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Tue Nov 17 19:01:07 2015 -0500

----------------------------------------------------------------------
 .../src/main/java/org/apache/nifi/web/api/AccessResource.java      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/16608aa8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
index 57de41d..8f4cd5a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -334,7 +334,7 @@ public class AccessResource extends ApplicationResource {
                 }
                 
                 // create the authentication token
-                loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getUsername(), expiration);
+                loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getIdentity(), expiration);
             } catch (final InvalidLoginCredentialsException ilce) {
                 throw new IllegalArgumentException("The supplied username and password are not valid.", ilce);
             } catch (final IdentityAccessException iae) {


[03/51] [abbrv] nifi git commit: NIFI-655: - Refactoring certificate extraction and validation. - Refactoring how expiration is specified in the login identity providers. - Adding unit tests for the access endpoints. - Code clean up.

Posted by mc...@apache.org.
http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/ProxiedEntitiesUtils.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/ProxiedEntitiesUtils.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/ProxiedEntitiesUtils.java
index ca42059..1b2f28a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/ProxiedEntitiesUtils.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/ProxiedEntitiesUtils.java
@@ -16,15 +16,12 @@
  */
 package org.apache.nifi.web.security;
 
-import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import org.apache.nifi.web.security.x509.SubjectDnX509PrincipalExtractor;
-import org.apache.nifi.web.security.x509.X509CertificateExtractor;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.user.NiFiUser;
 import org.springframework.security.core.Authentication;
@@ -42,33 +39,50 @@ public class ProxiedEntitiesUtils {
     private static final Pattern proxyChainPattern = Pattern.compile("<(.*?)>");
 
     /**
-     * @param request http request
-     * @return the X-ProxiedEntitiesChain from the specified request
+     * Formats the specified DN to be set as a HTTP header using well known conventions.
+     *
+     * @param dn raw dn
+     * @return the dn formatted as an HTTP header
      */
-    public static String getXProxiedEntitiesChain(final HttpServletRequest request) {
-        String xProxiedEntitiesChain = request.getHeader("X-ProxiedEntitiesChain");
-        final X509Certificate cert = new X509CertificateExtractor().extractClientCertificate(request);
-        if (cert != null) {
-            final SubjectDnX509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
-            final String extractedPrincipal = principalExtractor.extractPrincipal(cert).toString();
-            final String formattedPrincipal = formatProxyDn(extractedPrincipal);
-            if (StringUtils.isBlank(xProxiedEntitiesChain)) {
-                xProxiedEntitiesChain = formattedPrincipal;
-            } else {
-                xProxiedEntitiesChain += formattedPrincipal;
-            }
+    public static String formatProxyDn(String dn) {
+        return "<" + dn + ">";
+    }
+
+    /**
+     * Tokenizes the specified proxy chain.
+     *
+     * @param rawProxyChain raw chain
+     * @return tokenized proxy chain
+     */
+    public static List<String> tokenizeProxiedEntitiesChain(String rawProxyChain) {
+        final List<String> proxyChain = new ArrayList<>();
+        final Matcher rawProxyChainMatcher = proxyChainPattern.matcher(rawProxyChain);
+        while (rawProxyChainMatcher.find()) {
+            proxyChain.add(rawProxyChainMatcher.group(1));
         }
 
-        return xProxiedEntitiesChain;
+        return proxyChain;
+    }
+
+    /**
+     * Builds the proxy chain for the specified user.
+     *
+     * @param user The current user
+     * @return The proxy chain for that user in String form
+     */
+    public static String buildProxiedEntitiesChainString(final NiFiUser user) {
+        // calculate the dn chain
+        final List<String> proxyChain = buildProxiedEntitiesChain(user);
+        return formatProxyDn(StringUtils.join(proxyChain, "><"));
     }
 
     /**
-     * Builds the dn chain for the specified user.
+     * Builds the proxy chain for the specified user.
      *
      * @param user The current user
-     * @return The dn chain for that user
+     * @return The proxy chain for that user in List form
      */
-    public static List<String> getXProxiedEntitiesChain(final NiFiUser user) {
+    public static List<String> buildProxiedEntitiesChain(final NiFiUser user) {
         // calculate the dn chain
         final List<String> proxyChain = new ArrayList<>();
 
@@ -86,56 +100,25 @@ public class ProxiedEntitiesUtils {
     }
 
     /**
-     * Formats the specified DN to be set as a HTTP header using well known conventions.
+     * Builds the proxy chain from the specified request and user.
      *
-     * @param dn raw dn
-     * @return the dn formatted as an HTTP header
+     * @param request the request
+     * @param username the username
+     * @return the proxy chain in list form
      */
-    public static String formatProxyDn(String dn) {
-        return "<" + dn + ">";
+    public static List<String> buildProxiedEntitiesChain(final HttpServletRequest request, final String username) {
+        final String chain = buildProxiedEntitiesChainString(request, username);
+        return tokenizeProxiedEntitiesChain(chain);
     }
 
-//    /**
-//     * Tokenizes the specified proxy chain.
-//     *
-//     * @param rawProxyChain raw chain
-//     * @return tokenized proxy chain
-//     */
-//    public static Deque<String> tokenizeProxyChain(String rawProxyChain) {
-//        final Deque<String> dnList = new ArrayDeque<>();
-//
-//        // parse the proxy chain
-//        final Matcher rawProxyChainMatcher = proxyChainPattern.matcher(rawProxyChain);
-//        while (rawProxyChainMatcher.find()) {
-//            dnList.push(rawProxyChainMatcher.group(1));
-//        }
-//
-//        return dnList;
-//    }
-    public static List<String> buildProxyChain(final HttpServletRequest request, final String username) {
-        String principal;
-        if (username.startsWith("<") && username.endsWith(">")) {
-            principal = username;
-        } else {
-            principal = formatProxyDn(username);
-        }
-
-        // look for a proxied user
-        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
-            principal = request.getHeader(PROXY_ENTITIES_CHAIN) + principal;
-        }
-
-        // parse the proxy chain
-        final List<String> proxyChain = new ArrayList<>();
-        final Matcher rawProxyChainMatcher = proxyChainPattern.matcher(principal);
-        while (rawProxyChainMatcher.find()) {
-            proxyChain.add(rawProxyChainMatcher.group(1));
-        }
-
-        return proxyChain;
-    }
-
-    public static String extractProxiedEntitiesChain(final HttpServletRequest request, final String username) {
+    /**
+     * Builds the dn chain from the specified request and user.
+     *
+     * @param request the request
+     * @param username the username
+     * @return the dn chain in string form
+     */
+    public static String buildProxiedEntitiesChainString(final HttpServletRequest request, final String username) {
         String principal;
         if (username.startsWith("<") && username.endsWith(">")) {
             principal = username;

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.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/authorization/NiFiAuthorizationService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
index 7fb8e5c..23d9e61 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
@@ -54,10 +54,8 @@ public class NiFiAuthorizationService implements AuthenticationUserDetailsServic
     /**
      * Loads the user details for the specified dn.
      *
-     * Synchronizing because we want each request to be authorized atomically
-     * since each may contain any number of DNs. We wanted an access decision
-     * made for each individual request as a whole (without other request
-     * potentially impacting it).
+     * Synchronizing because we want each request to be authorized atomically since each may contain any number of DNs. We wanted an access decision made for each individual request as a whole
+     * (without other request potentially impacting it).
      *
      * @param request request
      * @return user details
@@ -109,9 +107,6 @@ public class NiFiAuthorizationService implements AuthenticationUserDetailsServic
 
                             // attempt to create a new user account for the proxying client
                             userService.createPendingUserAccount(dn, "Automatic account request generated for unknown proxy.");
-
-                            // propagate the exception to return the appropriate response
-                            throw new UsernameNotFoundException(String.format("An account request was generated for the proxy '%s'.", dn));
                         } catch (AdministrationException ae) {
                             throw new AuthenticationServiceException(String.format("Unable to create an account request for '%s': %s", dn, ae.getMessage()), ae);
                         } catch (IllegalArgumentException iae) {
@@ -122,10 +117,10 @@ public class NiFiAuthorizationService implements AuthenticationUserDetailsServic
                             throw new AccountStatusException(message) {
                             };
                         }
-                    } else {
-                        logger.warn(String.format("Untrusted proxy '%s' must be authorized with '%s' authority: %s", dn, Authority.ROLE_PROXY.toString(), unfe.getMessage()));
-                        throw new UntrustedProxyException(String.format("Untrusted proxy '%s' must be authorized with '%s'.", dn, Authority.ROLE_PROXY.toString()));
                     }
+
+                    logger.warn(String.format("Untrusted proxy '%s' must be authorized with '%s' authority: %s", dn, Authority.ROLE_PROXY.toString(), unfe.getMessage()));
+                    throw new UntrustedProxyException(String.format("Untrusted proxy '%s' must be authorized with '%s'.", dn, Authority.ROLE_PROXY.toString()));
                 } catch (AuthenticationException ae) {
                     logger.warn(String.format("Untrusted proxy '%s' must be authorized with '%s' authority: %s", dn, Authority.ROLE_PROXY.toString(), ae.getMessage()));
                     throw new UntrustedProxyException(String.format("Untrusted proxy '%s' must be authorized with '%s'.", dn, Authority.ROLE_PROXY.toString()));

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.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/JwtAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
index 22d9104..5a84e93 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
@@ -25,7 +25,6 @@ import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
 import org.apache.nifi.web.security.user.NewAccountRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.security.core.Authentication;
 
 /**
  */
@@ -36,7 +35,7 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
     private JwtService jwtService;
 
     @Override
-    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
+    public NiFiAuthenticationRequestToken attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
         // only suppport jwt login when running securely
         if (!request.isSecure()) {
             return null;

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/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 2f6c726..25e52d6 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
@@ -82,7 +82,7 @@ public class JwtService {
     /**
      * Generates a signed JWT token from the provided (Spring Security) login authentication token.
      *
-     * @param authenticationToken
+     * @param authenticationToken the authentication token
      * @return a signed JWT containing the user identity and the identity provider, Base64-encoded
      */
     public String generateSignedToken(final LoginAuthenticationToken authenticationToken) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.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/node/NodeAuthorizedUserFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java
index d1207b4..a3e6c3c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java
@@ -26,34 +26,26 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import org.apache.nifi.controller.FlowController;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.authentication.AuthenticationResponse;
 import org.apache.nifi.web.security.user.NiFiUserDetails;
-import org.apache.nifi.web.security.x509.SubjectDnX509PrincipalExtractor;
-import org.apache.nifi.web.security.x509.X509CertificateExtractor;
 import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.token.NiFiAuthorizationToken;
+import org.apache.nifi.web.security.x509.X509CertificateExtractor;
+import org.apache.nifi.web.security.x509.X509IdentityProvider;
 import org.apache.nifi.web.util.WebUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.ApplicationContext;
-import org.springframework.security.authentication.AuthenticationDetailsSource;
 import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
-import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
-import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
 import org.springframework.web.context.support.WebApplicationContextUtils;
 import org.springframework.web.filter.GenericFilterBean;
 
 /**
- * Custom filter to extract a user's authorities from the request where the user
- * was authenticated by the cluster manager and populate the threadlocal with
- * the authorized user. If the request contains the appropriate header with
- * authorities and the application instance is a node connected to the cluster,
- * then the authentication/authorization steps remaining in the filter chain are
- * skipped.
+ * Custom filter to extract a user's authorities from the request where the user was authenticated by the cluster manager and populate the threadlocal with the authorized user. If the request contains
+ * the appropriate header with authorities and the application instance is a node connected to the cluster, then the authentication/authorization steps remaining in the filter chain are skipped.
  *
- * Checking if the application instance is a connected node is important because
- * it prevents external clients from faking the request headers and bypassing
- * the authentication processing chain.
+ * Checking if the application instance is a connected node is important because it prevents external clients from faking the request headers and bypassing the authentication processing chain.
  */
 public class NodeAuthorizedUserFilter extends GenericFilterBean {
 
@@ -61,14 +53,9 @@ public class NodeAuthorizedUserFilter extends GenericFilterBean {
 
     public static final String PROXY_USER_DETAILS = "X-ProxiedEntityUserDetails";
 
-    private final NiFiProperties properties;
-    private final AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
-    private final X509CertificateExtractor certificateExtractor = new X509CertificateExtractor();
-    private final X509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
-
-    public NodeAuthorizedUserFilter(NiFiProperties properties) {
-        this.properties = properties;
-    }
+    private NiFiProperties properties;
+    private X509CertificateExtractor certificateExtractor;
+    private X509IdentityProvider certificateIdentityProvider;
 
     @Override
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
@@ -87,16 +74,15 @@ public class NodeAuthorizedUserFilter extends GenericFilterBean {
             // check that we are connected to the cluster
             if (flowController.getNodeId() != null) {
                 try {
-                    // get the DN from the cert in the request
-                    final X509Certificate certificate = certificateExtractor.extractClientCertificate((HttpServletRequest) request);
+                    // attempt to extract the client certificate
+                    final X509Certificate[] certificate = certificateExtractor.extractClientCertificate(httpServletRequest);
                     if (certificate != null) {
-                        // extract the principal from the certificate
-                        final Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
-                        final String dn = certificatePrincipal.toString();
+                        // authenticate the certificate
+                        final AuthenticationResponse authenticationResponse = certificateIdentityProvider.authenticate(certificate);
 
-                        // only consider the pre-authorized user when the request came from the NCM according to the DN in the certificate
-                        final String clusterManagerDN = flowController.getClusterManagerDN();
-                        if (clusterManagerDN != null && clusterManagerDN.equals(dn)) {
+                        // only consider the pre-authorized user when the request came directly from the NCM according to the DN in the certificate
+                        final String clusterManagerIdentity = flowController.getClusterManagerDN();
+                        if (clusterManagerIdentity != null && clusterManagerIdentity.equals(authenticationResponse.getIdentity())) {
                             // deserialize hex encoded object
                             final Serializable userDetailsObj = WebUtils.deserializeHexToObject(hexEncodedUserDetails);
 
@@ -109,19 +95,33 @@ public class NodeAuthorizedUserFilter extends GenericFilterBean {
                                 logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)", user.getIdentity(), httpServletRequest.getMethod(),
                                         httpServletRequest.getRequestURL().toString(), request.getRemoteAddr()));
 
-                                // we do not create the authentication token with the X509 certificate because the certificate is from the sending system, not the proxied user
-                                final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(userDetails, null, userDetails.getAuthorities());
-                                token.setDetails(authenticationDetailsSource.buildDetails(request));
+                                // create the authorized nifi token
+                                final NiFiAuthorizationToken token = new NiFiAuthorizationToken(userDetails);
                                 SecurityContextHolder.getContext().setAuthentication(token);
                             }
                         }
                     }
                 } catch (final ClassNotFoundException cnfe) {
                     LOGGER.warn("Classpath issue detected because failed to deserialize authorized user in request header due to: " + cnfe, cnfe);
+                } catch (final IllegalArgumentException iae) {
+                    // unable to authenticate a serialized user from the incoming request
                 }
             }
         }
 
         chain.doFilter(request, response);
     }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    public void setCertificateIdentityProvider(X509IdentityProvider certificateIdentityProvider) {
+        this.certificateIdentityProvider = certificateIdentityProvider;
+    }
+
+    public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
+        this.certificateExtractor = certificateExtractor;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.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/spring/LoginIdentityProviderFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
index e115b01..92a27ae 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
@@ -267,13 +267,6 @@ public class LoginIdentityProviderFactoryBean implements FactoryBean, Disposable
             }
 
             @Override
-            public long getExpiration() {
-                try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
-                    return baseProvider.getExpiration();
-                }
-            }
-
-            @Override
             public void initialize(LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException {
                 try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
                     baseProvider.initialize(initializationContext);

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.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/x509/X509AuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
index f84231f..e626f74 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
@@ -16,12 +16,11 @@
  */
 package org.apache.nifi.web.security.x509;
 
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.X509Certificate;
 import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import org.apache.nifi.authentication.AuthenticationResponse;
 import org.apache.nifi.web.security.NiFiAuthenticationFilter;
 import org.apache.nifi.web.security.ProxiedEntitiesUtils;
 import org.apache.nifi.web.security.token.NewAccountAuthenticationRequestToken;
@@ -29,8 +28,7 @@ import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
 import org.apache.nifi.web.security.user.NewAccountRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
+import org.springframework.security.authentication.BadCredentialsException;
 
 /**
  * Custom X509 filter that will inspect the HTTP headers for a proxied user before extracting the user details from the client certificate.
@@ -39,54 +37,31 @@ public class X509AuthenticationFilter extends NiFiAuthenticationFilter {
 
     private static final Logger logger = LoggerFactory.getLogger(X509AuthenticationFilter.class);
 
-    private X509PrincipalExtractor principalExtractor;
     private X509CertificateExtractor certificateExtractor;
-    private X509CertificateValidator certificateValidator;
+    private X509IdentityProvider certificateIdentityProvider;
 
     @Override
-    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
+    public NiFiAuthenticationRequestToken attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
         // only suppport x509 login when running securely
         if (!request.isSecure()) {
             return null;
         }
 
-        // extract the cert
-        X509Certificate certificate = certificateExtractor.extractClientCertificate(request);
-
-        // ensure the cert was found
-        if (certificate == null) {
+        // look for a client certificate
+        final X509Certificate[] certificates = certificateExtractor.extractClientCertificate(request);
+        if (certificates == null) {
             return null;
         }
 
-        // extract the principal
-        Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
-        final String principal = ProxiedEntitiesUtils.formatProxyDn(certificatePrincipal.toString());
-
+        // attempt to authenticate if certificates were found
+        final AuthenticationResponse authenticationResponse;
         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;
+            authenticationResponse = certificateIdentityProvider.authenticate(certificates);
+        } catch (final IllegalArgumentException iae) {
+            throw new BadCredentialsException(iae.getMessage(), iae);
         }
 
-        final List<String> proxyChain = ProxiedEntitiesUtils.buildProxyChain(request, principal);
+        final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(request, authenticationResponse.getIdentity());
         if (isNewAccountRequest(request)) {
             return new NewAccountAuthenticationRequestToken(new NewAccountRequest(proxyChain, getJustification(request)));
         } else {
@@ -95,16 +70,12 @@ public class X509AuthenticationFilter extends NiFiAuthenticationFilter {
     }
 
     /* setters */
-    public void setCertificateValidator(X509CertificateValidator certificateValidator) {
-        this.certificateValidator = certificateValidator;
-    }
-
-    public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) {
-        this.principalExtractor = principalExtractor;
-    }
-
     public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
         this.certificateExtractor = certificateExtractor;
     }
 
+    public void setCertificateIdentityProvider(X509IdentityProvider certificateIdentityProvider) {
+        this.certificateIdentityProvider = certificateIdentityProvider;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilterOld.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/x509/X509AuthenticationFilterOld.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilterOld.java
deleted file mode 100644
index 711639b..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilterOld.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web.security.x509;
-
-import org.apache.nifi.web.security.x509.ocsp.OcspCertificateValidator;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.X509Certificate;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.apache.nifi.admin.service.AdministrationException;
-import org.apache.nifi.admin.service.UserService;
-import org.apache.nifi.web.security.ProxiedEntitiesUtils;
-import org.apache.nifi.web.security.UntrustedProxyException;
-import org.apache.nifi.util.NiFiProperties;
-import org.apache.commons.lang3.StringUtils;
-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.preauth.AbstractPreAuthenticatedProcessingFilter;
-import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
-
-/**
- * Custom X509 filter that will inspect the HTTP headers for a proxied user
- * before extracting the user details from the client certificate.
- */
-public class X509AuthenticationFilterOld extends AbstractPreAuthenticatedProcessingFilter {
-
-    public static final String PROXY_ENTITIES_CHAIN = "X-ProxiedEntitiesChain";
-    public static final String PROXY_ENTITIES_ACCEPTED = "X-ProxiedEntitiesAccepted";
-    public static final String PROXY_ENTITIES_DETAILS = "X-ProxiedEntitiesDetails";
-
-    private final X509CertificateExtractor certificateExtractor = new X509CertificateExtractor();
-    private final X509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
-    private OcspCertificateValidator certificateValidator;
-    private NiFiProperties properties;
-    private UserService userService;
-
-    @Override
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-        final HttpServletResponse httpResponse = (HttpServletResponse) response;
-
-        // determine if this request is attempting to create a new account
-        if (isNewAccountRequest((HttpServletRequest) request)) {
-            // determine if this nifi supports new account requests
-            if (properties.getSupportNewAccountRequests()) {
-                // ensure there is a certificate in the request
-                X509Certificate certificate = certificateExtractor.extractClientCertificate((HttpServletRequest) request);
-                if (certificate != null) {
-                    // extract the principal from the certificate
-                    Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
-                    String principal = certificatePrincipal.toString();
-
-                    // log the new user account request
-                    logger.info("Requesting new user account for " + principal);
-
-                    try {
-                        // get the justification
-                        String justification = request.getParameter("justification");
-                        if (justification == null) {
-                            justification = StringUtils.EMPTY;
-                        }
-
-                        // create the pending user account
-                        userService.createPendingUserAccount(principal, justification);
-
-                        // generate a response
-                        httpResponse.setStatus(HttpServletResponse.SC_CREATED);
-                        httpResponse.setContentType("text/plain");
-
-                        // write the response message
-                        PrintWriter out = response.getWriter();
-                        out.println("Not authorized. User account created. Authorization pending.");
-                    } catch (IllegalArgumentException iae) {
-                        handleUserServiceError((HttpServletRequest) request, httpResponse, HttpServletResponse.SC_BAD_REQUEST, iae.getMessage());
-                    } catch (AdministrationException ae) {
-                        handleUserServiceError((HttpServletRequest) request, httpResponse, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ae.getMessage());
-                    }
-                } else {
-                    // can this really happen?
-                    handleMissingCertificate((HttpServletRequest) request, httpResponse);
-                }
-            } else {
-                handleUserServiceError((HttpServletRequest) request, httpResponse, HttpServletResponse.SC_NOT_FOUND, "This NiFi does not support new account requests.");
-            }
-        } else {
-            try {
-                // this not a request to create a user account - try to authorize
-                super.doFilter(request, response, chain);
-            } catch (AuthenticationException ae) {
-                // continue the filter chain since anonymous access should be supported
-                if (!properties.getNeedClientAuth()) {
-                    chain.doFilter(request, response);
-                } else {
-                    // create an appropriate response for the given exception
-                    handleUnsuccessfulAuthentication((HttpServletRequest) request, httpResponse, ae);
-                }
-            }
-        }
-    }
-
-    @Override
-    protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
-        String principal;
-
-        // extract the cert
-        X509Certificate certificate = certificateExtractor.extractClientCertificate(request);
-
-        // ensure the cert was found
-        if (certificate == null) {
-            return null;
-        }
-
-        // extract the principal
-        Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
-        principal = ProxiedEntitiesUtils.formatProxyDn(certificatePrincipal.toString());
-
-        try {
-            // ensure the cert is valid
-            certificate.checkValidity();
-        } 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;
-        }
-
-        // validate the certificate in question
-        try {
-            certificateValidator.validate(request);
-        } catch (final Exception e) {
-            logger.info(e.getMessage());
-            if (logger.isDebugEnabled()) {
-                logger.debug("", e);
-            }
-            return null;
-        }
-
-        // look for a proxied user
-        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
-            principal = request.getHeader(PROXY_ENTITIES_CHAIN) + principal;
-        }
-
-        // log the request attempt - response details will be logged later
-        logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)", principal, request.getMethod(),
-                request.getRequestURL().toString(), request.getRemoteAddr()));
-
-        return principal;
-    }
-
-    @Override
-    protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
-        return certificateExtractor.extractClientCertificate(request);
-    }
-
-    @Override
-    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
-        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
-            response.setHeader(PROXY_ENTITIES_ACCEPTED, Boolean.TRUE.toString());
-        }
-        super.successfulAuthentication(request, response, authResult);
-    }
-
-    @Override
-    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
-        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
-            response.setHeader(PROXY_ENTITIES_DETAILS, failed.getMessage());
-        }
-        super.unsuccessfulAuthentication(request, response, failed);
-    }
-
-    /**
-     * Determines if the specified request is attempting to register a new user
-     * account.
-     *
-     * @param request http request
-     * @return true if new user
-     */
-    private boolean isNewAccountRequest(HttpServletRequest request) {
-        if ("POST".equalsIgnoreCase(request.getMethod())) {
-            String path = request.getPathInfo();
-            if (StringUtils.isNotBlank(path)) {
-                if ("/controller/users".equals(path)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Handles requests that were unable to be authorized.
-     *
-     * @param request request
-     * @param response response
-     * @param ae ex
-     * @throws IOException ex
-     */
-    private void handleUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException ae) throws IOException {
-        // set the response status
-        response.setContentType("text/plain");
-
-        // write the response message
-        PrintWriter out = response.getWriter();
-
-        // use the type of authentication exception to determine the response code
-        if (ae instanceof UsernameNotFoundException) {
-            if (properties.getSupportNewAccountRequests()) {
-                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-                out.println("Not authorized.");
-            } else {
-                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-                out.println("Access is denied.");
-            }
-        } else if (ae instanceof AccountStatusException) {
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println(ae.getMessage());
-        } else if (ae instanceof UntrustedProxyException) {
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println(ae.getMessage());
-        } else if (ae instanceof AuthenticationServiceException) {
-            logger.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
-            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-            out.println(String.format("Unable to authorize: %s", ae.getMessage()));
-        } else {
-            logger.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println("Access is denied.");
-        }
-
-        // log the failure
-        logger.info(String.format("Rejecting access to web api: %s", ae.getMessage()));
-
-        // optionally log the stack trace
-        if (logger.isDebugEnabled()) {
-            logger.debug(StringUtils.EMPTY, ae);
-        }
-    }
-
-    private void handleUserServiceError(HttpServletRequest request, HttpServletResponse response, int responseCode, String message) throws IOException {
-        // set the response status
-        response.setContentType("text/plain");
-        response.setStatus(responseCode);
-
-        // write the response message
-        PrintWriter out = response.getWriter();
-        out.println(message);
-
-        // log the failure
-        logger.info(String.format("Unable to process request because %s", message));
-    }
-
-    /**
-     * Handles requests that failed because they were bad input.
-     *
-     * @param request request
-     * @param response response
-     * @throws IOException ioe
-     */
-    private void handleMissingCertificate(HttpServletRequest request, HttpServletResponse response) throws IOException {
-        // set the response status
-        response.setContentType("text/plain");
-        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-
-        // write the response message
-        PrintWriter out = response.getWriter();
-        out.println("Unable to process request because the user certificate was not specified.");
-
-        // log the failure
-        logger.info("Unable to process request because the user certificate was not specified.");
-    }
-
-    /* setters */
-    public void setProperties(NiFiProperties properties) {
-        this.properties = properties;
-    }
-
-    public void setUserService(UserService userService) {
-        this.userService = userService;
-    }
-
-    public void setCertificateValidator(OcspCertificateValidator certificateValidator) {
-        this.certificateValidator = certificateValidator;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.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/x509/X509CertificateExtractor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.java
index b40d5a5..98d0154 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.java
@@ -35,11 +35,11 @@ public class X509CertificateExtractor {
      * @param request http request
      * @return cert
      */
-    public X509Certificate extractClientCertificate(HttpServletRequest request) {
+    public X509Certificate[] extractClientCertificate(HttpServletRequest request) {
         X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
 
         if (certs != null && certs.length > 0) {
-            return certs[0];
+            return certs;
         }
 
         if (logger.isDebugEnabled()) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateValidator.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/x509/X509CertificateValidator.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateValidator.java
index 06b5148..cb56a11 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateValidator.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateValidator.java
@@ -19,7 +19,6 @@ package org.apache.nifi.web.security.x509;
 import java.security.cert.CertificateExpiredException;
 import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.X509Certificate;
-import javax.servlet.http.HttpServletRequest;
 import org.apache.nifi.web.security.x509.ocsp.CertificateStatusException;
 import org.apache.nifi.web.security.x509.ocsp.OcspCertificateValidator;
 import org.slf4j.Logger;
@@ -37,20 +36,19 @@ public class X509CertificateValidator {
     /**
      * Extract the client certificate from the specified HttpServletRequest or null if none is specified.
      *
-     * @param request the request
-     * @param certificate the certificate
+     * @param certificates the client certificates
      * @throws java.security.cert.CertificateExpiredException cert is expired
      * @throws java.security.cert.CertificateNotYetValidException cert is not yet valid
      * @throws org.apache.nifi.web.security.x509.ocsp.CertificateStatusException ocsp validation issue
      */
-    public void validateClientCertificate(final HttpServletRequest request, final X509Certificate certificate)
+    public void validateClientCertificate(final X509Certificate[] certificates)
             throws CertificateExpiredException, CertificateNotYetValidException, CertificateStatusException {
 
         // ensure the cert is valid
-        certificate.checkValidity();
+        certificates[0].checkValidity();
 
         // perform ocsp validator if necessary
-        ocspValidator.validate(request);
+        ocspValidator.validate(certificates);
     }
 
     public void setOcspValidator(OcspCertificateValidator ocspValidator) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.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/x509/X509IdentityProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
new file mode 100644
index 0000000..75a94d3
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
@@ -0,0 +1,92 @@
+/*
+ * 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.x509;
+
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.concurrent.TimeUnit;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
+
+/**
+ * Identity provider for extract the authenticating a ServletRequest with a X509Certificate.
+ */
+public class X509IdentityProvider {
+
+    private static final Logger logger = LoggerFactory.getLogger(X509IdentityProvider.class);
+
+    private X509CertificateValidator certificateValidator;
+    private X509PrincipalExtractor principalExtractor;
+
+    /**
+     * Authenticates the specified request by checking certificate validity.
+     *
+     * @param certificates the client certificates
+     * @return an authentication response
+     * @throws IllegalArgumentException the request did not contain a valid certificate (or no certificate)
+     */
+    public AuthenticationResponse authenticate(final X509Certificate[] certificates) throws IllegalArgumentException {
+        // ensure the cert was found
+        if (certificates == null || certificates.length == 0) {
+            throw new IllegalArgumentException("The specified request does not contain a client certificate.");
+        }
+
+        // extract the principal
+        final Object certificatePrincipal = principalExtractor.extractPrincipal(certificates[0]);
+        final String principal = certificatePrincipal.toString();
+
+        try {
+            certificateValidator.validateClientCertificate(certificates);
+        } 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);
+            }
+            throw new IllegalArgumentException(message, cee);
+        } 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);
+            }
+            throw new IllegalArgumentException(message, cnyve);
+        } catch (final Exception e) {
+            logger.info(e.getMessage());
+            if (logger.isDebugEnabled()) {
+                logger.debug("", e);
+            }
+            throw new IllegalArgumentException(e.getMessage(), e);
+        }
+
+        // build the authentication response
+        return new AuthenticationResponse(principal, principal, TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS));
+    }
+
+    /* setters */
+    public void setCertificateValidator(X509CertificateValidator certificateValidator) {
+        this.certificateValidator = certificateValidator;
+    }
+
+    public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) {
+        this.principalExtractor = principalExtractor;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.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/x509/ocsp/OcspCertificateValidator.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.java
index 81e4bd6..832a63c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.java
@@ -42,7 +42,6 @@ import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 import javax.net.ssl.X509TrustManager;
 import javax.security.auth.x500.X500Principal;
-import javax.servlet.http.HttpServletRequest;
 import org.apache.nifi.framework.security.util.SslContextFactory;
 import org.apache.nifi.web.security.x509.ocsp.OcspStatus.ValidationStatus;
 import org.apache.nifi.web.security.x509.ocsp.OcspStatus.VerificationStatus;
@@ -158,8 +157,7 @@ public class OcspCertificateValidator {
     }
 
     /**
-     * Loads the trusted certificate authorities according to the specified
-     * properties.
+     * Loads the trusted certificate authorities according to the specified properties.
      *
      * @param properties properties
      * @return map of certificate authorities
@@ -208,12 +206,10 @@ public class OcspCertificateValidator {
     /**
      * Validates the specified certificate using OCSP if configured.
      *
-     * @param request http request
+     * @param certificates the client certificates
      * @throws CertificateStatusException ex
      */
-    public void validate(final HttpServletRequest request) throws CertificateStatusException {
-        final X509Certificate[] certificates = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
-
+    public void validate(final X509Certificate[] certificates) throws CertificateStatusException {
         // only validate if configured to do so
         if (client != null && certificates != null && certificates.length > 0) {
             final X509Certificate subjectCertificate = getSubjectCertificate(certificates);
@@ -395,13 +391,9 @@ public class OcspCertificateValidator {
     }
 
     /**
-     * Gets the trusted responder certificate. The response contains the
-     * responder certificate, however we cannot blindly trust it. Instead, we
-     * use a configured trusted CA. If the responder certificate is a trusted
-     * CA, then we can use it. If the responder certificate is not directly
-     * trusted, we still may be able to trust it if it was issued by the same CA
-     * that issued the subject certificate. Other various checks may be required
-     * (this portion is currently not implemented).
+     * Gets the trusted responder certificate. The response contains the responder certificate, however we cannot blindly trust it. Instead, we use a configured trusted CA. If the responder
+     * certificate is a trusted CA, then we can use it. If the responder certificate is not directly trusted, we still may be able to trust it if it was issued by the same CA that issued the subject
+     * certificate. Other various checks may be required (this portion is currently not implemented).
      *
      * @param responderCertificate cert
      * @param issuerCertificate cert

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/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 c88d303..40f678c 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
@@ -33,6 +33,12 @@
         <property name="ocspValidator" ref="ocspValidator"/> 
     </bean>
     
+    <!-- x509 identity provider -->
+    <bean id="certificateIdentityProvider" class="org.apache.nifi.web.security.x509.X509IdentityProvider">
+        <property name="principalExtractor" ref="principalExtractor"/>
+        <property name="certificateValidator" ref="certificateValidator"/>
+    </bean>
+    
     <!-- user details service -->
     <bean id="userDetailsService" class="org.apache.nifi.web.security.authorization.NiFiAuthorizationService">
         <property name="userService" ref="userService"/>

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
index 5a1e859..5456552 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
@@ -187,7 +187,7 @@ public class NiFiAuthorizationServiceTest {
      *
      * @throws Exception ex
      */
-    @Test(expected = UsernameNotFoundException.class)
+    @Test(expected = UntrustedProxyException.class)
     public void testProxyNotFound() throws Exception {
         try {
             authorizationService.loadUserDetails(createRequestAuthentication(USER, PROXY_NOT_FOUND));

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/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 b61988f..89db6ce 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
@@ -27,8 +27,6 @@ nf.Login = (function () {
 
     var config = {
         urls: {
-            registrationStatus: '../nifi-api/registration/status',
-            registration: '../nifi-api/registration',
             identity: '../nifi-api/controller/identity',
             users: '../nifi-api/controller/users',
             token: '../nifi-api/access/token',

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java
index 501eb58..64c48bf 100644
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java
@@ -84,9 +84,9 @@ public abstract class AbstractLdapProvider implements LoginIdentityProvider {
             // attempt to get the ldap user details to get the DN
             if (authentication.getPrincipal() instanceof LdapUserDetails) {
                 final LdapUserDetails userDetails = (LdapUserDetails) authentication.getPrincipal();
-                return new AuthenticationResponse(userDetails.getDn(), credentials.getUsername());
+                return new AuthenticationResponse(userDetails.getDn(), credentials.getUsername(), expiration);
             } else {
-                return new AuthenticationResponse(authentication.getName(), credentials.getUsername());
+                return new AuthenticationResponse(authentication.getName(), credentials.getUsername(), expiration);
             }
         } catch (final CommunicationException | AuthenticationServiceException e) {
             logger.error(e.getMessage());
@@ -100,11 +100,6 @@ public abstract class AbstractLdapProvider implements LoginIdentityProvider {
     }
 
     @Override
-    public long getExpiration() {
-        return expiration;
-    }
-
-    @Override
     public final void preDestruction() throws ProviderDestructionException {
     }
 


[33/51] [abbrv] nifi git commit: Merge branch 'NIFI-655' of https://git-wip-us.apache.org/repos/asf/nifi into NIFI-655

Posted by mc...@apache.org.
Merge branch 'NIFI-655' of https://git-wip-us.apache.org/repos/asf/nifi into NIFI-655


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

Branch: refs/heads/master
Commit: 769f19ee867f69e2d3bf157cae0a1f09ea4f24fa
Parents: aaf14c4 5ef53b6
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 23 15:21:47 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 23 15:21:47 2015 -0500

----------------------------------------------------------------------

----------------------------------------------------------------------



[34/51] [abbrv] nifi git commit: NIFI-655: - Fixing the configuration property name for Authentication Expiration in the provided example configuration.

Posted by mc...@apache.org.
NIFI-655:
- Fixing the configuration property name for Authentication Expiration in the provided example configuration.

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

Branch: refs/heads/master
Commit: a5754986e2d46a13c9b1373d6befba24c08db016
Parents: 769f19e
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 23 15:55:24 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 23 15:55:24 2015 -0500

----------------------------------------------------------------------
 .../src/main/resources/conf/login-identity-providers.xml           | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/a5754986/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/login-identity-providers.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/login-identity-providers.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/login-identity-providers.xml
index 9868b9d..d6a0c2e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/login-identity-providers.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/login-identity-providers.xml
@@ -86,7 +86,7 @@
         <property name="User Search Base"></property>
         <property name="User Search Filter"></property>
 
-        <property name="Expiration Duration">12 hours</property>
+        <property name="Authentication Expiration">12 hours</property>
     </provider>
     To enable the ldap-provider remove 2 lines. This is 2 of 2. -->
 </loginIdentityProviders>
\ No newline at end of file


[32/51] [abbrv] nifi git commit: NIFI-655: - Refactoring web security to use Spring Security Java Configuration. - Introducing security in Web UI in order to get JWT.

Posted by mc...@apache.org.
NIFI-655:
- Refactoring web security to use Spring Security Java Configuration.
- Introducing security in Web UI in order to get JWT.

NIFI-655:
- Setting up the resources (js/css) for the login page.

NIFI-655:
- Adding support for configuring anonymous roles.
- Addressing checkstyle violations.

NIFI-655:
- Moving to token api to web-api.
- Creating an LoginProvider API for user/pass based authentication.
- Creating a module for funneling access to the authorized useres.

NIFI-655:
- Moving away from usage of DN to identity throughout the application (from the user db to the authorization provider).
- Updating the authorized users schema to support login users.
- Creating an extension point for authentication of users based on username/password.

NIFI-655:
- Creating an endpoint for returning the identity of the current user.
- Updating the LoginAuthenticationFilter.

NIFI-655:
- Moving NiFi registration to the login page.
- Running the authentication filters in a different order to ensure we can disambiguate each case.
- Starting to layout each case... Forbidden, Login, Create User, Create NiFi Account.

NIFI-655:
- Addressing checkstyle issues.

NIFI-655:
- Making nf-storage available in the login page.
- Requiring use of local storage.
- Ignoring security for GET requests when obtaining the login configuration.

NIFI-655:
- Adding a new endpoint to obtain the status of a user registration.
- Updated the login page loading to ensure all possible states work.

NIFI-655:
- Ensuring we know the necessary state before we attempt to render the login page.
- Building the proxy chain in the JWT authentication filter.
- Only rendering the login when appropriate.

NIFI-655:
- Starting to style the login page.
- Added simple 'login' support by identifying username/password. Issuing JWT token coming...
- Added logout support
- Rendering the username when appropriate.

NIFI-655:
- Extracting certificate validation into a utility class.
- Fixing checkstyle issues.
- Cleaning up the web security context.
- Removing proxy chain checking where possible.

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

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.

NIFI-655:
- Allowing the user to link back to the log in page from the new account page.
- Renaming DN to identity where possible.

NIFI-655:
- Fixing checkstyle issues.

NIFI-655:
- Adding more/better support for logging out.

NIFI-655:
- Fixing checkstyle issues.

NIFI-655:
- Adding a few new exceptions for the login identity provider.

NIFI-655:
- Disabling log in by default initially.
- Restoring authorization service unit test.

NIFI-655:
- Fixing checkstyle issues.

NIFI-655:
- Updating packages for log in filters.
- Handling new registration exceptions.
- Code clean up.

NIFI-655:
- Removing registration support.
- Removing file based implementation.

NIFI-655:
- Removing file based implementation.

NIFI-655:
- Removing unused spring configuration files.

NIFI-655:
- Making the auto wiring more explicit.

NIFI-655:
- Removing unused dependencies.

NIFI-655:
- Removing unused filter.

NIFI-655:
- Updating the login API authenticate method to use a richer set of exceptions.
- UI code clean.

NIFI-655:
- Ensuring the login identity provider is able to switch context classloaders via the standard NAR mechanisms.

NIFI-655:
- Initial commit of the LDAP based identity providers.
- Fixed issue when attempting to log into a NiFi that does not support new account requests.

NIFI-655:
- Allowing the ldap provider to specify if client authentication is required/desired.

NIFI-655:
- Persisting keys to sign user tokens.
- Allowing the identity provider to specify the token expiration.
- Code clean up.

NIFI-655:
- Ensuring identities are unique in the key table.

NIFI-655:
- Adding support for specifying the user search base and user search filter in the active directory provider.

NIFI-655:
- Fixing checkstyle issues.

NIFI-655:
- Adding automatic client side token renewal.

NIFI-655:
- Ensuring the logout link is rendered when appropriate.

NIFI-655:
- Adding configuration options for referrals and connect/read timeouts

NIFI-655:
- Added an endpoint for access details including configuration, creating tokens, and checking status.
- Updated DTOs and client side to utilize new endpoints.

NIFI-655:
- Refactoring certificate extraction and validation.
- Refactoring how expiration is specified in the login identity providers.
- Adding unit tests for the access endpoints.
- Code clean up.

NIFI-655:
- Keeping token expiration between 1 minute and 12 hours.

NIFI-655:
- Using the user identity provided by the login identity provider.

NIFI-655: - Fixed typo in error message for unrecognized authentication strategy.

Signed-off-by: Matt Gilman <ma...@gmail.com>

NIFI-655. - Added logback-test.xml configuration resource for nifi-web-security.

Signed-off-by: Matt Gilman <ma...@gmail.com>

NIFI-655. - Added issuer field to LoginAuthenticationToken. - Updated AccessResource to pass identity provider class name when creating LoginAuthenticationTokens. - Began refactoring JWT logic from request parsing logic in JwtService. - Added unit tests for JWT logic.

Signed-off-by: Matt Gilman <ma...@gmail.com>

NIFI-655. - Changed issuer field to use FQ class name because some classes return an empty string for getSimpleName(). - Finished refactoring JWT logic from request parsing logic in JwtService. - Updated AccessResource and JwtAuthenticationFilter to call new JwtService methods decoupled from request header parsing. - Added extensive unit tests for JWT logic.

Signed-off-by: Matt Gilman <ma...@gmail.com>

NIFI-655:
- Refactoring key service to expose the key id.
- Handling client side expiration better.
- Removing specialized active directory provider and abstract ldap provider.

NIFI-655. - Updated JwtService and JwtServiceTest to use Key POJO instead of raw String key from KeyService.

Signed-off-by: Matt Gilman <ma...@gmail.com>

NIFI-655:
- Fixing typo when loading the ldap connect timeout.
- Providing a better experience for session expiration.
- Using ellipsis for lengthly user name.
- Adding an issuer to the authentication response so the LIP can specify the appropriate value.

NIFI-655:
- Showing a logging in notification during the log in process.

NIFI-655:
- Removing unnecessary class.

NIFI-655:
- Fixing checkstyle issues.
- Showing the progress spinner while submitting account justification.

NIFI-655:
- Removing deprecated authentication strategy.
- Renaming TLS to START_TLS.
- Allowing the protocol to be configured.

NIFI-655:
- Fixing issue detecting the presence of DN column

NIFI-655:
- Pre-populating the login-identity-providers.xml file with necessary properties and documentation.
- Renaming the Authentication Duration property name.

NIFI-655:
- Updating documentation for the failure response codes.

NIFI-655:
- Ensuring the user identity is not too long.

NIFI-655:
- Updating default authentication expiration to 12 hours.

NIFI-655:
- Remaining on the login form when there is any unsuccessful login attempt.
- Fixing checkstyle issues.


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

Branch: refs/heads/master
Commit: aaf14c45c96077c0075af8f3442e392b717244c1
Parents: 4e2c94d
Author: Matt Gilman <ma...@gmail.com>
Authored: Wed Oct 7 13:33:34 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 23 14:50:13 2015 -0500

----------------------------------------------------------------------
 LICENSE                                         |   22 +
 .../authentication/AuthenticationResponse.java  |   65 +
 .../nifi/authentication/LoginCredentials.java   |   39 +
 .../authentication/LoginIdentityProvider.java   |   61 +
 ...ginIdentityProviderConfigurationContext.java |   48 +
 ...inIdentityProviderInitializationContext.java |   27 +
 .../LoginIdentityProviderLookup.java            |   25 +
 .../LoginIdentityProviderContext.java           |   35 +
 .../exception/IdentityAccessException.java      |   33 +
 .../InvalidLoginCredentialsException.java       |   33 +
 .../nifi/authorization/AuthorityProvider.java   |   42 +-
 .../nifi/web/NiFiWebConfigurationContext.java   |    2 +-
 .../org/apache/nifi/web/NiFiWebContext.java     |    2 +-
 nifi-assembly/LICENSE                           |   22 +
 nifi-assembly/pom.xml                           |    8 +
 .../org/apache/nifi/util/NiFiProperties.java    |   64 +-
 .../nifi/security/util/CertificateUtils.java    |   51 +-
 .../nifi-framework/nifi-administration/pom.xml  |    1 -
 .../nifi/admin/AuditDataSourceFactoryBean.java  |   23 +-
 .../nifi/admin/KeyDataSourceFactoryBean.java    |  154 ++
 .../nifi/admin/UserDataSourceFactoryBean.java   |   93 +-
 .../org/apache/nifi/admin/dao/DAOFactory.java   |    2 +
 .../java/org/apache/nifi/admin/dao/KeyDAO.java  |   49 +
 .../nifi/admin/dao/impl/DAOFactoryImpl.java     |    6 +
 .../nifi/admin/dao/impl/StandardActionDAO.java  |   10 +-
 .../nifi/admin/dao/impl/StandardKeyDAO.java     |  154 ++
 .../nifi/admin/dao/impl/StandardUserDAO.java    |   53 +-
 .../apache/nifi/admin/service/KeyService.java   |   42 +
 .../service/action/AbstractUserAction.java      |    4 +-
 .../service/action/AuthorizeUserAction.java     |   24 +-
 .../admin/service/action/DisableUserAction.java |    6 +-
 .../admin/service/action/GetKeyByIdAction.java  |   42 +
 .../service/action/GetKeyByIdentityAction.java  |   42 +
 .../service/action/GetOrCreateKeyAction.java    |   48 +
 .../action/RequestUserAccountAction.java        |   14 +-
 .../service/action/SeedUserAccountsAction.java  |   18 +-
 .../admin/service/action/UngroupUserAction.java |    6 +-
 .../admin/service/action/UpdateUserAction.java  |   24 +-
 .../service/action/UpdateUserGroupAction.java   |   32 +-
 .../admin/service/impl/StandardKeyService.java  |  126 ++
 .../src/main/java/org/apache/nifi/key/Key.java  |   69 +
 .../java/org/apache/nifi/user/NiFiUser.java     |   18 +-
 .../resources/nifi-administration-context.xml   |   16 +
 .../service/action/AuthorizeUserActionTest.java |  100 +-
 .../service/action/CreateUserActionTest.java    |   12 +-
 .../service/action/DisableUserActionTest.java   |   14 +-
 .../action/RequestUserAccountActionTest.java    |   22 +-
 .../action/SeedUserAccountsActionTest.java      |   44 +-
 .../action/SetUserAuthoritiesActionTest.java    |   18 +-
 .../web/api/dto/AccessConfigurationDTO.java     |   61 +
 .../nifi/web/api/dto/AccessStatusDTO.java       |  101 +
 .../api/entity/AccessConfigurationEntity.java   |   43 +
 .../nifi/web/api/entity/AccessStatusEntity.java |   43 +
 .../nifi/web/api/entity/IdentityEntity.java     |   52 +
 .../org/apache/nifi/nar/ExtensionManager.java   |   17 +-
 .../nifi/nar/NarThreadContextClassLoader.java   |    2 +
 .../resources/conf/login-identity-providers.xml |   92 +
 .../src/main/resources/conf/nifi.properties     |    3 +
 .../org/apache/nifi/web/server/JettyServer.java |    8 +-
 .../apache/nifi/audit/ControllerAuditor.java    |    8 +-
 .../nifi/audit/ControllerServiceAuditor.java    |   12 +-
 .../org/apache/nifi/audit/FunnelAuditor.java    |    2 +-
 .../java/org/apache/nifi/audit/PortAuditor.java |    6 +-
 .../apache/nifi/audit/ProcessGroupAuditor.java  |    6 +-
 .../org/apache/nifi/audit/ProcessorAuditor.java |    6 +-
 .../apache/nifi/audit/RelationshipAuditor.java  |    4 +-
 .../nifi/audit/RemoteProcessGroupAuditor.java   |    6 +-
 .../apache/nifi/audit/ReportingTaskAuditor.java |    6 +-
 .../org/apache/nifi/audit/SnippetAuditor.java   |    2 +-
 .../org/apache/nifi/web/NiFiServiceFacade.java  |    7 +
 .../nifi/web/NiFiWebApiConfiguration.java       |   40 +
 .../web/NiFiWebApiSecurityConfiguration.java    |  179 ++
 .../nifi/web/StandardNiFiServiceFacade.java     |   28 +-
 .../StandardNiFiWebConfigurationContext.java    |    8 +-
 .../apache/nifi/web/StandardNiFiWebContext.java |    8 +-
 .../org/apache/nifi/web/api/AccessResource.java |  424 ++++
 .../nifi/web/api/ApplicationResource.java       |   16 +-
 .../apache/nifi/web/api/ControllerResource.java |   65 +-
 .../org/apache/nifi/web/api/UserResource.java   |   49 +-
 .../api/config/AccessDeniedExceptionMapper.java |    2 +-
 .../InvalidAuthenticationExceptionMapper.java   |   44 +
 .../org/apache/nifi/web/api/dto/DtoFactory.java |    2 +-
 .../nifi/web/controller/ControllerFacade.java   |   17 +-
 .../web/dao/impl/StandardConnectionDAO.java     |    2 +-
 .../apache/nifi/web/filter/RequestLogger.java   |    6 +-
 .../src/main/resources/nifi-web-api-context.xml |    9 +
 .../src/main/webapp/WEB-INF/web.xml             |   13 +-
 .../accesscontrol/AccessTokenEndpointTest.java  |  292 +++
 .../util/NiFiTestAuthorizationProvider.java     |    3 +-
 .../util/NiFiTestLoginIdentityProvider.java     |   75 +
 .../nifi/integration/util/NiFiTestServer.java   |    9 +-
 .../nifi/integration/util/NiFiTestUser.java     |  232 ++-
 ...he.nifi.authentication.LoginIdentityProvider |   15 +
 .../access-control/controller-services.xml      |   18 -
 .../access-control/login-identity-providers.xml |   24 +
 .../resources/access-control/nifi.properties    |    8 +-
 .../access-control/reporting-tasks.xml          |   17 -
 .../nifi-web/nifi-web-security/pom.xml          |   43 +
 .../org/apache/nifi/web/security/DnUtils.java   |   85 -
 .../InvalidAuthenticationException.java         |   35 +
 .../security/NiFiAuthenticationEntryPoint.java  |   71 +
 .../web/security/NiFiAuthenticationFilter.java  |  209 ++
 .../security/NiFiAuthenticationProvider.java    |   73 +
 .../nifi/web/security/ProxiedEntitiesUtils.java |  147 ++
 .../anonymous/NiFiAnonymousUserFilter.java      |   56 +-
 .../NiFiAuthenticationEntryPoint.java           |   69 -
 .../authorization/NiFiAuthorizationService.java |   46 +-
 .../authorization/NodeAuthorizedUserFilter.java |  128 --
 .../security/jwt/JwtAuthenticationFilter.java   |   80 +
 .../nifi/web/security/jwt/JwtService.java       |  162 ++
 .../security/node/NodeAuthorizedUserFilter.java |  127 ++
 .../LoginIdentityProviderFactoryBean.java       |  312 +++
 ...ginIdentityProviderConfigurationContext.java |   51 +
 ...inIdentityProviderInitializationContext.java |   45 +
 .../token/LoginAuthenticationToken.java         |  123 ++
 .../NewAccountAuthenticationRequestToken.java   |   40 +
 .../token/NewAccountAuthenticationToken.java    |   46 +
 .../token/NiFiAuthenticationRequestToken.java   |   54 +
 .../security/token/NiFiAuthorizationToken.java  |   50 +
 .../web/security/user/NewAccountRequest.java    |   47 +
 .../nifi/web/security/user/NiFiUserDetails.java |    3 +-
 .../nifi/web/security/user/NiFiUserUtils.java   |   27 +-
 .../security/x509/X509AuthenticationFilter.java |  304 +--
 .../security/x509/X509CertificateExtractor.java |    4 +-
 .../security/x509/X509CertificateValidator.java |   58 +
 .../web/security/x509/X509IdentityProvider.java |   94 +
 .../x509/ocsp/OcspCertificateValidator.java     |   20 +-
 .../resources/nifi-web-security-context.xml     |   83 +-
 .../src/main/xsd/login-identity-providers.xsd   |   49 +
 .../NiFiAuthorizationServiceTest.java           |   64 +-
 .../nifi/web/security/jwt/JwtServiceTest.java   |  445 ++++
 .../src/test/resources/logback-test.xml         |   36 +
 .../nifi-framework/nifi-web/nifi-web-ui/pom.xml |   53 +-
 .../src/main/resources/META-INF/LICENSE         |   22 +
 .../resources/filters/bulletin-board.properties |    1 +
 .../main/resources/filters/canvas.properties    |    3 +-
 .../main/resources/filters/cluster.properties   |    1 +
 .../main/resources/filters/counters.properties  |    1 +
 .../main/resources/filters/history.properties   |    1 +
 .../main/resources/filters/login-min.properties |   18 +
 .../src/main/resources/filters/login.properties |   25 +
 .../resources/filters/provenance.properties     |    1 +
 .../main/resources/filters/summary.properties   |    1 +
 .../main/resources/filters/templates.properties |    1 +
 .../src/main/resources/filters/users.properties |    1 +
 .../webapp/WEB-INF/pages/bulletin-board.jsp     |    1 +
 .../src/main/webapp/WEB-INF/pages/canvas.jsp    |    3 +-
 .../src/main/webapp/WEB-INF/pages/cluster.jsp   |    1 +
 .../src/main/webapp/WEB-INF/pages/counters.jsp  |    1 +
 .../src/main/webapp/WEB-INF/pages/history.jsp   |    1 +
 .../src/main/webapp/WEB-INF/pages/login.jsp     |   54 +
 .../main/webapp/WEB-INF/pages/message-page.jsp  |    4 +-
 .../main/webapp/WEB-INF/pages/provenance.jsp    |    1 +
 .../src/main/webapp/WEB-INF/pages/summary.jsp   |    1 +
 .../src/main/webapp/WEB-INF/pages/templates.jsp |    1 +
 .../src/main/webapp/WEB-INF/pages/users.jsp     |    1 +
 .../WEB-INF/partials/canvas/canvas-header.jsp   |   11 +
 .../WEB-INF/partials/canvas/registration.jsp    |   44 -
 .../WEB-INF/partials/login/login-form.jsp       |   32 +
 .../WEB-INF/partials/login/login-message.jsp    |   21 +
 .../WEB-INF/partials/login/login-progress.jsp   |   22 +
 .../WEB-INF/partials/login/login-submission.jsp |   20 +
 .../partials/login/nifi-registration-form.jsp   |   38 +
 .../webapp/WEB-INF/partials/message-pane.jsp    |    7 +-
 .../partials/users/user-details-dialog.jsp      |    2 +-
 .../nifi-web-ui/src/main/webapp/WEB-INF/web.xml |   22 +-
 .../nifi-web-ui/src/main/webapp/css/canvas.css  |    1 -
 .../nifi-web-ui/src/main/webapp/css/header.css  |   18 +
 .../nifi-web-ui/src/main/webapp/css/login.css   |   95 +
 .../nifi-web-ui/src/main/webapp/css/main.css    |   13 +-
 .../src/main/webapp/css/registration.css        |   45 -
 .../src/main/webapp/js/jquery/jquery.base64.js  |  123 ++
 .../js/nf/bulletin-board/nf-bulletin-board.js   |    2 +
 .../webapp/js/nf/canvas/nf-canvas-header.js     |   49 +-
 .../src/main/webapp/js/nf/canvas/nf-canvas.js   |  248 ++-
 .../main/webapp/js/nf/canvas/nf-registration.js |   71 -
 .../src/main/webapp/js/nf/canvas/nf-storage.js  |  139 --
 .../src/main/webapp/js/nf/cluster/nf-cluster.js |    2 +
 .../main/webapp/js/nf/counters/nf-counters.js   |    2 +
 .../src/main/webapp/js/nf/history/nf-history.js |    2 +
 .../src/main/webapp/js/nf/login/nf-login.js     |  302 +++
 .../src/main/webapp/js/nf/nf-common.js          | 1926 ++++++++++--------
 .../src/main/webapp/js/nf/nf-dialog.js          |   23 +-
 .../src/main/webapp/js/nf/nf-storage.js         |  172 ++
 .../webapp/js/nf/provenance/nf-provenance.js    |    2 +
 .../src/main/webapp/js/nf/summary/nf-summary.js |    2 +
 .../main/webapp/js/nf/templates/nf-templates.js |    2 +
 .../src/main/webapp/js/nf/users/nf-users.js     |    2 +
 .../nifi-ldap-iaa-providers-nar/pom.xml         |   32 +
 .../nifi-ldap-iaa-providers/pom.xml             |   60 +
 .../nifi/ldap/LdapAuthenticationStrategy.java   |   27 +
 .../java/org/apache/nifi/ldap/LdapProvider.java |  279 +++
 .../org/apache/nifi/ldap/ReferralStrategy.java  |   39 +
 ...he.nifi.authentication.LoginIdentityProvider |   15 +
 .../nifi-ldap-iaa-providers-bundle/pom.xml      |   38 +
 nifi-nar-bundles/pom.xml                        |    1 +
 pom.xml                                         |   67 +-
 197 files changed, 8863 insertions(+), 2596 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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/aaf14c45/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java
----------------------------------------------------------------------
diff --git a/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java b/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java
new file mode 100644
index 0000000..e9999fc
--- /dev/null
+++ b/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java
@@ -0,0 +1,65 @@
+/*
+ * 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.authentication;
+
+/**
+ * Authentication response for a user login attempt.
+ */
+public class AuthenticationResponse {
+
+    private final String identity;
+    private final String username;
+    private final long expiration;
+    private final String issuer;
+
+    /**
+     * Creates an authentication response. The username and how long the authentication is valid in milliseconds
+     *
+     * @param identity The user identity
+     * @param username The username
+     * @param expiration The expiration in milliseconds
+     * @param issuer The issuer of the token
+     */
+    public AuthenticationResponse(final String identity, final String username, final long expiration, final String issuer) {
+        this.identity = identity;
+        this.username = username;
+        this.expiration = expiration;
+        this.issuer = issuer;
+    }
+
+    public String getIdentity() {
+        return identity;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public String getIssuer() {
+        return issuer;
+    }
+
+    /**
+     * Returns the expiration of a given authentication in milliseconds.
+     *
+     * @return The expiration in milliseconds
+     */
+    public long getExpiration() {
+        return expiration;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-api/src/main/java/org/apache/nifi/authentication/LoginCredentials.java
----------------------------------------------------------------------
diff --git a/nifi-api/src/main/java/org/apache/nifi/authentication/LoginCredentials.java b/nifi-api/src/main/java/org/apache/nifi/authentication/LoginCredentials.java
new file mode 100644
index 0000000..afb7827
--- /dev/null
+++ b/nifi-api/src/main/java/org/apache/nifi/authentication/LoginCredentials.java
@@ -0,0 +1,39 @@
+/*
+ * 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.authentication;
+
+/**
+ * Login credentials for a user.
+ */
+public class LoginCredentials {
+
+    private final String username;
+    private final String password;
+
+    public LoginCredentials(String username, String password) {
+        this.username = username;
+        this.password = password;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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
new file mode 100644
index 0000000..54becb3
--- /dev/null
+++ b/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java
@@ -0,0 +1,61 @@
+/*
+ * 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.authentication;
+
+import org.apache.nifi.authentication.exception.IdentityAccessException;
+import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException;
+import org.apache.nifi.authorization.exception.ProviderCreationException;
+import org.apache.nifi.authorization.exception.ProviderDestructionException;
+
+/**
+ * Identity provider that is able to authentication a user with username/password credentials.
+ */
+public interface LoginIdentityProvider {
+
+    /**
+     * Authenticates the specified login credentials.
+     *
+     * @param credentials the credentials
+     * @return The authentication response
+     * @throws InvalidLoginCredentialsException The login credentials were invalid
+     * @throws IdentityAccessException Unable to register the user due to an issue accessing the underlying storage
+     */
+    AuthenticationResponse authenticate(LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException;
+
+    /**
+     * Called immediately after instance creation for implementers to perform additional setup
+     *
+     * @param initializationContext in which to initialize
+     * @throws ProviderCreationException Unable to initialize
+     */
+    void initialize(LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException;
+
+    /**
+     * Called to configure the AuthorityProvider.
+     *
+     * @param configurationContext at the time of configuration
+     * @throws ProviderCreationException for any issues configuring the provider
+     */
+    void onConfigured(LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException;
+
+    /**
+     * Called immediately before instance destruction for implementers to release resources.
+     *
+     * @throws ProviderDestructionException If pre-destruction fails.
+     */
+    void preDestruction() throws ProviderDestructionException;
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProviderConfigurationContext.java
----------------------------------------------------------------------
diff --git a/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProviderConfigurationContext.java b/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProviderConfigurationContext.java
new file mode 100644
index 0000000..6fe61fc
--- /dev/null
+++ b/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProviderConfigurationContext.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.authentication;
+
+import java.util.Map;
+
+/**
+ *
+ */
+public interface LoginIdentityProviderConfigurationContext {
+
+    /**
+     * @return identifier for the authority provider
+     */
+    String getIdentifier();
+
+    /**
+     * Retrieves all properties the component currently understands regardless
+     * of whether a value has been set for them or not. If no value is present
+     * then its value is null and thus any registered default for the property
+     * descriptor applies.
+     *
+     * @return Map of all properties
+     */
+    Map<String, String> getProperties();
+
+    /**
+     * @param property to lookup the descriptor and value of
+     * @return the value the component currently understands for the given
+     * PropertyDescriptor. This method does not substitute default
+     * PropertyDescriptor values, so the value returned will be null if not set
+     */
+    String getProperty(String property);
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProviderInitializationContext.java
----------------------------------------------------------------------
diff --git a/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProviderInitializationContext.java b/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProviderInitializationContext.java
new file mode 100644
index 0000000..7aa72a7
--- /dev/null
+++ b/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProviderInitializationContext.java
@@ -0,0 +1,27 @@
+/*
+ * 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.authentication;
+
+/**
+ *
+ */
+public interface LoginIdentityProviderInitializationContext {
+
+    public String getIdentifier();
+
+    public LoginIdentityProviderLookup getAuthorityProviderLookup();
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProviderLookup.java
----------------------------------------------------------------------
diff --git a/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProviderLookup.java b/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProviderLookup.java
new file mode 100644
index 0000000..3cd0ba7
--- /dev/null
+++ b/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProviderLookup.java
@@ -0,0 +1,25 @@
+/*
+ * 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.authentication;
+
+/**
+ *
+ */
+public interface LoginIdentityProviderLookup {
+
+    LoginIdentityProvider getLoginIdentityProvider(String identifier);
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-api/src/main/java/org/apache/nifi/authentication/annotation/LoginIdentityProviderContext.java
----------------------------------------------------------------------
diff --git a/nifi-api/src/main/java/org/apache/nifi/authentication/annotation/LoginIdentityProviderContext.java b/nifi-api/src/main/java/org/apache/nifi/authentication/annotation/LoginIdentityProviderContext.java
new file mode 100644
index 0000000..88df259
--- /dev/null
+++ b/nifi-api/src/main/java/org/apache/nifi/authentication/annotation/LoginIdentityProviderContext.java
@@ -0,0 +1,35 @@
+/*
+ * 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.authentication.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ *
+ */
+@Documented
+@Target({ElementType.FIELD, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface LoginIdentityProviderContext {
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-api/src/main/java/org/apache/nifi/authentication/exception/IdentityAccessException.java
----------------------------------------------------------------------
diff --git a/nifi-api/src/main/java/org/apache/nifi/authentication/exception/IdentityAccessException.java b/nifi-api/src/main/java/org/apache/nifi/authentication/exception/IdentityAccessException.java
new file mode 100644
index 0000000..b68c675
--- /dev/null
+++ b/nifi-api/src/main/java/org/apache/nifi/authentication/exception/IdentityAccessException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.authentication.exception;
+
+/**
+ * Represents the case when the identity could not be confirmed because it was unable
+ * to access the backing store.
+ */
+public class IdentityAccessException extends RuntimeException {
+
+    public IdentityAccessException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public IdentityAccessException(String message) {
+        super(message);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-api/src/main/java/org/apache/nifi/authentication/exception/InvalidLoginCredentialsException.java
----------------------------------------------------------------------
diff --git a/nifi-api/src/main/java/org/apache/nifi/authentication/exception/InvalidLoginCredentialsException.java b/nifi-api/src/main/java/org/apache/nifi/authentication/exception/InvalidLoginCredentialsException.java
new file mode 100644
index 0000000..fbfa324
--- /dev/null
+++ b/nifi-api/src/main/java/org/apache/nifi/authentication/exception/InvalidLoginCredentialsException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.authentication.exception;
+
+/**
+ * Represents the case when the identity could not be confirmed because the
+ * login credentials were invalid.
+ */
+public class InvalidLoginCredentialsException extends RuntimeException {
+
+    public InvalidLoginCredentialsException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public InvalidLoginCredentialsException(String message) {
+        super(message);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-api/src/main/java/org/apache/nifi/authorization/AuthorityProvider.java
----------------------------------------------------------------------
diff --git a/nifi-api/src/main/java/org/apache/nifi/authorization/AuthorityProvider.java b/nifi-api/src/main/java/org/apache/nifi/authorization/AuthorityProvider.java
index 723ec33..716216d 100644
--- a/nifi-api/src/main/java/org/apache/nifi/authorization/AuthorityProvider.java
+++ b/nifi-api/src/main/java/org/apache/nifi/authorization/AuthorityProvider.java
@@ -31,33 +31,33 @@ import org.apache.nifi.authorization.exception.UnknownIdentityException;
 public interface AuthorityProvider {
 
     /**
-     * @param dn of the user
-     * @return whether the user with the specified DN is known to this authority
+     * @param identity of the user. The identity may be a dn, an email, a username, or any string that identities the user.
+     * @return whether the user with the specified identity is known to this authority
      * provider. It is not necessary for the user to have any authorities
      */
-    boolean doesDnExist(String dn) throws AuthorityAccessException;
+    boolean doesDnExist(String identity) throws AuthorityAccessException;
 
     /**
      * Get the authorities for the specified user. If the specified user exists
      * but does not have any authorities, an empty set should be returned.
      *
-     * @param dn of the user to lookup
+     * @param identity of the user. The identity may be a dn, an email, a username, or any string that identities the user.
      * @return the authorities for the specified user. If the specified user
      * exists but does not have any authorities, an empty set should be returned
      * @throws UnknownIdentityException if identity is not known
      * @throws AuthorityAccessException if unable to access authorities
      */
-    Set<Authority> getAuthorities(String dn) throws UnknownIdentityException, AuthorityAccessException;
+    Set<Authority> getAuthorities(String identity) throws UnknownIdentityException, AuthorityAccessException;
 
     /**
      * Sets the specified authorities for the specified user.
      *
-     * @param dn the specified user
+     * @param identity of the user. The identity may be a dn, an email, a username, or any string that identities the user.
      * @param authorities the new authorities for the user
      * @throws UnknownIdentityException if identity is not known
      * @throws AuthorityAccessException if unable to access authorities
      */
-    void setAuthorities(String dn, Set<Authority> authorities) throws UnknownIdentityException, AuthorityAccessException;
+    void setAuthorities(String identity, Set<Authority> authorities) throws UnknownIdentityException, AuthorityAccessException;
 
     /**
      * Gets the users for the specified authority.
@@ -72,32 +72,32 @@ public interface AuthorityProvider {
      * Revokes the specified user. Its up to the implementor to determine the
      * semantics of revocation.
      *
-     * @param dn the dn of the user
+     * @param identity of the user. The identity may be a dn, an email, a username, or any string that identities the user.
      * @throws UnknownIdentityException if the user is not known
      * @throws AuthorityAccessException if unable to access the authorities
      */
-    void revokeUser(String dn) throws UnknownIdentityException, AuthorityAccessException;
+    void revokeUser(String identity) throws UnknownIdentityException, AuthorityAccessException;
 
     /**
      * Add the specified user.
      *
-     * @param dn of the user
+     * @param identity of the user. The identity may be a dn, an email, a username, or any string that identities the user.
      * @param group Optional
      * @throws UnknownIdentityException if the user is not known
      * @throws AuthorityAccessException if unable to access the authorities
      */
-    void addUser(String dn, String group) throws IdentityAlreadyExistsException, AuthorityAccessException;
+    void addUser(String identity, String group) throws IdentityAlreadyExistsException, AuthorityAccessException;
 
     /**
      * Gets the group for the specified user. Return null if the user does not
      * belong to a group.
      *
-     * @param dn the user
+     * @param identity of the user. The identity may be a dn, an email, a username, or any string that identities the user.
      * @return the group of the given user
      * @throws UnknownIdentityException if the user is not known
      * @throws AuthorityAccessException if unable to access the authorities
      */
-    String getGroupForUser(String dn) throws UnknownIdentityException, AuthorityAccessException;
+    String getGroupForUser(String identity) throws UnknownIdentityException, AuthorityAccessException;
 
     /**
      * Revokes all users for a specified group. Its up to the implementor to
@@ -112,21 +112,21 @@ public interface AuthorityProvider {
     /**
      * Adds the specified users to the specified group.
      *
-     * @param dn the set of users to add to the group
+     * @param identity of the user. The identity may be a dn, an email, a username, or any string that identities the user.
      * @param group to add users to
      * @throws UnknownIdentityException if the user is not known
      * @throws AuthorityAccessException if unable to access the authorities
      */
-    void setUsersGroup(Set<String> dn, String group) throws UnknownIdentityException, AuthorityAccessException;
+    void setUsersGroup(Set<String> identity, String group) throws UnknownIdentityException, AuthorityAccessException;
 
     /**
      * Ungroups the specified user.
      *
-     * @param dn of the user
+     * @param identity of the user. The identity may be a dn, an email, a username, or any string that identities the user.
      * @throws UnknownIdentityException if the user is not known
      * @throws AuthorityAccessException if unable to access the authorities
      */
-    void ungroupUser(String dn) throws UnknownIdentityException, AuthorityAccessException;
+    void ungroupUser(String identity) throws UnknownIdentityException, AuthorityAccessException;
 
     /**
      * Ungroups the specified group. Since the semantics of revocation is up to
@@ -143,18 +143,18 @@ public interface AuthorityProvider {
      * Determines whether the user in the specified dnChain should be able to
      * download the content for the flowfile with the specified attributes.
      *
-     * The first dn in the chain is the end user that the request was issued on
-     * behalf of. The subsequent dn's in the chain represent entities proxying
+     * The first identity in the chain is the end user that the request was issued on
+     * behalf of. The subsequent identities in the chain represent entities proxying
      * the user's request with the last being the proxy that sent the current
      * request.
      *
-     * @param dnChain of the user
+     * @param proxyChain proxy chain of user identities that for the download request
      * @param attributes of the flowfile being requested
      * @return the authorization result
      * @throws UnknownIdentityException if the user is not known
      * @throws AuthorityAccessException if unable to access the authorities
      */
-    DownloadAuthorization authorizeDownload(List<String> dnChain, Map<String, String> attributes) throws UnknownIdentityException, AuthorityAccessException;
+    DownloadAuthorization authorizeDownload(List<String> proxyChain, Map<String, String> attributes) throws UnknownIdentityException, AuthorityAccessException;
 
     /**
      * Called immediately after instance creation for implementers to perform

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationContext.java
----------------------------------------------------------------------
diff --git a/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationContext.java b/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationContext.java
index ae32b10..39bea4f 100644
--- a/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationContext.java
+++ b/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationContext.java
@@ -54,7 +54,7 @@ public interface NiFiWebConfigurationContext {
     void saveActions(NiFiWebRequestContext requestContext, Collection<ConfigurationAction> actions);
 
     /**
-     * @return the current user dn. Returns null if no user is found
+     * @return the current user identity. The value may be a DN, an email, a username, or any string that identities the user. Returns null if no user is found
      */
     String getCurrentUserDn();
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContext.java
----------------------------------------------------------------------
diff --git a/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContext.java b/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContext.java
index 55e90e8..96261e5 100644
--- a/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContext.java
+++ b/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContext.java
@@ -51,7 +51,7 @@ public interface NiFiWebContext {
     void saveActions(Collection<ProcessorConfigurationAction> actions);
 
     /**
-     * @return the current user dn. Returns null if no user is found
+     * @return the current user identity. It may be a dn, an email, a username, or any string that identities the user. Returns null if no user is found
      */
     String getCurrentUserDn();
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-assembly/LICENSE
----------------------------------------------------------------------
diff --git a/nifi-assembly/LICENSE b/nifi-assembly/LICENSE
index 5c499e3..e21ecc9 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/aaf14c45/nifi-assembly/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-assembly/pom.xml b/nifi-assembly/pom.xml
index 961349f..86aa8bc 100644
--- a/nifi-assembly/pom.xml
+++ b/nifi-assembly/pom.xml
@@ -174,6 +174,11 @@ language governing permissions and limitations under the License. -->
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-ldap-iaa-providers-nar</artifactId>
+            <type>nar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-dbcp-service-nar</artifactId>
             <type>nar</type>
         </dependency>
@@ -268,6 +273,7 @@ language governing permissions and limitations under the License. -->
 
         <nifi.flow.configuration.file>./conf/flow.xml.gz</nifi.flow.configuration.file>
         <nifi.flow.configuration.archive.dir>./conf/archive/</nifi.flow.configuration.archive.dir>
+        <nifi.login.identity.provider.configuration.file>./conf/login-identity-providers.xml</nifi.login.identity.provider.configuration.file>
         <nifi.authority.provider.configuration.file>./conf/authority-providers.xml</nifi.authority.provider.configuration.file>
         <nifi.templates.directory>./conf/templates</nifi.templates.directory>
         <nifi.database.directory>./database_repository</nifi.database.directory>
@@ -353,8 +359,10 @@ language governing permissions and limitations under the License. -->
         <nifi.security.authorizedUsers.file>./conf/authorized-users.xml</nifi.security.authorizedUsers.file>
         <nifi.security.user.credential.cache.duration>24 hours</nifi.security.user.credential.cache.duration>
         <nifi.security.user.authority.provider>file-provider</nifi.security.user.authority.provider>
+        <nifi.security.user.login.identity.provider></nifi.security.user.login.identity.provider>
         <nifi.security.x509.principal.extractor />
         <nifi.security.support.new.account.requests />
+        <nifi.security.anonymous.authorities>ROLE_MONITOR,ROLE_DFM,ROLE_ADMIN,ROLE_PROVENANCE,ROLE_NIFI</nifi.security.anonymous.authorities>
         <nifi.security.ocsp.responder.url />
         <nifi.security.ocsp.responder.certificate />
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
index a51d9be..809dd09 100644
--- a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
+++ b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
@@ -25,10 +25,14 @@ import java.nio.file.InvalidPathException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
+import java.util.Set;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -45,6 +49,7 @@ public class NiFiProperties extends Properties {
     public static final String FLOW_CONFIGURATION_FILE = "nifi.flow.configuration.file";
     public static final String FLOW_CONFIGURATION_ARCHIVE_FILE = "nifi.flow.configuration.archive.file";
     public static final String AUTHORITY_PROVIDER_CONFIGURATION_FILE = "nifi.authority.provider.configuration.file";
+    public static final String LOGIN_IDENTITY_PROVIDER_CONFIGURATION_FILE = "nifi.login.identity.provider.configuration.file";
     public static final String REPOSITORY_DATABASE_DIRECTORY = "nifi.database.directory";
     public static final String RESTORE_DIRECTORY = "nifi.restore.directory";
     public static final String VERSION = "nifi.version";
@@ -126,11 +131,12 @@ public class NiFiProperties extends Properties {
     public static final String SECURITY_TRUSTSTORE_PASSWD = "nifi.security.truststorePasswd";
     public static final String SECURITY_NEED_CLIENT_AUTH = "nifi.security.needClientAuth";
     public static final String SECURITY_USER_AUTHORITY_PROVIDER = "nifi.security.user.authority.provider";
+    public static final String SECURITY_USER_LOGIN_IDENTITY_PROVIDER = "nifi.security.user.login.identity.provider";
     public static final String SECURITY_CLUSTER_AUTHORITY_PROVIDER_PORT = "nifi.security.cluster.authority.provider.port";
     public static final String SECURITY_CLUSTER_AUTHORITY_PROVIDER_THREADS = "nifi.security.cluster.authority.provider.threads";
     public static final String SECURITY_USER_CREDENTIAL_CACHE_DURATION = "nifi.security.user.credential.cache.duration";
     public static final String SECURITY_SUPPORT_NEW_ACCOUNT_REQUESTS = "nifi.security.support.new.account.requests";
-    public static final String SECURITY_DEFAULT_USER_ROLES = "nifi.security.default.user.roles";
+    public static final String SECURITY_ANONYMOUS_AUTHORITIES = "nifi.security.anonymous.authorities";
     public static final String SECURITY_OCSP_RESPONDER_URL = "nifi.security.ocsp.responder.url";
     public static final String SECURITY_OCSP_RESPONDER_CERTIFICATE = "nifi.security.ocsp.responder.certificate";
 
@@ -187,6 +193,7 @@ public class NiFiProperties extends Properties {
     public static final String DEFAULT_TITLE = "NiFi";
     public static final Boolean DEFAULT_AUTO_RESUME_STATE = true;
     public static final String DEFAULT_AUTHORITY_PROVIDER_CONFIGURATION_FILE = "conf/authority-providers.xml";
+    public static final String DEFAULT_LOGIN_IDENTITY_PROVIDER_CONFIGURATION_FILE = "conf/login-identity-providers.xml";
     public static final String DEFAULT_USER_CREDENTIAL_CACHE_DURATION = "24 hours";
     public static final Integer DEFAULT_REMOTE_INPUT_PORT = null;
     public static final Path DEFAULT_TEMPLATE_DIRECTORY = Paths.get("conf", "templates");
@@ -234,8 +241,7 @@ public class NiFiProperties extends Properties {
     }
 
     /**
-     * This is the method through which the NiFiProperties object should be
-     * obtained.
+     * This is the method through which the NiFiProperties object should be obtained.
      *
      * @return the NiFiProperties object to use
      * @throws RuntimeException if unable to load properties file
@@ -424,8 +430,7 @@ public class NiFiProperties extends Properties {
     }
 
     /**
-     * Returns whether the processors should be started automatically when the
-     * application loads.
+     * Returns whether the processors should be started automatically when the application loads.
      *
      * @return Whether to auto start the processors or not
      */
@@ -436,8 +441,7 @@ public class NiFiProperties extends Properties {
     }
 
     /**
-     * Returns the number of partitions that should be used for the FlowFile
-     * Repository
+     * Returns the number of partitions that should be used for the FlowFile Repository
      *
      * @return the number of partitions
      */
@@ -448,8 +452,7 @@ public class NiFiProperties extends Properties {
     }
 
     /**
-     * Returns the number of milliseconds between FlowFileRepository
-     * checkpointing
+     * Returns the number of milliseconds between FlowFileRepository checkpointing
      *
      * @return the number of milliseconds between checkpoint events
      */
@@ -483,6 +486,18 @@ public class NiFiProperties extends Properties {
     }
 
     /**
+     * @return the user authorities file
+     */
+    public File getLoginIdentityProviderConfiguraitonFile() {
+        final String value = getProperty(LOGIN_IDENTITY_PROVIDER_CONFIGURATION_FILE);
+        if (StringUtils.isBlank(value)) {
+            return new File(DEFAULT_LOGIN_IDENTITY_PROVIDER_CONFIGURATION_FILE);
+        } else {
+            return new File(value);
+        }
+    }
+
+    /**
      * Will default to true unless the value is explicitly set to false.
      *
      * @return Whether client auth is required
@@ -510,6 +525,19 @@ public class NiFiProperties extends Properties {
         return shouldSupport;
     }
 
+    public Set<String> getAnonymousAuthorities() {
+        final Set<String> authorities;
+
+        final String rawAnonymousAuthorities = getProperty(SECURITY_ANONYMOUS_AUTHORITIES);
+        if (!StringUtils.isEmpty(rawAnonymousAuthorities)) {
+            authorities = new HashSet<>(Arrays.asList(rawAnonymousAuthorities.split(",")));
+        } else {
+            authorities = Collections.EMPTY_SET;
+        }
+
+        return authorities;
+    }
+
     // getters for web properties //
     public Integer getPort() {
         Integer port = null;
@@ -851,8 +879,7 @@ public class NiFiProperties extends Properties {
     }
 
     /**
-     * Returns the database repository path. It simply returns the value
-     * configured. No directories will be created as a result of this operation.
+     * Returns the database repository path. It simply returns the value configured. No directories will be created as a result of this operation.
      *
      * @return database repository path
      * @throws InvalidPathException If the configured path is invalid
@@ -862,8 +889,7 @@ public class NiFiProperties extends Properties {
     }
 
     /**
-     * Returns the flow file repository path. It simply returns the value
-     * configured. No directories will be created as a result of this operation.
+     * Returns the flow file repository path. It simply returns the value configured. No directories will be created as a result of this operation.
      *
      * @return database repository path
      * @throws InvalidPathException If the configured path is invalid
@@ -873,10 +899,8 @@ public class NiFiProperties extends Properties {
     }
 
     /**
-     * Returns the content repository paths. This method returns a mapping of
-     * file repository name to file repository paths. It simply returns the
-     * values configured. No directories will be created as a result of this
-     * operation.
+     * Returns the content repository paths. This method returns a mapping of file repository name to file repository paths. It simply returns the values configured. No directories will be created as
+     * a result of this operation.
      *
      * @return file repositories paths
      * @throws InvalidPathException If any of the configured paths are invalid
@@ -900,10 +924,8 @@ public class NiFiProperties extends Properties {
     }
 
     /**
-     * Returns the provenance repository paths. This method returns a mapping of
-     * file repository name to file repository paths. It simply returns the
-     * values configured. No directories will be created as a result of this
-     * operation.
+     * Returns the provenance repository paths. This method returns a mapping of file repository name to file repository paths. It simply returns the values configured. No directories will be created
+     * as a result of this operation.
      *
      * @return the name and paths of all provenance repository locations
      */

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
index 5126933..6236d8e 100644
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
+++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
@@ -34,8 +34,7 @@ public final class CertificateUtils {
     private static final Logger logger = LoggerFactory.getLogger(CertificateUtils.class);
 
     /**
-     * Returns true if the given keystore can be loaded using the given keystore
-     * type and password. Returns false otherwise.
+     * Returns true if the given keystore can be loaded using the given keystore type and password. Returns false otherwise.
      *
      * @param keystore the keystore to validate
      * @param keystoreType the type of the keystore
@@ -77,40 +76,29 @@ public final class CertificateUtils {
     }
 
     /**
-     * Extracts the username from the specified DN. If the username cannot be
-     * extracted because the CN is in an unrecognized format, the entire CN is
-     * returned. If the CN cannot be extracted because the DN is in an
-     * unrecognized format, the entire DN is returned.
+     * Extracts the username from the specified DN. If the username cannot be extracted because the CN is in an unrecognized format, the entire CN is returned. If the CN cannot be extracted because
+     * the DN is in an unrecognized format, the entire DN is returned.
      *
      * @param dn the dn to extract the username from
      * @return the exatracted username
      */
     public static String extractUsername(String dn) {
         String username = dn;
-        String cn = "";
 
         // ensure the dn is specified
         if (StringUtils.isNotBlank(dn)) {
-
-            // attempt to locate the cn
-            if (dn.startsWith("CN=")) {
-                cn = StringUtils.substringBetween(dn, "CN=", ",");
-            } else if (dn.startsWith("/CN=")) {
-                cn = StringUtils.substringBetween(dn, "CN=", "/");
-            } else if (dn.startsWith("C=") || dn.startsWith("/C=")) {
-                cn = StringUtils.substringAfter(dn, "CN=");
-            } else if (dn.startsWith("/") && StringUtils.contains(dn, "CN=")) {
-                cn = StringUtils.substringAfter(dn, "CN=");
-            }
-
-            // attempt to get the username from the cn
-            if (StringUtils.isNotBlank(cn)) {
-                if (cn.endsWith(")")) {
-                    username = StringUtils.substringBetween(cn, "(", ")");
-                } else if (cn.contains(" ")) {
-                    username = StringUtils.substringAfterLast(cn, " ");
+            // determine the separate
+            final String separator = StringUtils.indexOfIgnoreCase(dn, "/cn=") > 0 ? "/" : ",";
+
+            // attempt to locate the cd
+            final String cnPattern = "cn=";
+            final int cnIndex = StringUtils.indexOfIgnoreCase(dn, cnPattern);
+            if (cnIndex >= 0) {
+                int separatorIndex = StringUtils.indexOf(dn, separator, cnIndex);
+                if (separatorIndex > 0) {
+                    username = StringUtils.substring(dn, cnIndex + cnPattern.length(), separatorIndex);
                 } else {
-                    username = cn;
+                    username = StringUtils.substring(dn, cnIndex + cnPattern.length());
                 }
             }
         }
@@ -119,9 +107,7 @@ public final class CertificateUtils {
     }
 
     /**
-     * Returns a list of subject alternative names. Any name that is represented
-     * as a String by X509Certificate.getSubjectAlternativeNames() is converted
-     * to lowercase and returned.
+     * Returns a list of subject alternative names. Any name that is represented as a String by X509Certificate.getSubjectAlternativeNames() is converted to lowercase and returned.
      *
      * @param certificate a certificate
      * @return a list of subject alternative names; list is never null
@@ -137,12 +123,9 @@ public final class CertificateUtils {
         final List<String> result = new ArrayList<>();
         for (final List<?> generalName : altNames) {
             /**
-             * generalName has the name type as the first element a String or
-             * byte array for the second element.  We return any general names
-             * that are String types.
+             * generalName has the name type as the first element a String or byte array for the second element. We return any general names that are String types.
              *
-             * We don't inspect the numeric name type because some certificates
-             * incorrectly put IPs and DNS names under the wrong name types.
+             * We don't inspect the numeric name type because some certificates incorrectly put IPs and DNS names under the wrong name types.
              */
             final Object value = generalName.get(1);
             if (value instanceof String) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/pom.xml
index aeaeda8..b117618 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/pom.xml
@@ -53,7 +53,6 @@
                     <excludes>**/authorization/generated/*.java,</excludes>
                 </configuration>
             </plugin>            
-
         </plugins>
     </build>
     <dependencies>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/AuditDataSourceFactoryBean.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/AuditDataSourceFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/AuditDataSourceFactoryBean.java
index aeb2755..87cd420 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/AuditDataSourceFactoryBean.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/AuditDataSourceFactoryBean.java
@@ -45,8 +45,8 @@ public class AuditDataSourceFactoryBean implements FactoryBean {
     // ------------
     private static final String CREATE_ACTION_TABLE = "CREATE TABLE ACTION ("
             + "ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
-            + "USER_DN VARCHAR2(255) NOT NULL, "
-            + "USER_NAME VARCHAR2(100) NOT NULL, "
+            + "IDENTITY VARCHAR2(4096) NOT NULL, "
+            + "USER_NAME VARCHAR2(4096) NOT NULL, "
             + "SOURCE_ID VARCHAR2(100) NOT NULL, "
             + "SOURCE_NAME VARCHAR2(1000) NOT NULL, "
             + "SOURCE_TYPE VARCHAR2(1000) NOT NULL, "
@@ -107,6 +107,10 @@ public class AuditDataSourceFactoryBean implements FactoryBean {
             + "FOREIGN KEY (ACTION_ID) REFERENCES ACTION(ID)"
             + ")";
 
+    private static final String RENAME_DN_COLUMN = "ALTER TABLE ACTION ALTER COLUMN USER_DN RENAME TO IDENTITY";
+    private static final String RESIZE_IDENTITY_COLUMN = "ALTER TABLE ACTION MODIFY IDENTITY VARCHAR(4096)";
+    private static final String RESIZE_USER_NAME_COLUMN = "ALTER TABLE ACTION MODIFY USER_NAME VARCHAR(4096)";
+
     private JdbcConnectionPool connectionPool;
 
     private NiFiProperties properties;
@@ -148,15 +152,15 @@ public class AuditDataSourceFactoryBean implements FactoryBean {
                 connection = connectionPool.getConnection();
                 connection.setAutoCommit(false);
 
+                // create a statement for initializing the database
+                statement = connection.createStatement();
+
                 // determine if the tables need to be created
                 rs = connection.getMetaData().getTables(null, null, "ACTION", null);
                 if (!rs.next()) {
                     logger.info("Database not built for repository: " + databaseUrl + ".  Building now...");
                     RepositoryUtils.closeQuietly(rs);
 
-                    // create a statement for initializing the database
-                    statement = connection.createStatement();
-
                     // action table
                     statement.execute(CREATE_ACTION_TABLE);
 
@@ -171,6 +175,15 @@ public class AuditDataSourceFactoryBean implements FactoryBean {
                     statement.execute(CREATE_PURGE_DETAILS_TABLE);
                 } else {
                     logger.info("Existing database found and connected to at: " + databaseUrl);
+                    RepositoryUtils.closeQuietly(rs);
+
+                    // check if the DN column exists to see if we need to transform the table
+                    rs = connection.getMetaData().getColumns(null, null, "ACTION", "USER_DN");
+                    if (rs.next()) {
+                        statement.execute(RENAME_DN_COLUMN);
+                        statement.execute(RESIZE_IDENTITY_COLUMN);
+                        statement.execute(RESIZE_USER_NAME_COLUMN);
+                    }
                 }
 
                 // commit any changes

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/KeyDataSourceFactoryBean.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/KeyDataSourceFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/KeyDataSourceFactoryBean.java
new file mode 100644
index 0000000..688aa5a
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/KeyDataSourceFactoryBean.java
@@ -0,0 +1,154 @@
+/*
+ * 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.admin;
+
+import java.io.File;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import org.apache.commons.lang3.StringUtils;
+import org.h2.jdbcx.JdbcConnectionPool;
+import org.apache.nifi.util.NiFiProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.FactoryBean;
+
+/**
+ *
+ */
+public class KeyDataSourceFactoryBean implements FactoryBean {
+
+    private static final Logger logger = LoggerFactory.getLogger(KeyDataSourceFactoryBean.class);
+    private static final String NF_USERNAME_PASSWORD = "nf";
+    private static final int MAX_CONNECTIONS = 5;
+
+    // database file name
+    private static final String KEY_DATABASE_FILE_NAME = "nifi-key";
+
+    // ----------
+    // keys table
+    // ----------
+    private static final String CREATE_KEY_TABLE = "CREATE TABLE KEY ("
+            + "ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
+            + "IDENTITY VARCHAR2(4096) NOT NULL UNIQUE, "
+            + "KEY VARCHAR2(100) NOT NULL"
+            + ")";
+
+    private JdbcConnectionPool connectionPool;
+
+    private NiFiProperties properties;
+
+    @Override
+    public Object getObject() throws Exception {
+        if (connectionPool == null) {
+
+            // locate the repository directory
+            String repositoryDirectoryPath = properties.getProperty(NiFiProperties.REPOSITORY_DATABASE_DIRECTORY);
+
+            // ensure the repository directory is specified
+            if (repositoryDirectoryPath == null) {
+                throw new NullPointerException("Database directory must be specified.");
+            }
+
+            // create a handle to the repository directory
+            File repositoryDirectory = new File(repositoryDirectoryPath);
+
+            // get a handle to the database file
+            File databaseFile = new File(repositoryDirectory, KEY_DATABASE_FILE_NAME);
+
+            // format the database url
+            String databaseUrl = "jdbc:h2:" + databaseFile + ";AUTOCOMMIT=OFF;DB_CLOSE_ON_EXIT=FALSE;LOCK_MODE=3";
+            String databaseUrlAppend = properties.getProperty(NiFiProperties.H2_URL_APPEND);
+            if (StringUtils.isNotBlank(databaseUrlAppend)) {
+                databaseUrl += databaseUrlAppend;
+            }
+
+            // create the pool
+            connectionPool = JdbcConnectionPool.create(databaseUrl, NF_USERNAME_PASSWORD, NF_USERNAME_PASSWORD);
+            connectionPool.setMaxConnections(MAX_CONNECTIONS);
+
+            Connection connection = null;
+            ResultSet rs = null;
+            Statement statement = null;
+            try {
+                // get a connection
+                connection = connectionPool.getConnection();
+                connection.setAutoCommit(false);
+
+                // create a statement for initializing the database
+                statement = connection.createStatement();
+
+                // determine if the tables need to be created
+                rs = connection.getMetaData().getTables(null, null, "KEY", null);
+                if (!rs.next()) {
+                    logger.info("Database not built for repository: " + databaseUrl + ".  Building now...");
+                    RepositoryUtils.closeQuietly(rs);
+
+                    // action table
+                    statement.execute(CREATE_KEY_TABLE);
+                } else {
+                    logger.info("Existing database found and connected to at: " + databaseUrl);
+                }
+
+                // commit any changes
+                connection.commit();
+            } catch (SQLException sqle) {
+                RepositoryUtils.rollback(connection, logger);
+                throw sqle;
+            } finally {
+                RepositoryUtils.closeQuietly(rs);
+                RepositoryUtils.closeQuietly(statement);
+                RepositoryUtils.closeQuietly(connection);
+            }
+        }
+
+        return connectionPool;
+    }
+
+    @Override
+    public Class getObjectType() {
+        return JdbcConnectionPool.class;
+    }
+
+    @Override
+    public boolean isSingleton() {
+        return true;
+    }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    /**
+     * Disposes resources.
+     */
+    public void shutdown() {
+
+        // shutdown the connection pool
+        if (connectionPool != null) {
+            try {
+                connectionPool.dispose();
+            } catch (Exception e) {
+                logger.warn("Unable to dispose of connection pool: " + e.getMessage());
+                if (logger.isDebugEnabled()) {
+                    logger.warn(StringUtils.EMPTY, e);
+                }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/UserDataSourceFactoryBean.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/UserDataSourceFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/UserDataSourceFactoryBean.java
index ebcf574..6d8566e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/UserDataSourceFactoryBean.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/UserDataSourceFactoryBean.java
@@ -21,8 +21,11 @@ import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.UUID;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.authorization.Authority;
 import org.h2.jdbcx.JdbcConnectionPool;
 import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.util.NiFiProperties;
@@ -41,8 +44,8 @@ public class UserDataSourceFactoryBean implements FactoryBean {
 
     private static final String CREATE_USER_TABLE = "CREATE TABLE USER ("
             + "ID VARCHAR2(100) NOT NULL PRIMARY KEY, "
-            + "DN VARCHAR2(255) NOT NULL UNIQUE, "
-            + "USER_NAME VARCHAR2(100) NOT NULL, "
+            + "IDENTITY VARCHAR2(4096) NOT NULL UNIQUE, "
+            + "USER_NAME VARCHAR2(4096) NOT NULL, "
             + "USER_GROUP VARCHAR2(100), "
             + "CREATION TIMESTAMP NOT NULL, "
             + "LAST_ACCESSED TIMESTAMP, "
@@ -60,57 +63,30 @@ public class UserDataSourceFactoryBean implements FactoryBean {
             + ")";
 
     private static final String INSERT_ANONYMOUS_USER = "INSERT INTO USER ("
-            + "ID, DN, USER_NAME, CREATION, LAST_VERIFIED, JUSTIFICATION, STATUS"
+            + "ID, IDENTITY, USER_NAME, CREATION, LAST_VERIFIED, JUSTIFICATION, STATUS"
             + ") VALUES ("
             + "'" + UUID.randomUUID().toString() + "', "
-            + "'" + NiFiUser.ANONYMOUS_USER_DN + "', "
-            + "'" + NiFiUser.ANONYMOUS_USER_DN + "', "
+            + "'" + NiFiUser.ANONYMOUS_USER_IDENTITY + "', "
+            + "'" + NiFiUser.ANONYMOUS_USER_IDENTITY + "', "
             + "NOW(), "
             + "NOW(), "
             + "'Anonymous user needs no justification', "
             + "'ACTIVE'"
             + ")";
 
-    private static final String INSERT_ANONYMOUS_MONITOR_AUTHORITY = "INSERT INTO AUTHORITY ("
+    private static final String INSERT_ANONYMOUS_AUTHORITY = "INSERT INTO AUTHORITY ("
             + "USER_ID, ROLE"
             + ") VALUES ("
-            + "(SELECT ID FROM USER WHERE DN = '" + NiFiUser.ANONYMOUS_USER_DN + "'), "
-            + "'ROLE_MONITOR'"
+            + "(SELECT ID FROM USER WHERE IDENTITY = '" + NiFiUser.ANONYMOUS_USER_IDENTITY + "'), "
+            + "'%s'"
             + ")";
 
-    private static final String INSERT_ANONYMOUS_DFM_AUTHORITY = "INSERT INTO AUTHORITY ("
-            + "USER_ID, ROLE"
-            + ") VALUES ("
-            + "(SELECT ID FROM USER WHERE DN = '" + NiFiUser.ANONYMOUS_USER_DN + "'), "
-            + "'ROLE_DFM'"
-            + ")";
+    private static final String DELETE_ANONYMOUS_AUTHORITIES = "DELETE FROM AUTHORITY "
+            + "WHERE USER_ID = (SELECT ID FROM USER WHERE IDENTITY = '" + NiFiUser.ANONYMOUS_USER_IDENTITY + "')";
 
-    private static final String INSERT_ANONYMOUS_ADMIN_AUTHORITY = "INSERT INTO AUTHORITY ("
-            + "USER_ID, ROLE"
-            + ") VALUES ("
-            + "(SELECT ID FROM USER WHERE DN = '" + NiFiUser.ANONYMOUS_USER_DN + "'), "
-            + "'ROLE_ADMIN'"
-            + ")";
-
-    private static final String INSERT_ANONYMOUS_NIFI_AUTHORITY = "INSERT INTO AUTHORITY ("
-            + "USER_ID, ROLE"
-            + ") VALUES ("
-            + "(SELECT ID FROM USER WHERE DN = '" + NiFiUser.ANONYMOUS_USER_DN + "'), "
-            + "'ROLE_NIFI'"
-            + ")";
-
-    private static final String INSERT_ANONYMOUS_PROVENANCE_AUTHORITY = "INSERT INTO AUTHORITY ("
-            + "USER_ID, ROLE"
-            + ") VALUES ("
-            + "(SELECT ID FROM USER WHERE DN = '" + NiFiUser.ANONYMOUS_USER_DN + "'), "
-            + "'ROLE_PROVENANCE'"
-            + ")";
-
-    private static final String SELECT_ANONYMOUS_PROVENANCE_AUTHORITY = "SELECT * FROM AUTHORITY "
-            + "WHERE "
-            + "USER_ID = (SELECT ID FROM USER WHERE DN = '" + NiFiUser.ANONYMOUS_USER_DN + "') "
-            + "AND "
-            + "ROLE = 'ROLE_PROVENANCE'";
+    private static final String RENAME_DN_COLUMN = "ALTER TABLE USER ALTER COLUMN DN RENAME TO IDENTITY";
+    private static final String RESIZE_IDENTITY_COLUMN = "ALTER TABLE USER MODIFY IDENTITY VARCHAR(4096)";
+    private static final String RESIZE_USER_NAME_COLUMN = "ALTER TABLE USER MODIFY USER_NAME VARCHAR(4096)";
 
     private JdbcConnectionPool connectionPool;
 
@@ -128,6 +104,17 @@ public class UserDataSourceFactoryBean implements FactoryBean {
                 throw new NullPointerException("Database directory must be specified.");
             }
 
+            // get the roles being granted to anonymous users
+            final Set<String> rawAnonymousAuthorities = new HashSet<>(properties.getAnonymousAuthorities());
+            final Set<Authority> anonymousAuthorities = Authority.convertRawAuthorities(rawAnonymousAuthorities);
+
+            // ensure every authorities was recognized
+            if (rawAnonymousAuthorities.size() != anonymousAuthorities.size()) {
+                final Set<String> validAuthorities = Authority.convertAuthorities(anonymousAuthorities);
+                rawAnonymousAuthorities.removeAll(validAuthorities);
+                throw new IllegalStateException("Invalid authorities specified: " + StringUtils.join(rawAnonymousAuthorities, ", "));
+            }
+
             // create a handle to the repository directory
             File repositoryDirectory = new File(repositoryDirectoryPath);
 
@@ -161,21 +148,25 @@ public class UserDataSourceFactoryBean implements FactoryBean {
 
                     // seed the anonymous user
                     statement.execute(INSERT_ANONYMOUS_USER);
-                    statement.execute(INSERT_ANONYMOUS_MONITOR_AUTHORITY);
-                    statement.execute(INSERT_ANONYMOUS_DFM_AUTHORITY);
-                    statement.execute(INSERT_ANONYMOUS_ADMIN_AUTHORITY);
-                    statement.execute(INSERT_ANONYMOUS_NIFI_AUTHORITY);
                 } else {
                     logger.info("Existing database found and connected to at: " + databaseUrl);
+                    RepositoryUtils.closeQuietly(rs);
+
+                    // if the DN column exists, transform the table
+                    rs = connection.getMetaData().getColumns(null, null, "USER", "DN");
+                    if (rs.next()) {
+                        statement.execute(RENAME_DN_COLUMN);
+                        statement.execute(RESIZE_IDENTITY_COLUMN);
+                        statement.execute(RESIZE_USER_NAME_COLUMN);
+                    }
+
+                    // remove all authorities for the anonymous user
+                    statement.execute(DELETE_ANONYMOUS_AUTHORITIES);
                 }
 
-                // close the previous result set
-                RepositoryUtils.closeQuietly(rs);
-
-                // merge in the provenance role to handle existing databases
-                rs = statement.executeQuery(SELECT_ANONYMOUS_PROVENANCE_AUTHORITY);
-                if (!rs.next()) {
-                    statement.execute(INSERT_ANONYMOUS_PROVENANCE_AUTHORITY);
+                // add all authorities for the anonymous user
+                for (final Authority authority : anonymousAuthorities) {
+                    statement.execute(String.format(INSERT_ANONYMOUS_AUTHORITY, authority.name()));
                 }
 
                 // commit any changes

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/DAOFactory.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/DAOFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/DAOFactory.java
index dee4ef9..eb7e3ce 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/DAOFactory.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/DAOFactory.java
@@ -26,4 +26,6 @@ public interface DAOFactory {
     ActionDAO getActionDAO();
 
     AuthorityDAO getAuthorityDAO();
+
+    KeyDAO getKeyDAO();
 }


[36/51] [abbrv] nifi git commit: NIFI-655: - Updating the version of ldap provider nar.

Posted by mc...@apache.org.
NIFI-655:
- Updating the version of ldap provider nar.

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

Branch: refs/heads/master
Commit: f2d82ee1403579c73928259c61e129737856e02c
Parents: 36eaddb
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 23 16:53:26 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 23 16:53:26 2015 -0500

----------------------------------------------------------------------
 .../nifi-ldap-iaa-providers-nar/pom.xml                          | 2 +-
 .../nifi-ldap-iaa-providers/pom.xml                              | 2 +-
 nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/pom.xml          | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/f2d82ee1/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers-nar/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers-nar/pom.xml b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers-nar/pom.xml
index 59681b9..d021ed2 100644
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers-nar/pom.xml
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers-nar/pom.xml
@@ -18,7 +18,7 @@
     <parent>
         <groupId>org.apache.nifi</groupId>
         <artifactId>nifi-ldap-iaa-providers-bundle</artifactId>
-        <version>0.3.1-SNAPSHOT</version>
+        <version>0.4.0-SNAPSHOT</version>
     </parent>
     <artifactId>nifi-ldap-iaa-providers-nar</artifactId>
     <packaging>nar</packaging>

http://git-wip-us.apache.org/repos/asf/nifi/blob/f2d82ee1/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/pom.xml b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/pom.xml
index 5cbd6f9..d13728e 100644
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/pom.xml
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/pom.xml
@@ -18,7 +18,7 @@
     <parent>
         <groupId>org.apache.nifi</groupId>
         <artifactId>nifi-ldap-iaa-providers-bundle</artifactId>
-        <version>0.3.1-SNAPSHOT</version>
+        <version>0.4.0-SNAPSHOT</version>
     </parent>
     <artifactId>nifi-ldap-iaa-providers</artifactId>
     <packaging>jar</packaging>

http://git-wip-us.apache.org/repos/asf/nifi/blob/f2d82ee1/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/pom.xml b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/pom.xml
index e038c2c..bb57fdb 100644
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/pom.xml
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/pom.xml
@@ -18,7 +18,7 @@
     <parent>
         <groupId>org.apache.nifi</groupId>
         <artifactId>nifi-nar-bundles</artifactId>
-        <version>0.3.1-SNAPSHOT</version>
+        <version>0.4.0-SNAPSHOT</version>
     </parent>
     <artifactId>nifi-ldap-iaa-providers-bundle</artifactId>
     <packaging>pom</packaging>
@@ -31,7 +31,7 @@
             <dependency>
                 <groupId>org.apache.nifi</groupId>
                 <artifactId>nifi-ldap-iaa-providers</artifactId>
-                <version>0.3.1-SNAPSHOT</version>
+                <version>0.4.0-SNAPSHOT</version>
             </dependency>
         </dependencies>
     </dependencyManagement>


[23/51] [abbrv] nifi git commit: NIFI-655: - Refactoring web security to use Spring Security Java Configuration. - Introducing security in Web UI in order to get JWT.

Posted by mc...@apache.org.
http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js
new file mode 100644
index 0000000..7b0c3f6
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+/* global nf, d3 */
+
+nf.Storage = (function () {
+
+    // Store items for two days before being eligible for removal.
+    var TWO_DAYS = nf.Common.MILLIS_PER_DAY * 2;
+
+    /**
+     * Checks the expiration for the specified entry.
+     * 
+     * @param {object} entry
+     * @returns {boolean}
+     */
+    var checkExpiration = function (entry) {
+        if (nf.Common.isDefinedAndNotNull(entry.expires)) {
+            // get the expiration
+            var expires = new Date(entry.expires);
+            var now = new Date();
+
+            // return whether the expiration date has passed
+            return expires.valueOf() < now.valueOf();
+        } else {
+            return false;
+        }
+    };
+    
+    /**
+     * Gets an enty for the key. The entry expiration is not checked.
+     * 
+     * @param {string} key
+     */
+    var getEntry = function (key) {
+        try {
+            // parse the entry
+            var entry = JSON.parse(localStorage.getItem(key));
+
+            // ensure the entry and item are present
+            if (nf.Common.isDefinedAndNotNull(entry)) {
+                return entry;
+            } else {
+                return null;
+            }
+        } catch (e) {
+            return null;
+        }
+    };
+    
+    return {
+        /**
+         * Initializes the storage. Items will be persisted for two days. Once the scripts runs
+         * thereafter, all eligible items will be removed. This strategy does not support persistence.
+         */
+        init: function () {
+            for (var i = 0; i < localStorage.length; i++) {
+                try {
+                    // get the next item
+                    var key = localStorage.key(i);
+                    
+                    // attempt to get the item which will expire if necessary
+                    nf.Storage.getItem(key);
+                } catch (e) {
+                }
+            }
+        },
+        
+        /**
+         * Stores the specified item.
+         * 
+         * @param {string} key
+         * @param {object} item
+         * @param {integer} expires
+         */
+        setItem: function (key, item, expires) {
+            // calculate the expiration
+            expires = nf.Common.isDefinedAndNotNull(expires) ? expires : new Date().valueOf() + TWO_DAYS;
+
+            // create the entry
+            var entry = {
+                expires: expires,
+                item: item
+            };
+
+            // store the item
+            localStorage.setItem(key, JSON.stringify(entry));
+        },
+        
+        /**
+         * Returns whether there is an entry for this key. This will not check the expiration. If
+         * the entry is expired, it will return null on a subsequent getItem invocation.
+         * 
+         * @param {string} key
+         * @returns {boolean}
+         */
+        hasItem: function (key) {
+            return getEntry(key) !== null;
+        },
+        
+        /**
+         * Gets the item with the specified key. If an item with this key does
+         * not exist, null is returned. If an item exists but cannot be parsed
+         * or is malformed/unrecognized, null is returned.
+         * 
+         * @param {type} key
+         */
+        getItem: function (key) {
+            var entry = getEntry(key);
+            if (entry === null) {
+                return null;
+            }
+
+            // if the entry is expired, drop it and return null
+            if (checkExpiration(entry)) {
+                nf.Storage.removeItem(key);
+                return null;
+            }
+
+            // if the entry has the specified field return its value
+            if (nf.Common.isDefinedAndNotNull(entry['item'])) {
+                return entry['item'];
+            } else {
+                return null;
+            }
+        },
+        
+        /**
+         * Gets the expiration for the specified item. This will not check the expiration. If
+         * the entry is expired, it will return null on a subsequent getItem invocation.
+         * 
+         * @param {string} key
+         * @returns {integer}
+         */
+        getItemExpiration: function (key) {
+            var entry = getEntry(key);
+            if (entry === null) {
+                return null;
+            }
+
+            // if the entry has the specified field return its value
+            if (nf.Common.isDefinedAndNotNull(entry['expires'])) {
+                return entry['expires'];
+            } else {
+                return null;
+            }
+        },
+        
+        /**
+         * Removes the item with the specified key.
+         * 
+         * @param {type} key
+         */
+        removeItem: function (key) {
+            localStorage.removeItem(key);
+        }
+    };
+}());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js
index 0edbe08..876e06d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js
@@ -174,6 +174,8 @@ nf.Provenance = (function () {
          * Initializes the status page.
          */
         init: function () {
+            nf.Storage.init();
+            
             // load the users authorities and detect if the NiFi is clustered
             $.when(loadControllerConfig(), loadAuthorities(), detectedCluster()).done(function () {
                 // create the provenance table

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js
index 97f8626..4f06662 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js
@@ -128,6 +128,8 @@ nf.Summary = (function () {
          * Initializes the status page.
          */
         init: function () {
+            nf.Storage.init();
+            
             // intialize the summary table
             initializeSummaryTable().done(function () {
                 // load the table

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js
index b55bee2..678e352 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js
@@ -204,6 +204,8 @@ nf.Templates = (function () {
          * Initializes the templates page.
          */
         init: function () {
+            nf.Storage.init();
+            
             // load the users authorities
             loadAuthorities().done(function () {
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users.js
index 96f73a5..9364aec 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users.js
@@ -116,6 +116,8 @@ nf.Users = (function () {
          * Initializes the counters page.
          */
         init: function () {
+            nf.Storage.init();
+            
             // load the users authorities
             loadAuthorities().done(function () {
                 // create the counters table

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers-nar/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers-nar/pom.xml b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers-nar/pom.xml
new file mode 100644
index 0000000..59681b9
--- /dev/null
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers-nar/pom.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-ldap-iaa-providers-bundle</artifactId>
+        <version>0.3.1-SNAPSHOT</version>
+    </parent>
+    <artifactId>nifi-ldap-iaa-providers-nar</artifactId>
+    <packaging>nar</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-ldap-iaa-providers</artifactId>
+        </dependency>
+    </dependencies>
+    <name>nifi-ldap-iaa-providers-nar</name>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/pom.xml b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/pom.xml
new file mode 100644
index 0000000..5cbd6f9
--- /dev/null
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/pom.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-ldap-iaa-providers-bundle</artifactId>
+        <version>0.3.1-SNAPSHOT</version>
+    </parent>
+    <artifactId>nifi-ldap-iaa-providers</artifactId>
+    <packaging>jar</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-security-utils</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-ldap</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-beans</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-tx</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+    </dependencies>
+    <name>nifi-ldap-iaa-providers</name>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapAuthenticationStrategy.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapAuthenticationStrategy.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapAuthenticationStrategy.java
new file mode 100644
index 0000000..7124ce1
--- /dev/null
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapAuthenticationStrategy.java
@@ -0,0 +1,27 @@
+/*
+ * 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.ldap;
+
+/**
+ *
+ */
+public enum LdapAuthenticationStrategy {
+
+    ANONYMOUS,
+    SIMPLE,
+    START_TLS
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
new file mode 100644
index 0000000..4dc7ea4
--- /dev/null
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
@@ -0,0 +1,279 @@
+/*
+ * 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.ldap;
+
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.apache.nifi.authentication.LoginCredentials;
+import org.apache.nifi.authentication.LoginIdentityProvider;
+import org.apache.nifi.authentication.LoginIdentityProviderConfigurationContext;
+import org.apache.nifi.authentication.LoginIdentityProviderInitializationContext;
+import org.apache.nifi.authentication.exception.IdentityAccessException;
+import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException;
+import org.apache.nifi.authorization.exception.ProviderCreationException;
+import org.apache.nifi.authorization.exception.ProviderDestructionException;
+import org.apache.nifi.security.util.SslContextFactory;
+import org.apache.nifi.security.util.SslContextFactory.ClientAuth;
+import org.apache.nifi.util.FormatUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ldap.CommunicationException;
+import org.springframework.ldap.core.support.AbstractTlsDirContextAuthenticationStrategy;
+import org.springframework.ldap.core.support.DefaultTlsDirContextAuthenticationStrategy;
+import org.springframework.ldap.core.support.LdapContextSource;
+import org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider;
+import org.springframework.security.ldap.authentication.BindAuthenticator;
+import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
+import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
+import org.springframework.security.ldap.search.LdapUserSearch;
+import org.springframework.security.ldap.userdetails.LdapUserDetails;
+
+/**
+ * Abstract LDAP based implementation of a login identity provider.
+ */
+public class LdapProvider implements LoginIdentityProvider {
+
+    private static final Logger logger = LoggerFactory.getLogger(LdapProvider.class);
+
+    private AbstractLdapAuthenticationProvider provider;
+    private String issuer;
+    private long expiration;
+
+    @Override
+    public final void initialize(final LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException {
+        this.issuer = getClass().getSimpleName();
+    }
+
+    @Override
+    public final void onConfigured(final LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException {
+        final String rawExpiration = configurationContext.getProperty("Authentication Expiration");
+        if (StringUtils.isBlank(rawExpiration)) {
+            throw new ProviderCreationException("The Authentication Expiration must be specified.");
+        }
+
+        try {
+            expiration = FormatUtils.getTimeDuration(rawExpiration, TimeUnit.MILLISECONDS);
+        } catch (final IllegalArgumentException iae) {
+            throw new ProviderCreationException(String.format("The Expiration Duration '%s' is not a valid time duration", rawExpiration));
+        }
+
+        final LdapContextSource context = new LdapContextSource();
+
+        final Map<String, Object> baseEnvironment = new HashMap<>();
+
+        // connect/read time out
+        setTimeout(configurationContext, baseEnvironment, "Connect Timeout", "com.sun.jndi.ldap.connect.timeout");
+        setTimeout(configurationContext, baseEnvironment, "Read Timeout", "com.sun.jndi.ldap.read.timeout");
+
+        // set the base environment is necessary
+        if (!baseEnvironment.isEmpty()) {
+            context.setBaseEnvironmentProperties(baseEnvironment);
+        }
+
+        // authentication strategy
+        final String rawAuthenticationStrategy = configurationContext.getProperty("Authentication Strategy");
+        final LdapAuthenticationStrategy authenticationStrategy;
+        try {
+            authenticationStrategy = LdapAuthenticationStrategy.valueOf(rawAuthenticationStrategy);
+        } catch (final IllegalArgumentException iae) {
+            throw new ProviderCreationException(String.format("Unrecognized authentication strategy '%s'. Possible values are [%s]",
+                    rawAuthenticationStrategy, StringUtils.join(LdapAuthenticationStrategy.values(), ", ")));
+        }
+
+        switch (authenticationStrategy) {
+            case ANONYMOUS:
+                context.setAnonymousReadOnly(true);
+                break;
+            default:
+                final String userDn = configurationContext.getProperty("Manager DN");
+                final String password = configurationContext.getProperty("Manager Password");
+
+                context.setUserDn(userDn);
+                context.setPassword(password);
+
+                switch (authenticationStrategy) {
+                    case SIMPLE:
+                        context.setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy());
+                        break;
+                    case START_TLS:
+                        final AbstractTlsDirContextAuthenticationStrategy tlsAuthenticationStrategy = new DefaultTlsDirContextAuthenticationStrategy();
+
+                        // shutdown gracefully
+                        final String rawShutdownGracefully = configurationContext.getProperty("TLS - Shutdown Gracefully");
+                        if (StringUtils.isNotBlank(rawShutdownGracefully)) {
+                            final boolean shutdownGracefully = Boolean.TRUE.toString().equalsIgnoreCase(rawShutdownGracefully);
+                            tlsAuthenticationStrategy.setShutdownTlsGracefully(shutdownGracefully);
+                        }
+
+                        final String rawKeystore = configurationContext.getProperty("TLS - Keystore");
+                        final String rawKeystorePassword = configurationContext.getProperty("TLS - Keystore Password");
+                        final String rawKeystoreType = configurationContext.getProperty("TLS - Keystore Type");
+                        final String rawTruststore = configurationContext.getProperty("TLS - Truststore");
+                        final String rawTruststorePassword = configurationContext.getProperty("TLS - Truststore Password");
+                        final String rawTruststoreType = configurationContext.getProperty("TLS - Truststore Type");
+                        final String rawClientAuth = configurationContext.getProperty("TLS - Client Auth");
+                        final String rawProtocol = configurationContext.getProperty("TLS - Protocol");
+
+                        final ClientAuth clientAuth;
+                        if (StringUtils.isBlank(rawClientAuth)) {
+                            clientAuth = ClientAuth.NONE;
+                        } else {
+                            try {
+                                clientAuth = ClientAuth.valueOf(rawClientAuth);
+                            } catch (final IllegalArgumentException iae) {
+                                throw new ProviderCreationException(String.format("Unrecognized client auth '%s'. Possible values are [%s]",
+                                        rawClientAuth, StringUtils.join(ClientAuth.values(), ", ")));
+                            }
+                        }
+
+                        try {
+                            final SSLContext sslContext;
+                            if (StringUtils.isBlank(rawKeystore)) {
+                                sslContext = SslContextFactory.createTrustSslContext(rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, rawProtocol);
+                            } else {
+                                if (StringUtils.isBlank(rawTruststore)) {
+                                    sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType, rawProtocol);
+                                } else {
+                                    sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType,
+                                            rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, clientAuth, rawProtocol);
+                                }
+                            }
+                            tlsAuthenticationStrategy.setSslSocketFactory(sslContext.getSocketFactory());
+                        } catch (final KeyStoreException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException | KeyManagementException | IOException e) {
+                            throw new ProviderCreationException(e.getMessage(), e);
+                        }
+
+                        context.setAuthenticationStrategy(tlsAuthenticationStrategy);
+                        break;
+                }
+                break;
+        }
+
+        // referrals
+        final String rawReferralStrategy = configurationContext.getProperty("Referral Strategy");
+
+        final ReferralStrategy referralStrategy;
+        try {
+            referralStrategy = ReferralStrategy.valueOf(rawReferralStrategy);
+        } catch (final IllegalArgumentException iae) {
+            throw new ProviderCreationException(String.format("Unrecgonized authentication strategy '%s'. Possible values are [%s]",
+                    rawAuthenticationStrategy, StringUtils.join(ReferralStrategy.values(), ", ")));
+        }
+
+        context.setReferral(referralStrategy.toString());
+
+        // url
+        final String url = configurationContext.getProperty("Url");
+
+        if (StringUtils.isBlank(url)) {
+            throw new ProviderCreationException("LDAP identity provider 'Url' must be specified.");
+        }
+
+        // connection
+        context.setUrl(url);
+
+        // search criteria
+        final String userSearchBase = configurationContext.getProperty("User Search Base");
+        final String userSearchFilter = configurationContext.getProperty("User Search Filter");
+
+        if (StringUtils.isBlank(userSearchBase) || StringUtils.isBlank(userSearchFilter)) {
+            throw new ProviderCreationException("LDAP identity provider 'User Search Base' and 'User Search Filter' must be specified.");
+        }
+
+        final LdapUserSearch userSearch = new FilterBasedLdapUserSearch(userSearchBase, userSearchFilter, context);
+
+        // bind
+        final BindAuthenticator authenticator = new BindAuthenticator(context);
+        authenticator.setUserSearch(userSearch);
+
+        try {
+            // handling initializing beans
+            context.afterPropertiesSet();
+            authenticator.afterPropertiesSet();
+        } catch (final Exception e) {
+            throw new ProviderCreationException(e.getMessage(), e);
+        }
+
+        // create the underlying provider
+        provider = new LdapAuthenticationProvider(authenticator);
+    }
+
+    private void setTimeout(final LoginIdentityProviderConfigurationContext configurationContext,
+            final Map<String, Object> baseEnvironment,
+            final String configurationProperty,
+            final String environmentKey) {
+
+        final String rawTimeout = configurationContext.getProperty(configurationProperty);
+        if (StringUtils.isNotBlank(rawTimeout)) {
+            try {
+                final Long timeout = FormatUtils.getTimeDuration(rawTimeout, TimeUnit.MILLISECONDS);
+                baseEnvironment.put(environmentKey, timeout.toString());
+            } catch (final IllegalArgumentException iae) {
+                throw new ProviderCreationException(String.format("The %s '%s' is not a valid time duration", configurationProperty, rawTimeout));
+            }
+        }
+    }
+
+    @Override
+    public final AuthenticationResponse authenticate(final LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException {
+        if (provider == null) {
+            throw new IdentityAccessException("The LDAP authentication provider is not initialized.");
+        }
+
+        try {
+            // perform the authentication
+            final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword());
+            final Authentication authentication = provider.authenticate(token);
+
+            // attempt to get the ldap user details to get the DN
+            if (authentication.getPrincipal() instanceof LdapUserDetails) {
+                final LdapUserDetails userDetails = (LdapUserDetails) authentication.getPrincipal();
+                return new AuthenticationResponse(userDetails.getDn(), credentials.getUsername(), expiration, issuer);
+            } else {
+                return new AuthenticationResponse(authentication.getName(), credentials.getUsername(), expiration, issuer);
+            }
+        } catch (final CommunicationException | AuthenticationServiceException e) {
+            logger.error(e.getMessage());
+            if (logger.isDebugEnabled()) {
+                logger.debug(StringUtils.EMPTY, e);
+            }
+            throw new IdentityAccessException("Unable to query the configured directory server. See the logs for additional details.", e);
+        } catch (final BadCredentialsException bce) {
+            throw new InvalidLoginCredentialsException(bce.getMessage(), bce);
+        }
+    }
+
+    @Override
+    public final void preDestruction() throws ProviderDestructionException {
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/ReferralStrategy.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/ReferralStrategy.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/ReferralStrategy.java
new file mode 100644
index 0000000..f4c5131
--- /dev/null
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/ReferralStrategy.java
@@ -0,0 +1,39 @@
+/*
+ * 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.ldap;
+
+/**
+ *
+ */
+public enum ReferralStrategy {
+
+    FOLLOW("follow"),
+    INGORE("ignore"),
+    THROW("throw");
+
+    private final String value;
+
+    private ReferralStrategy(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return value;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider
new file mode 100644
index 0000000..b5ca1fe
--- /dev/null
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider
@@ -0,0 +1,15 @@
+# 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.
+org.apache.nifi.ldap.LdapProvider
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/pom.xml b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/pom.xml
new file mode 100644
index 0000000..e038c2c
--- /dev/null
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/pom.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-nar-bundles</artifactId>
+        <version>0.3.1-SNAPSHOT</version>
+    </parent>
+    <artifactId>nifi-ldap-iaa-providers-bundle</artifactId>
+    <packaging>pom</packaging>
+    <modules>
+        <module>nifi-ldap-iaa-providers</module>
+        <module>nifi-ldap-iaa-providers-nar</module>
+    </modules>
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.apache.nifi</groupId>
+                <artifactId>nifi-ldap-iaa-providers</artifactId>
+                <version>0.3.1-SNAPSHOT</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/pom.xml b/nifi-nar-bundles/pom.xml
index 4c0925f..a9c7728 100644
--- a/nifi-nar-bundles/pom.xml
+++ b/nifi-nar-bundles/pom.xml
@@ -48,6 +48,7 @@
         <module>nifi-avro-bundle</module>
         <module>nifi-couchbase-bundle</module>
         <module>nifi-azure-bundle</module>
+        <module>nifi-ldap-iaa-providers-bundle</module>
     </modules>
     <dependencyManagement>
         <dependencies>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 0708812..249f01c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,14 +1,14 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- 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. -->
+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. -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
@@ -91,7 +91,7 @@
         <jetty.version>9.2.11.v20150529</jetty.version>
         <lucene.version>4.10.4</lucene.version>
         <spring.version>4.1.6.RELEASE</spring.version>
-        <spring.security.version>3.2.7.RELEASE</spring.security.version>
+        <spring.security.version>4.0.3.RELEASE</spring.security.version>
         <jersey.version>1.19</jersey.version>
         <hadoop.version>2.6.2</hadoop.version>
         <hadoop.guava.version>12.0.1</hadoop.guava.version>
@@ -290,8 +290,8 @@
                 <version>2.2.1</version>
                 <exclusions>
                     <!-- | Exclude the quartz 2.2.1 bundled version of c3p0 because it is 
-                        lgpl licensed | We also don't use the JDBC related features of quartz for 
-                        which the dependency would matter -->
+                    lgpl licensed | We also don't use the JDBC related features of quartz for 
+                    which the dependency would matter -->
                     <exclusion>
                         <groupId>c3p0</groupId>
                         <artifactId>c3p0</artifactId>
@@ -361,7 +361,7 @@
                 <version>${spring.version}</version>
                 <exclusions>
                     <!-- <artifactId>jcl-over-slf4j</artifactId> is used in dependencies 
-                        section -->
+                    section -->
                     <exclusion>
                         <groupId>commons-logging</groupId>
                         <artifactId>commons-logging</artifactId>
@@ -504,6 +504,29 @@
                 </exclusions>
             </dependency>
             <dependency>
+                <groupId>org.springframework.security</groupId>
+                <artifactId>spring-security-ldap</artifactId>
+                <version>${spring.security.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.springframework</groupId>
+                        <artifactId>spring-core</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>org.springframework</groupId>
+                        <artifactId>spring-beans</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>org.springframework</groupId>
+                        <artifactId>spring-context</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>org.springframework</groupId>
+                        <artifactId>spring-tx</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
                 <groupId>org.aspectj</groupId>
                 <artifactId>aspectjweaver</artifactId>
                 <version>1.8.5</version>
@@ -977,6 +1000,12 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.nifi</groupId>
+                <artifactId>nifi-ldap-iaa-providers-nar</artifactId>
+                <version>0.3.1-SNAPSHOT</version>
+                <type>nar</type>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.nifi</groupId>
                 <artifactId>nifi-properties</artifactId>
                 <version>0.4.0-SNAPSHOT</version>
             </dependency>
@@ -1262,7 +1291,7 @@
                                 <module name="OuterTypeFilename" />
                                 <module name="LineLength">
                                     <!-- needs extra, because Eclipse formatter ignores the ending left 
-                                        brace -->
+                                    brace -->
                                     <property name="max" value="200" />
                                     <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://" />
                                 </module>
@@ -1352,11 +1381,11 @@
     <profiles>
         <profile>
             <!-- Checks style and licensing requirements. This is a good idea to run 
-                for contributions and for the release process. While it would be nice to 
-                run always these plugins can considerably slow the build and have proven 
-                to create unstable builds in our multi-module project and when building using 
-                multiple threads. The stability issues seen with Checkstyle in multi-module 
-                builds include false-positives and false negatives. -->
+            for contributions and for the release process. While it would be nice to 
+            run always these plugins can considerably slow the build and have proven 
+            to create unstable builds in our multi-module project and when building using 
+            multiple threads. The stability issues seen with Checkstyle in multi-module 
+            builds include false-positives and false negatives. -->
             <id>contrib-check</id>
             <build>
                 <plugins>


[25/51] [abbrv] nifi git commit: NIFI-655: - Refactoring web security to use Spring Security Java Configuration. - Introducing security in Web UI in order to get JWT.

Posted by mc...@apache.org.
http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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
new file mode 100644
index 0000000..978d019
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp
@@ -0,0 +1,54 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>NiFi Login</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <link rel="shortcut icon" href="images/nifi16.ico"/>
+        <link rel="stylesheet" href="css/reset.css" type="text/css" />
+        ${nf.login.style.tags}
+        <link rel="stylesheet" href="js/jquery/modal/jquery.modal.css?${project.version}" type="text/css" />
+        <link rel="stylesheet" href="js/jquery/qtip2/jquery.qtip.min.css?" type="text/css" />
+        <link rel="stylesheet" href="js/jquery/ui-smoothness/jquery-ui-1.10.4.min.css" type="text/css" />
+        <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>
+        <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>
+        <script type="text/javascript" src="js/nf/nf-namespace.js?${project.version}"></script>
+        ${nf.login.script.tags}
+    </head>
+    <body class="login-body">
+        <div id="user-logout-container" class="hidden">
+            <span id="user-logout" class="link">logout</span>
+        </div>
+        <div id="login-contents-container">
+            <jsp:include page="/WEB-INF/partials/login/login-message.jsp"/>
+            <jsp:include page="/WEB-INF/partials/login/login-form.jsp"/>
+            <jsp:include page="/WEB-INF/partials/login/nifi-registration-form.jsp"/>
+            <jsp:include page="/WEB-INF/partials/login/login-submission.jsp"/>
+            <jsp:include page="/WEB-INF/partials/login/login-progress.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/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/message-page.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/message-page.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/message-page.jsp
index 796877f..b0ba026 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/message-page.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/message-page.jsp
@@ -27,8 +27,8 @@
 
     <body class="message-pane">
         <div class="message-pane-message-box">
-            <p class="message-pane-title"><%= request.getAttribute("title") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getAttribute("title").toString()) %></p>
-            <p class="message-pane-content"><%= request.getAttribute("messages") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getAttribute("messages").toString()) %></p>
+            <div class="message-pane-title"><%= request.getAttribute("title") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getAttribute("title").toString()) %></div>
+            <div class="message-pane-content"><%= request.getAttribute("messages") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getAttribute("messages").toString()) %></div>
         </div>
     </body>
 </html>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp
index e02a7cd..496bfd1 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp
@@ -31,6 +31,7 @@
         <link rel="stylesheet" href="js/jquery/slickgrid/css/slick.grid.css" type="text/css" />
         <link rel="stylesheet" href="js/jquery/slickgrid/css/slick-default-theme.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.center.js"></script>
         <script type="text/javascript" src="js/jquery/modal/jquery.modal.js?${project.version}"></script>
         <script type="text/javascript" src="js/jquery/tabbs/jquery.tabbs.js?${project.version}"></script>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
index e6f3305..45dff93 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
@@ -38,6 +38,7 @@
         <script type="text/javascript" src="js/codemirror/lib/codemirror-compressed.js"></script>
         <script type="text/javascript" src="js/jquery/jquery-2.1.1.min.js"></script>
         <script type="text/javascript" src="js/jquery/ui-smoothness/jquery-ui-1.10.4.min.js"></script>
+        <script type="text/javascript" src="js/jquery/jquery.base64.js"></script>
         <script type="text/javascript" src="js/jquery/jquery.center.js"></script>
         <script type="text/javascript" src="js/jquery/tabbs/jquery.tabbs.js?${project.version}"></script>
         <script type="text/javascript" src="js/jquery/combo/jquery.combo.js?${project.version}"></script>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp
index 126c388..452064d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp
@@ -30,6 +30,7 @@
         <link rel="stylesheet" href="js/jquery/slickgrid/css/slick.grid.css" type="text/css" />
         <link rel="stylesheet" href="js/jquery/slickgrid/css/slick-default-theme.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.center.js"></script>
         <script type="text/javascript" src="js/jquery/jquery.form.min.js"></script>
         <script type="text/javascript" src="js/jquery/combo/jquery.combo.js?${project.version}"></script>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp
index a5f422c..b3e0968 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp
@@ -31,6 +31,7 @@
         <link rel="stylesheet" href="js/jquery/slickgrid/css/slick.grid.css" type="text/css" />
         <link rel="stylesheet" href="js/jquery/slickgrid/css/slick-default-theme.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.center.js"></script>
         <script type="text/javascript" src="js/jquery/tabbs/jquery.tabbs.js?${project.version}"></script>
         <script type="text/javascript" src="js/jquery/combo/jquery.combo.js?${project.version}"></script>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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 f312327..2ea7ca6 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
@@ -44,6 +44,17 @@
     </div>
     <div id="header-links-container">
         <ul>
+            <li id="current-user-container">
+                <div id="anonymous-user-alert" class="hidden"></div>
+                <div id="current-user"></div>
+                <div class="clear"></div>
+            </li>
+            <li id="login-link-container">
+                <span id="login-link" class="link">login</span>
+            </li>
+            <li id="logout-link-container" style="display: none;">
+                <span id="logout-link" class="link">logout</span>
+            </li>
             <li>
                 <span id="help-link" class="link">help</span>
             </li>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/registration.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/registration.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/registration.jsp
deleted file mode 100644
index 56b3236..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/registration.jsp
+++ /dev/null
@@ -1,44 +0,0 @@
-<%--
- Licensed to the Apache Software Foundation (ASF) under one or more
-  contributor license agreements.  See the NOTICE file distributed with
-  this work for additional information regarding copyright ownership.
-  The ASF licenses this file to You under the Apache License, Version 2.0
-  (the "License"); you may not use this file except in compliance with
-  the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
---%>
-<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
-<div id="registration-pane" class="message-pane hidden">
-    <div class="message-pane-message-box">
-        <p id="register-title" class="message-pane-title">You are not authorized to access this data flow</p>
-        <p id="register-content" class="message-pane-content">
-        <div>
-            <div id="expand-registration-button" class="collapsed pointer"></div>
-            <span id="expand-registration-text" class="link">Request Access</span>
-        </div>
-        <div id="registration-form" class="settings hidden">
-            <div class="setting">
-                <div class="setting-name">Justification</div>
-                <div class="setting-field">
-                    <textarea cols="30" rows="4" id="registration-justification" maxlength="500" name="registration-justification" class="setting-input"></textarea>
-                </div>
-                <div style="text-align: right; color: #666; margin-top: 2px;">
-                    <span id="remaining-characters"></span>&nbsp;characters remaining
-                </div>
-                <div class="clear"></div>
-            </div>
-            <div>
-                <div id="registration-form-submit" class="button">Submit</div>
-                <div class="clear"></div>
-            </div>
-        </div>
-        </p>
-    </div>
-</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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
new file mode 100644
index 0000000..f8f06f3
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-form.jsp
@@ -0,0 +1,32 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="login-container" class="hidden">
+    <div class="login-title">Log In</div>
+    <div class="setting">
+        <div class="setting-name">Username</div>
+        <div class="setting-field">
+            <input type="text" id="username"/>
+        </div>
+    </div>
+    <div class="setting">
+        <div class="setting-name">Password</div>
+        <div class="setting-field">
+            <input type="password" id="password"/>
+        </div>
+    </div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-message.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-message.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-message.jsp
new file mode 100644
index 0000000..053af81
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-message.jsp
@@ -0,0 +1,21 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="login-message-container" class="hidden">
+    <div id="login-message-title"></div>
+    <div id="login-message"></div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-progress.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-progress.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-progress.jsp
new file mode 100644
index 0000000..6b08e54
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-progress.jsp
@@ -0,0 +1,22 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="login-progress-container" class="login-container hidden">
+    <div id="login-progress-spinner" class="loading-container"></div>
+    <div id="login-progress-label"></div>
+    <div class="clear"></div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-submission.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-submission.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-submission.jsp
new file mode 100644
index 0000000..508ead3
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-submission.jsp
@@ -0,0 +1,20 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="login-submission-container" class="login-container hidden">
+    <div id="login-submission-button" class="button">Log in</div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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
new file mode 100644
index 0000000..3806bd5
--- /dev/null
+++ 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
@@ -0,0 +1,38 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="nifi-registration-container" class="login-container hidden">
+    <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>
+            </div>
+        </div>
+    </div>
+    <div class="setting">
+        <div class="setting-name">Justification</div>
+        <div class="setting-field">
+            <textarea cols="30" rows="4" id="nifi-registration-justification" maxlength="500" class="setting-input"></textarea>
+        </div>
+        <div style="text-align: right; color: #666; margin-top: 2px; float: right;">
+            <span id="remaining-characters"></span>&nbsp;characters remaining
+        </div>
+        <div class="clear"></div>
+    </div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/message-pane.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/message-pane.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/message-pane.jsp
index 1bdec3d..db5dece 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/message-pane.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/message-pane.jsp
@@ -16,8 +16,11 @@
 --%>
 <%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
 <div id="message-pane" class="message-pane hidden">
+    <div id="user-logout-container" class="hidden">
+        <span id="user-logout" class="link">logout</span>
+    </div>
     <div class="message-pane-message-box">
-        <p id="message-title" class="message-pane-title"></p>
-        <p id="message-content" class="message-pane-content"></p>
+        <div id="message-title" class="message-pane-title"></div>
+        <div id="message-content" class="message-pane-content"></div>
     </div>
 </div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/user-details-dialog.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/user-details-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/user-details-dialog.jsp
index 8587be3..8a81882 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/user-details-dialog.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/user-details-dialog.jsp
@@ -25,7 +25,7 @@
             <div class="clear"></div>
         </div>
         <div class="setting">
-            <div class="setting-name">DN</div>
+            <div class="setting-name">Identity</div>
             <div class="setting-field">
                 <span id="user-dn-details-dialog"></span>
             </div>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/web.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/web.xml
index d0a5e39..232dbce 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/web.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/web.xml
@@ -17,18 +17,17 @@
     <display-name>nifi</display-name>
     
     <!-- servlet to map to canvas page -->
-    
     <servlet>
         <servlet-name>NiFiCanvas</servlet-name>
         <jsp-file>/WEB-INF/pages/canvas.jsp</jsp-file>
     </servlet>
     <servlet-mapping>
         <servlet-name>NiFiCanvas</servlet-name>
+        <!--<url-pattern>/token</url-pattern>-->
         <url-pattern>/canvas</url-pattern>
     </servlet-mapping>
     
     <!-- servlet to map to summary page -->
-    
     <servlet>
         <servlet-name>NiFiSummary</servlet-name>
         <jsp-file>/WEB-INF/pages/summary.jsp</jsp-file>
@@ -39,7 +38,6 @@
     </servlet-mapping>
     
     <!-- servlet to map to history page -->
-    
     <servlet>
         <servlet-name>NiFiHistory</servlet-name>
         <jsp-file>/WEB-INF/pages/history.jsp</jsp-file>
@@ -50,7 +48,6 @@
     </servlet-mapping>
     
     <!-- servlet to map to provenance page -->
-    
     <servlet>
         <servlet-name>NiFiProvenance</servlet-name>
         <jsp-file>/WEB-INF/pages/provenance.jsp</jsp-file>
@@ -61,7 +58,6 @@
     </servlet-mapping>
     
     <!-- servlet to map to counters page -->
-    
     <servlet>
         <servlet-name>NiFiCounters</servlet-name>
         <jsp-file>/WEB-INF/pages/counters.jsp</jsp-file>
@@ -72,7 +68,6 @@
     </servlet-mapping>
     
     <!-- servlet to map to templates page -->
-    
     <servlet>
         <servlet-name>NiFiTemplates</servlet-name>
         <jsp-file>/WEB-INF/pages/templates.jsp</jsp-file>
@@ -83,7 +78,6 @@
     </servlet-mapping>
     
     <!-- servlet to map to users page -->
-    
     <servlet>
         <servlet-name>NiFiUsers</servlet-name>
         <jsp-file>/WEB-INF/pages/users.jsp</jsp-file>
@@ -94,7 +88,6 @@
     </servlet-mapping>
     
     <!-- servlet to map to cluster page -->
-    
     <servlet>
         <servlet-name>NiFiCluster</servlet-name>
         <jsp-file>/WEB-INF/pages/cluster.jsp</jsp-file>
@@ -105,7 +98,6 @@
     </servlet-mapping>
     
     <!-- servlet to map to bulletin board page -->
-    
     <servlet>
         <servlet-name>BulletinBoard</servlet-name>
         <jsp-file>/WEB-INF/pages/bulletin-board.jsp</jsp-file>
@@ -116,7 +108,6 @@
     </servlet-mapping>
     
     <!-- servlet to support message page -->
-    
     <servlet>
         <servlet-name>MessagePage</servlet-name>
         <jsp-file>/WEB-INF/pages/message-page.jsp</jsp-file>
@@ -127,7 +118,6 @@
     </servlet-mapping>
     
     <!-- servlet to support image downloading -->
-    
     <servlet>
         <servlet-name>DownloadSvg</servlet-name>
         <servlet-class>org.apache.nifi.web.servlet.DownloadSvg</servlet-class>
@@ -137,6 +127,16 @@
         <url-pattern>/download-svg</url-pattern>
     </servlet-mapping>
     
+    <!-- servlet to login page -->
+    <servlet>
+        <servlet-name>Login</servlet-name>
+        <jsp-file>/WEB-INF/pages/login.jsp</jsp-file>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>Login</servlet-name>
+        <url-pattern>/login</url-pattern>
+    </servlet-mapping>
+    
     <filter>
         <filter-name>IeEdgeHeader</filter-name>
         <filter-class>org.apache.nifi.web.filter.IeEdgeHeader</filter-class>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css
index a8866ad..abb5ebd 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css
@@ -28,7 +28,6 @@
 @import url(connection-configuration.css);
 @import url(connection-details.css);
 @import url(shell.css);
-@import url(registration.css);
 @import url(dialog.css);
 @import url(new-processor-dialog.css);
 @import url(new-controller-service-dialog.css);

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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 0f08b47..49dd3a0 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,6 +506,24 @@ 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;
+    max-width: 250px;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
 #utilities-container {
     float: right;
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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
new file mode 100644
index 0000000..68086a7
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/login.css
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+/*
+    Login Styles
+*/
+
+#login-contents-container {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    bottom: 0px;
+    right: 0px;
+    background: #fff url(../images/bg-error.png) left top no-repeat;
+    font-family: Verdana, Geneva, sans-serif;
+    padding-top: 100px;
+    padding-left: 100px;
+}
+
+#login-message-title {
+    font-size: 18px;
+    color: #294c58;
+    margin-bottom: 16px;
+}
+
+#login-message {
+    font-size: 11px;
+}
+
+.login-title {
+    font-size: 12px;
+    font-weight: bold;
+    margin-bottom: 10px;
+    color: #000;
+}
+
+/*
+    Login
+*/
+
+body.login-body input, body.login-body textarea {
+    width: 400px;
+}
+
+div.login-container {
+    width: 412px;
+}
+
+/*
+    NiFi Registration
+*/
+
+#nifi-user-submit-justification-container {
+    margin-bottom: 10px;
+}
+
+#nifi-user-submit-justification {
+    font-weight: bold;
+}
+
+#nifi-registration-justification {
+    height: 200px;
+}
+
+/*
+    Login Progress
+*/
+
+#login-progress-label {
+    float: right;
+    font-weight: bold;
+    line-height: 16px;
+}
+
+#login-progress-spinner {
+    float: right;
+    width: 16px;
+    height: 16px;
+    background-color: transparent;
+    margin-left: 3px;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
index 95ee641..deadcd5 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
@@ -77,6 +77,17 @@ div.context-menu-provenance {
     background-position: top left;
 }
 
+#user-logout-container {
+    position: absolute;
+    left: 478px;
+    top: 100px;
+    z-index: 1300;
+}
+
+#user-logout {
+    text-decoration: underline;
+}
+
 /*
     General Styles
 */
@@ -153,7 +164,7 @@ input.filter-list {
     background: transparent url(../images/iconTwistArrow.png) no-repeat scroll top right;
 }
 
-input[type=text], textarea {
+input[type=text], input[type=password], textarea {
     background: white url(../images/bgInputText.png) repeat-x scroll top;
     border: 1px solid #ccc;
     font-family: Verdana;

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/registration.css
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/registration.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/registration.css
deleted file mode 100644
index d4fdc7e..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/registration.css
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
-    Registration form styles.
-*/
-
-#registration-pane {
-    z-index: 1299;
-}
-
-#registration-form {
-    margin-top: 10px;
-    width: 610px;
-}
-
-#expand-registration-button {
-    width: 10px;
-    height: 10px;
-    float: left;
-    margin-right: 5px;
-}
-
-#expand-registration-text {
-    -webkit-user-select: none;
-    -moz-user-select: none;
-}
-
-#registration-justification {
-    width: 600px;
-    height: 200px;
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/bulletin-board/nf-bulletin-board.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/bulletin-board/nf-bulletin-board.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/bulletin-board/nf-bulletin-board.js
index 656755e..55d0a9d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/bulletin-board/nf-bulletin-board.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/bulletin-board/nf-bulletin-board.js
@@ -417,6 +417,8 @@ nf.BulletinBoard = (function () {
          * Initializes the bulletin board page.
          */
         init: function () {
+            nf.Storage.init();
+            
             initializePage().done(function () {
                 start();
             });

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.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-header.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js
index 5cc1eff..7d63534 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js
@@ -31,8 +31,10 @@ nf.CanvasHeader = (function () {
     return {
         /**
          * Initialize the canvas header.
+         * 
+         * @argument {boolean} supportsLogin Whether login is supported
          */
-        init: function () {
+        init: function (supportsLogin) {
             // mouse over for the reporting link
             nf.Common.addHoverEffect('#reporting-link', 'reporting-link', 'reporting-link-hover').click(function () {
                 nf.Shell.showPage('summary');
@@ -139,6 +141,28 @@ nf.CanvasHeader = (function () {
                 nf.Shell.showPage(config.urls.helpDocument);
             });
 
+            // show the login link if supported and user is currently anonymous
+            var isAnonymous = $('#current-user').text() === nf.Common.ANONYMOUS_USER_TEXT;
+            if (supportsLogin === true && isAnonymous) {
+                // login link
+                $('#login-link').click(function () {
+                    nf.Shell.showPage('login', false);
+                });
+            } else {
+                $('#login-link-container').css('display', 'none');
+            }
+
+            // if login is not supported, don't show the current user
+            if (supportsLogin === false) {
+                $('#current-user-container').css('display', 'none');
+            }
+
+            // logout link
+            $('#logout-link').click(function () {
+                nf.Storage.removeItem("jwt");
+                window.location = '/nifi';
+            });
+
             // initialize the new template dialog
             $('#new-template-dialog').modal({
                 headerText: 'Create Template',
@@ -154,11 +178,11 @@ nf.CanvasHeader = (function () {
                         handler: {
                             click: function () {
                                 var selection = nf.CanvasUtils.getSelection();
-                                
+
                                 // color the selected components
                                 selection.each(function (d) {
                                     var selected = d3.select(this);
-                                    
+
                                     var revision = nf.Client.getRevision();
                                     var selectedData = selected.datum();
 
@@ -197,7 +221,7 @@ nf.CanvasHeader = (function () {
                                         });
                                     }
                                 });
-                                
+
                                 // close the dialog
                                 $('#fill-color-dialog').modal('hide');
                             }
@@ -238,20 +262,20 @@ nf.CanvasHeader = (function () {
                     });
                 }
             });
-            
+
             // updates the color if its a valid hex color string
             var updateColor = function () {
                 var hex = $('#fill-color-value').val();
-                
+
                 // only update the fill color when its a valid hex color string
                 // #[six hex characters|three hex characters] case insensitive
                 if (/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex)) {
                     $('#fill-color').minicolors('value', hex);
                 }
             };
-            
+
             // apply fill color from field on blur and enter press
-            $('#fill-color-value').on('blur', updateColor).on('keyup', function(e) {
+            $('#fill-color-value').on('blur', updateColor).on('keyup', function (e) {
                 var code = e.keyCode ? e.keyCode : e.which;
                 if (code === $.ui.keyCode.ENTER) {
                     updateColor();
@@ -310,23 +334,22 @@ nf.CanvasHeader = (function () {
                     }
                 }
             });
-            
+
             var toolbar = $('#toolbar');
             var groupButton = $('#action-group');
-            $(window).on('resize', function() {
+            $(window).on('resize', function () {
                 if (toolbar.width() < MIN_TOOLBAR_WIDTH && groupButton.is(':visible')) {
                     toolbar.find('.secondary').hide();
                 } else if (toolbar.width() > MIN_TOOLBAR_WIDTH && groupButton.is(':hidden')) {
                     toolbar.find('.secondary').show();
                 }
             });
-            
+
             // set up the initial visibility
             if (toolbar.width() < MIN_TOOLBAR_WIDTH) {
                 toolbar.find('.secondary').hide();
             }
         },
-        
         /**
          * Reloads and clears any warnings.
          */
@@ -341,7 +364,7 @@ nf.CanvasHeader = (function () {
                 // hide the refresh link on the canvas
                 $('#stats-last-refreshed').removeClass('alert');
                 $('#refresh-required-container').hide();
-                
+
                 // hide the refresh link on the settings
                 $('#settings-last-refreshed').removeClass('alert');
                 $('#settings-refresh-required-icon').hide();

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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 7137fe4..c9498fe 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
@@ -57,6 +57,7 @@ nf.Canvas = (function () {
 
     var config = {
         urls: {
+            identity: '../nifi-api/controller/identity',
             authorities: '../nifi-api/controller/authorities',
             revision: '../nifi-api/controller/revision',
             status: '../nifi-api/controller/status',
@@ -64,6 +65,7 @@ nf.Canvas = (function () {
             banners: '../nifi-api/controller/banners',
             controller: '../nifi-api/controller',
             controllerConfig: '../nifi-api/controller/config',
+            accessConfig: '../nifi-api/access/config',
             cluster: '../nifi-api/cluster',
             d3Script: 'js/d3/d3.min.js'
         }
@@ -1018,122 +1020,166 @@ nf.Canvas = (function () {
          * Initialize NiFi.
          */
         init: function () {
-            // init the registration form before performing the first query since 
-            // the response could lead to a registration attempt
-            nf.Registration.init();
+            // get the current user's identity
+            var identityXhr = $.ajax({
+                type: 'GET',
+                url: config.urls.identity,
+                dataType: 'json'
+            });
 
-            // get the controller config to register the status poller
-            var configXhr = $.ajax({
+            // get the current user's authorities
+            var authoritiesXhr = $.ajax({
                 type: 'GET',
-                url: config.urls.controllerConfig,
+                url: config.urls.authorities,
                 dataType: 'json'
             });
 
-            // create the deferred cluster request
-            var isClusteredRequest = $.Deferred(function (deferred) {
-                $.ajax({
-                    type: 'HEAD',
-                    url: config.urls.cluster
-                }).done(function (response, status, xhr) {
-                    clustered = true;
-                    deferred.resolve(response, status, xhr);
+            // load the identity and authorities for the current user
+            var userXhr = $.Deferred(function (deferred) {
+                $.when(authoritiesXhr, identityXhr).done(function (authoritiesResult, identityResult) {
+                    var authoritiesResponse = authoritiesResult[0];
+                    var identityResponse = identityResult[0];
+
+                    // set the user's authorities
+                    nf.Common.setAuthorities(authoritiesResponse.authorities);
+
+                    // at this point the user may be themselves or anonymous
+
+                    // 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
+                        if (nf.Storage.getItem('jwt') !== null) {
+                            $('#logout-link-container').show();
+                        }
+                    } else {
+                        // set the anonymous user label
+                        nf.Common.setAnonymousUserLabel();
+                    }
+                    deferred.resolve();
                 }).fail(function (xhr, status, error) {
-                    if (xhr.status === 404) {
-                        clustered = false;
-                        deferred.resolve('', 'success', xhr);
+                    // 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 || xhr.status === 403) {
+                        window.location = '/nifi/login';
                     } else {
                         deferred.reject(xhr, status, error);
                     }
                 });
             }).promise();
 
-            // load the authorities
-            var authoritiesXhr = $.ajax({
-                type: 'GET',
-                url: config.urls.authorities,
-                dataType: 'json'
-            });
+            userXhr.done(function () {
+                // get the controller config to register the status poller
+                var configXhr = $.ajax({
+                    type: 'GET',
+                    url: config.urls.controllerConfig,
+                    dataType: 'json'
+                });
 
-            // ensure the authorities and config request is processed first
-            $.when(authoritiesXhr, configXhr).done(function (authoritiesResult, configResult) {
-                var authoritiesResponse = authoritiesResult[0];
-                var configResponse = configResult[0];
+                // get the login config
+                var loginXhr = $.ajax({
+                    type: 'GET',
+                    url: config.urls.accessConfig,
+                    dataType: 'json'
+                });
 
-                // set the user's authorities
-                nf.Common.setAuthorities(authoritiesResponse.authorities);
+                // create the deferred cluster request
+                var isClusteredRequest = $.Deferred(function (deferred) {
+                    $.ajax({
+                        type: 'HEAD',
+                        url: config.urls.cluster
+                    }).done(function (response, status, xhr) {
+                        clustered = true;
+                        deferred.resolve(response, status, xhr);
+                    }).fail(function (xhr, status, error) {
+                        if (xhr.status === 404) {
+                            clustered = false;
+                            deferred.resolve('', 'success', xhr);
+                        } else {
+                            deferred.reject(xhr, status, error);
+                        }
+                    });
+                }).promise();
 
-                // calculate the canvas offset
-                var canvasContainer = $('#canvas-container');
-                nf.Canvas.CANVAS_OFFSET = canvasContainer.offset().top;
-
-                // get the config details
-                var configDetails = configResponse.config;
-
-                // when both request complete, load the application
-                isClusteredRequest.done(function () {
-                    // get the auto refresh interval
-                    var autoRefreshIntervalSeconds = parseInt(configDetails.autoRefreshIntervalSeconds, 10);
-
-                    // initialize whether site to site is secure
-                    secureSiteToSite = configDetails.siteToSiteSecure;
-
-                    // load d3
-                    loadD3().done(function () {
-                        nf.Storage.init();
-
-                        // initialize the application
-                        initCanvas();
-                        nf.Canvas.View.init();
-                        nf.ContextMenu.init();
-                        nf.CanvasToolbar.init();
-                        nf.CanvasToolbox.init();
-                        nf.CanvasHeader.init();
-                        nf.GraphControl.init();
-                        nf.Search.init();
-                        nf.Settings.init();
-                        nf.Actions.init();
-
-                        // initialize the component behaviors
-                        nf.Draggable.init();
-                        nf.Selectable.init();
-                        nf.Connectable.init();
-
-                        // initialize the chart
-                        nf.StatusHistory.init(configDetails.timeOffset);
-
-                        // initialize the birdseye
-                        nf.Birdseye.init();
-
-                        // initialize components
-                        nf.ConnectionConfiguration.init();
-                        nf.ControllerService.init();
-                        nf.ReportingTask.init();
-                        nf.ProcessorConfiguration.init();
-                        nf.ProcessGroupConfiguration.init();
-                        nf.RemoteProcessGroupConfiguration.init();
-                        nf.RemoteProcessGroupPorts.init();
-                        nf.PortConfiguration.init();
-                        nf.SecurePortConfiguration.init();
-                        nf.LabelConfiguration.init();
-                        nf.ProcessorDetails.init();
-                        nf.ProcessGroupDetails.init();
-                        nf.PortDetails.init();
-                        nf.SecurePortDetails.init();
-                        nf.ConnectionDetails.init();
-                        nf.RemoteProcessGroupDetails.init();
-                        nf.GoTo.init();
-                        nf.Graph.init().done(function () {
-                            // determine the split between the polling
-                            var pollingSplit = autoRefreshIntervalSeconds / 2;
-
-                            // register the revision and status polling
-                            startRevisionPolling(autoRefreshIntervalSeconds);
-                            setTimeout(function () {
-                                startStatusPolling(autoRefreshIntervalSeconds);
-                            }, pollingSplit * 1000);
-
-                            // hide the splash screen
-                            nf.Canvas.hideSplash();
+                // ensure the config requests are loaded
+                $.when(configXhr, loginXhr, userXhr).done(function (configResult, loginResult) {
+                    var configResponse = configResult[0];
+                    var loginResponse = loginResult[0];
+
+                    // calculate the canvas offset
+                    var canvasContainer = $('#canvas-container');
+                    nf.Canvas.CANVAS_OFFSET = canvasContainer.offset().top;
+
+                    // get the config details
+                    var configDetails = configResponse.config;
+                    var loginDetails = loginResponse.config;
+
+                    // when both request complete, load the application
+                    isClusteredRequest.done(function () {
+                        // get the auto refresh interval
+                        var autoRefreshIntervalSeconds = parseInt(configDetails.autoRefreshIntervalSeconds, 10);
+
+                        // initialize whether site to site is secure
+                        secureSiteToSite = configDetails.siteToSiteSecure;
+
+                        // load d3
+                        loadD3().done(function () {
+                            nf.Storage.init();
+
+                            // initialize the application
+                            initCanvas();
+                            nf.Canvas.View.init();
+                            nf.ContextMenu.init();
+                            nf.CanvasToolbar.init();
+                            nf.CanvasToolbox.init();
+                            nf.CanvasHeader.init(loginDetails.supportsLogin);
+                            nf.GraphControl.init();
+                            nf.Search.init();
+                            nf.Settings.init();
+
+                            // initialize the component behaviors
+                            nf.Draggable.init();
+                            nf.Selectable.init();
+                            nf.Connectable.init();
+
+                            // initialize the chart
+                            nf.StatusHistory.init(configDetails.timeOffset);
+
+                            // initialize the birdseye
+                            nf.Birdseye.init();
+
+                            // initialize components
+                            nf.ConnectionConfiguration.init();
+                            nf.ControllerService.init();
+                            nf.ReportingTask.init();
+                            nf.ProcessorConfiguration.init();
+                            nf.ProcessGroupConfiguration.init();
+                            nf.RemoteProcessGroupConfiguration.init();
+                            nf.RemoteProcessGroupPorts.init();
+                            nf.PortConfiguration.init();
+                            nf.SecurePortConfiguration.init();
+                            nf.LabelConfiguration.init();
+                            nf.ProcessorDetails.init();
+                            nf.ProcessGroupDetails.init();
+                            nf.PortDetails.init();
+                            nf.SecurePortDetails.init();
+                            nf.ConnectionDetails.init();
+                            nf.RemoteProcessGroupDetails.init();
+                            nf.GoTo.init();
+                            nf.Graph.init().done(function () {
+                                // determine the split between the polling
+                                var pollingSplit = autoRefreshIntervalSeconds / 2;
+
+                                // register the revision and status polling
+                                startRevisionPolling(autoRefreshIntervalSeconds);
+                                setTimeout(function () {
+                                    startStatusPolling(autoRefreshIntervalSeconds);
+                                }, pollingSplit * 1000);
+
+                                // hide the splash screen
+                                nf.Canvas.hideSplash();
+                            }).fail(nf.Common.handleAjaxError);
                         }).fail(nf.Common.handleAjaxError);
                     }).fail(nf.Common.handleAjaxError);
                 }).fail(nf.Common.handleAjaxError);

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-registration.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-registration.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-registration.js
deleted file mode 100644
index b678e27..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-registration.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* global nf */
-
-nf.Registration = (function () {
-
-    var config = {
-        urls: {
-            users: '../nifi-api/controller/users'
-        }
-    };
-
-    return {
-        /**
-         * Initializes the user account registration form.
-         */
-        init: function () {
-            $('#registration-justification').count({
-                charCountField: '#remaining-characters'
-            });
-
-            // register a click listener to expand/collapse the registration form
-            $('#expand-registration-button, #expand-registration-text').click(function () {
-                var registrationForm = $('#registration-form');
-                if (registrationForm.is(':visible')) {
-                    $('#expand-registration-button').removeClass('registration-expanded').addClass('collapsed');
-                } else {
-                    $('#expand-registration-button').removeClass('registration-collapsed').addClass('expanded');
-                }
-                registrationForm.toggle();
-            });
-
-            // register a click listener for submitting user account requests
-            $('#registration-form-submit').one('click', function () {
-                var justification = $('#registration-justification').val();
-
-                // attempt to create the user account registration
-                $.ajax({
-                    type: 'POST',
-                    url: config.urls.users,
-                    data: {
-                        'justification': justification
-                    }
-                }).done(function (response) {
-                    // hide the registration pane
-                    $('#registration-pane').hide();
-
-                    // show the message pane
-                    $('#message-pane').show();
-                    $('#message-title').text('Thanks');
-                    $('#message-content').text('Your request will be processed shortly.');
-                }).fail(nf.Common.handleAjaxError);
-            });
-        }
-    };
-}());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-storage.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-storage.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-storage.js
deleted file mode 100644
index 3937970..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-storage.js
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* global nf, d3 */
-
-nf.Storage = (function () {
-
-    // Store items for two days before being eligible for removal.
-    var TWO_DAYS = 86400000 * 2;
-
-    // alternative to local storage thats used when localStorage isn't available
-    var alternativeStorage;
-
-    // determine if this browser supports local storage
-    var SUPPORTS_LOCAL_STORAGE = (function () {
-        var test = 'test';
-        try {
-            localStorage.setItem(test, test);
-            localStorage.removeItem(test);
-            return true;
-        } catch (e) {
-            return false;
-        }
-    }());
-
-    return {
-        /**
-         * Initializes the storage. When the browser supports localStorage, 
-         * the items will be persisted for two days. Once the scripts runs
-         * thereafter, all eligible items will be removed. If localStorage
-         * is not supported, a data structure will be used to hold items. 
-         * This strategy does not support persistence.
-         */
-        init: function () {
-            // if we support local storage, see if anything can be removed
-            if (SUPPORTS_LOCAL_STORAGE) {
-                for (var i = 0; i < localStorage.length; i++) {
-                    try {
-                        // get the next item
-                        var key = localStorage.key(i);
-                        var entry = JSON.parse(localStorage.getItem(key));
-
-                        // get the expiration
-                        var expires = new Date(entry.expires);
-                        var now = new Date();
-
-                        // if the expiration date has passed, remove it
-                        if (expires.valueOf() < now.valueOf()) {
-                            localStorage.removeItem(key);
-                        }
-                    } catch (e) {
-                        // likely unable to parse the item
-                    }
-                }
-            } else {
-                alternativeStorage = d3.map();
-            }
-        },
-        
-        /**
-         * Stores the specified item. If supported, will be persisted
-         * in localStorage.
-         * 
-         * @param {type} key
-         * @param {type} item
-         */
-        setItem: function (key, item) {
-            if (SUPPORTS_LOCAL_STORAGE) {
-                // calculate the expiration
-                var expires = new Date().valueOf() + TWO_DAYS;
-
-                // create the entry
-                var entry = {
-                    expires: expires,
-                    item: item
-                };
-
-                // store the item
-                localStorage.setItem(key, JSON.stringify(entry));
-            } else {
-                alternativeStorage.set(key, item);
-            }
-        },
-        
-        /**
-         * Gets the item with the specified key. If an item with this key does
-         * not exist, null is returned. If an item exists but cannot be parsed
-         * or is malformed/unrecognized, null is returned.
-         * 
-         * @param {type} key
-         */
-        getItem: function (key) {
-            if (SUPPORTS_LOCAL_STORAGE) {
-                try {
-                    // parse the entry
-                    var entry = JSON.parse(localStorage.getItem(key));
-
-                    // ensure the entry and item are present
-                    if (nf.Common.isDefinedAndNotNull(entry) && nf.Common.isDefinedAndNotNull(entry.item)) {
-                        return entry.item;
-                    } else {
-                        return null;
-                    }
-                } catch (e) {
-                    return null;
-                }
-            } else {
-                return alternativeStorage.get(key);
-            }
-        },
-        
-        /**
-         * Removes the item with the specified key.
-         * 
-         * @param {type} key
-         */
-        removeItem: function (key) {
-            if (SUPPORTS_LOCAL_STORAGE) {
-                localStorage.removeItem(key);
-            } else {
-                alternativeStorage.remove(key);
-            }
-        }
-    };
-}());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js
index 5f00e2c..1eafa67 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js
@@ -123,6 +123,8 @@ nf.Cluster = (function () {
          * Initializes the counters page.
          */
         init: function () {
+            nf.Storage.init();
+            
             // load the users authorities
             loadAuthorities().done(function () {
                 // create the counters table

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js
index cbfe336..6af3d20 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js
@@ -123,6 +123,8 @@ nf.Counters = (function () {
          * Initializes the counters page.
          */
         init: function () {
+            nf.Storage.init();
+            
             // load the users authorities
             loadAuthorities().done(function () {
                 // create the counters table

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js
index 516507c..8f749c0 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js
@@ -124,6 +124,8 @@ nf.History = (function () {
          * Initializes the status page.
          */
         init: function () {
+            nf.Storage.init();
+            
             // load the users authorities
             loadAuthorities().done(function () {
                 // create the history table


[49/51] [abbrv] nifi git commit: NIFI-655: - Changing default expiration time to 12 hours.

Posted by mc...@apache.org.
NIFI-655:
- Changing default expiration time to 12 hours.

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

Branch: refs/heads/master
Commit: 85eb8defdd0bb8b91c627493f6218b77c370c071
Parents: c100052
Author: Matt Gilman <ma...@gmail.com>
Authored: Tue Dec 1 09:36:33 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Tue Dec 1 09:36:33 2015 -0500

----------------------------------------------------------------------
 .../org/apache/nifi/web/security/x509/X509IdentityProvider.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/85eb8def/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.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/x509/X509IdentityProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
index db0b529..c20a28e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
@@ -79,7 +79,7 @@ public class X509IdentityProvider {
         }
 
         // build the authentication response
-        return new AuthenticationResponse(principal, principal, TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS), issuer);
+        return new AuthenticationResponse(principal, principal, TimeUnit.MILLISECONDS.convert(12, TimeUnit.HOURS), issuer);
     }
 
     /* setters */


[42/51] [abbrv] nifi git commit: NIFI-655: - Ensuring the protocol is specified.

Posted by mc...@apache.org.
NIFI-655:
- Ensuring the protocol is specified.

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

Branch: refs/heads/master
Commit: 99016a835eba1048a1246f920ed1f8097fd64f3d
Parents: 64beeef
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 30 12:38:17 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 30 12:38:17 2015 -0500

----------------------------------------------------------------------
 .../src/main/java/org/apache/nifi/ldap/LdapProvider.java        | 5 +++++
 1 file changed, 5 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/99016a83/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
index 4dc7ea4..b604be4 100644
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
@@ -155,6 +155,11 @@ public class LdapProvider implements LoginIdentityProvider {
                             }
                         }
 
+                        // ensure the protocol is specified
+                        if (StringUtils.isBlank(rawProtocol)) {
+                            throw new ProviderCreationException("TLS - Protocol must be specified.");
+                        }
+
                         try {
                             final SSLContext sslContext;
                             if (StringUtils.isBlank(rawKeystore)) {


[35/51] [abbrv] nifi git commit: NIFI-655: - Updating the version of ldap provider nar.

Posted by mc...@apache.org.
NIFI-655:
- Updating the version of ldap provider nar.

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

Branch: refs/heads/master
Commit: 36eaddb7de295a313c0e677afd99a956ec15f04f
Parents: a575498
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 23 16:51:24 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 23 16:51:24 2015 -0500

----------------------------------------------------------------------
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/36eaddb7/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 249f01c..91e7616 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1001,7 +1001,7 @@ language governing permissions and limitations under the License. -->
             <dependency>
                 <groupId>org.apache.nifi</groupId>
                 <artifactId>nifi-ldap-iaa-providers-nar</artifactId>
-                <version>0.3.1-SNAPSHOT</version>
+                <version>0.4.0-SNAPSHOT</version>
                 <type>nar</type>
             </dependency>
             <dependency>


[08/51] [abbrv] nifi git commit: NIFI-655. - Added logback-test.xml configuration resource for nifi-web-security.

Posted by mc...@apache.org.
NIFI-655. - Added logback-test.xml configuration resource for nifi-web-security.

Signed-off-by: Matt Gilman <ma...@gmail.com>


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

Branch: refs/heads/master
Commit: caeede5773ac75061049a1c792a7b15188cae098
Parents: 45b24a4
Author: Andy LoPresto <an...@andylopresto.com>
Authored: Tue Nov 17 17:27:31 2015 -0800
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Nov 18 08:31:08 2015 -0500

----------------------------------------------------------------------
 .../src/test/resources/logback-test.xml         | 36 ++++++++++++++++++++
 1 file changed, 36 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/caeede57/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/resources/logback-test.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/resources/logback-test.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..fe5d27e
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/resources/logback-test.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+
+<configuration>
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%-4r [%t] %-5p %c - %m%n</pattern>
+        </encoder>
+    </appender>
+    
+    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+        <file>./target/log</file>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%date %level [%thread] %logger{40} %msg%n</pattern>
+        </encoder>
+    </appender>
+    
+    
+    <logger name="org.apache.nifi" level="TRACE"/>
+    <root level="INFO">
+        <appender-ref ref="CONSOLE"/>
+    </root>
+</configuration>


[46/51] [abbrv] nifi git commit: NIFI-655: - Removing proxied user chain as user details are already serialized.

Posted by mc...@apache.org.
NIFI-655:
- Removing proxied user chain as user details are already serialized.

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

Branch: refs/heads/master
Commit: 014b2ac4e8c9e0eb22d9658c79752f6e855e5269
Parents: 774d626
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 30 16:30:12 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 30 16:30:12 2015 -0500

----------------------------------------------------------------------
 .../java/org/apache/nifi/web/api/ApplicationResource.java     | 7 -------
 1 file changed, 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/014b2ac4/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
index 399879d..38c0ae3 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
@@ -365,13 +365,6 @@ public abstract class ApplicationResource {
 
         if (httpServletRequest.isSecure()) {
 
-            // add the certificate DN to the proxy chain
-            final NiFiUser user = NiFiUserUtils.getNiFiUser();
-            if (user != null) {
-                // add the proxied user details
-                result.put(PROXIED_ENTITIES_CHAIN_HTTP_HEADER, ProxiedEntitiesUtils.buildProxiedEntitiesChainString(user));
-            }
-
             // add the user's authorities (if any) to the headers
             final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
             if (authentication != null) {


[24/51] [abbrv] nifi git commit: NIFI-655: - Refactoring web security to use Spring Security Java Configuration. - Introducing security in Web UI in order to get JWT.

Posted by mc...@apache.org.
http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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
new file mode 100644
index 0000000..6cefd4f
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js
@@ -0,0 +1,302 @@
+/*
+ * 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.
+ */
+
+/* global nf, top */
+
+$(document).ready(function () {
+    nf.Login.init();
+});
+
+nf.Login = (function () {
+
+    var supportsAnonymous = false;
+
+    var config = {
+        urls: {
+            identity: '../nifi-api/controller/identity',
+            users: '../nifi-api/controller/users',
+            token: '../nifi-api/access/token',
+            accessStatus: '../nifi-api/access',
+            accessConfig: '../nifi-api/access/config'
+        }
+    };
+
+    var initializeMessage = function () {
+        $('#login-message-container').show();
+    };
+
+    var showLogin = function () {
+        // reset the forms
+        $('#username').val('');
+        $('#password').val('');
+        $('#login-submission-button').text('Log in');
+
+        // update the form visibility
+        $('#login-container').show();
+        $('#nifi-registration-container').hide();
+
+        // set the focus
+        $('#username').focus();
+    };
+
+    var initializeNiFiRegistration = function () {
+        $('#nifi-registration-justification').count({
+            charCountField: '#remaining-characters'
+        });
+
+        // toggle between signup and login
+        $('#login-to-account-link').on('click', function () {
+            showLogin();
+        });
+    };
+
+    var showNiFiRegistration = function () {
+        // reset the forms
+        $('#login-submission-button').text('Submit');
+        $('#nifi-registration-justification').val('');
+
+        // update the form visibility
+        $('#login-container').hide();
+        $('#nifi-registration-container').show();
+    };
+
+    var initializeSubmission = function () {
+        $('#login-submission-button').on('click', function () {
+            if ($('#login-container').is(':visible')) {
+                login();
+            } else if ($('#nifi-registration-container').is(':visible')) {
+                submitJustification();
+            }
+        });
+
+        $('#login-submission-container').show();
+    };
+
+    var login = function () {
+        // show the logging message...
+        $('#login-progress-label').text('Logging in...');
+        $('#login-progress-container').show();
+        $('#login-submission-container').hide();
+        
+        // login submit
+        $.ajax({
+            type: 'POST',
+            url: config.urls.token,
+            data: {
+                'username': $('#username').val(),
+                'password': $('#password').val()
+            }
+        }).done(function (jwt) {
+            // get the payload and store the token with the appropirate expiration
+            var token = nf.Common.getJwtPayload(jwt);
+            var expiration = parseInt(token['exp'], 10) * nf.Common.MILLIS_PER_SECOND;
+            nf.Storage.setItem('jwt', jwt, expiration);
+
+            // check to see if they actually have access now
+            $.ajax({
+                type: 'GET',
+                url: config.urls.identity,
+                dataType: 'json'
+            }).done(function (response) {
+                if (response.identity === 'anonymous') {
+                    showLogoutLink();
+
+                    // schedule automatic token refresh
+                    nf.Common.scheduleTokenRefresh();
+            
+                    // show the user
+                    $('#nifi-user-submit-justification').text(token['preferred_username']);
+
+                    // show the registration form
+                    initializeNiFiRegistration();
+                    showNiFiRegistration();
+                    
+                    // update the form visibility
+                    $('#login-submission-container').show();
+                    $('#login-progress-container').hide();
+                } else {
+                    // reload as appropriate - no need to schedule token refresh as the page is reloading
+                    if (top !== window) {
+                        parent.window.location = '/nifi';
+                    } else {
+                        window.location = '/nifi';
+                    }
+                }
+            }).fail(function (xhr, status, error) {
+                showLogoutLink();
+
+                // schedule automatic token refresh
+                nf.Common.scheduleTokenRefresh();
+
+                // show the user
+                $('#nifi-user-submit-justification').text(token['preferred_username']);
+
+                if (xhr.status === 401) {
+                    initializeNiFiRegistration();
+                    showNiFiRegistration();
+                    
+                    // update the form visibility
+                    $('#login-submission-container').show();
+                    $('#login-progress-container').hide();
+                } else {
+                    $('#login-message-title').text('Unable to log in');
+                    $('#login-message').text(xhr.responseText);
+
+                    // update visibility
+                    $('#login-container').hide();
+                    $('#login-submission-container').hide();
+                    $('#login-progress-container').hide();
+                    $('#login-message-container').show();
+                }
+            });
+        }).fail(function (xhr, status, error) {
+            nf.Dialog.showOkDialog({
+                dialogContent: nf.Common.escapeHtml(xhr.responseText),
+                overlayBackground: false
+            });
+
+            // update the form visibility
+            $('#login-submission-container').show();
+            $('#login-progress-container').hide();
+        });
+    };
+
+    var submitJustification = function () {
+        // show the logging message...
+        $('#login-progress-label').text('Submitting...');
+        $('#login-progress-container').show();
+        $('#login-submission-container').hide();
+        
+        // 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 (supportsAnonymous === 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-progress-container').hide();
+            $('#login-message-container').show();
+        });
+    };
+
+    var showLogoutLink = function () {
+        nf.Common.showLogoutLink();
+    };
+
+    return {
+        /**
+         * Initializes the login page.
+         */
+        init: function () {
+            nf.Storage.init();
+
+            if (nf.Storage.getItem('jwt') !== null) {
+                showLogoutLink();
+            }
+
+            // access status
+            var accessStatus = $.ajax({
+                type: 'GET',
+                url: config.urls.accessStatus,
+                dataType: 'json'
+            }).fail(function (xhr, status, error) {
+                $('#login-message-title').text('Unable to check Access Status');
+                $('#login-message').text(xhr.responseText);
+                initializeMessage();
+            });
+            
+            // access config
+            var accessConfigXhr = $.ajax({
+                type: 'GET',
+                url: config.urls.accessConfig,
+                dataType: 'json'
+            });
+            
+            $.when(accessStatus, accessConfigXhr).done(function (accessStatusResult, accessConfigResult) {
+                var accessStatusResponse = accessStatusResult[0];
+                var accessStatus = accessStatusResponse.accessStatus;
+                
+                var accessConfigResponse = accessConfigResult[0];
+                var accessConfig = accessConfigResponse.config;
+                
+                // record whether this NiFi supports anonymous access
+                supportsAnonymous = accessConfig.supportsAnonymous;
+            
+                // possible login states
+                var needsLogin = false;
+                var needsNiFiRegistration = false;
+                var showMessage = false;
+                
+                // handle the status appropriately
+                if (accessStatus.status === 'UNKNOWN') {
+                    needsLogin = true;
+                } else if (accessStatus.status === 'UNREGISTERED') {
+                    needsNiFiRegistration = true;
+                    
+                    $('#nifi-user-submit-justification').text(accessStatus.username);
+                } else if (accessStatus.status === 'NOT_ACTIVE') {
+                    showMessage = true;
+                    
+                    $('#login-message-title').text('Access Denied');
+                    $('#login-message').text(accessStatus.message);
+                } else if (accessStatus.status === 'ACTIVE') {
+                    showMessage = true;
+                    
+                    $('#login-message-title').text('Success');
+                    $('#login-message').text('Your account is active and you are already logged in.');
+                }
+                
+                // if login is required, verify its supported
+                if (accessConfig.supportsLogin === false && needsLogin === true) {
+                    $('#login-message-title').text('Access Denied');
+                    $('#login-message').text('This NiFi is not configured to support login.');
+                    showMessage = true;
+                    needsLogin = false;
+                }
+
+                // initialize the page as appropriate
+                if (showMessage === true) {
+                    initializeMessage();
+                } else if (needsLogin === true) {
+                    showLogin();
+                } else if (needsNiFiRegistration === true) {
+                    initializeNiFiRegistration();
+                    showNiFiRegistration();
+                }
+
+                if (needsLogin === true || needsNiFiRegistration === true) {
+                    initializeSubmission();
+                }
+            });
+        }
+    };
+}());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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 06c34f9..321044f 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
@@ -50,948 +50,1110 @@ $(document).ready(function () {
         // hide the loading indicator 
         $('div.loading-container').removeClass('ajax-loading');
     });
-
-    // initialize the tooltips
-    $('img.setting-icon').qtip(nf.Common.config.tooltipConfig);
-});
-
-// Define a common utility class used across the entire application.
-nf.Common = {
-    config: {
-        sensitiveText: 'Sensitive value set',
-        tooltipConfig: {
-            style: {
-                classes: 'nifi-tooltip'
-            },
-            show: {
-                solo: true,
-                effect: false
-            },
-            hide: {
-                effect: false
-            },
-            position: {
-                at: 'top right',
-                my: 'bottom left'
-            }
-        }
-    },
-    
-    /**
-     * Determines if the current broswer supports SVG.
-     */
-    SUPPORTS_SVG: !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect,
-    
-    /**
-     * The authorities for the current user.
-     */
-    authorities: undefined,
     
-    /**
-     * Sets the authorities for the current user.
-     * 
-     * @argument {array} roles      The current users authorities
-     */
-    setAuthorities: function (roles) {
-        nf.Common.authorities = roles;
-    },
-    
-    /**
-     * Loads a script at the specified URL. Supports caching the script on the browser.
-     * 
-     * @param {string} url
-     */
-    cachedScript: function (url) {
-        return $.ajax({
-            dataType: 'script',
-            cache: true,
-            url: url
-        });
-    },
-    
-    /**
-     * Determines whether the current user can access provenance.
-     * 
-     * @returns {boolean}
-     */
-    canAccessProvenance: function () {
-        var canAccessProvenance = false;
-        if (nf.Common.isDefinedAndNotNull(nf.Common.authorities)) {
-            $.each(nf.Common.authorities, function (i, authority) {
-                if (authority === 'ROLE_PROVENANCE') {
-                    canAccessProvenance = true;
-                    return false;
-                }
-            });
-        }
-        return canAccessProvenance;
-    },
-    
-    /**
-     * Returns whether or not the current user is a DFM.
-     */
-    isDFM: function () {
-        var dfm = false;
-        if (nf.Common.isDefinedAndNotNull(nf.Common.authorities)) {
-            $.each(nf.Common.authorities, function (i, authority) {
-                if (authority === 'ROLE_DFM') {
-                    dfm = true;
+    // include jwt when possible
+    $.ajaxSetup({
+        'beforeSend': function(xhr) {
+            var hadToken = nf.Storage.hasItem('jwt');
+            
+            // get the token to include in all requests
+            var token = nf.Storage.getItem('jwt');
+            if (token !== null) {
+                xhr.setRequestHeader('Authorization', 'Bearer ' + token);
+            } else {
+                // if the current user was logged in with a token and the token just expired, reload
+                if (hadToken === true) {
                     return false;
                 }
-            });
+            }
         }
-        return dfm;
-    },
+    });
+
+    // initialize the tooltips
+    $('img.setting-icon').qtip(nf.Common.config.tooltipConfig);
     
-    /**
-     * Returns whether or not the current user is a DFM.
-     */
-    isAdmin: function () {
-        var admin = false;
-        if (nf.Common.isDefinedAndNotNull(nf.Common.authorities)) {
-            $.each(nf.Common.authorities, function (i, authority) {
-                if (authority === 'ROLE_ADMIN') {
-                    admin = true;
-                    return false;
-                }
-            });
-        }
-        return admin;
-    },
+    // shows the logout link in the message-pane when appropriate and schedule token refresh
+    if (nf.Storage.getItem('jwt') !== null) {
+        $('#user-logout-container').show();
+        nf.Common.scheduleTokenRefresh();
+    }
     
-    /**
-     * Adds a mouse over effect for the specified selector using
-     * the specified styles.
-     * 
-     * @argument {string} selector      The selector for the element to add a hover effect for
-     * @argument {string} normalStyle   The css style for the normal state
-     * @argument {string} overStyle     The css style for the over state
-     */
-    addHoverEffect: function (selector, normalStyle, overStyle) {
-        $(document).on('mouseenter', selector, function () {
-            $(this).removeClass(normalStyle).addClass(overStyle);
-        }).on('mouseleave', selector, function () {
-            $(this).removeClass(overStyle).addClass(normalStyle);
-        });
-        return $(selector).addClass(normalStyle);
-    },
+    // handle logout
+    $('#user-logout').on('click', function () {
+        nf.Storage.removeItem('jwt');
+        window.location = '/nifi/login';
+    });
+});
+
+// Define a common utility class used across the entire application.
+nf.Common = (function () {
+    // interval for cancelling token refresh when necessary
+    var tokenRefreshInterval = null;
     
-    /**
-     * Method for handling ajax errors.
-     * 
-     * @argument {object} xhr       The XmlHttpRequest
-     * @argument {string} status    The status of the request
-     * @argument {string} error     The error
-     */
-    handleAjaxError: function (xhr, status, error) {
-        // show the account registration page if necessary
-        if (xhr.status === 401 && $('#registration-pane').length) {
-            // show the registration pane
-            $('#registration-pane').show();
-
-            // close the canvas
-            nf.Common.closeCanvas();
-            return;
-        }
+    return {
+        ANONYMOUS_USER_TEXT: 'Anonymous user',
         
-        // 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 ($.trim(xhr.responseText) === '') {
-                $('#message-content').text('Please check the logs.');
-            } else {
-                $('#message-content').text(xhr.responseText);
+        config: {
+            sensitiveText: 'Sensitive value set',
+            tooltipConfig: {
+                style: {
+                    classes: 'nifi-tooltip'
+                },
+                show: {
+                    solo: true,
+                    effect: false
+                },
+                hide: {
+                    effect: false
+                },
+                position: {
+                    at: 'top right',
+                    my: 'bottom left'
+                }
             }
+        },
 
-            // show the error pane
-            $('#message-pane').show();
+        /**
+         * Determines if the current broswer supports SVG.
+         */
+        SUPPORTS_SVG: !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect,
 
-            // close the canvas
-            nf.Common.closeCanvas();
-            return;
-        }
-        
-        // status code 400, 404, and 409 are expected response codes for common errors.
-        if (xhr.status === 400 || xhr.status === 404 || xhr.status === 409) {
-            nf.Dialog.showOkDialog({
-                dialogContent: nf.Common.escapeHtml(xhr.responseText),
-                overlayBackground: false
+        /**
+         * The authorities for the current user.
+         */
+        authorities: undefined,
+
+        /**
+         * Sets the authorities for the current user.
+         * 
+         * @argument {array} roles      The current users authorities
+         */
+        setAuthorities: function (roles) {
+            nf.Common.authorities = roles;
+        },
+
+        /**
+         * Loads a script at the specified URL. Supports caching the script on the browser.
+         * 
+         * @param {string} url
+         */
+        cachedScript: function (url) {
+            return $.ajax({
+                dataType: 'script',
+                cache: true,
+                url: url
             });
-        } else {
-            if (xhr.status < 99 || xhr.status === 12007 || xhr.status === 12029) {
-                var content = 'Please ensure the application is running and check the logs for any errors.';
-                if (nf.Common.isDefinedAndNotNull(status)) {
-                    if (status === 'timeout') {
-                        content = 'Request has timed out. Please ensure the application is running and check the logs for any errors.';
-                    } else if (status === 'abort') {
-                        content = 'Request has been aborted.';
-                    } else if (status === 'No Transport') {
-                        content = 'Request transport mechanism failed. Please ensure the host where the application is running is accessible.';
+        },
+
+        /**
+         * Automatically refresh tokens by checking once an hour if its going to expire soon.
+         */
+        scheduleTokenRefresh: function () {
+            // if we are currently polling for token refresh, cancel it
+            if (tokenRefreshInterval !== null) {
+                clearInterval(tokenRefreshInterval);
+            }
+            
+            // set the interval to one hour
+            var interval = nf.Common.MILLIS_PER_MINUTE;
+            
+            var checkExpiration = function () {
+                var expiration = nf.Storage.getItemExpiration('jwt');
+
+                // ensure there is an expiration and token present
+                if (expiration !== null) {
+                    var expirationDate = new Date(expiration);
+                    var now = new Date();
+
+                    // get the time remainging plus a little bonus time to reload the token
+                    var timeRemaining = expirationDate.valueOf() - now.valueOf() - (30 * nf.Common.MILLIS_PER_SECOND);
+                    if (timeRemaining < interval) {
+                        if ($('#current-user').text() !== nf.Common.ANONYMOUS_USER_TEXT && !$('#anonymous-user-alert').is(':visible')) {
+                            // if the token will expire before the next interval minus some bonus time, notify the user to re-login
+                            $('#anonymous-user-alert').show().qtip($.extend({}, nf.Common.config.tooltipConfig, {
+                                content: 'Your session will expire soon. Please log in again to avoid being automatically logged out.',
+                                position: {
+                                    my: 'top right',
+                                    at: 'bottom left'
+                                }
+                            }));
+                        }
                     }
                 }
-                $('#message-title').text('Unable to communicate with NiFi');
-                $('#message-content').text(content);
-            } else if (xhr.status === 401) {
-                $('#message-title').text('Unauthorized');
-                if ($.trim(xhr.responseText) === '') {
-                    $('#message-content').text('Authorization is required to use this NiFi.');
+            };
+            
+            // perform initial check
+            checkExpiration();
+            
+            // schedule subsequent checks
+            tokenRefreshInterval = setInterval(checkExpiration, interval);
+        },
+
+        /**
+         * Sets the anonymous user label.
+         */
+        setAnonymousUserLabel: function () {
+            var anonymousUserAlert = $('#anonymous-user-alert');
+            if (anonymousUserAlert.data('qtip')) {
+                anonymousUserAlert.qtip('api').destroy(true);
+            }
+                        
+            // alert user's of anonymous access
+            anonymousUserAlert.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.Common.ANONYMOUS_USER_TEXT).show();  
+        },
+
+        /**
+         * Extracts the subject from the specified jwt. If the jwt is not as expected
+         * an empty string is returned.
+         * 
+         * @param {string} jwt
+         * @returns {string}
+         */
+        getJwtPayload: 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)) {
+                    return payload;
                 } else {
-                    $('#message-content').text(xhr.responseText);
+                    return null;
                 }
-            } else if (xhr.status === 403) {
-                $('#message-title').text('Forbidden');
-                if ($.trim(xhr.responseText) === '') {
-                    $('#message-content').text('Unable to authorize you to use this NiFi.');
+            }
+
+            return null;
+        },
+
+        /**
+         * Determines whether the current user can access provenance.
+         * 
+         * @returns {boolean}
+         */
+        canAccessProvenance: function () {
+            var canAccessProvenance = false;
+            if (nf.Common.isDefinedAndNotNull(nf.Common.authorities)) {
+                $.each(nf.Common.authorities, function (i, authority) {
+                    if (authority === 'ROLE_PROVENANCE') {
+                        canAccessProvenance = true;
+                        return false;
+                    }
+                });
+            }
+            return canAccessProvenance;
+        },
+
+        /**
+         * Returns whether or not the current user is a DFM.
+         */
+        isDFM: function () {
+            var dfm = false;
+            if (nf.Common.isDefinedAndNotNull(nf.Common.authorities)) {
+                $.each(nf.Common.authorities, function (i, authority) {
+                    if (authority === 'ROLE_DFM') {
+                        dfm = true;
+                        return false;
+                    }
+                });
+            }
+            return dfm;
+        },
+
+        /**
+         * Returns whether or not the current user is a DFM.
+         */
+        isAdmin: function () {
+            var admin = false;
+            if (nf.Common.isDefinedAndNotNull(nf.Common.authorities)) {
+                $.each(nf.Common.authorities, function (i, authority) {
+                    if (authority === 'ROLE_ADMIN') {
+                        admin = true;
+                        return false;
+                    }
+                });
+            }
+            return admin;
+        },
+
+        /**
+         * Adds a mouse over effect for the specified selector using
+         * the specified styles.
+         * 
+         * @argument {string} selector      The selector for the element to add a hover effect for
+         * @argument {string} normalStyle   The css style for the normal state
+         * @argument {string} overStyle     The css style for the over state
+         */
+        addHoverEffect: function (selector, normalStyle, overStyle) {
+            $(document).on('mouseenter', selector, function () {
+                $(this).removeClass(normalStyle).addClass(overStyle);
+            }).on('mouseleave', selector, function () {
+                $(this).removeClass(overStyle).addClass(normalStyle);
+            });
+            return $(selector).addClass(normalStyle);
+        },
+
+        /**
+         * Method for handling ajax errors.
+         * 
+         * @argument {object} xhr       The XmlHttpRequest
+         * @argument {string} status    The status of the request
+         * @argument {string} error     The error
+         */
+        handleAjaxError: function (xhr, status, error) {
+            if (status === 'canceled') {
+                if ($('#splash').is(':visible')) {
+                    $('#message-title').text('Session Expired');
+                    $('#message-content').text('Your session has expired. Please reload to log in again.');
+
+                    // show the error pane
+                    $('#message-pane').show();
                 } else {
-                    $('#message-content').text(xhr.responseText);
+                    nf.Dialog.showOkDialog({
+                        dialogContent: 'Your session has expired. Please press Ok to log in again.',
+                        overlayBackground: false,
+                        okHandler: function () {
+                            window.location = '/nifi';
+                        }
+                    });
                 }
-            } else if (xhr.status === 500) {
-                $('#message-title').text('An unexpected error has occurred');
-                if ($.trim(xhr.responseText) === '') {
-                    $('#message-content').text('An error occurred communicating with the application core. Please check the logs and fix any configuration issues before restarting.');
+                
+                // close the canvas
+                nf.Common.closeCanvas();
+                return;
+            }
+            
+            // if an error occurs while the splash screen is visible close the canvas show the error message
+            if ($('#splash').is(':visible')) {
+                if (xhr.status === 401) {
+                    $('#message-title').text('Unauthorized');
+                } else if (xhr.status === 403) {
+                    $('#message-title').text('Access Denied');
                 } else {
-                    $('#message-content').text(xhr.responseText);
+                    $('#message-title').text('An unexpected error has occurred');
                 }
-            } else if (xhr.status === 200 || xhr.status === 201) {
-                $('#message-title').text('Parse Error');
+
                 if ($.trim(xhr.responseText) === '') {
-                    $('#message-content').text('Unable to interpret response from NiFi.');
+                    $('#message-content').text('Please check the logs.');
                 } else {
                     $('#message-content').text(xhr.responseText);
                 }
+
+                // show the error pane
+                $('#message-pane').show();
+
+                // close the canvas
+                nf.Common.closeCanvas();
+                return;
+            }
+
+            // status code 400, 404, and 409 are expected response codes for common errors.
+            if (xhr.status === 400 || xhr.status === 404 || xhr.status === 409) {
+                nf.Dialog.showOkDialog({
+                    dialogContent: nf.Common.escapeHtml(xhr.responseText),
+                    overlayBackground: false
+                });
             } else {
-                $('#message-title').text(xhr.status + ': Unexpected Response');
-                $('#message-content').text('An unexpected error has occurred. Please check the logs.');
+                if (xhr.status < 99 || xhr.status === 12007 || xhr.status === 12029) {
+                    var content = 'Please ensure the application is running and check the logs for any errors.';
+                    if (nf.Common.isDefinedAndNotNull(status)) {
+                        if (status === 'timeout') {
+                            content = 'Request has timed out. Please ensure the application is running and check the logs for any errors.';
+                        } else if (status === 'abort') {
+                            content = 'Request has been aborted.';
+                        } else if (status === 'No Transport') {
+                            content = 'Request transport mechanism failed. Please ensure the host where the application is running is accessible.';
+                        }
+                    }
+                    $('#message-title').text('Unable to communicate with NiFi');
+                    $('#message-content').text(content);
+                } else if (xhr.status === 401) {
+                    $('#message-title').text('Unauthorized');
+                    if ($.trim(xhr.responseText) === '') {
+                        $('#message-content').text('Authorization is required to use this NiFi.');
+                    } else {
+                        $('#message-content').text(xhr.responseText);
+                    }
+                } else if (xhr.status === 403) {
+                    $('#message-title').text('Access Denied');
+                    if ($.trim(xhr.responseText) === '') {
+                        $('#message-content').text('Unable to authorize you to use this NiFi.');
+                    } else {
+                        $('#message-content').text(xhr.responseText);
+                    }
+                } else if (xhr.status === 500) {
+                    $('#message-title').text('An unexpected error has occurred');
+                    if ($.trim(xhr.responseText) === '') {
+                        $('#message-content').text('An error occurred communicating with the application core. Please check the logs and fix any configuration issues before restarting.');
+                    } else {
+                        $('#message-content').text(xhr.responseText);
+                    }
+                } else if (xhr.status === 200 || xhr.status === 201) {
+                    $('#message-title').text('Parse Error');
+                    if ($.trim(xhr.responseText) === '') {
+                        $('#message-content').text('Unable to interpret response from NiFi.');
+                    } else {
+                        $('#message-content').text(xhr.responseText);
+                    }
+                } else {
+                    $('#message-title').text(xhr.status + ': Unexpected Response');
+                    $('#message-content').text('An unexpected error has occurred. Please check the logs.');
+                }
+
+                // show the error pane
+                $('#message-pane').show();
+
+                // close the canvas
+                nf.Common.closeCanvas();
             }
+        },
 
-            // show the error pane
-            $('#message-pane').show();
+        /**
+         * Closes the canvas by removing the splash screen and stats poller.
+         */
+        closeCanvas: function () {
+            nf.Common.showLogoutLink();
+            
+            // ensure this javascript has been loaded in the nf canvas page
+            if (nf.Common.isDefinedAndNotNull(nf.Canvas)) {
+                // hide the splash screen if required
+                if ($('#splash').is(':visible')) {
+                    nf.Canvas.hideSplash();
+                }
 
-            // close the canvas
-            nf.Common.closeCanvas();
-        }
-    },
-    
-    /**
-     * Closes the canvas by removing the splash screen and stats poller.
-     */
-    closeCanvas: function () {
-        // ensure this javascript has been loaded in the nf canvas page
-        if (nf.Common.isDefinedAndNotNull(nf.Canvas)) {
-            // hide the splash screen if required
-            if ($('#splash').is(':visible')) {
-                nf.Canvas.hideSplash();
+                // hide the context menu
+                nf.ContextMenu.hide();
+
+                // shut off the auto refresh
+                nf.Canvas.stopRevisionPolling();
+                nf.Canvas.stopStatusPolling();
             }
+        },
 
-            // hide the context menu
-            nf.ContextMenu.hide();
+        /**
+         * Shows the logout link if appropriate.
+         */
+        showLogoutLink: function () {
+            if (nf.Storage.getItem('jwt') === null) {
+                $('#user-logout-container').hide();
+            } else {
+                $('#user-logout-container').show();
+            }
+        },
 
-            // shut off the auto refresh
-            nf.Canvas.stopRevisionPolling();
-            nf.Canvas.stopStatusPolling();
-        }
-    },
-    
-    /**
-     * Populates the specified field with the specified value. If the value is 
-     * undefined, the field will read 'No value set.' If the value is an empty
-     * string, the field will read 'Empty string set.'
-     * 
-     * @argument {string} target        The dom Id of the target
-     * @argument {string} value         The value
-     */
-    populateField: function (target, value) {
-        if (nf.Common.isUndefined(value) || nf.Common.isNull(value)) {
-            return $('#' + target).addClass('unset').text('No value set');
-        } else if (value === '') {
-            return $('#' + target).addClass('blank').text('Empty string set');
-        } else {
-            return $('#' + target).text(value);
-        }
-    },
-    
-    /**
-     * Clears the specified field. Removes any style that may have been applied
-     * by a preceeding call to populateField.
-     * 
-     * @argument {string} target        The dom Id of the target
-     */
-    clearField: function (target) {
-        return $('#' + target).removeClass('unset blank').text('');
-    },
-    
-    /**
-     * Cleans up any tooltips that have been created for the specified container.
-     * 
-     * @param {jQuery} container
-     * @param {string} tooltipTarget
-     */
-    cleanUpTooltips: function(container, tooltipTarget) {
-        container.find(tooltipTarget).each(function () {
-            var tip = $(this);
-            if (tip.data('qtip')) {
-                var api = tip.qtip('api');
-                api.destroy(true);
-            }
-        });
-    },
-    
-    /**
-     * Removes all read only property detail dialogs.
-     */
-    removeAllPropertyDetailDialogs: function () {
-        var propertyDetails = $('body').children('div.property-detail');
-        propertyDetails.find('div.nfel-editor').nfeditor('destroy');
-        propertyDetails.hide().remove();
-    },
-    
-    /**
-     * Formats the tooltip for the specified property.
-     * 
-     * @param {object} propertyDescriptor      The property descriptor
-     * @param {object} propertyHistory         The property history
-     * @returns {string}
-     */
-    formatPropertyTooltip: function (propertyDescriptor, propertyHistory) {
-        var tipContent = [];
-
-        // show the property description if applicable
-        if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-            if (!nf.Common.isBlank(propertyDescriptor.description)) {
-                tipContent.push(nf.Common.escapeHtml(propertyDescriptor.description));
-            }
-            if (!nf.Common.isBlank(propertyDescriptor.defaultValue)) {
-                tipContent.push('<b>Default value:</b> ' + nf.Common.escapeHtml(propertyDescriptor.defaultValue));
-            }
-            if (!nf.Common.isBlank(propertyDescriptor.supportsEl)) {
-                tipContent.push('<b>Supports expression language:</b> ' + nf.Common.escapeHtml(propertyDescriptor.supportsEl));
+        /**
+         * Populates the specified field with the specified value. If the value is 
+         * undefined, the field will read 'No value set.' If the value is an empty
+         * string, the field will read 'Empty string set.'
+         * 
+         * @argument {string} target        The dom Id of the target
+         * @argument {string} value         The value
+         */
+        populateField: function (target, value) {
+            if (nf.Common.isUndefined(value) || nf.Common.isNull(value)) {
+                return $('#' + target).addClass('unset').text('No value set');
+            } else if (value === '') {
+                return $('#' + target).addClass('blank').text('Empty string set');
+            } else {
+                return $('#' + target).text(value);
             }
-        }
+        },
 
-        if (nf.Common.isDefinedAndNotNull(propertyHistory)) {
-            if (!nf.Common.isEmpty(propertyHistory.previousValues)) {
-                var history = [];
-                $.each(propertyHistory.previousValues, function (_, previousValue) {
-                    history.push('<li>' + nf.Common.escapeHtml(previousValue.previousValue) + ' - ' + nf.Common.escapeHtml(previousValue.timestamp) + ' (' + nf.Common.escapeHtml(previousValue.userName) + ')</li>');
-                });
-                tipContent.push('<b>History:</b><ul class="property-info">' + history.join('') + '</ul>');
+        /**
+         * Clears the specified field. Removes any style that may have been applied
+         * by a preceeding call to populateField.
+         * 
+         * @argument {string} target        The dom Id of the target
+         */
+        clearField: function (target) {
+            return $('#' + target).removeClass('unset blank').text('');
+        },
+
+        /**
+         * Cleans up any tooltips that have been created for the specified container.
+         * 
+         * @param {jQuery} container
+         * @param {string} tooltipTarget
+         */
+        cleanUpTooltips: function(container, tooltipTarget) {
+            container.find(tooltipTarget).each(function () {
+                var tip = $(this);
+                if (tip.data('qtip')) {
+                    var api = tip.qtip('api');
+                    api.destroy(true);
+                }
+            });
+        },
+
+        /**
+         * Removes all read only property detail dialogs.
+         */
+        removeAllPropertyDetailDialogs: function () {
+            var propertyDetails = $('body').children('div.property-detail');
+            propertyDetails.find('div.nfel-editor').nfeditor('destroy');
+            propertyDetails.hide().remove();
+        },
+
+        /**
+         * Formats the tooltip for the specified property.
+         * 
+         * @param {object} propertyDescriptor      The property descriptor
+         * @param {object} propertyHistory         The property history
+         * @returns {string}
+         */
+        formatPropertyTooltip: function (propertyDescriptor, propertyHistory) {
+            var tipContent = [];
+
+            // show the property description if applicable
+            if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+                if (!nf.Common.isBlank(propertyDescriptor.description)) {
+                    tipContent.push(nf.Common.escapeHtml(propertyDescriptor.description));
+                }
+                if (!nf.Common.isBlank(propertyDescriptor.defaultValue)) {
+                    tipContent.push('<b>Default value:</b> ' + nf.Common.escapeHtml(propertyDescriptor.defaultValue));
+                }
+                if (!nf.Common.isBlank(propertyDescriptor.supportsEl)) {
+                    tipContent.push('<b>Supports expression language:</b> ' + nf.Common.escapeHtml(propertyDescriptor.supportsEl));
+                }
             }
-        }
 
-        if (tipContent.length > 0) {
-            return tipContent.join('<br/><br/>');
-        } else {
-            return null;
-        }
-    },
-    
-    /**
-     * Formats the specified property (name and value) accordingly.
-     * 
-     * @argument {string} name      The name of the property
-     * @argument {string} value     The value of the property
-     */
-    formatProperty: function (name, value) {
-        return '<div><span class="label">' + nf.Common.formatValue(name) + ': </span>' + nf.Common.formatValue(value) + '</div>';
-    },
-    
-    /**
-     * Formats the specified value accordingly.
-     * 
-     * @argument {string} value     The value of the property
-     */
-    formatValue: function (value) {
-        if (nf.Common.isDefinedAndNotNull(value)) {
-            if (value === '') {
-                return '<span class="blank">Empty string set</span>';
+            if (nf.Common.isDefinedAndNotNull(propertyHistory)) {
+                if (!nf.Common.isEmpty(propertyHistory.previousValues)) {
+                    var history = [];
+                    $.each(propertyHistory.previousValues, function (_, previousValue) {
+                        history.push('<li>' + nf.Common.escapeHtml(previousValue.previousValue) + ' - ' + nf.Common.escapeHtml(previousValue.timestamp) + ' (' + nf.Common.escapeHtml(previousValue.userName) + ')</li>');
+                    });
+                    tipContent.push('<b>History:</b><ul class="property-info">' + history.join('') + '</ul>');
+                }
+            }
+
+            if (tipContent.length > 0) {
+                return tipContent.join('<br/><br/>');
             } else {
-                return nf.Common.escapeHtml(value);
+                return null;
             }
-        } else {
-            return '<span class="unset">No value set</span>';
-        }
-    },
-    
-    /**
-     * HTML escapes the specified string. If the string is null 
-     * or undefined, an empty string is returned.
-     * 
-     * @returns {string}
-     */
-    escapeHtml: (function () {
-        var entityMap = {
-            '&': '&amp;',
-            '<': '&lt;',
-            '>': '&gt;',
-            '"': '&quot;',
-            "'": '&#39;',
-            '/': '&#x2f;'
-        };
-
-        return function (string) {
-            if (nf.Common.isDefinedAndNotNull(string)) {
-                return String(string).replace(/[&<>"'\/]/g, function (s) {
-                    return entityMap[s];
-                });
+        },
+
+        /**
+         * Formats the specified property (name and value) accordingly.
+         * 
+         * @argument {string} name      The name of the property
+         * @argument {string} value     The value of the property
+         */
+        formatProperty: function (name, value) {
+            return '<div><span class="label">' + nf.Common.formatValue(name) + ': </span>' + nf.Common.formatValue(value) + '</div>';
+        },
+
+        /**
+         * Formats the specified value accordingly.
+         * 
+         * @argument {string} value     The value of the property
+         */
+        formatValue: function (value) {
+            if (nf.Common.isDefinedAndNotNull(value)) {
+                if (value === '') {
+                    return '<span class="blank">Empty string set</span>';
+                } else {
+                    return nf.Common.escapeHtml(value);
+                }
             } else {
-                return '';
+                return '<span class="unset">No value set</span>';
             }
-        };
-    }()),
-    
-    /**
-     * Determines if the specified property is sensitive.
-     * 
-     * @argument {object} propertyDescriptor        The property descriptor
-     */
-    isSensitiveProperty: function (propertyDescriptor) {
-        if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-            return propertyDescriptor.sensitive === true;
-        } else {
-            return false;
-        }
-    },
-
-    /**
-     * Determines if the specified property is required.
-     * 
-     * @param {object} propertyDescriptor           The property descriptor
-     */
-    isRequiredProperty: function (propertyDescriptor) {
-        if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-            return propertyDescriptor.required === true;
-        } else {
-            return false;
-        }
-    },
-
-    /**
-     * Determines if the specified property is required.
-     * 
-     * @param {object} propertyDescriptor           The property descriptor
-     */
-    isDynamicProperty: function (propertyDescriptor) {
-        if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-            return propertyDescriptor.dynamic === true;
-        } else {
-            return false;
-        }
-    },
-
-    /**
-     * Gets the allowable values for the specified property.
-     * 
-     * @argument {object} propertyDescriptor        The property descriptor
-     */
-    getAllowableValues: function (propertyDescriptor) {
-        if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-            return propertyDescriptor.allowableValues;
-        } else {
-            return null;
-        }
-    },
-
-    /**
-     * Returns whether the specified property supports EL.
-     * 
-     * @param {object} propertyDescriptor           The property descriptor
-     */
-    supportsEl: function (propertyDescriptor) {
-        if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-            return propertyDescriptor.supportsEl === true;
-        } else {
-            return false;
-        }
-    },
-    
-    /**
-     * Creates a form inline in order to submit the specified params to the specified URL
-     * using the specified method.
-     * 
-     * @param {string} url          The URL
-     * @param {object} params       An object with the params to include in the submission
-     */
-    post: function (url, params) {
-        // temporarily override beforeunload
-        var previousBeforeUnload = window.onbeforeunload;
-        window.onbeforeunload = null;
-
-        // create a form for submission
-        var form = $('<form></form>').attr({
-            'method': 'POST',
-            'action': url,
-            'style': 'display: none;'
-        });
-
-        // add each parameter when specified
-        if (nf.Common.isDefinedAndNotNull(params)) {
-            $.each(params, function (name, value) {
-                $('<textarea></textarea>').attr('name', name).val(value).appendTo(form);
-            });
-        }
+        },
 
-        // submit the form and clean up
-        form.appendTo('body').submit().remove();
+        /**
+         * HTML escapes the specified string. If the string is null 
+         * or undefined, an empty string is returned.
+         * 
+         * @returns {string}
+         */
+        escapeHtml: (function () {
+            var entityMap = {
+                '&': '&amp;',
+                '<': '&lt;',
+                '>': '&gt;',
+                '"': '&quot;',
+                "'": '&#39;',
+                '/': '&#x2f;'
+            };
 
-        // restore previous beforeunload if necessary
-        if (previousBeforeUnload !== null) {
-            window.onbeforeunload = previousBeforeUnload;
-        }
-    },
-    
-    /**
-     * Formats the specified array as an unordered list. If the array is not an 
-     * array, null is returned.
-     * 
-     * @argument {array} array      The array to convert into an unordered list
-     */
-    formatUnorderedList: function (array) {
-        if ($.isArray(array)) {
-            var ul = $('<ul class="result"></ul>');
-            $.each(array, function (_, item) {
-                var li = $('<li></li>').appendTo(ul);
-                if (item instanceof jQuery) {
-                    li.append(item);
+            return function (string) {
+                if (nf.Common.isDefinedAndNotNull(string)) {
+                    return String(string).replace(/[&<>"'\/]/g, function (s) {
+                        return entityMap[s];
+                    });
                 } else {
-                    li.text(item);
+                    return '';
                 }
+            };
+        }()),
+
+        /**
+         * Determines if the specified property is sensitive.
+         * 
+         * @argument {object} propertyDescriptor        The property descriptor
+         */
+        isSensitiveProperty: function (propertyDescriptor) {
+            if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+                return propertyDescriptor.sensitive === true;
+            } else {
+                return false;
+            }
+        },
+
+        /**
+         * Determines if the specified property is required.
+         * 
+         * @param {object} propertyDescriptor           The property descriptor
+         */
+        isRequiredProperty: function (propertyDescriptor) {
+            if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+                return propertyDescriptor.required === true;
+            } else {
+                return false;
+            }
+        },
+
+        /**
+         * Determines if the specified property is required.
+         * 
+         * @param {object} propertyDescriptor           The property descriptor
+         */
+        isDynamicProperty: function (propertyDescriptor) {
+            if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+                return propertyDescriptor.dynamic === true;
+            } else {
+                return false;
+            }
+        },
+
+        /**
+         * Gets the allowable values for the specified property.
+         * 
+         * @argument {object} propertyDescriptor        The property descriptor
+         */
+        getAllowableValues: function (propertyDescriptor) {
+            if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+                return propertyDescriptor.allowableValues;
+            } else {
+                return null;
+            }
+        },
+
+        /**
+         * Returns whether the specified property supports EL.
+         * 
+         * @param {object} propertyDescriptor           The property descriptor
+         */
+        supportsEl: function (propertyDescriptor) {
+            if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+                return propertyDescriptor.supportsEl === true;
+            } else {
+                return false;
+            }
+        },
+
+        /**
+         * Creates a form inline in order to submit the specified params to the specified URL
+         * using the specified method.
+         * 
+         * @param {string} url          The URL
+         * @param {object} params       An object with the params to include in the submission
+         */
+        post: function (url, params) {
+            // temporarily override beforeunload
+            var previousBeforeUnload = window.onbeforeunload;
+            window.onbeforeunload = null;
+
+            // create a form for submission
+            var form = $('<form></form>').attr({
+                'method': 'POST',
+                'action': url,
+                'style': 'display: none;'
             });
-            return ul;
-        } else {
-            return null;
-        }
-    },
-    
-    /**
-     * Extracts the contents of the specified str after the strToFind. If the
-     * strToFind is not found or the last part of the str, an empty string is
-     * returned.
-     * 
-     * @argument {string} str       The full string
-     * @argument {string} strToFind The substring to find
-     */
-    substringAfterLast: function (str, strToFind) {
-        var result = '';
-        var indexOfStrToFind = str.lastIndexOf(strToFind);
-        if (indexOfStrToFind >= 0) {
-            var indexAfterStrToFind = indexOfStrToFind + strToFind.length;
-            if (indexAfterStrToFind < str.length) {
-                result = str.substr(indexAfterStrToFind);
+
+            // add each parameter when specified
+            if (nf.Common.isDefinedAndNotNull(params)) {
+                $.each(params, function (name, value) {
+                    $('<textarea></textarea>').attr('name', name).val(value).appendTo(form);
+                });
             }
-        }
-        return result;
-    },
-    
-    /**
-     * Updates the mouse pointer.
-     * 
-     * @argument {string} domId         The id of the element for the new cursor style
-     * @argument {boolean} isMouseOver  Whether or not the mouse is over the element
-     */
-    setCursor: function (domId, isMouseOver) {
-        if (isMouseOver) {
-            $('#' + domId).addClass('pointer');
-        } else {
-            $('#' + domId).removeClass('pointer');
-        }
-    },
-    
-    /**
-     * Constants for time duration formatting.
-     */
-    MILLIS_PER_DAY: 86400000,
-    MILLIS_PER_HOUR: 3600000,
-    MILLIS_PER_MINUTE: 60000,
-    MILLIS_PER_SECOND: 1000,
-    
-    /**
-     * Formats the specified duration.
-     * 
-     * @param {integer} duration in millis
-     */
-    formatDuration: function (duration) {
-        // don't support sub millisecond resolution
-        duration = duration < 1 ? 0 : duration;
-
-        // determine the number of days in the specified duration
-        var days = duration / nf.Common.MILLIS_PER_DAY;
-        days = days >= 1 ? parseInt(days, 10) : 0;
-        duration %= nf.Common.MILLIS_PER_DAY;
-
-        // remaining duration should be less than 1 day, get number of hours
-        var hours = duration / nf.Common.MILLIS_PER_HOUR;
-        hours = hours >= 1 ? parseInt(hours, 10) : 0;
-        duration %= nf.Common.MILLIS_PER_HOUR;
-
-        // remaining duration should be less than 1 hour, get number of minutes
-        var minutes = duration / nf.Common.MILLIS_PER_MINUTE;
-        minutes = minutes >= 1 ? parseInt(minutes, 10) : 0;
-        duration %= nf.Common.MILLIS_PER_MINUTE;
-
-        // remaining duration should be less than 1 minute, get number of seconds
-        var seconds = duration / nf.Common.MILLIS_PER_SECOND;
-        seconds = seconds >= 1 ? parseInt(seconds, 10) : 0;
-
-        // remaining duration is the number millis (don't support sub millisecond resolution)
-        duration = Math.floor(duration % nf.Common.MILLIS_PER_SECOND);
-
-        // format the time
-        var time = nf.Common.pad(hours, 2, '0') +
-                ':' +
-                nf.Common.pad(minutes, 2, '0') +
-                ':' +
-                nf.Common.pad(seconds, 2, '0') +
-                '.' +
-                nf.Common.pad(duration, 3, '0');
-
-        // only include days if appropriate
-        if (days > 0) {
-            return days + ' days and ' + time;
-        } else {
-            return time;
-        }
-    },
-    
-    /**
-     * Constants for formatting data size.
-     */
-    BYTES_IN_KILOBYTE: 1024,
-    BYTES_IN_MEGABYTE: 1048576,
-    BYTES_IN_GIGABYTE: 1073741824,
-    BYTES_IN_TERABYTE: 1099511627776,
-    
-    /**
-     * Formats the specified number of bytes into a human readable string.
-     * 
-     * @param {integer} dataSize
-     * @returns {string}
-     */
-    formatDataSize: function (dataSize) {
-        // check terabytes
-        var dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_TERABYTE);
-        if (dataSizeToFormat > 1) {
-            return dataSizeToFormat.toFixed(2) + " TB";
-        }
 
-        // check gigabytes
-        dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_GIGABYTE);
-        if (dataSizeToFormat > 1) {
-            return dataSizeToFormat.toFixed(2) + " GB";
-        }
+            // submit the form and clean up
+            form.appendTo('body').submit().remove();
 
-        // check megabytes
-        dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_MEGABYTE);
-        if (dataSizeToFormat > 1) {
-            return dataSizeToFormat.toFixed(2) + " MB";
-        }
+            // restore previous beforeunload if necessary
+            if (previousBeforeUnload !== null) {
+                window.onbeforeunload = previousBeforeUnload;
+            }
+        },
 
-        // check kilobytes
-        dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_KILOBYTE);
-        if (dataSizeToFormat > 1) {
-            return dataSizeToFormat.toFixed(2) + " KB";
-        }
+        /**
+         * Formats the specified array as an unordered list. If the array is not an 
+         * array, null is returned.
+         * 
+         * @argument {array} array      The array to convert into an unordered list
+         */
+        formatUnorderedList: function (array) {
+            if ($.isArray(array)) {
+                var ul = $('<ul class="result"></ul>');
+                $.each(array, function (_, item) {
+                    var li = $('<li></li>').appendTo(ul);
+                    if (item instanceof jQuery) {
+                        li.append(item);
+                    } else {
+                        li.text(item);
+                    }
+                });
+                return ul;
+            } else {
+                return null;
+            }
+        },
 
-        // default to bytes
-        return parseFloat(dataSize).toFixed(2) + " bytes";
-    },
-    
-    /**
-     * Formats the specified integer as a string (adding commas). At this
-     * point this does not take into account any locales.
-     * 
-     * @param {integer} integer
-     */
-    formatInteger: function (integer) {
-        var string = integer + '';
-        var regex = /(\d+)(\d{3})/;
-        while (regex.test(string)) {
-            string = string.replace(regex, '$1' + ',' + '$2');
-        }
-        return string;
-    },
-    
-    /**
-     * Formats the specified float using two demical places.
-     * 
-     * @param {float} f
-     */
-    formatFloat: function (f) {
-        if (nf.Common.isUndefinedOrNull(f)) {
-            return 0.00 + '';
-        }
-        return f.toFixed(2) + '';
-    },
-    
-    /**
-     * Pads the specified value to the specified width with the specified character.
-     * If the specified value is already wider than the specified width, the original
-     * value is returned.
-     * 
-     * @param {integer} value
-     * @param {integer} width
-     * @param {string} character
-     * @returns {string}
-     */
-    pad: function (value, width, character) {
-        var s = value + '';
-
-        // pad until wide enough
-        while (s.length < width) {
-            s = character + s;
-        }
+        /**
+         * Extracts the contents of the specified str after the strToFind. If the
+         * strToFind is not found or the last part of the str, an empty string is
+         * returned.
+         * 
+         * @argument {string} str       The full string
+         * @argument {string} strToFind The substring to find
+         */
+        substringAfterLast: function (str, strToFind) {
+            var result = '';
+            var indexOfStrToFind = str.lastIndexOf(strToFind);
+            if (indexOfStrToFind >= 0) {
+                var indexAfterStrToFind = indexOfStrToFind + strToFind.length;
+                if (indexAfterStrToFind < str.length) {
+                    result = str.substr(indexAfterStrToFind);
+                }
+            }
+            return result;
+        },
 
-        return s;
-    },
-    
-    /**
-     * Formats the specified DateTime.
-     * 
-     * @param {Date} date
-     * @returns {String}
-     */
-    formatDateTime: function (date) {
-        return nf.Common.pad(date.getMonth() + 1, 2, '0') +
-                '/' +
-                nf.Common.pad(date.getDate(), 2, '0') +
-                '/' +
-                nf.Common.pad(date.getFullYear(), 2, '0') +
-                ' ' +
-                nf.Common.pad(date.getHours(), 2, '0') +
-                ':' +
-                nf.Common.pad(date.getMinutes(), 2, '0') +
-                ':' +
-                nf.Common.pad(date.getSeconds(), 2, '0') +
-                '.' +
-                nf.Common.pad(date.getMilliseconds(), 3, '0');
-    },
-    
-    /**
-     * Parses the specified date time into a Date object. The resulting
-     * object does not account for timezone and should only be used for
-     * performing relative comparisons.
-     * 
-     * @param {string} rawDateTime
-     * @returns {Date}
-     */
-    parseDateTime: function (rawDateTime) {
-        // handle non date values
-        if (!nf.Common.isDefinedAndNotNull(rawDateTime)) {
-            return new Date();
-        }
-        if (rawDateTime === 'No value set') {
-            return new Date();
-        }
-        if (rawDateTime === 'Empty string set') {
-            return new Date();
-        }
+        /**
+         * Updates the mouse pointer.
+         * 
+         * @argument {string} domId         The id of the element for the new cursor style
+         * @argument {boolean} isMouseOver  Whether or not the mouse is over the element
+         */
+        setCursor: function (domId, isMouseOver) {
+            if (isMouseOver) {
+                $('#' + domId).addClass('pointer');
+            } else {
+                $('#' + domId).removeClass('pointer');
+            }
+        },
 
-        // parse the date time
-        var dateTime = rawDateTime.split(/ /);
+        /**
+         * Constants for time duration formatting.
+         */
+        MILLIS_PER_DAY: 86400000,
+        MILLIS_PER_HOUR: 3600000,
+        MILLIS_PER_MINUTE: 60000,
+        MILLIS_PER_SECOND: 1000,
 
-        // ensure the correct number of tokens
-        if (dateTime.length !== 3) {
-            return new Date();
-        }
+        /**
+         * Formats the specified duration.
+         * 
+         * @param {integer} duration in millis
+         */
+        formatDuration: function (duration) {
+            // don't support sub millisecond resolution
+            duration = duration < 1 ? 0 : duration;
 
-        // get the date and time
-        var date = dateTime[0].split(/\//);
-        var time = dateTime[1].split(/:/);
+            // determine the number of days in the specified duration
+            var days = duration / nf.Common.MILLIS_PER_DAY;
+            days = days >= 1 ? parseInt(days, 10) : 0;
+            duration %= nf.Common.MILLIS_PER_DAY;
 
-        // ensure the correct number of tokens
-        if (date.length !== 3 || time.length !== 3) {
-            return new Date();
-        }
+            // remaining duration should be less than 1 day, get number of hours
+            var hours = duration / nf.Common.MILLIS_PER_HOUR;
+            hours = hours >= 1 ? parseInt(hours, 10) : 0;
+            duration %= nf.Common.MILLIS_PER_HOUR;
 
-        // detect if there is millis
-        var seconds = time[2].split(/\./);
-        if (seconds.length === 2) {
-            return new Date(parseInt(date[2], 10), parseInt(date[0], 10), parseInt(date[1], 10), parseInt(time[0], 10), parseInt(time[1], 10), parseInt(seconds[0], 10), parseInt(seconds[1], 10));
-        } else {
-            return new Date(parseInt(date[2], 10), parseInt(date[0], 10), parseInt(date[1], 10), parseInt(time[0], 10), parseInt(time[1], 10), parseInt(time[2], 10), 0);
-        }
-    },
-    
-    /**
-     * Parses the specified duration and returns the total number of millis.
-     * 
-     * @param {string} rawDuration
-     * @returns {number}        The number of millis
-     */
-    parseDuration: function (rawDuration) {
-        var duration = rawDuration.split(/:/);
-
-        // ensure the appropriate number of tokens
-        if (duration.length !== 3) {
-            return 0;
-        }
+            // remaining duration should be less than 1 hour, get number of minutes
+            var minutes = duration / nf.Common.MILLIS_PER_MINUTE;
+            minutes = minutes >= 1 ? parseInt(minutes, 10) : 0;
+            duration %= nf.Common.MILLIS_PER_MINUTE;
 
-        // detect if there is millis
-        var seconds = duration[2].split(/\./);
-        if (seconds.length === 2) {
-            return new Date(1970, 0, 1, parseInt(duration[0], 10), parseInt(duration[1], 10), parseInt(seconds[0], 10), parseInt(seconds[1], 10)).getTime();
-        } else {
-            return new Date(1970, 0, 1, parseInt(duration[0], 10), parseInt(duration[1], 10), parseInt(duration[2], 10), 0).getTime();
-        }
-    },
-    
-    /**
-     * Parses the specified size.
-     * 
-     * @param {string} rawSize
-     * @returns {int}
-     */
-    parseSize: function (rawSize) {
-        var tokens = rawSize.split(/ /);
-        var size = parseFloat(tokens[0].replace(/,/g, ''));
-        var units = tokens[1];
-
-        if (units === 'KB') {
-            return size * 1024;
-        } else if (units === 'MB') {
-            return size * 1024 * 1024;
-        } else if (units === 'GB') {
-            return size * 1024 * 1024 * 1024;
-        } else if (units === 'TB') {
-            return size * 1024 * 1024 * 1024 * 1024;
-        } else {
-            return size;
-        }
-    },
-    
-    /**
-     * Parses the specified count.
-     * 
-     * @param {string} rawCount
-     * @returns {int}
-     */
-    parseCount: function (rawCount) {
-        // extract the count
-        var count = rawCount.split(/ /, 1);
-
-        // ensure the string was split successfully
-        if (count.length !== 1) {
-            return 0;
-        }
+            // remaining duration should be less than 1 minute, get number of seconds
+            var seconds = duration / nf.Common.MILLIS_PER_SECOND;
+            seconds = seconds >= 1 ? parseInt(seconds, 10) : 0;
 
-        // convert the count to an integer
-        var intCount = parseInt(count[0].replace(/,/g, ''), 10);
+            // remaining duration is the number millis (don't support sub millisecond resolution)
+            duration = Math.floor(duration % nf.Common.MILLIS_PER_SECOND);
 
-        // ensure it was parsable as an integer
-        if (isNaN(intCount)) {
-            return 0;
-        }
-        return intCount;
-    },
-    
-    /**
-     * Determines if the specified object is defined and not null.
-     * 
-     * @argument {object} obj   The object to test
-     */
-    isDefinedAndNotNull: function (obj) {
-        return !nf.Common.isUndefined(obj) && !nf.Common.isNull(obj);
-    },
-    
-    /**
-     * Determines if the specified object is undefined or null.
-     * 
-     * @param {object} obj      The object to test
-     */
-    isUndefinedOrNull: function (obj) {
-        return nf.Common.isUndefined(obj) || nf.Common.isNull(obj);
-    },
-    
-    /**
-     * Determines if the specified object is undefined.
-     * 
-     * @argument {object} obj   The object to test
-     */
-    isUndefined: function (obj) {
-        return typeof obj === 'undefined';
-    },
-    
-    /**
-     * Determines whether the specified string is blank (or null or undefined).
-     * 
-     * @argument {string} str   The string to test
-     */
-    isBlank: function (str) {
-        return nf.Common.isUndefined(str) || nf.Common.isNull(str) || $.trim(str) === '';
-    },
-    
-    /**
-     * Determines if the specified object is null.
-     * 
-     * @argument {object} obj   The object to test
-     */
-    isNull: function (obj) {
-        return obj === null;
-    },
-    
-    /**
-     * Determines if the specified array is empty. If the specified arg is not an
-     * array, then true is returned.
-     * 
-     * @argument {array} arr    The array to test
-     */
-    isEmpty: function (arr) {
-        return $.isArray(arr) ? arr.length === 0 : true;
-    },
-    
-    /**
-     * Determines if these are the same bulletins. If both arguments are not
-     * arrays, false is returned.
-     * 
-     * @param {array} bulletins
-     * @param {array} otherBulletins
-     * @returns {boolean}
-     */
-    doBulletinsDiffer: function (bulletins, otherBulletins) {
-        if ($.isArray(bulletins) && $.isArray(otherBulletins)) {
-            if (bulletins.length === otherBulletins.length) {
-                for (var i = 0; i < bulletins.length; i++) {
-                    if (bulletins[i].id !== otherBulletins[i].id) {
-                        return true;
+            // format the time
+            var time = nf.Common.pad(hours, 2, '0') +
+                    ':' +
+                    nf.Common.pad(minutes, 2, '0') +
+                    ':' +
+                    nf.Common.pad(seconds, 2, '0') +
+                    '.' +
+                    nf.Common.pad(duration, 3, '0');
+
+            // only include days if appropriate
+            if (days > 0) {
+                return days + ' days and ' + time;
+            } else {
+                return time;
+            }
+        },
+
+        /**
+         * Constants for formatting data size.
+         */
+        BYTES_IN_KILOBYTE: 1024,
+        BYTES_IN_MEGABYTE: 1048576,
+        BYTES_IN_GIGABYTE: 1073741824,
+        BYTES_IN_TERABYTE: 1099511627776,
+
+        /**
+         * Formats the specified number of bytes into a human readable string.
+         * 
+         * @param {integer} dataSize
+         * @returns {string}
+         */
+        formatDataSize: function (dataSize) {
+            // check terabytes
+            var dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_TERABYTE);
+            if (dataSizeToFormat > 1) {
+                return dataSizeToFormat.toFixed(2) + " TB";
+            }
+
+            // check gigabytes
+            dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_GIGABYTE);
+            if (dataSizeToFormat > 1) {
+                return dataSizeToFormat.toFixed(2) + " GB";
+            }
+
+            // check megabytes
+            dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_MEGABYTE);
+            if (dataSizeToFormat > 1) {
+                return dataSizeToFormat.toFixed(2) + " MB";
+            }
+
+            // check kilobytes
+            dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_KILOBYTE);
+            if (dataSizeToFormat > 1) {
+                return dataSizeToFormat.toFixed(2) + " KB";
+            }
+
+            // default to bytes
+            return parseFloat(dataSize).toFixed(2) + " bytes";
+        },
+
+        /**
+         * Formats the specified integer as a string (adding commas). At this
+         * point this does not take into account any locales.
+         * 
+         * @param {integer} integer
+         */
+        formatInteger: function (integer) {
+            var string = integer + '';
+            var regex = /(\d+)(\d{3})/;
+            while (regex.test(string)) {
+                string = string.replace(regex, '$1' + ',' + '$2');
+            }
+            return string;
+        },
+
+        /**
+         * Formats the specified float using two demical places.
+         * 
+         * @param {float} f
+         */
+        formatFloat: function (f) {
+            if (nf.Common.isUndefinedOrNull(f)) {
+                return 0.00 + '';
+            }
+            return f.toFixed(2) + '';
+        },
+
+        /**
+         * Pads the specified value to the specified width with the specified character.
+         * If the specified value is already wider than the specified width, the original
+         * value is returned.
+         * 
+         * @param {integer} value
+         * @param {integer} width
+         * @param {string} character
+         * @returns {string}
+         */
+        pad: function (value, width, character) {
+            var s = value + '';
+
+            // pad until wide enough
+            while (s.length < width) {
+                s = character + s;
+            }
+
+            return s;
+        },
+
+        /**
+         * Formats the specified DateTime.
+         * 
+         * @param {Date} date
+         * @returns {String}
+         */
+        formatDateTime: function (date) {
+            return nf.Common.pad(date.getMonth() + 1, 2, '0') +
+                    '/' +
+                    nf.Common.pad(date.getDate(), 2, '0') +
+                    '/' +
+                    nf.Common.pad(date.getFullYear(), 2, '0') +
+                    ' ' +
+                    nf.Common.pad(date.getHours(), 2, '0') +
+                    ':' +
+                    nf.Common.pad(date.getMinutes(), 2, '0') +
+                    ':' +
+                    nf.Common.pad(date.getSeconds(), 2, '0') +
+                    '.' +
+                    nf.Common.pad(date.getMilliseconds(), 3, '0');
+        },
+
+        /**
+         * Parses the specified date time into a Date object. The resulting
+         * object does not account for timezone and should only be used for
+         * performing relative comparisons.
+         * 
+         * @param {string} rawDateTime
+         * @returns {Date}
+         */
+        parseDateTime: function (rawDateTime) {
+            // handle non date values
+            if (!nf.Common.isDefinedAndNotNull(rawDateTime)) {
+                return new Date();
+            }
+            if (rawDateTime === 'No value set') {
+                return new Date();
+            }
+            if (rawDateTime === 'Empty string set') {
+                return new Date();
+            }
+
+            // parse the date time
+            var dateTime = rawDateTime.split(/ /);
+
+            // ensure the correct number of tokens
+            if (dateTime.length !== 3) {
+                return new Date();
+            }
+
+            // get the date and time
+            var date = dateTime[0].split(/\//);
+            var time = dateTime[1].split(/:/);
+
+            // ensure the correct number of tokens
+            if (date.length !== 3 || time.length !== 3) {
+                return new Date();
+            }
+
+            // detect if there is millis
+            var seconds = time[2].split(/\./);
+            if (seconds.length === 2) {
+                return new Date(parseInt(date[2], 10), parseInt(date[0], 10), parseInt(date[1], 10), parseInt(time[0], 10), parseInt(time[1], 10), parseInt(seconds[0], 10), parseInt(seconds[1], 10));
+            } else {
+                return new Date(parseInt(date[2], 10), parseInt(date[0], 10), parseInt(date[1], 10), parseInt(time[0], 10), parseInt(time[1], 10), parseInt(time[2], 10), 0);
+            }
+        },
+
+        /**
+         * Parses the specified duration and returns the total number of millis.
+         * 
+         * @param {string} rawDuration
+         * @returns {number}        The number of millis
+         */
+        parseDuration: function (rawDuration) {
+            var duration = rawDuration.split(/:/);
+
+            // ensure the appropriate number of tokens
+            if (duration.length !== 3) {
+                return 0;
+            }
+
+            // detect if there is millis
+            var seconds = duration[2].split(/\./);
+            if (seconds.length === 2) {
+                return new Date(1970, 0, 1, parseInt(duration[0], 10), parseInt(duration[1], 10), parseInt(seconds[0], 10), parseInt(seconds[1], 10)).getTime();
+            } else {
+                return new Date(1970, 0, 1, parseInt(duration[0], 10), parseInt(duration[1], 10), parseInt(duration[2], 10), 0).getTime();
+            }
+        },
+
+        /**
+         * Parses the specified size.
+         * 
+         * @param {string} rawSize
+         * @returns {int}
+         */
+        parseSize: function (rawSize) {
+            var tokens = rawSize.split(/ /);
+            var size = parseFloat(tokens[0].replace(/,/g, ''));
+            var units = tokens[1];
+
+            if (units === 'KB') {
+                return size * 1024;
+            } else if (units === 'MB') {
+                return size * 1024 * 1024;
+            } else if (units === 'GB') {
+                return size * 1024 * 1024 * 1024;
+            } else if (units === 'TB') {
+                return size * 1024 * 1024 * 1024 * 1024;
+            } else {
+                return size;
+            }
+        },
+
+        /**
+         * Parses the specified count.
+         * 
+         * @param {string} rawCount
+         * @returns {int}
+         */
+        parseCount: function (rawCount) {
+            // extract the count
+            var count = rawCount.split(/ /, 1);
+
+            // ensure the string was split successfully
+            if (count.length !== 1) {
+                return 0;
+            }
+
+            // convert the count to an integer
+            var intCount = parseInt(count[0].replace(/,/g, ''), 10);
+
+            // ensure it was parsable as an integer
+            if (isNaN(intCount)) {
+                return 0;
+            }
+            return intCount;
+        },
+
+        /**
+         * Determines if the specified object is defined and not null.
+         * 
+         * @argument {object} obj   The object to test
+         */
+        isDefinedAndNotNull: function (obj) {
+            return !nf.Common.isUndefined(obj) && !nf.Common.isNull(obj);
+        },
+
+        /**
+         * Determines if the specified object is undefined or null.
+         * 
+         * @param {object} obj      The object to test
+         */
+        isUndefinedOrNull: function (obj) {
+            return nf.Common.isUndefined(obj) || nf.Common.isNull(obj);
+        },
+
+        /**
+         * Determines if the specified object is undefined.
+         * 
+         * @argument {object} obj   The object to test
+         */
+        isUndefined: function (obj) {
+            return typeof obj === 'undefined';
+        },
+
+        /**
+         * Determines whether the specified string is blank (or null or undefined).
+         * 
+         * @argument {string} str   The string to test
+         */
+        isBlank: function (str) {
+            return nf.Common.isUndefined(str) || nf.Common.isNull(str) || $.trim(str) === '';
+        },
+
+        /**
+         * Determines if the specified object is null.
+         * 
+         * @argument {object} obj   The object to test
+         */
+        isNull: function (obj) {
+            return obj === null;
+        },
+
+        /**
+         * Determines if the specified array is empty. If the specified arg is not an
+         * array, then true is returned.
+         * 
+         * @argument {array} arr    The array to test
+         */
+        isEmpty: function (arr) {
+            return $.isArray(arr) ? arr.length === 0 : true;
+        },
+
+        /**
+         * Determines if these are the same bulletins. If both arguments are not
+         * arrays, false is returned.
+         * 
+         * @param {array} bulletins
+         * @param {array} otherBulletins
+         * @returns {boolean}
+         */
+        doBulletinsDiffer: function (bulletins, otherBulletins) {
+            if ($.isArray(bulletins) && $.isArray(otherBulletins)) {
+                if (bulletins.length === otherBulletins.length) {
+                    for (var i = 0; i < bulletins.length; i++) {
+                        if (bulletins[i].id !== otherBulletins[i].id) {
+                            return true;
+                        }
                     }
+                } else {
+                    return true;
                 }
-            } else {
+            } else if ($.isArray(bulletins) || $.isArray(otherBulletins)) {
                 return true;
             }
-        } else if ($.isArray(bulletins) || $.isArray(otherBulletins)) {
-            return true;
+            return false;
+        },
+
+        /**
+         * Formats the specified bulletin list.
+         * 
+         * @argument {array} bulletins      The bulletins
+         * @return {array}                  The jQuery objects
+         */
+        getFormattedBulletins: function (bulletins) {
+            var formattedBulletins = [];
+            $.each(bulletins, function (j, bulletin) {
+                // format the node address
+                var nodeAddress = '';
+                if (nf.Common.isDefinedAndNotNull(bulletin.nodeAddress)) {
+                    nodeAddress = '-&nbsp' + nf.Common.escapeHtml(bulletin.nodeAddress) + '&nbsp;-&nbsp;';
+                }
+
+                // set the bulletin message (treat as text)
+                var bulletinMessage = $('<pre></pre>').css({
+                    'white-space': 'pre-wrap'
+                }).text(bulletin.message);
+
+                // create the bulletin message
+                var formattedBulletin = $('<div>' +
+                        nf.Common.escapeHtml(bulletin.timestamp) + '&nbsp;' +
+                        nodeAddress + '&nbsp;' +
+                        '<b>' + nf.Common.escapeHtml(bulletin.level) + '</b>&nbsp;' +
+                        '</div>').append(bulletinMessage);
+
+                formattedBulletins.push(formattedBulletin);
+            });
+            return formattedBulletins;
         }
-        return false;
-    },
-    
-    /**
-     * Formats the specified bulletin list.
-     * 
-     * @argument {array} bulletins      The bulletins
-     * @return {array}                  The jQuery objects
-     */
-    getFormattedBulletins: function (bulletins) {
-        var formattedBulletins = [];
-        $.each(bulletins, function (j, bulletin) {
-            // format the node address
-            var nodeAddress = '';
-            if (nf.Common.isDefinedAndNotNull(bulletin.nodeAddress)) {
-                nodeAddress = '-&nbsp' + nf.Common.escapeHtml(bulletin.nodeAddress) + '&nbsp;-&nbsp;';
-            }
-
-            // set the bulletin message (treat as text)
-            var bulletinMessage = $('<pre></pre>').css({
-                'white-space': 'pre-wrap'
-            }).text(bulletin.message);
-
-            // create the bulletin message
-            var formattedBulletin = $('<div>' +
-                    nf.Common.escapeHtml(bulletin.timestamp) + '&nbsp;' +
-                    nodeAddress + '&nbsp;' +
-                    '<b>' + nf.Common.escapeHtml(bulletin.level) + '</b>&nbsp;' +
-                    '</div>').append(bulletinMessage);
-
-            formattedBulletins.push(formattedBulletin);
-        });
-        return formattedBulletins;
-    }
-};
+    };
+}());

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
index 13f5cf3..9a926ef 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
@@ -23,15 +23,6 @@ $(document).ready(function () {
 
     // configure the ok dialog
     $('#nf-ok-dialog').modal({
-        buttons: [{
-                buttonText: 'Ok',
-                handler: {
-                    click: function () {
-                        // close the dialog
-                        $('#nf-ok-dialog').modal('hide');
-                    }
-                }
-            }],
         handler: {
             close: function () {
                 // clear the content
@@ -78,6 +69,20 @@ nf.Dialog = (function () {
             var content = $('<p></p>').append(options.dialogContent);
             $('#nf-ok-dialog-content').append(content);
 
+            // update the button model
+            $('#nf-ok-dialog').modal('setButtonModel', [{
+                buttonText: 'Ok',
+                handler: {
+                    click: function () {
+                        // close the dialog
+                        $('#nf-ok-dialog').modal('hide');
+                        if (typeof options.okHandler === 'function') {
+                            options.okHandler.call(this);
+                        }
+                    }
+                }
+            }]);
+
             // show the dialog
             $('#nf-ok-dialog').modal('setHeaderText', options.headerText).modal('setOverlayBackground', options.overlayBackground).modal('show');
         },


[30/51] [abbrv] nifi git commit: NIFI-655: - Refactoring web security to use Spring Security Java Configuration. - Introducing security in Web UI in order to get JWT.

Posted by mc...@apache.org.
http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/CreateUserActionTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/CreateUserActionTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/CreateUserActionTest.java
index 6486d32..e372781 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/CreateUserActionTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/CreateUserActionTest.java
@@ -40,8 +40,8 @@ public class CreateUserActionTest {
     private final String USER_ID_2 = "2";
     private final String USER_ID_3 = "3";
 
-    private final String USER_DN_1 = "data access exception when creating user";
-    private final String USER_DN_3 = "general create user case";
+    private final String USER_IDENTITY_1 = "data access exception when creating user";
+    private final String USER_IDENTITY_3 = "general create user case";
 
     private DAOFactory daoFactory;
     private UserDAO userDao;
@@ -57,9 +57,9 @@ public class CreateUserActionTest {
                 Object[] args = invocation.getArguments();
                 NiFiUser user = (NiFiUser) args[0];
 
-                if (USER_DN_1.equals(user.getDn())) {
+                if (USER_IDENTITY_1.equals(user.getIdentity())) {
                     throw new DataAccessException();
-                } else if (USER_DN_3.equals(user.getDn())) {
+                } else if (USER_IDENTITY_3.equals(user.getIdentity())) {
                     user.setId(USER_ID_3);
                 }
 
@@ -100,7 +100,7 @@ public class CreateUserActionTest {
     @Test(expected = DataAccessException.class)
     public void testExceptionCreatingUser() throws Exception {
         NiFiUser user = new NiFiUser();
-        user.setDn(USER_DN_1);
+        user.setIdentity(USER_IDENTITY_1);
 
         CreateUserAction createUser = new CreateUserAction(user);
         createUser.execute(daoFactory, null);
@@ -128,7 +128,7 @@ public class CreateUserActionTest {
     @Test
     public void testCreateUserAccount() throws Exception {
         NiFiUser user = new NiFiUser();
-        user.setDn(USER_DN_3);
+        user.setIdentity(USER_IDENTITY_3);
         user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_DFM, Authority.ROLE_ADMIN));
 
         CreateUserAction createUser = new CreateUserAction(user);

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/DisableUserActionTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/DisableUserActionTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/DisableUserActionTest.java
index b0e1ac1..ac2ab29 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/DisableUserActionTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/DisableUserActionTest.java
@@ -40,8 +40,8 @@ public class DisableUserActionTest {
     private static final String USER_ID_3 = "3";
     private static final String USER_ID_4 = "4";
 
-    private static final String USER_DN_3 = "authority access exception";
-    private static final String USER_DN_4 = "general disable user case";
+    private static final String USER_IDENTITY_3 = "authority access exception";
+    private static final String USER_IDENTITY_4 = "general disable user case";
 
     private DAOFactory daoFactory;
     private UserDAO userDao;
@@ -66,11 +66,11 @@ public class DisableUserActionTest {
                 } else if (USER_ID_3.equals(id)) {
                     user = new NiFiUser();
                     user.setId(id);
-                    user.setDn(USER_DN_3);
+                    user.setIdentity(USER_IDENTITY_3);
                 } else if (USER_ID_4.equals(id)) {
                     user = new NiFiUser();
                     user.setId(id);
-                    user.setDn(USER_DN_4);
+                    user.setIdentity(USER_IDENTITY_4);
                     user.setStatus(AccountStatus.ACTIVE);
                 }
                 return user;
@@ -103,7 +103,7 @@ public class DisableUserActionTest {
                 Object[] args = invocation.getArguments();
                 String dn = (String) args[0];
 
-                if (USER_DN_3.equals(dn)) {
+                if (USER_IDENTITY_3.equals(dn)) {
                     throw new AuthorityAccessException(StringUtils.EMPTY);
                 }
 
@@ -158,11 +158,11 @@ public class DisableUserActionTest {
 
         // verify the user
         Assert.assertEquals(USER_ID_4, user.getId());
-        Assert.assertEquals(USER_DN_4, user.getDn());
+        Assert.assertEquals(USER_IDENTITY_4, user.getIdentity());
         Assert.assertEquals(AccountStatus.DISABLED, user.getStatus());
 
         // verify the interaction with the dao and provider
         Mockito.verify(userDao, Mockito.times(1)).updateUser(user);
-        Mockito.verify(authorityProvider, Mockito.times(1)).revokeUser(USER_DN_4);
+        Mockito.verify(authorityProvider, Mockito.times(1)).revokeUser(USER_IDENTITY_4);
     }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/RequestUserAccountActionTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/RequestUserAccountActionTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/RequestUserAccountActionTest.java
index 7707b2c..7bc863b 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/RequestUserAccountActionTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/RequestUserAccountActionTest.java
@@ -36,9 +36,9 @@ public class RequestUserAccountActionTest {
 
     private static final String USER_ID_3 = "3";
 
-    private static final String USER_DN_1 = "existing user account dn";
-    private static final String USER_DN_2 = "data access exception";
-    private static final String USER_DN_3 = "new account request";
+    private static final String USER_IDENTITY_1 = "existing user account";
+    private static final String USER_IDENTITY_2 = "data access exception";
+    private static final String USER_IDENTITY_3 = "new account request";
 
     private DAOFactory daoFactory;
     private UserDAO userDao;
@@ -54,7 +54,7 @@ public class RequestUserAccountActionTest {
                 String dn = (String) args[0];
 
                 NiFiUser user = null;
-                if (USER_DN_1.equals(dn)) {
+                if (USER_IDENTITY_1.equals(dn)) {
                     user = new NiFiUser();
                 }
                 return user;
@@ -65,10 +65,10 @@ public class RequestUserAccountActionTest {
             public Void answer(InvocationOnMock invocation) throws Throwable {
                 Object[] args = invocation.getArguments();
                 NiFiUser user = (NiFiUser) args[0];
-                switch (user.getDn()) {
-                    case USER_DN_2:
+                switch (user.getIdentity()) {
+                    case USER_IDENTITY_2:
                         throw new DataAccessException();
-                    case USER_DN_3:
+                    case USER_IDENTITY_3:
                         user.setId(USER_ID_3);
                         break;
                 }
@@ -90,7 +90,7 @@ public class RequestUserAccountActionTest {
      */
     @Test(expected = IllegalArgumentException.class)
     public void testExistingAccount() throws Exception {
-        RequestUserAccountAction requestUserAccount = new RequestUserAccountAction(USER_DN_1, StringUtils.EMPTY);
+        RequestUserAccountAction requestUserAccount = new RequestUserAccountAction(USER_IDENTITY_1, StringUtils.EMPTY);
         requestUserAccount.execute(daoFactory, null);
     }
 
@@ -102,7 +102,7 @@ public class RequestUserAccountActionTest {
      */
     @Test(expected = DataAccessException.class)
     public void testDataAccessException() throws Exception {
-        RequestUserAccountAction requestUserAccount = new RequestUserAccountAction(USER_DN_2, StringUtils.EMPTY);
+        RequestUserAccountAction requestUserAccount = new RequestUserAccountAction(USER_IDENTITY_2, StringUtils.EMPTY);
         requestUserAccount.execute(daoFactory, null);
     }
 
@@ -113,12 +113,12 @@ public class RequestUserAccountActionTest {
      */
     @Test
     public void testRequestUserAccountAction() throws Exception {
-        RequestUserAccountAction requestUserAccount = new RequestUserAccountAction(USER_DN_3, StringUtils.EMPTY);
+        RequestUserAccountAction requestUserAccount = new RequestUserAccountAction(USER_IDENTITY_3, StringUtils.EMPTY);
         NiFiUser user = requestUserAccount.execute(daoFactory, null);
 
         // verfiy the user
         Assert.assertEquals(USER_ID_3, user.getId());
-        Assert.assertEquals(USER_DN_3, user.getDn());
+        Assert.assertEquals(USER_IDENTITY_3, user.getIdentity());
         Assert.assertEquals(AccountStatus.PENDING, user.getStatus());
 
         // verify interaction with dao

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SeedUserAccountsActionTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SeedUserAccountsActionTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SeedUserAccountsActionTest.java
index 652d992..58db56a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SeedUserAccountsActionTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SeedUserAccountsActionTest.java
@@ -44,10 +44,10 @@ public class SeedUserAccountsActionTest {
     private static final String USER_ID_3 = "3";
     private static final String USER_ID_4 = "4";
 
-    private static final String USER_DN_1 = "user dn 1 - active user - remove monitor and operator, add dfm";
-    private static final String USER_DN_2 = "user dn 2 - active user - no action";
-    private static final String USER_DN_3 = "user dn 3 - pending user - add operator";
-    private static final String USER_DN_4 = "user dn 4 - new user - add monitor";
+    private static final String USER_IDENTITY_1 = "user 1 - active user - remove monitor and operator, add dfm";
+    private static final String USER_IDENTITY_2 = "user 2 - active user - no action";
+    private static final String USER_IDENTITY_3 = "user 3 - pending user - add operator";
+    private static final String USER_IDENTITY_4 = "user 4 - new user - add monitor";
 
     private DAOFactory daoFactory;
     private UserDAO userDao;
@@ -68,19 +68,19 @@ public class SeedUserAccountsActionTest {
                 if (USER_ID_1.equals(id)) {
                     user = new NiFiUser();
                     user.setId(USER_ID_1);
-                    user.setDn(USER_DN_1);
+                    user.setIdentity(USER_IDENTITY_1);
                     user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_MONITOR));
                     user.setStatus(AccountStatus.ACTIVE);
                 } else if (USER_ID_2.equals(id)) {
                     user = new NiFiUser();
                     user.setId(USER_ID_2);
-                    user.setDn(USER_DN_2);
+                    user.setIdentity(USER_IDENTITY_2);
                     user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_ADMIN));
                     user.setStatus(AccountStatus.ACTIVE);
                 } else if (USER_ID_3.equals(id)) {
                     user = new NiFiUser();
                     user.setId(USER_ID_3);
-                    user.setDn(USER_DN_3);
+                    user.setIdentity(USER_IDENTITY_3);
                     user.setStatus(AccountStatus.PENDING);
                 }
                 return user;
@@ -93,22 +93,22 @@ public class SeedUserAccountsActionTest {
                 String dn = (String) args[0];
 
                 NiFiUser user = null;
-                if (USER_DN_1.equals(dn)) {
+                if (USER_IDENTITY_1.equals(dn)) {
                     user = new NiFiUser();
                     user.setId(USER_ID_1);
-                    user.setDn(USER_DN_1);
+                    user.setIdentity(USER_IDENTITY_1);
                     user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_MONITOR));
                     user.setStatus(AccountStatus.ACTIVE);
-                } else if (USER_DN_2.equals(dn)) {
+                } else if (USER_IDENTITY_2.equals(dn)) {
                     user = new NiFiUser();
                     user.setId(USER_ID_2);
-                    user.setDn(USER_DN_2);
+                    user.setIdentity(USER_IDENTITY_2);
                     user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_ADMIN));
                     user.setStatus(AccountStatus.ACTIVE);
-                } else if (USER_DN_3.equals(dn)) {
+                } else if (USER_IDENTITY_3.equals(dn)) {
                     user = new NiFiUser();
                     user.setId(USER_ID_3);
-                    user.setDn(USER_DN_3);
+                    user.setIdentity(USER_IDENTITY_3);
                     user.setStatus(AccountStatus.PENDING);
                 }
                 return user;
@@ -120,7 +120,7 @@ public class SeedUserAccountsActionTest {
                 Object[] args = invocation.getArguments();
                 NiFiUser user = (NiFiUser) args[0];
 
-                if (USER_DN_4.equals(user.getDn())) {
+                if (USER_IDENTITY_4.equals(user.getIdentity())) {
                     user.setId(USER_ID_4);
                 }
 
@@ -141,13 +141,13 @@ public class SeedUserAccountsActionTest {
 
                 Set<String> users = new HashSet<>();
                 if (Authority.ROLE_DFM.equals(role)) {
-                    users.add(USER_DN_1);
+                    users.add(USER_IDENTITY_1);
                 } else if (Authority.ROLE_ADMIN.equals(role)) {
-                    users.add(USER_DN_2);
+                    users.add(USER_IDENTITY_2);
                 } else if (Authority.ROLE_PROXY.equals(role)) {
-                    users.add(USER_DN_3);
+                    users.add(USER_IDENTITY_3);
                 } else if (Authority.ROLE_MONITOR.equals(role)) {
-                    users.add(USER_DN_4);
+                    users.add(USER_IDENTITY_4);
                 }
                 return users;
             }
@@ -160,16 +160,16 @@ public class SeedUserAccountsActionTest {
 
                 Set<Authority> authorities = EnumSet.noneOf(Authority.class);
                 switch (dn) {
-                    case USER_DN_1:
+                    case USER_IDENTITY_1:
                         authorities.add(Authority.ROLE_DFM);
                         break;
-                    case USER_DN_2:
+                    case USER_IDENTITY_2:
                         authorities.add(Authority.ROLE_ADMIN);
                         break;
-                    case USER_DN_3:
+                    case USER_IDENTITY_3:
                         authorities.add(Authority.ROLE_PROXY);
                         break;
-                    case USER_DN_4:
+                    case USER_IDENTITY_4:
                         authorities.add(Authority.ROLE_MONITOR);
                         break;
                 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SetUserAuthoritiesActionTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SetUserAuthoritiesActionTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SetUserAuthoritiesActionTest.java
index 22504f7..5effdbb 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SetUserAuthoritiesActionTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SetUserAuthoritiesActionTest.java
@@ -46,8 +46,8 @@ public class SetUserAuthoritiesActionTest {
     private static final String USER_ID_2 = "2";
     private static final String USER_ID_3 = "3";
 
-    private static final String USER_DN_2 = "user dn 2";
-    private static final String USER_DN_3 = "user dn 3";
+    private static final String USER_IDENTITY_2 = "user 2";
+    private static final String USER_IDENTITY_3 = "user 3";
 
     private DAOFactory daoFactory;
     private UserDAO userDao;
@@ -70,11 +70,11 @@ public class SetUserAuthoritiesActionTest {
                 } else if (USER_ID_2.equals(id)) {
                     user = new NiFiUser();
                     user.setId(USER_ID_2);
-                    user.setDn(USER_DN_2);
+                    user.setIdentity(USER_IDENTITY_2);
                 } else if (USER_ID_3.equals(id)) {
                     user = new NiFiUser();
                     user.setId(USER_ID_3);
-                    user.setDn(USER_DN_3);
+                    user.setIdentity(USER_IDENTITY_3);
                     user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_MONITOR));
                     user.setStatus(AccountStatus.ACTIVE);
                 }
@@ -88,10 +88,10 @@ public class SetUserAuthoritiesActionTest {
                 String dn = (String) args[0];
 
                 NiFiUser user = null;
-                if (USER_DN_3.equals(dn)) {
+                if (USER_IDENTITY_3.equals(dn)) {
                     user = new NiFiUser();
                     user.setId(USER_ID_3);
-                    user.setDn(USER_DN_3);
+                    user.setIdentity(USER_IDENTITY_3);
                     user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_MONITOR));
                     user.setStatus(AccountStatus.ACTIVE);
                 }
@@ -148,7 +148,7 @@ public class SetUserAuthoritiesActionTest {
                 String dn = (String) args[0];
 
                 Set<Authority> authorities = EnumSet.noneOf(Authority.class);
-                if (USER_DN_3.equals(dn)) {
+                if (USER_IDENTITY_3.equals(dn)) {
                     authorities.add(Authority.ROLE_DFM);
                 }
 
@@ -162,7 +162,7 @@ public class SetUserAuthoritiesActionTest {
                 String dn = (String) args[0];
                 Set<Authority> authorites = (Set<Authority>) args[1];
 
-                if (USER_DN_2.equals(dn)) {
+                if (USER_IDENTITY_2.equals(dn)) {
                     throw new AuthorityAccessException(StringUtils.EMPTY);
                 }
 
@@ -218,6 +218,6 @@ public class SetUserAuthoritiesActionTest {
         Set<Authority> authoritiesAddedToProvider = EnumSet.of(Authority.ROLE_ADMIN);
 
         // verify interaction with provider
-        Mockito.verify(authorityProvider, Mockito.times(1)).setAuthorities(USER_DN_3, authoritiesAddedToProvider);
+        Mockito.verify(authorityProvider, Mockito.times(1)).setAuthorities(USER_IDENTITY_3, authoritiesAddedToProvider);
     }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessConfigurationDTO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessConfigurationDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessConfigurationDTO.java
new file mode 100644
index 0000000..d9719b3
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessConfigurationDTO.java
@@ -0,0 +1,61 @@
+/*
+ * 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.api.dto;
+
+import com.wordnik.swagger.annotations.ApiModelProperty;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * Details for the access configuration.
+ */
+@XmlType(name = "accessConfig")
+public class AccessConfigurationDTO {
+
+    private Boolean supportsLogin;
+    private Boolean supportsAnonymous;
+
+    /**
+     * @return Indicates whether or not this NiFi supports user login.
+     */
+    @ApiModelProperty(
+            value = "Indicates whether or not this NiFi supports user login.",
+            readOnly = true
+    )
+    public Boolean getSupportsLogin() {
+        return supportsLogin;
+    }
+
+    public void setSupportsLogin(Boolean supportsLogin) {
+        this.supportsLogin = supportsLogin;
+    }
+
+    /**
+     * @return Indicates whether or not this NiFi supports anonymous access.
+     */
+    @ApiModelProperty(
+            value = "Indicates whether or not this NiFi supports anonymous.",
+            readOnly = true
+    )
+    public Boolean getSupportsAnonymous() {
+        return supportsAnonymous;
+    }
+
+    public void setSupportsAnonymous(Boolean supportsAnonymous) {
+        this.supportsAnonymous = supportsAnonymous;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java
new file mode 100644
index 0000000..712da0e
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java
@@ -0,0 +1,101 @@
+/*
+ * 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.api.dto;
+
+import com.wordnik.swagger.annotations.ApiModelProperty;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of a response to the API. This particular entity holds the users access status.
+ */
+@XmlRootElement(name = "accessStatus")
+public class AccessStatusDTO {
+
+    public static enum Status {
+
+        UNKNOWN,
+        UNREGISTERED,
+        NOT_ACTIVE,
+        ACTIVE
+    }
+
+    private String identity;
+    private String username;
+    private String status;
+    private String message;
+
+    /**
+     * @return the user identity
+     */
+    @ApiModelProperty(
+            value = "The user identity.",
+            readOnly = true
+    )
+    public String getIdentity() {
+        return identity;
+    }
+
+    public void setIdentity(String identity) {
+        this.identity = identity;
+    }
+
+    /**
+     * @return the username
+     */
+    @ApiModelProperty(
+            value = "The username.",
+            readOnly = true
+    )
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    /**
+     * @return the user access status
+     */
+    @ApiModelProperty(
+            value = "The user access status.",
+            readOnly = true
+    )
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    /**
+     * @return additional details about the user access status
+     */
+    @ApiModelProperty(
+            value = "Additional details about the user access status.",
+            readOnly = true
+    )
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessConfigurationEntity.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessConfigurationEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessConfigurationEntity.java
new file mode 100644
index 0000000..3af0e49
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessConfigurationEntity.java
@@ -0,0 +1,43 @@
+/*
+ * 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.api.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.web.api.dto.AccessConfigurationDTO;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a AccessConfigurationDTO.
+ */
+@XmlRootElement(name = "accessConfigurationEntity")
+public class AccessConfigurationEntity extends Entity {
+
+    private AccessConfigurationDTO config;
+
+    /**
+     * The AccessConfigurationDTO that is being serialized.
+     *
+     * @return The AccessConfigurationDTO object
+     */
+    public AccessConfigurationDTO getConfig() {
+        return config;
+    }
+
+    public void setConfig(AccessConfigurationDTO config) {
+        this.config = config;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessStatusEntity.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessStatusEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessStatusEntity.java
new file mode 100644
index 0000000..f19a268
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessStatusEntity.java
@@ -0,0 +1,43 @@
+/*
+ * 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.api.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.web.api.dto.AccessStatusDTO;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a AccessStatusDTO.
+ */
+@XmlRootElement(name = "accessStatusEntity")
+public class AccessStatusEntity extends Entity {
+
+    private AccessStatusDTO accessStatus;
+
+    /**
+     * The AccessStatusDTO that is being serialized.
+     *
+     * @return The AccessStatusDTO object
+     */
+    public AccessStatusDTO getAccessStatus() {
+        return accessStatus;
+    }
+
+    public void setAccessStatus(AccessStatusDTO accessStatus) {
+        this.accessStatus = accessStatus;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/IdentityEntity.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/IdentityEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/IdentityEntity.java
new file mode 100644
index 0000000..02991c7
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/IdentityEntity.java
@@ -0,0 +1,52 @@
+/*
+ * 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.api.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of a response to the API. This particular entity holds the users identity.
+ */
+@XmlRootElement(name = "identityEntity")
+public class IdentityEntity extends Entity {
+
+    private String userId;
+    private String identity;
+
+    /**
+     * @return current user id
+     */
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    /**
+     * @return the user identity being serialized
+     */
+    public String getIdentity() {
+        return identity;
+    }
+
+    public void setIdentity(String identity) {
+        this.identity = identity;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java
index 9bbc3a3..db0b35e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java
@@ -22,6 +22,7 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.ServiceLoader;
 import java.util.Set;
+import org.apache.nifi.authentication.LoginIdentityProvider;
 
 import org.apache.nifi.authorization.AuthorityProvider;
 import org.apache.nifi.controller.ControllerService;
@@ -38,9 +39,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Scans through the classpath to load all FlowFileProcessors,
- * FlowFileComparators, and ReportingTasks using the service provider API and
- * running through all classloaders (root, NARs).
+ * Scans through the classpath to load all FlowFileProcessors, FlowFileComparators, and ReportingTasks using the service provider API and running through all classloaders (root, NARs).
  *
  * @ThreadSafe - is immutable
  */
@@ -60,6 +59,7 @@ public class ExtensionManager {
         definitionMap.put(ReportingTask.class, new HashSet<Class>());
         definitionMap.put(ControllerService.class, new HashSet<Class>());
         definitionMap.put(AuthorityProvider.class, new HashSet<Class>());
+        definitionMap.put(LoginIdentityProvider.class, new HashSet<Class>());
         definitionMap.put(ProvenanceEventRepository.class, new HashSet<Class>());
         definitionMap.put(ComponentStatusRepository.class, new HashSet<Class>());
         definitionMap.put(FlowFileRepository.class, new HashSet<Class>());
@@ -68,9 +68,7 @@ public class ExtensionManager {
     }
 
     /**
-     * Loads all FlowFileProcessor, FlowFileComparator, ReportingTask class
-     * types that can be found on the bootstrap classloader and by creating
-     * classloaders for all NARs found within the classpath.
+     * Loads all FlowFileProcessor, FlowFileComparator, ReportingTask class types that can be found on the bootstrap classloader and by creating classloaders for all NARs found within the classpath.
      */
     public static void discoverExtensions() {
         final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
@@ -113,8 +111,7 @@ public class ExtensionManager {
     }
 
     /**
-     * Registers extension for the specified type from the specified
-     * ClassLoader.
+     * Registers extension for the specified type from the specified ClassLoader.
      *
      * @param type the extension type
      * @param classloaderMap mapping of classname to classloader
@@ -152,9 +149,7 @@ public class ExtensionManager {
     }
 
     /**
-     * Determines the effective classloader for classes of the given type. If
-     * returns null it indicates the given type is not known or was not
-     * detected.
+     * Determines the effective classloader for classes of the given type. If returns null it indicates the given type is not known or was not detected.
      *
      * @param classType to lookup the classloader of
      * @return String of fully qualified class name; null if not a detected type

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/NarThreadContextClassLoader.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/NarThreadContextClassLoader.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/NarThreadContextClassLoader.java
index 9471ba6..9e9bd03 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/NarThreadContextClassLoader.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/NarThreadContextClassLoader.java
@@ -23,6 +23,7 @@ import java.net.URLClassLoader;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.List;
+import org.apache.nifi.authentication.LoginIdentityProvider;
 
 import org.apache.nifi.authorization.AuthorityProvider;
 import org.apache.nifi.components.Validator;
@@ -58,6 +59,7 @@ public class NarThreadContextClassLoader extends URLClassLoader {
         narSpecificClasses.add(StreamCallback.class);
         narSpecificClasses.add(ControllerService.class);
         narSpecificClasses.add(AuthorityProvider.class);
+        narSpecificClasses.add(LoginIdentityProvider.class);
         narSpecificClasses.add(ProvenanceEventRepository.class);
         narSpecificClasses.add(ComponentStatusRepository.class);
         narSpecificClasses.add(FlowFileRepository.class);

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/login-identity-providers.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/login-identity-providers.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/login-identity-providers.xml
new file mode 100644
index 0000000..9868b9d
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/login-identity-providers.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+  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.
+-->
+<!--
+    This file lists the login identity providers to use when running securely. In order
+    to use a specific provider it must be configured here and it's identifier
+    must be specified in the nifi.properties file.
+-->
+<loginIdentityProviders>
+    <!--
+        Identity Provider for users logging in with username/password against an LDAP server.
+        
+        'Authentication Strategy' - How the connection to the LDAP server is authenticated. Possible
+            values are ANONYMOUS, SIMPLE, or START_TLS.
+        
+        'Manager DN' - The DN of the manager that is used to bind to the LDAP server to search for users.
+        'Manager Password' - The password of the manager that is used to bind to the LDAP server to
+            search for users.
+            
+        'TLS - Keystore' - Path to the Keystore that is used when connecting to LDAP using START_TLS.
+        'TLS - Keystore Password' - Password for the Keystore that is used when connecting to LDAP
+            using START_TLS.
+        'TLS - Keystore Type' - Type of the Keystore that is used when connecting to LDAP using
+            START_TLS (i.e. JKS or PKCS12).
+        'TLS - Truststore' - Path to the Truststore that is used when connecting to LDAP using START_TLS.
+        'TLS - Truststore Password' - Password for the Truststore that is used when connecting to
+            LDAP using START_TLS.
+        'TLS - Truststore Type' - Type of the Truststore that is used when connecting to LDAP using
+            START_TLS (i.e. JKS or PKCS12).
+        'TLS - Client Auth' - Client authentication policy when connecting to LDAP using START_TLS.
+            Possible values are REQUIRED, WANT, NONE.
+        'TLS - Protocol' - Protocol to use when connecting to LDAP using START_TLS. (i.e. TLS,
+            TLSv1.1, TLSv1.2, etc).
+        'TLS - Shutdown Gracefully' - Specifies whether the TLS should be shut down gracefully 
+            before the target context is closed. Defaults to false.
+            
+        'Referral Strategy' - Strategy for handling referrals. Possible values are FOLLOW, IGNORE, THROW.
+        'Connect Timeout' - Duration of connect timeout. (i.e. 10 secs).
+        'Read Timeout' - Duration of read timeout. (i.e. 10 secs).
+       
+        'Url' - Url of the LDAP servier (i.e. ldap://<hostname>:<port>).
+        'User Search Base' - Base DN for searching for users (i.e. CN=Users,DC=example,DC=com).
+        'User Search Filter' - Filter for searching for users against the 'User Search Base'.
+            (i.e. sAMAccountName={0}). The user specified name is inserted into '{0}'.
+            
+        'Authentication Expiration' - The duration of how long the user authentication is valid
+            for. If the user never logs out, they will be required to log back in following
+            this duration.
+    -->
+    <!-- To enable the ldap-provider remove 2 lines. This is 1 of 2. 
+    <provider>
+        <identifier>ldap-provider</identifier>
+        <class>org.apache.nifi.ldap.LdapProvider</class>
+        <property name="Authentication Strategy">START_TLS</property>
+
+        <property name="Manager DN"></property>
+        <property name="Manager Password"></property>
+
+        <property name="TLS - Keystore"></property>
+        <property name="TLS - Keystore Password"></property>
+        <property name="TLS - Keystore Type"></property>
+        <property name="TLS - Truststore"></property>
+        <property name="TLS - Truststore Password"></property>
+        <property name="TLS - Truststore Type"></property>
+        <property name="TLS - Client Auth"></property>
+        <property name="TLS - Protocol"></property>
+        <property name="TLS - Shutdown Gracefully"></property>
+        
+        <property name="Referral Strategy">FOLLOW</property>
+        <property name="Connect Timeout">10 secs</property>
+        <property name="Read Timeout">10 secs</property>
+
+        <property name="Url"></property>
+        <property name="User Search Base"></property>
+        <property name="User Search Filter"></property>
+
+        <property name="Expiration Duration">12 hours</property>
+    </provider>
+    To enable the ldap-provider remove 2 lines. This is 2 of 2. -->
+</loginIdentityProviders>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
index 54b5283..b25d05a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
@@ -25,6 +25,7 @@ nifi.administrative.yield.duration=${nifi.administrative.yield.duration}
 nifi.bored.yield.duration=${nifi.bored.yield.duration}
 
 nifi.authority.provider.configuration.file=${nifi.authority.provider.configuration.file}
+nifi.login.identity.provider.configuration.file=${nifi.login.identity.provider.configuration.file}
 nifi.templates.directory=${nifi.templates.directory}
 nifi.ui.banner.text=${nifi.ui.banner.text}
 nifi.ui.autorefresh.interval=${nifi.ui.autorefresh.interval}
@@ -124,7 +125,9 @@ nifi.security.truststorePasswd=${nifi.security.truststorePasswd}
 nifi.security.needClientAuth=${nifi.security.needClientAuth}
 nifi.security.user.credential.cache.duration=${nifi.security.user.credential.cache.duration}
 nifi.security.user.authority.provider=${nifi.security.user.authority.provider}
+nifi.security.user.login.identity.provider=${nifi.security.user.login.identity.provider}
 nifi.security.support.new.account.requests=${nifi.security.support.new.account.requests}
+nifi.security.anonymous.authorities=${nifi.security.anonymous.authorities}
 nifi.security.ocsp.responder.url=${nifi.security.ocsp.responder.url}
 nifi.security.ocsp.responder.certificate=${nifi.security.ocsp.responder.certificate}
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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 ecfe2c0..d1bd5c8 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
@@ -615,8 +615,12 @@ public class JettyServer implements NiFiServer {
     private SslContextFactory createSslContextFactory() {
         final SslContextFactory contextFactory = new SslContextFactory();
 
-        // need client auth
-        contextFactory.setNeedClientAuth(props.getNeedClientAuth());
+        // 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/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerAuditor.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerAuditor.java
index cede675..4357633 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerAuditor.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerAuditor.java
@@ -78,7 +78,7 @@ public class ControllerAuditor extends NiFiAuditor {
 
                 // create the config action
                 FlowChangeAction configAction = new FlowChangeAction();
-                configAction.setUserIdentity(user.getDn());
+                configAction.setUserIdentity(user.getIdentity());
                 configAction.setUserName(user.getUserName());
                 configAction.setOperation(Operation.Configure);
                 configAction.setTimestamp(new Date());
@@ -131,7 +131,7 @@ public class ControllerAuditor extends NiFiAuditor {
 
                 // create the config action
                 FlowChangeAction configAction = new FlowChangeAction();
-                configAction.setUserIdentity(user.getDn());
+                configAction.setUserIdentity(user.getIdentity());
                 configAction.setUserName(user.getUserName());
                 configAction.setOperation(Operation.Configure);
                 configAction.setTimestamp(new Date());
@@ -184,7 +184,7 @@ public class ControllerAuditor extends NiFiAuditor {
 
                 // create the config action
                 FlowChangeAction configAction = new FlowChangeAction();
-                configAction.setUserIdentity(user.getDn());
+                configAction.setUserIdentity(user.getIdentity());
                 configAction.setUserName(user.getUserName());
                 configAction.setOperation(Operation.Configure);
                 configAction.setTimestamp(new Date());
@@ -237,7 +237,7 @@ public class ControllerAuditor extends NiFiAuditor {
 
                 // create the config action
                 FlowChangeAction configAction = new FlowChangeAction();
-                configAction.setUserIdentity(user.getDn());
+                configAction.setUserIdentity(user.getIdentity());
                 configAction.setUserName(user.getUserName());
                 configAction.setOperation(Operation.Configure);
                 configAction.setTimestamp(new Date());

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerServiceAuditor.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerServiceAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerServiceAuditor.java
index 0187ee4..af8428d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerServiceAuditor.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerServiceAuditor.java
@@ -167,7 +167,7 @@ public class ControllerServiceAuditor extends NiFiAuditor {
 
                     // create a configuration action
                     FlowChangeAction configurationAction = new FlowChangeAction();
-                    configurationAction.setUserIdentity(user.getDn());
+                    configurationAction.setUserIdentity(user.getIdentity());
                     configurationAction.setUserName(user.getUserName());
                     configurationAction.setOperation(operation);
                     configurationAction.setTimestamp(actionTimestamp);
@@ -187,7 +187,7 @@ public class ControllerServiceAuditor extends NiFiAuditor {
             if (isDisabled != updateIsDisabled) {
                 // create a controller service action
                 FlowChangeAction serviceAction = new FlowChangeAction();
-                serviceAction.setUserIdentity(user.getDn());
+                serviceAction.setUserIdentity(user.getIdentity());
                 serviceAction.setUserName(user.getUserName());
                 serviceAction.setTimestamp(new Date());
                 serviceAction.setSourceId(controllerService.getIdentifier());
@@ -271,7 +271,7 @@ public class ControllerServiceAuditor extends NiFiAuditor {
 
                 // create a processor action
                 FlowChangeAction processorAction = new FlowChangeAction();
-                processorAction.setUserIdentity(user.getDn());
+                processorAction.setUserIdentity(user.getIdentity());
                 processorAction.setUserName(user.getUserName());
                 processorAction.setTimestamp(new Date());
                 processorAction.setSourceId(processor.getIdentifier());
@@ -289,7 +289,7 @@ public class ControllerServiceAuditor extends NiFiAuditor {
 
                 // create a reporting task action
                 FlowChangeAction reportingTaskAction = new FlowChangeAction();
-                reportingTaskAction.setUserIdentity(user.getDn());
+                reportingTaskAction.setUserIdentity(user.getIdentity());
                 reportingTaskAction.setUserName(user.getUserName());
                 reportingTaskAction.setTimestamp(new Date());
                 reportingTaskAction.setSourceId(reportingTask.getIdentifier());
@@ -307,7 +307,7 @@ public class ControllerServiceAuditor extends NiFiAuditor {
 
                 // create a controller service action
                 FlowChangeAction serviceAction = new FlowChangeAction();
-                serviceAction.setUserIdentity(user.getDn());
+                serviceAction.setUserIdentity(user.getIdentity());
                 serviceAction.setUserName(user.getUserName());
                 serviceAction.setTimestamp(new Date());
                 serviceAction.setSourceId(controllerService.getIdentifier());
@@ -387,7 +387,7 @@ public class ControllerServiceAuditor extends NiFiAuditor {
 
             // create the controller service action for adding this controller service
             action = new FlowChangeAction();
-            action.setUserIdentity(user.getDn());
+            action.setUserIdentity(user.getIdentity());
             action.setUserName(user.getUserName());
             action.setOperation(operation);
             action.setTimestamp(new Date());

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/FunnelAuditor.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/FunnelAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/FunnelAuditor.java
index 51cb20c..3949028 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/FunnelAuditor.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/FunnelAuditor.java
@@ -121,7 +121,7 @@ public class FunnelAuditor extends NiFiAuditor {
         if (user != null) {
             // create the action for adding this funnel
             action = new FlowChangeAction();
-            action.setUserIdentity(user.getDn());
+            action.setUserIdentity(user.getIdentity());
             action.setUserName(user.getUserName());
             action.setOperation(operation);
             action.setTimestamp(new Date());

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/PortAuditor.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/PortAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/PortAuditor.java
index b07d64f..e99a1aa 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/PortAuditor.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/PortAuditor.java
@@ -205,7 +205,7 @@ public class PortAuditor extends NiFiAuditor {
                 for (ActionDetails detail : configurationDetails) {
                     // create the port action for updating the name
                     FlowChangeAction portAction = new FlowChangeAction();
-                    portAction.setUserIdentity(user.getDn());
+                    portAction.setUserIdentity(user.getIdentity());
                     portAction.setUserName(user.getUserName());
                     portAction.setOperation(Operation.Configure);
                     portAction.setTimestamp(timestamp);
@@ -225,7 +225,7 @@ public class PortAuditor extends NiFiAuditor {
             if (scheduledState != updatedScheduledState) {
                 // create a processor action
                 FlowChangeAction processorAction = new FlowChangeAction();
-                processorAction.setUserIdentity(user.getDn());
+                processorAction.setUserIdentity(user.getIdentity());
                 processorAction.setUserName(user.getUserName());
                 processorAction.setTimestamp(new Date());
                 processorAction.setSourceId(updatedPort.getIdentifier());
@@ -323,7 +323,7 @@ public class PortAuditor extends NiFiAuditor {
 
             // create the port action for adding this processor
             action = new FlowChangeAction();
-            action.setUserIdentity(user.getDn());
+            action.setUserIdentity(user.getIdentity());
             action.setUserName(user.getUserName());
             action.setOperation(operation);
             action.setTimestamp(new Date());

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java
index d563555..89871e6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java
@@ -140,7 +140,7 @@ public class ProcessGroupAuditor extends NiFiAuditor {
 
                     // create the port action for updating the name
                     FlowChangeAction processGroupAction = new FlowChangeAction();
-                    processGroupAction.setUserIdentity(user.getDn());
+                    processGroupAction.setUserIdentity(user.getIdentity());
                     processGroupAction.setUserName(user.getUserName());
                     processGroupAction.setOperation(operation);
                     processGroupAction.setTimestamp(timestamp);
@@ -157,7 +157,7 @@ public class ProcessGroupAuditor extends NiFiAuditor {
             if (processGroupDTO.isRunning() != null) {
                 // create a process group action
                 FlowChangeAction processGroupAction = new FlowChangeAction();
-                processGroupAction.setUserIdentity(user.getDn());
+                processGroupAction.setUserIdentity(user.getIdentity());
                 processGroupAction.setUserName(user.getUserName());
                 processGroupAction.setSourceId(processGroup.getIdentifier());
                 processGroupAction.setSourceName(processGroup.getName());
@@ -242,7 +242,7 @@ public class ProcessGroupAuditor extends NiFiAuditor {
 
             // create the process group action for adding this process group
             action = new FlowChangeAction();
-            action.setUserIdentity(user.getDn());
+            action.setUserIdentity(user.getIdentity());
             action.setUserName(user.getUserName());
             action.setOperation(operation);
             action.setTimestamp(new Date());

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessorAuditor.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessorAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessorAuditor.java
index b8a2c69..4f147fb 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessorAuditor.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessorAuditor.java
@@ -177,7 +177,7 @@ public class ProcessorAuditor extends NiFiAuditor {
 
                     // create a configuration action
                     FlowChangeAction configurationAction = new FlowChangeAction();
-                    configurationAction.setUserIdentity(user.getDn());
+                    configurationAction.setUserIdentity(user.getIdentity());
                     configurationAction.setUserName(user.getUserName());
                     configurationAction.setOperation(operation);
                     configurationAction.setTimestamp(actionTimestamp);
@@ -197,7 +197,7 @@ public class ProcessorAuditor extends NiFiAuditor {
             if (scheduledState != updatedScheduledState) {
                 // create a processor action
                 FlowChangeAction processorAction = new FlowChangeAction();
-                processorAction.setUserIdentity(user.getDn());
+                processorAction.setUserIdentity(user.getIdentity());
                 processorAction.setUserName(user.getUserName());
                 processorAction.setTimestamp(new Date());
                 processorAction.setSourceId(processor.getIdentifier());
@@ -294,7 +294,7 @@ public class ProcessorAuditor extends NiFiAuditor {
 
             // create the processor action for adding this processor
             action = new FlowChangeAction();
-            action.setUserIdentity(user.getDn());
+            action.setUserIdentity(user.getIdentity());
             action.setUserName(user.getUserName());
             action.setOperation(operation);
             action.setTimestamp(new Date());

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RelationshipAuditor.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RelationshipAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RelationshipAuditor.java
index f90d572..95000d8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RelationshipAuditor.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RelationshipAuditor.java
@@ -188,7 +188,7 @@ public class RelationshipAuditor extends NiFiAuditor {
 
                     // create a configuration action
                     FlowChangeAction configurationAction = new FlowChangeAction();
-                    configurationAction.setUserIdentity(user.getDn());
+                    configurationAction.setUserIdentity(user.getIdentity());
                     configurationAction.setUserName(user.getUserName());
                     configurationAction.setOperation(Operation.Configure);
                     configurationAction.setTimestamp(actionTimestamp);
@@ -353,7 +353,7 @@ public class RelationshipAuditor extends NiFiAuditor {
 
             // create a new relationship action
             action = new FlowChangeAction();
-            action.setUserIdentity(user.getDn());
+            action.setUserIdentity(user.getIdentity());
             action.setUserName(user.getUserName());
             action.setOperation(operation);
             action.setTimestamp(actionTimestamp);

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RemoteProcessGroupAuditor.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RemoteProcessGroupAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RemoteProcessGroupAuditor.java
index e145a62..5815634 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RemoteProcessGroupAuditor.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RemoteProcessGroupAuditor.java
@@ -246,7 +246,7 @@ public class RemoteProcessGroupAuditor extends NiFiAuditor {
                 for (ActionDetails detail : details) {
                     // create the port action for updating the name
                     FlowChangeAction remoteProcessGroupAction = new FlowChangeAction();
-                    remoteProcessGroupAction.setUserIdentity(user.getDn());
+                    remoteProcessGroupAction.setUserIdentity(user.getIdentity());
                     remoteProcessGroupAction.setUserName(user.getUserName());
                     remoteProcessGroupAction.setOperation(Operation.Configure);
                     remoteProcessGroupAction.setTimestamp(timestamp);
@@ -267,7 +267,7 @@ public class RemoteProcessGroupAuditor extends NiFiAuditor {
             if (transmissionState != updatedTransmissionState) {
                 // create a processor action
                 FlowChangeAction remoteProcessGroupAction = new FlowChangeAction();
-                remoteProcessGroupAction.setUserIdentity(user.getDn());
+                remoteProcessGroupAction.setUserIdentity(user.getIdentity());
                 remoteProcessGroupAction.setUserName(user.getUserName());
                 remoteProcessGroupAction.setTimestamp(new Date());
                 remoteProcessGroupAction.setSourceId(updatedRemoteProcessGroup.getIdentifier());
@@ -356,7 +356,7 @@ public class RemoteProcessGroupAuditor extends NiFiAuditor {
 
             // create the remote process group action
             action = new FlowChangeAction();
-            action.setUserIdentity(user.getDn());
+            action.setUserIdentity(user.getIdentity());
             action.setUserName(user.getUserName());
             action.setOperation(operation);
             action.setTimestamp(new Date());

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ReportingTaskAuditor.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ReportingTaskAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ReportingTaskAuditor.java
index 712f99a..77df12a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ReportingTaskAuditor.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ReportingTaskAuditor.java
@@ -160,7 +160,7 @@ public class ReportingTaskAuditor extends NiFiAuditor {
 
                     // create a configuration action
                     FlowChangeAction configurationAction = new FlowChangeAction();
-                    configurationAction.setUserIdentity(user.getDn());
+                    configurationAction.setUserIdentity(user.getIdentity());
                     configurationAction.setUserName(user.getUserName());
                     configurationAction.setOperation(operation);
                     configurationAction.setTimestamp(actionTimestamp);
@@ -180,7 +180,7 @@ public class ReportingTaskAuditor extends NiFiAuditor {
             if (scheduledState != updatedScheduledState) {
                 // create a reporting task action
                 FlowChangeAction taskAction = new FlowChangeAction();
-                taskAction.setUserIdentity(user.getDn());
+                taskAction.setUserIdentity(user.getIdentity());
                 taskAction.setUserName(user.getUserName());
                 taskAction.setTimestamp(new Date());
                 taskAction.setSourceId(reportingTask.getIdentifier());
@@ -276,7 +276,7 @@ public class ReportingTaskAuditor extends NiFiAuditor {
 
             // create the reporting task action for adding this reporting task
             action = new FlowChangeAction();
-            action.setUserIdentity(user.getDn());
+            action.setUserIdentity(user.getIdentity());
             action.setUserName(user.getUserName());
             action.setOperation(operation);
             action.setTimestamp(new Date());

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java
index 34382b3..4b7c38a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java
@@ -232,7 +232,7 @@ public class SnippetAuditor extends NiFiAuditor {
         if (user != null) {
             // create the action for adding this funnel
             action = new FlowChangeAction();
-            action.setUserIdentity(user.getDn());
+            action.setUserIdentity(user.getIdentity());
             action.setUserName(user.getUserName());
             action.setOperation(operation);
             action.setTimestamp(timestamp);

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
index 2d3355a..73d76bd 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
@@ -1267,6 +1267,13 @@ public interface NiFiServiceFacade {
     Collection<UserDTO> getUsers(Boolean grouped);
 
     /**
+     * Creates a new account request.
+     *
+     * @return user
+     */
+    UserDTO createUser();
+
+    /**
      * Updates the specified user accordingly.
      *
      * @param user The user to update

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java
new file mode 100644
index 0000000..58b0af8
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.ImportResource;
+
+/**
+ *
+ */
+@Configuration
+@Import({NiFiWebApiSecurityConfiguration.class})
+@ImportResource({"classpath:nifi-context.xml",
+    "classpath:nifi-administration-context.xml",
+    "classpath:nifi-cluster-manager-context.xml",
+    "classpath:nifi-cluster-protocol-context.xml",
+    "classpath:nifi-web-security-context.xml",
+    "classpath:nifi-web-api-context.xml"})
+public class NiFiWebApiConfiguration {
+
+    public NiFiWebApiConfiguration() {
+        super();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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
new file mode 100644
index 0000000..e8ed267
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
@@ -0,0 +1,179 @@
+/*
+ * 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;
+
+import org.apache.nifi.admin.service.UserService;
+import org.apache.nifi.authentication.LoginIdentityProvider;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.NiFiAuthenticationProvider;
+import org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter;
+import org.apache.nifi.web.security.NiFiAuthenticationEntryPoint;
+import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
+import org.apache.nifi.web.security.jwt.JwtService;
+import org.apache.nifi.web.security.node.NodeAuthorizedUserFilter;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.x509.X509AuthenticationFilter;
+import org.apache.nifi.web.security.x509.X509CertificateExtractor;
+import org.apache.nifi.web.security.x509.X509IdentityProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
+import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
+
+/**
+ * NiFi Web Api Spring security
+ */
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapter {
+
+    private NiFiProperties properties;
+    private UserService userService;
+    private AuthenticationUserDetailsService userDetailsService;
+    private JwtService jwtService;
+    private X509CertificateExtractor certificateExtractor;
+    private X509IdentityProvider certificateIdentityProvider;
+    private LoginIdentityProvider loginIdentityProvider;
+
+    public NiFiWebApiSecurityConfiguration() {
+        super(true); // disable defaults
+    }
+
+    @Override
+    public void configure(WebSecurity webSecurity) throws Exception {
+        webSecurity
+                .ignoring()
+                    .antMatchers("/access/**");
+    }
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http
+                .rememberMe().disable()
+                .exceptionHandling()
+                    .authenticationEntryPoint(new NiFiAuthenticationEntryPoint(properties))
+                    .and()
+                .authorizeRequests()
+                    .anyRequest().fullyAuthenticated()
+                    .and()
+                .sessionManagement()
+                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
+
+        // cluster authorized user
+        http.addFilterBefore(buildNodeAuthorizedUserFilter(), AnonymousAuthenticationFilter.class);
+
+        // anonymous
+        http.anonymous().authenticationFilter(buildAnonymousFilter());
+
+        // x509
+        http.addFilterAfter(buildX509Filter(), AnonymousAuthenticationFilter.class);
+
+        // jwt - consider when configured for log in
+        if (loginIdentityProvider != null) {
+            http.addFilterAfter(buildJwtFilter(), AnonymousAuthenticationFilter.class);
+        }
+    }
+
+    @Bean
+    @Override
+    public AuthenticationManager authenticationManagerBean() throws Exception {
+        // override xxxBean method so the authentication manager is available in app context (necessary for the method level security)
+        return super.authenticationManagerBean();
+    }
+
+    @Override
+    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+        auth.authenticationProvider(new NiFiAuthenticationProvider(userDetailsService));
+    }
+
+    private NodeAuthorizedUserFilter buildNodeAuthorizedUserFilter() {
+        final NodeAuthorizedUserFilter nodeFilter = new NodeAuthorizedUserFilter();
+        nodeFilter.setProperties(properties);
+        nodeFilter.setCertificateExtractor(certificateExtractor);
+        nodeFilter.setCertificateIdentityProvider(certificateIdentityProvider);
+        return nodeFilter;
+    }
+
+    private JwtAuthenticationFilter buildJwtFilter() throws Exception {
+        final JwtAuthenticationFilter jwtFilter = new JwtAuthenticationFilter();
+        jwtFilter.setProperties(properties);
+        jwtFilter.setJwtService(jwtService);
+        jwtFilter.setAuthenticationManager(authenticationManager());
+        return jwtFilter;
+    }
+
+    private X509AuthenticationFilter buildX509Filter() throws Exception {
+        final X509AuthenticationFilter x509Filter = new X509AuthenticationFilter();
+        x509Filter.setProperties(properties);
+        x509Filter.setCertificateExtractor(certificateExtractor);
+        x509Filter.setCertificateIdentityProvider(certificateIdentityProvider);
+        x509Filter.setAuthenticationManager(authenticationManager());
+        return x509Filter;
+    }
+
+    private AnonymousAuthenticationFilter buildAnonymousFilter() {
+        final NiFiAnonymousUserFilter anonymousFilter = new NiFiAnonymousUserFilter();
+        anonymousFilter.setUserService(userService);
+        return anonymousFilter;
+    }
+
+    @Autowired
+    public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) {
+        this.userDetailsService = userDetailsService;
+    }
+
+    @Autowired
+    public void setUserService(UserService userService) {
+        this.userService = userService;
+    }
+
+    @Autowired
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    @Autowired
+    public void setJwtService(JwtService jwtService) {
+        this.jwtService = jwtService;
+    }
+
+    @Autowired
+    public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) {
+        this.loginIdentityProvider = loginIdentityProvider;
+    }
+
+    @Autowired
+    public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
+        this.certificateExtractor = certificateExtractor;
+    }
+
+    @Autowired
+    public void setCertificateIdentityProvider(X509IdentityProvider certificateIdentityProvider) {
+        this.certificateIdentityProvider = certificateIdentityProvider;
+    }
+
+}


[28/51] [abbrv] nifi git commit: NIFI-655: - Refactoring web security to use Spring Security Java Configuration. - Introducing security in Web UI in order to get JWT.

Posted by mc...@apache.org.
http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
index 52f4522..621dc09 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
@@ -22,8 +22,7 @@ import com.sun.jersey.api.client.WebResource;
 import com.sun.jersey.core.util.MultivaluedMapImpl;
 import java.util.Map;
 import javax.ws.rs.core.MediaType;
-import org.apache.nifi.web.security.DnUtils;
-import org.apache.nifi.web.security.x509.X509AuthenticationFilter;
+import org.apache.nifi.web.security.ProxiedEntitiesUtils;
 
 /**
  *
@@ -35,9 +34,27 @@ public class NiFiTestUser {
     private final Client client;
     private final String proxyDn;
 
-    public NiFiTestUser(Client client, String dn) {
+    public NiFiTestUser(Client client, String proxyDn) {
         this.client = client;
-        this.proxyDn = DnUtils.formatProxyDn(dn);
+        if (proxyDn != null) {
+            this.proxyDn = ProxiedEntitiesUtils.formatProxyDn(proxyDn);
+        } else {
+            this.proxyDn = null;
+        }
+    }
+
+    /**
+     * Conditionally adds the proxied entities chain.
+     *
+     * @param builder the resource builder
+     * @return the resource builder
+     */
+    private WebResource.Builder addProxiedEntities(final WebResource.Builder builder) {
+        if (proxyDn == null) {
+            return builder;
+        } else {
+            return builder.header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn);
+        }
     }
 
     /**
@@ -59,6 +76,18 @@ public class NiFiTestUser {
      * @return response
      */
     public ClientResponse testGet(String url, Map<String, String> queryParams) {
+        return testGetWithHeaders(url, queryParams, null);
+    }
+
+    /**
+     * Performs a GET using the specified url and query parameters.
+     *
+     * @param url url
+     * @param queryParams params
+     * @param headers http headers
+     * @return response
+     */
+    public ClientResponse testGetWithHeaders(String url, Map<String, String> queryParams, Map<String, String> headers) {
         // get the resource
         WebResource resource = client.resource(url);
 
@@ -69,8 +98,18 @@ public class NiFiTestUser {
             }
         }
 
+        // get the builder
+        WebResource.Builder builder = addProxiedEntities(resource.accept(MediaType.APPLICATION_JSON));
+
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                builder = builder.header(key, headers.get(key));
+            }
+        }
+
         // perform the query
-        return resource.accept(MediaType.APPLICATION_JSON).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn).get(ClientResponse.class);
+        return builder.get(ClientResponse.class);
     }
 
     /**
@@ -93,14 +132,34 @@ public class NiFiTestUser {
      * @throws Exception ex
      */
     public ClientResponse testPost(String url, Object entity) throws Exception {
+        return testPostWithHeaders(url, entity, null);
+    }
+
+    /**
+     * Performs a POST using the specified url and entity body.
+     *
+     * @param url url
+     * @param entity entity
+     * @param headers http headers
+     * @return response
+     * @throws Exception ex
+     */
+    public ClientResponse testPostWithHeaders(String url, Object entity, Map<String, String> headers) throws Exception {
         // get the resource
-        WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn);
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON));
 
         // include the request entity
         if (entity != null) {
             resourceBuilder = resourceBuilder.entity(entity);
         }
 
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                resourceBuilder = resourceBuilder.header(key, headers.get(key));
+            }
+        }
+
         // perform the request
         return resourceBuilder.post(ClientResponse.class);
     }
@@ -110,18 +169,38 @@ public class NiFiTestUser {
      *
      * @param url url
      * @param entity entity
-     * @return repsonse
+     * @return response
      * @throws Exception ex
      */
     public ClientResponse testPostMultiPart(String url, Object entity) throws Exception {
+        return testPostMultiPartWithHeaders(url, entity, null);
+    }
+
+    /**
+     * Performs a POST using the specified url and entity body.
+     *
+     * @param url url
+     * @param entity entity
+     * @param headers http headers
+     * @return response
+     * @throws Exception ex
+     */
+    public ClientResponse testPostMultiPartWithHeaders(String url, Object entity, Map<String, String> headers) throws Exception {
         // get the resource
-        WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_XML).type(MediaType.MULTIPART_FORM_DATA).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn);
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_XML).type(MediaType.MULTIPART_FORM_DATA));
 
         // include the request entity
         if (entity != null) {
             resourceBuilder = resourceBuilder.entity(entity);
         }
 
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                resourceBuilder = resourceBuilder.header(key, headers.get(key));
+            }
+        }
+
         // perform the request
         return resourceBuilder.post(ClientResponse.class);
     }
@@ -135,6 +214,19 @@ public class NiFiTestUser {
      * @throws java.lang.Exception ex
      */
     public ClientResponse testPost(String url, Map<String, String> formData) throws Exception {
+        return testPostWithHeaders(url, formData, null);
+    }
+
+    /**
+     * Performs a POST using the specified url and form data.
+     *
+     * @param url url
+     * @param formData form data
+     * @param headers http headers
+     * @return response
+     * @throws java.lang.Exception ex
+     */
+    public ClientResponse testPostWithHeaders(String url, Map<String, String> formData, Map<String, String> headers) throws Exception {
         // convert the form data
         MultivaluedMapImpl entity = new MultivaluedMapImpl();
         for (String key : formData.keySet()) {
@@ -142,14 +234,20 @@ public class NiFiTestUser {
         }
 
         // get the resource
-        WebResource.Builder resourceBuilder
-                = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn);
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED));
 
         // add the form data if necessary
         if (!entity.isEmpty()) {
             resourceBuilder = resourceBuilder.entity(entity);
         }
 
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                resourceBuilder = resourceBuilder.header(key, headers.get(key));
+            }
+        }
+
         // perform the request
         return resourceBuilder.post(ClientResponse.class);
     }
@@ -163,14 +261,34 @@ public class NiFiTestUser {
      * @throws java.lang.Exception ex
      */
     public ClientResponse testPut(String url, Object entity) throws Exception {
+        return testPutWithHeaders(url, entity, null);
+    }
+
+    /**
+     * Performs a PUT using the specified url and entity body.
+     *
+     * @param url url
+     * @param entity entity
+     * @param headers http headers
+     * @return response
+     * @throws java.lang.Exception ex
+     */
+    public ClientResponse testPutWithHeaders(String url, Object entity, Map<String, String> headers) throws Exception {
         // get the resource
-        WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn);
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON));
 
         // include the request entity
         if (entity != null) {
             resourceBuilder = resourceBuilder.entity(entity);
         }
 
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                resourceBuilder = resourceBuilder.header(key, headers.get(key));
+            }
+        }
+
         // perform the request
         return resourceBuilder.put(ClientResponse.class);
     }
@@ -184,6 +302,19 @@ public class NiFiTestUser {
      * @throws java.lang.Exception ex
      */
     public ClientResponse testPut(String url, Map<String, String> formData) throws Exception {
+        return testPutWithHeaders(url, formData, null);
+    }
+
+    /**
+     * Performs a PUT using the specified url and form data.
+     *
+     * @param url url
+     * @param formData form data
+     * @param headers http headers
+     * @return response
+     * @throws java.lang.Exception ex
+     */
+    public ClientResponse testPutWithHeaders(String url, Map<String, String> formData, Map<String, String> headers) throws Exception {
         // convert the form data
         MultivaluedMapImpl entity = new MultivaluedMapImpl();
         for (String key : formData.keySet()) {
@@ -191,14 +322,20 @@ public class NiFiTestUser {
         }
 
         // get the resource
-        WebResource.Builder resourceBuilder
-                = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn);
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED));
 
         // add the form data if necessary
         if (!entity.isEmpty()) {
             resourceBuilder = resourceBuilder.entity(entity);
         }
 
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                resourceBuilder = resourceBuilder.header(key, headers.get(key));
+            }
+        }
+
         // perform the request
         return resourceBuilder.put(ClientResponse.class);
     }
@@ -211,24 +348,26 @@ public class NiFiTestUser {
      * @throws java.lang.Exception ex
      */
     public ClientResponse testDelete(String url) throws Exception {
-        return testDelete(url, (Object) null);
+        return testDelete(url, null);
     }
 
     /**
      * Performs a DELETE using the specified url and entity.
      *
      * @param url url
-     * @param entity entity
-     * @return repsonse
+     * @param headers http headers
+     * @return response
      * @throws java.lang.Exception ex
      */
-    public ClientResponse testDelete(String url, Object entity) throws Exception {
+    public ClientResponse testDeleteWithHeaders(String url, Map<String, String> headers) throws Exception {
         // get the resource
-        WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_JSON).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn);
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_JSON));
 
-        // append any query parameters
-        if (entity != null) {
-            resourceBuilder = resourceBuilder.entity(entity);
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                resourceBuilder = resourceBuilder.header(key, headers.get(key));
+            }
         }
 
         // perform the query
@@ -255,7 +394,56 @@ public class NiFiTestUser {
         }
 
         // perform the request
-        return resource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN, proxyDn).delete(ClientResponse.class);
+        return addProxiedEntities(resource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED)).delete(ClientResponse.class);
     }
 
+    /**
+     * Attempts to create a token with the specified username and password.
+     *
+     * @param url the url
+     * @param username the username
+     * @param password the password
+     * @return response
+     * @throws Exception ex
+     */
+    public ClientResponse testCreateToken(String url, String username, String password) throws Exception {
+        // convert the form data
+        MultivaluedMapImpl entity = new MultivaluedMapImpl();
+        entity.add("username", username);
+        entity.add("password", password);
+
+        // get the resource
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.TEXT_PLAIN).type(MediaType.APPLICATION_FORM_URLENCODED)).entity(entity);
+
+        // perform the request
+        return resourceBuilder.post(ClientResponse.class);
+    }
+
+    /**
+     * Attempts to create a token with the specified username and password.
+     *
+     * @param url the url
+     * @param justification justification
+     * @param headers http headers
+     * @return response
+     * @throws Exception ex
+     */
+    public ClientResponse testRegisterUser(String url, String justification, Map<String, String> headers) throws Exception {
+        // convert the form data
+        MultivaluedMapImpl entity = new MultivaluedMapImpl();
+        entity.add("justification", justification);
+
+        // get the resource
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.TEXT_PLAIN).type(MediaType.APPLICATION_FORM_URLENCODED)).entity(entity);
+
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                resourceBuilder = resourceBuilder.header(key, headers.get(key));
+            }
+        }
+
+        // perform the request
+        return resourceBuilder.post(ClientResponse.class);
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider
new file mode 100644
index 0000000..4b42e4f
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider
@@ -0,0 +1,15 @@
+# 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.
+org.apache.nifi.integration.util.NiFiTestLoginIdentityProvider
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/controller-services.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/controller-services.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/controller-services.xml
deleted file mode 100644
index f5bd96a..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/controller-services.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
-  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.
--->
-<services>
-
-</services>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/login-identity-providers.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/login-identity-providers.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/login-identity-providers.xml
new file mode 100644
index 0000000..04120c9
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/login-identity-providers.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+  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.
+-->
+<!--
+    This file lists all authority providers to use when running securely.
+-->
+<loginIdentityProviders>
+    <provider>
+        <identifier>test-provider</identifier>
+        <class>org.apache.nifi.integration.util.NiFiTestLoginIdentityProvider</class>
+    </provider>
+</loginIdentityProviders>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties
index 0aa5a14..10db651 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties
@@ -14,16 +14,15 @@
 # limitations under the License.
 
 # Core Properties #
-nifi.version=nifi 0.2.1-SNAPSHOT 
+nifi.version=nifi version 
 nifi.flow.configuration.file=
 nifi.flow.configuration.archive.dir=target/archive
 nifi.flowcontroller.autoResumeState=true
 nifi.flowcontroller.graceful.shutdown.period=10 sec
 nifi.flowservice.writedelay.interval=2 sec
 
-nifi.reporting.task.configuration.file=target/test-classes/access-control/reporting-tasks.xml
-nifi.controller.service.configuration.file=target/test-classes/access-control/controller-services.xml
 nifi.authority.provider.configuration.file=target/test-classes/access-control/authority-providers.xml
+nifi.login.identity.provider.configuration.file=target/test-classes/access-control/login-identity-providers.xml
 nifi.templates.directory=target/test-classes/access-control/templates
 nifi.ui.banner.text=TEST BANNER
 nifi.ui.autorefresh.interval=30 sec
@@ -93,10 +92,11 @@ nifi.security.truststoreType=JKS
 nifi.security.truststorePasswd=localtest
 nifi.security.needClientAuth=true
 nifi.security.user.authority.provider=test-provider
+nifi.security.user.login.identity.provider=test-provider
 nifi.security.authorizedUsers.file=target/test-classes/access-control/users.xml
 nifi.security.user.credential.cache.duration=1 hr
 nifi.security.support.new.account.requests=
-nifi.security.default.user.roles=
+nifi.security.anonymous.authorities=
 
 # cluster common properties (cluster manager and nodes must have same values) #
 nifi.cluster.protocol.heartbeat.interval=5 sec

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/reporting-tasks.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/reporting-tasks.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/reporting-tasks.xml
deleted file mode 100644
index 251735e..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/reporting-tasks.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<!--
-  Licensed to the Apache Software Foundation (ASF) under one or more
-  contributor license agreements.  See the NOTICE file distributed with
-  this work for additional information regarding copyright ownership.
-  The ASF licenses this file to You under the Apache License, Version 2.0
-  (the "License"); you may not use this file except in compliance with
-  the License.  You may obtain a copy of the License at
-      http://www.apache.org/licenses/LICENSE-2.0
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-<tasks>
-</tasks>

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/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 daeef5c..ee18bff 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
@@ -22,6 +22,40 @@
     </parent>
     <groupId>org.apache.nifi</groupId>
     <artifactId>nifi-web-security</artifactId>
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+            </resource>
+            <resource>
+                <directory>src/main/xsd</directory>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>jaxb2-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>current</id>
+                        <goals>
+                            <goal>xjc</goal>
+                        </goals>
+                        <configuration>
+                            <packageName>org.apache.nifi.authentication.generated</packageName>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+                <configuration>
+                    <excludes>**/authentication/generated/*.java,</excludes>
+                </configuration>
+            </plugin>            
+        </plugins>
+    </build>
     <dependencies>
         <dependency>
             <groupId>org.apache.nifi</groupId>
@@ -29,6 +63,10 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-nar-utils</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-api</artifactId>
         </dependency>
         <dependency>
@@ -40,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/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/DnUtils.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/DnUtils.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/DnUtils.java
deleted file mode 100644
index f3bd11e..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/DnUtils.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web.security;
-
-import java.security.cert.X509Certificate;
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import javax.servlet.http.HttpServletRequest;
-import org.apache.nifi.web.security.x509.SubjectDnX509PrincipalExtractor;
-import org.apache.nifi.web.security.x509.X509CertificateExtractor;
-import org.apache.commons.lang3.StringUtils;
-
-/**
- *
- */
-public class DnUtils {
-
-    private static final Pattern proxyChainPattern = Pattern.compile("<(.*?)>");
-
-    /**
-     * @param request http request
-     * @return the X-ProxiedEntitiesChain from the specified request
-     */
-    public static String getXProxiedEntitiesChain(final HttpServletRequest request) {
-        String xProxiedEntitiesChain = request.getHeader("X-ProxiedEntitiesChain");
-        final X509Certificate cert = new X509CertificateExtractor().extractClientCertificate(request);
-        if (cert != null) {
-            final SubjectDnX509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
-            final String extractedPrincipal = principalExtractor.extractPrincipal(cert).toString();
-            final String formattedPrincipal = formatProxyDn(extractedPrincipal);
-            if (StringUtils.isBlank(xProxiedEntitiesChain)) {
-                xProxiedEntitiesChain = formattedPrincipal;
-            } else {
-                xProxiedEntitiesChain += formattedPrincipal;
-            }
-        }
-
-        return xProxiedEntitiesChain;
-    }
-
-    /**
-     * Formats the specified DN to be set as a HTTP header using well known
-     * conventions.
-     *
-     * @param dn raw dn
-     * @return the dn formatted as an HTTP header
-     */
-    public static String formatProxyDn(String dn) {
-        return "<" + dn + ">";
-    }
-
-    /**
-     * Tokenizes the specified proxy chain.
-     *
-     * @param rawProxyChain raw chain
-     * @return tokenized proxy chain
-     */
-    public static Deque<String> tokenizeProxyChain(String rawProxyChain) {
-        final Deque<String> dnList = new ArrayDeque<>();
-
-        // parse the proxy chain
-        final Matcher rawProxyChainMatcher = proxyChainPattern.matcher(rawProxyChain);
-        while (rawProxyChainMatcher.find()) {
-            dnList.push(rawProxyChainMatcher.group(1));
-        }
-
-        return dnList;
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/InvalidAuthenticationException.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/InvalidAuthenticationException.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/InvalidAuthenticationException.java
new file mode 100644
index 0000000..1065152
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/InvalidAuthenticationException.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ * Thrown if the authentication of a given request is invalid. For instance,
+ * an expired certificate or token.
+ */
+public class InvalidAuthenticationException extends AuthenticationException {
+
+    public InvalidAuthenticationException(String msg) {
+        super(msg);
+    }
+
+    public InvalidAuthenticationException(String msg, Throwable t) {
+        super(msg, t);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationEntryPoint.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/NiFiAuthenticationEntryPoint.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationEntryPoint.java
new file mode 100644
index 0000000..ef1dfb2
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationEntryPoint.java
@@ -0,0 +1,71 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+
+/**
+ * This is our own implementation of org.springframework.security.web.AuthenticationEntryPoint that allows us to send the response to the client exactly how we want to and log the results.
+ */
+public class NiFiAuthenticationEntryPoint implements AuthenticationEntryPoint {
+
+    private static final Logger logger = LoggerFactory.getLogger(NiFiAuthenticationEntryPoint.class);
+
+    private final NiFiProperties properties;
+
+    public NiFiAuthenticationEntryPoint(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    /**
+     * Always returns a 403 error code to the client.
+     *
+     * @param request request
+     * @param response response
+     * @param ae ae
+     * @throws java.io.IOException ex
+     * @throws javax.servlet.ServletException ex
+     */
+    @Override
+    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ae) throws IOException, ServletException {
+        // if the content type is not set, mark as access denied
+        if (StringUtils.isBlank(response.getContentType())) {
+            // write the response message
+            PrintWriter out = response.getWriter();
+            response.setContentType("text/plain");
+
+            // return authorized if the request is secure and this nifi supports new account requests
+            if (request.isSecure() && properties.getSupportNewAccountRequests()) {
+                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+                out.println("Not authorized.");
+            } else {
+                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+                out.println("Access is denied.");
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.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/NiFiAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
new file mode 100644
index 0000000..f09d610
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
@@ -0,0 +1,209 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.user.NiFiUser;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.AccountStatusException;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+/**
+ *
+ */
+public abstract class NiFiAuthenticationFilter implements Filter {
+
+    private static final Logger logger = LoggerFactory.getLogger(NiFiAuthenticationFilter.class);
+
+    private AuthenticationManager authenticationManager;
+    private NiFiProperties properties;
+
+    @Override
+    public void init(final FilterConfig filterConfig) throws ServletException {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
+        if (logger.isDebugEnabled()) {
+            logger.debug("Checking secure context token: " + SecurityContextHolder.getContext().getAuthentication());
+        }
+
+        if (requiresAuthentication((HttpServletRequest) request)) {
+            authenticate((HttpServletRequest) request, (HttpServletResponse) response);
+        }
+
+        chain.doFilter(request, response);
+    }
+
+    private boolean requiresAuthentication(final HttpServletRequest request) {
+        // continue attempting authorization if the user is anonymous
+        if (isAnonymousUser()) {
+            return true;
+        }
+
+        // or there is no user yet
+        return NiFiUserUtils.getNiFiUser() == null && NiFiUserUtils.getNewAccountRequest() == null;
+    }
+
+    private boolean isAnonymousUser() {
+        final NiFiUser user = NiFiUserUtils.getNiFiUser();
+        return user != null && NiFiUser.ANONYMOUS_USER_IDENTITY.equals(user.getIdentity());
+    }
+
+    private void authenticate(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
+        try {
+            final NiFiAuthenticationRequestToken authenticated = attemptAuthentication(request, response);
+            if (authenticated != null) {
+                // log the request attempt - response details will be logged later
+                logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)",
+                        ProxiedEntitiesUtils.formatProxyDn(StringUtils.join(authenticated.getChain(), "><")), request.getMethod(),
+                        request.getRequestURL().toString(), request.getRemoteAddr()));
+
+                final Authentication authorized = authenticationManager.authenticate(authenticated);
+                successfulAuthorization(request, response, authorized);
+            }
+        } catch (final AuthenticationException ae) {
+            if (!isAnonymousUser()) {
+                unsuccessfulAuthorization(request, response, ae);
+            }
+        }
+    }
+
+    public abstract NiFiAuthenticationRequestToken attemptAuthentication(HttpServletRequest request, HttpServletResponse response);
+
+    protected void successfulAuthorization(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("Authentication success: " + authResult);
+        }
+
+        SecurityContextHolder.getContext().setAuthentication(authResult);
+        ProxiedEntitiesUtils.successfulAuthorization(request, response, authResult);
+    }
+
+    protected void unsuccessfulAuthorization(HttpServletRequest request, HttpServletResponse response, AuthenticationException ae) throws IOException {
+        // populate the response
+        ProxiedEntitiesUtils.unsuccessfulAuthorization(request, response, ae);
+
+        // set the response status
+        response.setContentType("text/plain");
+
+        // write the response message
+        PrintWriter out = response.getWriter();
+
+        // use the type of authentication exception to determine the response code
+        if (ae instanceof UsernameNotFoundException) {
+            if (properties.getSupportNewAccountRequests()) {
+                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+                out.println("Not authorized.");
+            } else {
+                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+                out.println("Access is denied.");
+            }
+        } else if (ae instanceof InvalidAuthenticationException) {
+            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+            out.println(ae.getMessage());
+        } else if (ae instanceof AccountStatusException) {
+            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+            out.println(ae.getMessage());
+        } else if (ae instanceof UntrustedProxyException) {
+            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+            out.println(ae.getMessage());
+        } else if (ae instanceof AuthenticationServiceException) {
+            logger.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            out.println(String.format("Unable to authorize: %s", ae.getMessage()));
+        } else {
+            logger.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
+            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+            out.println("Access is denied.");
+        }
+
+        // log the failure
+        logger.info(String.format("Rejecting access to web api: %s", ae.getMessage()));
+
+        // optionally log the stack trace
+        if (logger.isDebugEnabled()) {
+            logger.debug(StringUtils.EMPTY, ae);
+        }
+    }
+
+    /**
+     * Determines if the specified request is attempting to register a new user account.
+     *
+     * @param request http request
+     * @return true if new user
+     */
+    protected final boolean isNewAccountRequest(HttpServletRequest request) {
+        if ("POST".equalsIgnoreCase(request.getMethod())) {
+            String path = request.getPathInfo();
+            if (StringUtils.isNotBlank(path)) {
+                if ("/controller/users".equals(path)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Extracts the justification from the specified request.
+     *
+     * @param request The request
+     * @return The justification
+     */
+    protected final String getJustification(HttpServletRequest request) {
+        // get the justification
+        String justification = request.getParameter("justification");
+        if (justification == null) {
+            justification = StringUtils.EMPTY;
+        }
+        return justification;
+    }
+
+    @Override
+    public void destroy() {
+    }
+
+    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
+        this.authenticationManager = authenticationManager;
+    }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.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/NiFiAuthenticationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
new file mode 100644
index 0000000..eb0684b
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
@@ -0,0 +1,73 @@
+/*
+ * 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;
+
+import org.apache.nifi.web.security.token.NewAccountAuthenticationRequestToken;
+import org.apache.nifi.web.security.token.NewAccountAuthenticationToken;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthorizationToken;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+/**
+ *
+ */
+public class NiFiAuthenticationProvider implements AuthenticationProvider {
+
+    private final AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService;
+
+    public NiFiAuthenticationProvider(final AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) {
+        this.userDetailsService = userDetailsService;
+    }
+
+    @Override
+    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+        final NiFiAuthenticationRequestToken request = (NiFiAuthenticationRequestToken) authentication;
+
+        try {
+            // defer to the nifi user details service to authorize the user
+            final UserDetails userDetails = userDetailsService.loadUserDetails(request);
+
+            // build an authentication for accesing nifi
+            final NiFiAuthorizationToken result = new NiFiAuthorizationToken(userDetails);
+            result.setDetails(request.getDetails());
+            return result;
+        } catch (final UsernameNotFoundException unfe) {
+            // if the authentication request is for a new account and it could not be authorized because the user was not found,
+            // return the token so the new account could be created. this must go here toe nsure that any proxies have been authorized
+            if (isNewAccountAuthenticationToken(request)) {
+                return new NewAccountAuthenticationToken(((NewAccountAuthenticationRequestToken) authentication).getNewAccountRequest());
+            } else {
+                throw unfe;
+            }
+        }
+    }
+
+    private boolean isNewAccountAuthenticationToken(final Authentication authentication) {
+        return NewAccountAuthenticationRequestToken.class.isAssignableFrom(authentication.getClass());
+    }
+
+    @Override
+    public boolean supports(Class<?> authentication) {
+        return NiFiAuthenticationRequestToken.class.isAssignableFrom(authentication);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/ProxiedEntitiesUtils.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/ProxiedEntitiesUtils.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/ProxiedEntitiesUtils.java
new file mode 100644
index 0000000..1b2f28a
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/ProxiedEntitiesUtils.java
@@ -0,0 +1,147 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.user.NiFiUser;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ *
+ */
+public class ProxiedEntitiesUtils {
+
+    public static final String PROXY_ENTITIES_CHAIN = "X-ProxiedEntitiesChain";
+    public static final String PROXY_ENTITIES_ACCEPTED = "X-ProxiedEntitiesAccepted";
+    public static final String PROXY_ENTITIES_DETAILS = "X-ProxiedEntitiesDetails";
+
+    private static final Pattern proxyChainPattern = Pattern.compile("<(.*?)>");
+
+    /**
+     * Formats the specified DN to be set as a HTTP header using well known conventions.
+     *
+     * @param dn raw dn
+     * @return the dn formatted as an HTTP header
+     */
+    public static String formatProxyDn(String dn) {
+        return "<" + dn + ">";
+    }
+
+    /**
+     * Tokenizes the specified proxy chain.
+     *
+     * @param rawProxyChain raw chain
+     * @return tokenized proxy chain
+     */
+    public static List<String> tokenizeProxiedEntitiesChain(String rawProxyChain) {
+        final List<String> proxyChain = new ArrayList<>();
+        final Matcher rawProxyChainMatcher = proxyChainPattern.matcher(rawProxyChain);
+        while (rawProxyChainMatcher.find()) {
+            proxyChain.add(rawProxyChainMatcher.group(1));
+        }
+
+        return proxyChain;
+    }
+
+    /**
+     * Builds the proxy chain for the specified user.
+     *
+     * @param user The current user
+     * @return The proxy chain for that user in String form
+     */
+    public static String buildProxiedEntitiesChainString(final NiFiUser user) {
+        // calculate the dn chain
+        final List<String> proxyChain = buildProxiedEntitiesChain(user);
+        return formatProxyDn(StringUtils.join(proxyChain, "><"));
+    }
+
+    /**
+     * Builds the proxy chain for the specified user.
+     *
+     * @param user The current user
+     * @return The proxy chain for that user in List form
+     */
+    public static List<String> buildProxiedEntitiesChain(final NiFiUser user) {
+        // calculate the dn chain
+        final List<String> proxyChain = new ArrayList<>();
+
+        // build the dn chain
+        NiFiUser chainedUser = user;
+        do {
+            // add the entry for this user
+            proxyChain.add(chainedUser.getIdentity());
+
+            // go to the next user in the chain
+            chainedUser = chainedUser.getChain();
+        } while (chainedUser != null);
+
+        return proxyChain;
+    }
+
+    /**
+     * Builds the proxy chain from the specified request and user.
+     *
+     * @param request the request
+     * @param username the username
+     * @return the proxy chain in list form
+     */
+    public static List<String> buildProxiedEntitiesChain(final HttpServletRequest request, final String username) {
+        final String chain = buildProxiedEntitiesChainString(request, username);
+        return tokenizeProxiedEntitiesChain(chain);
+    }
+
+    /**
+     * Builds the dn chain from the specified request and user.
+     *
+     * @param request the request
+     * @param username the username
+     * @return the dn chain in string form
+     */
+    public static String buildProxiedEntitiesChainString(final HttpServletRequest request, final String username) {
+        String principal;
+        if (username.startsWith("<") && username.endsWith(">")) {
+            principal = username;
+        } else {
+            principal = formatProxyDn(username);
+        }
+
+        // look for a proxied user
+        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
+            principal = request.getHeader(PROXY_ENTITIES_CHAIN) + principal;
+        }
+        return principal;
+    }
+
+    public static void successfulAuthorization(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
+        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
+            response.setHeader(PROXY_ENTITIES_ACCEPTED, Boolean.TRUE.toString());
+        }
+    }
+
+    public static void unsuccessfulAuthorization(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
+        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
+            response.setHeader(PROXY_ENTITIES_DETAILS, failed.getMessage());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.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/anonymous/NiFiAnonymousUserFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java
index 295f09c..e67ed2c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java
@@ -16,25 +16,22 @@
  */
 package org.apache.nifi.web.security.anonymous;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.EnumSet;
 import javax.servlet.http.HttpServletRequest;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.admin.service.AdministrationException;
 import org.apache.nifi.admin.service.UserService;
+import org.apache.nifi.authorization.Authority;
 import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.web.security.user.NiFiUserDetails;
-import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.token.NiFiAuthorizationToken;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.security.authentication.AnonymousAuthenticationToken;
 import org.springframework.security.core.Authentication;
-import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
 
 /**
- * Custom AnonymouseAuthenticationFilter used to grant additional authorities
- * depending on the current operating mode.
+ * Custom AnonymouseAuthenticationFilter used to grant additional authorities depending on the current operating mode.
  */
 public class NiFiAnonymousUserFilter extends AnonymousAuthenticationFilter {
 
@@ -42,7 +39,6 @@ public class NiFiAnonymousUserFilter extends AnonymousAuthenticationFilter {
 
     private static final String ANONYMOUS_KEY = "anonymousNifiKey";
 
-    private NiFiProperties properties;
     private UserService userService;
 
     public NiFiAnonymousUserFilter() {
@@ -51,51 +47,37 @@ public class NiFiAnonymousUserFilter extends AnonymousAuthenticationFilter {
 
     @Override
     protected Authentication createAuthentication(HttpServletRequest request) {
-        Authentication authentication;
+        Authentication authentication = null;
+
         try {
             // load the anonymous user from the database
-            NiFiUser user = userService.getUserByDn(NiFiUser.ANONYMOUS_USER_DN);
-            NiFiUserDetails userDetails = new NiFiUserDetails(user);
+            NiFiUser user = userService.getUserByDn(NiFiUser.ANONYMOUS_USER_IDENTITY);
+
+            // if this is an unsecure request allow full access
+            if (!request.isSecure()) {
+                user.getAuthorities().addAll(EnumSet.allOf(Authority.class));
+            }
+
+            // only create an authentication token if the anonymous user has some authorities
+            if (!user.getAuthorities().isEmpty()) {
+                NiFiUserDetails userDetails = new NiFiUserDetails(user);
 
-            // get the granted authorities
-            List<GrantedAuthority> authorities = new ArrayList<>(userDetails.getAuthorities());
-            authentication = new AnonymousAuthenticationToken(ANONYMOUS_KEY, userDetails, authorities);
+                // get the granted authorities
+                authentication = new NiFiAuthorizationToken(userDetails);
+            }
         } catch (AdministrationException ase) {
             // record the issue
             anonymousUserFilterLogger.warn("Unable to load anonymous user from accounts database: " + ase.getMessage());
             if (anonymousUserFilterLogger.isDebugEnabled()) {
                 anonymousUserFilterLogger.warn(StringUtils.EMPTY, ase);
             }
-
-            // defer to the base implementation
-            authentication = super.createAuthentication(request);
         }
         return authentication;
     }
 
-    /**
-     * Only supports anonymous users for non-secure requests or one way ssl.
-     *
-     * @param request request
-     * @return true if allowed
-     */
-    @Override
-    protected boolean applyAnonymousForThisRequest(HttpServletRequest request) {
-        // anonymous for non secure requests
-        if ("http".equalsIgnoreCase(request.getScheme())) {
-            return true;
-        }
-
-        return !properties.getNeedClientAuth();
-    }
-
     /* setters */
     public void setUserService(UserService userService) {
         this.userService = userService;
     }
 
-    public void setProperties(NiFiProperties properties) {
-        this.properties = properties;
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authentication/NiFiAuthenticationEntryPoint.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/authentication/NiFiAuthenticationEntryPoint.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authentication/NiFiAuthenticationEntryPoint.java
deleted file mode 100644
index cd5f1ac..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authentication/NiFiAuthenticationEntryPoint.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web.security.authentication;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.web.AuthenticationEntryPoint;
-import org.springframework.security.web.WebAttributes;
-
-/**
- * This is our own implementation of
- * org.springframework.security.web.AuthenticationEntryPoint that allows us to
- * send the response to the client exactly how we want to and log the results.
- */
-public class NiFiAuthenticationEntryPoint implements AuthenticationEntryPoint {
-
-    private static final Logger logger = LoggerFactory.getLogger(NiFiAuthenticationEntryPoint.class);
-
-    /**
-     * Always returns a 403 error code to the client.
-     *
-     * @param request request
-     * @param response response
-     * @param ae ae
-     * @throws java.io.IOException ex
-     * @throws javax.servlet.ServletException ex
-     */
-    @Override
-    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ae) throws IOException, ServletException {
-        // get the last exception - the exception that is being passed in is a generic no credentials found
-        // exception because the authentication could not be found in the security context. the actual cause
-        // of the problem is stored in the session as the authentication_exception
-        Object authenticationException = request.getSession().getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
-
-        // log request result
-        if (authenticationException instanceof AuthenticationException) {
-            ae = (AuthenticationException) authenticationException;
-            logger.info(String.format("Rejecting access to web api: %s", ae.getMessage()));
-        }
-
-        // set the response status
-        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-        response.setContentType("text/plain");
-
-        // write the response message
-        PrintWriter out = response.getWriter();
-        out.println("Access is denied.");
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.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/authorization/NiFiAuthorizationService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
index 95b4669..23d9e61 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
@@ -16,34 +16,35 @@
  */
 package org.apache.nifi.web.security.authorization;
 
-import java.util.Deque;
-import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+import org.apache.commons.lang3.StringUtils;
 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.authorization.Authority;
-import org.apache.nifi.web.security.DnUtils;
 import org.apache.nifi.user.NiFiUser;
-import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.security.UntrustedProxyException;
 import org.apache.nifi.web.security.user.NiFiUserDetails;
-import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.dao.DataAccessException;
 import org.springframework.security.authentication.AccountStatusException;
 import org.springframework.security.authentication.AuthenticationServiceException;
 import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
 import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 
 /**
  * UserDetailsService that will verify user identity and grant user authorities.
  */
-public class NiFiAuthorizationService implements UserDetailsService {
+public class NiFiAuthorizationService implements AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> {
 
     private static final Logger logger = LoggerFactory.getLogger(NiFiAuthorizationService.class);
 
@@ -53,35 +54,33 @@ public class NiFiAuthorizationService implements UserDetailsService {
     /**
      * Loads the user details for the specified dn.
      *
-     * Synchronizing because we want each request to be authorized atomically
-     * since each may contain any number of DNs. We wanted an access decision
-     * made for each individual request as a whole (without other request
-     * potentially impacting it).
+     * Synchronizing because we want each request to be authorized atomically since each may contain any number of DNs. We wanted an access decision made for each individual request as a whole
+     * (without other request potentially impacting it).
      *
-     * @param rawProxyChain proxy chain
+     * @param request request
      * @return user details
      * @throws UsernameNotFoundException ex
      * @throws org.springframework.dao.DataAccessException ex
      */
     @Override
-    public synchronized UserDetails loadUserByUsername(String rawProxyChain) throws UsernameNotFoundException, DataAccessException {
+    public synchronized UserDetails loadUserDetails(NiFiAuthenticationRequestToken request) throws UsernameNotFoundException, DataAccessException {
         NiFiUserDetails userDetails = null;
-        final Deque<String> dnList = DnUtils.tokenizeProxyChain(rawProxyChain);
+        final List<String> chain = new ArrayList<>(request.getChain());
 
         // ensure valid input
-        if (dnList.size() == 0) {
-            logger.warn("Malformed proxy chain: " + rawProxyChain);
+        if (chain.isEmpty()) {
+            logger.warn("Malformed proxy chain: " + StringUtils.join(request.getChain()));
             throw new UntrustedProxyException("Malformed proxy chain.");
         }
 
         NiFiUser proxy = null;
 
         // process each part of the proxy chain
-        for (final Iterator<String> dnIter = dnList.iterator(); dnIter.hasNext();) {
-            final String dn = dnIter.next();
+        for (final ListIterator<String> chainIter = request.getChain().listIterator(chain.size()); chainIter.hasPrevious();) {
+            final String dn = chainIter.previous();
 
             // if there is another dn after this one, this dn is a proxy for the request
-            if (dnIter.hasNext()) {
+            if (chainIter.hasPrevious()) {
                 try {
                     // get the user details for the proxy
                     final NiFiUserDetails proxyDetails = getNiFiUserDetails(dn);
@@ -108,9 +107,6 @@ public class NiFiAuthorizationService implements UserDetailsService {
 
                             // attempt to create a new user account for the proxying client
                             userService.createPendingUserAccount(dn, "Automatic account request generated for unknown proxy.");
-
-                            // propagate the exception to return the appropriate response
-                            throw new UsernameNotFoundException(String.format("An account request was generated for the proxy '%s'.", dn));
                         } catch (AdministrationException ae) {
                             throw new AuthenticationServiceException(String.format("Unable to create an account request for '%s': %s", dn, ae.getMessage()), ae);
                         } catch (IllegalArgumentException iae) {
@@ -121,10 +117,10 @@ public class NiFiAuthorizationService implements UserDetailsService {
                             throw new AccountStatusException(message) {
                             };
                         }
-                    } else {
-                        logger.warn(String.format("Untrusted proxy '%s' must be authorized with '%s' authority: %s", dn, Authority.ROLE_PROXY.toString(), unfe.getMessage()));
-                        throw new UntrustedProxyException(String.format("Untrusted proxy '%s' must be authorized with '%s'.", dn, Authority.ROLE_PROXY.toString()));
                     }
+
+                    logger.warn(String.format("Untrusted proxy '%s' must be authorized with '%s' authority: %s", dn, Authority.ROLE_PROXY.toString(), unfe.getMessage()));
+                    throw new UntrustedProxyException(String.format("Untrusted proxy '%s' must be authorized with '%s'.", dn, Authority.ROLE_PROXY.toString()));
                 } catch (AuthenticationException ae) {
                     logger.warn(String.format("Untrusted proxy '%s' must be authorized with '%s' authority: %s", dn, Authority.ROLE_PROXY.toString(), ae.getMessage()));
                     throw new UntrustedProxyException(String.format("Untrusted proxy '%s' must be authorized with '%s'.", dn, Authority.ROLE_PROXY.toString()));

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NodeAuthorizedUserFilter.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/authorization/NodeAuthorizedUserFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NodeAuthorizedUserFilter.java
deleted file mode 100644
index 80feed7..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NodeAuthorizedUserFilter.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web.security.authorization;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.security.cert.X509Certificate;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import org.apache.nifi.controller.FlowController;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.web.security.user.NiFiUserDetails;
-import org.apache.nifi.web.security.x509.SubjectDnX509PrincipalExtractor;
-import org.apache.nifi.web.security.x509.X509CertificateExtractor;
-import org.apache.nifi.user.NiFiUser;
-import org.apache.nifi.util.NiFiProperties;
-import org.apache.nifi.web.util.WebUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.context.ApplicationContext;
-import org.springframework.security.authentication.AuthenticationDetailsSource;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
-import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
-import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
-import org.springframework.web.context.support.WebApplicationContextUtils;
-import org.springframework.web.filter.GenericFilterBean;
-
-/**
- * Custom filter to extract a user's authorities from the request where the user
- * was authenticated by the cluster manager and populate the threadlocal with
- * the authorized user. If the request contains the appropriate header with
- * authorities and the application instance is a node connected to the cluster,
- * then the authentication/authorization steps remaining in the filter chain are
- * skipped.
- *
- * Checking if the application instance is a connected node is important because
- * it prevents external clients from faking the request headers and bypassing
- * the authentication processing chain.
- */
-public class NodeAuthorizedUserFilter extends GenericFilterBean {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(NodeAuthorizedUserFilter.class);
-
-    public static final String PROXY_USER_DETAILS = "X-ProxiedEntityUserDetails";
-
-    private NiFiProperties properties;
-    private final AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
-    private final X509CertificateExtractor certificateExtractor = new X509CertificateExtractor();
-    private final X509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
-
-    @Override
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-        final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
-
-        // get the proxied user's authorities
-        final String hexEncodedUserDetails = httpServletRequest.getHeader(PROXY_USER_DETAILS);
-
-        // check if the request has the necessary header information and this instance is configured as a node
-        if (StringUtils.isNotBlank(hexEncodedUserDetails) && properties.isNode()) {
-
-            // get the flow controller from the Spring context
-            final ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
-            final FlowController flowController = ctx.getBean("flowController", FlowController.class);
-
-            // check that we are connected to the cluster
-            if (flowController.getNodeId() != null) {
-                try {
-                    // get the DN from the cert in the request
-                    final X509Certificate certificate = certificateExtractor.extractClientCertificate((HttpServletRequest) request);
-                    if (certificate != null) {
-                        // extract the principal from the certificate
-                        final Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
-                        final String dn = certificatePrincipal.toString();
-
-                        // only consider the pre-authorized user when the request came from the NCM according to the DN in the certificate
-                        final String clusterManagerDN = flowController.getClusterManagerDN();
-                        if (clusterManagerDN != null && clusterManagerDN.equals(dn)) {
-                            // deserialize hex encoded object
-                            final Serializable userDetailsObj = WebUtils.deserializeHexToObject(hexEncodedUserDetails);
-
-                            // if we have a valid object, set the authentication token and bypass the remaining authentication processing chain
-                            if (userDetailsObj instanceof NiFiUserDetails) {
-                                final NiFiUserDetails userDetails = (NiFiUserDetails) userDetailsObj;
-                                final NiFiUser user = userDetails.getNiFiUser();
-
-                                // log the request attempt - response details will be logged later
-                                logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)", user.getDn(), httpServletRequest.getMethod(),
-                                        httpServletRequest.getRequestURL().toString(), request.getRemoteAddr()));
-
-                                // we do not create the authentication token with the X509 certificate because the certificate is from the sending system, not the proxied user
-                                final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(userDetails, null, userDetails.getAuthorities());
-                                token.setDetails(authenticationDetailsSource.buildDetails(request));
-                                SecurityContextHolder.getContext().setAuthentication(token);
-                            }
-                        }
-                    }
-                } catch (final ClassNotFoundException cnfe) {
-                    LOGGER.warn("Classpath issue detected because failed to deserialize authorized user in request header due to: " + cnfe, cnfe);
-                }
-            }
-        }
-
-        chain.doFilter(request, response);
-    }
-
-    /* setters */
-    public void setProperties(NiFiProperties properties) {
-        this.properties = properties;
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.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/JwtAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
new file mode 100644
index 0000000..20675fb
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
@@ -0,0 +1,80 @@
+/*
+ * 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.jwt;
+
+import io.jsonwebtoken.JwtException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.web.security.NiFiAuthenticationFilter;
+import org.apache.nifi.web.security.token.NewAccountAuthenticationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.user.NewAccountRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Arrays;
+import org.apache.nifi.web.security.InvalidAuthenticationException;
+
+/**
+ */
+public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
+
+    private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
+
+    private static final String AUTHORIZATION = "Authorization";
+
+    private JwtService jwtService;
+
+    @Override
+    public NiFiAuthenticationRequestToken attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
+        // only suppport jwt login when running securely
+        if (!request.isSecure()) {
+            return null;
+        }
+
+        // TODO: Refactor request header extraction logic to shared utility as it is duplicated in AccessResource
+
+        // get the principal out of the user token
+        final String authorization = request.getHeader(AUTHORIZATION);
+
+        // if there is no authorization header, we don't know the user
+        if (authorization == null) {
+            return null;
+        } else {
+            // Extract the Base64 encoded token from the Authorization header
+            final String token = StringUtils.substringAfterLast(authorization, " ");
+
+            try {
+                final String jwtPrincipal = jwtService.getAuthenticationFromToken(token);
+
+                if (isNewAccountRequest(request)) {
+                    return new NewAccountAuthenticationRequestToken(new NewAccountRequest(Arrays.asList(jwtPrincipal), getJustification(request)));
+                } else {
+                    return new NiFiAuthenticationRequestToken(Arrays.asList(jwtPrincipal));
+                }
+            } catch (JwtException e) {
+                throw new InvalidAuthenticationException(e.getMessage(), e);
+            }
+        }
+    }
+
+    public void setJwtService(JwtService jwtService) {
+        this.jwtService = jwtService;
+    }
+
+}


[51/51] [abbrv] nifi git commit: Merge branch 'NIFI-655'

Posted by mc...@apache.org.
Merge branch 'NIFI-655'


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

Branch: refs/heads/master
Commit: 7726d069cdc98ec1b9d9be32c163ab09973e0073
Parents: e5281f1 e22b51f
Author: Matt Gilman <ma...@gmail.com>
Authored: Tue Dec 1 11:20:56 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Tue Dec 1 11:20:56 2015 -0500

----------------------------------------------------------------------
 LICENSE                                         |   22 +
 .../authentication/AuthenticationResponse.java  |   65 +
 .../nifi/authentication/LoginCredentials.java   |   39 +
 .../authentication/LoginIdentityProvider.java   |   61 +
 ...ginIdentityProviderConfigurationContext.java |   48 +
 ...inIdentityProviderInitializationContext.java |   27 +
 .../LoginIdentityProviderLookup.java            |   25 +
 .../LoginIdentityProviderContext.java           |   35 +
 .../exception/IdentityAccessException.java      |   33 +
 .../InvalidLoginCredentialsException.java       |   33 +
 .../nifi/authorization/AuthorityProvider.java   |   42 +-
 .../nifi/web/NiFiWebConfigurationContext.java   |    2 +-
 .../org/apache/nifi/web/NiFiWebContext.java     |    2 +-
 nifi-assembly/LICENSE                           |   22 +
 nifi-assembly/pom.xml                           |    8 +
 .../org/apache/nifi/util/NiFiProperties.java    |   70 +-
 .../nifi/security/util/CertificateUtils.java    |   51 +-
 .../src/main/asciidoc/administration-guide.adoc |  111 +-
 .../main/asciidoc/images/anonymous-access.png   |  Bin 0 -> 81725 bytes
 nifi-docs/src/main/asciidoc/images/login.png    |  Bin 0 -> 93233 bytes
 .../src/main/asciidoc/images/request-access.png |  Bin 0 -> 94494 bytes
 nifi-docs/src/main/asciidoc/user-guide.adoc     |   26 +
 .../nifi-framework/nifi-administration/pom.xml  |    1 -
 .../nifi/admin/AuditDataSourceFactoryBean.java  |   23 +-
 .../nifi/admin/UserDataSourceFactoryBean.java   |  101 +-
 .../org/apache/nifi/admin/dao/DAOFactory.java   |    2 +
 .../java/org/apache/nifi/admin/dao/KeyDAO.java  |   56 +
 .../nifi/admin/dao/impl/DAOFactoryImpl.java     |    6 +
 .../nifi/admin/dao/impl/StandardActionDAO.java  |   10 +-
 .../nifi/admin/dao/impl/StandardKeyDAO.java     |  179 ++
 .../nifi/admin/dao/impl/StandardUserDAO.java    |   53 +-
 .../apache/nifi/admin/service/UserService.java  |   31 +-
 .../service/action/AbstractUserAction.java      |    4 +-
 .../service/action/AuthorizeUserAction.java     |   24 +-
 .../admin/service/action/DeleteKeysAction.java  |   46 +
 .../admin/service/action/DeleteUserAction.java  |    5 +
 .../admin/service/action/DisableUserAction.java |   11 +-
 .../service/action/DisableUserGroupAction.java  |   13 +-
 .../admin/service/action/GetKeyByIdAction.java  |   42 +
 .../service/action/GetKeyByIdentityAction.java  |   42 +
 .../service/action/GetOrCreateKeyAction.java    |   48 +
 .../action/RequestUserAccountAction.java        |   14 +-
 .../service/action/SeedUserAccountsAction.java  |   18 +-
 .../admin/service/action/UngroupUserAction.java |    6 +-
 .../admin/service/action/UpdateUserAction.java  |   24 +-
 .../service/action/UpdateUserGroupAction.java   |   32 +-
 .../admin/service/impl/StandardUserService.java |   97 +-
 .../src/main/java/org/apache/nifi/key/Key.java  |   69 +
 .../java/org/apache/nifi/user/NiFiUser.java     |   18 +-
 .../resources/nifi-administration-context.xml   |    4 +-
 .../service/action/AuthorizeUserActionTest.java |  100 +-
 .../service/action/CreateUserActionTest.java    |   12 +-
 .../service/action/DisableUserActionTest.java   |   22 +-
 .../action/RequestUserAccountActionTest.java    |   22 +-
 .../action/SeedUserAccountsActionTest.java      |   44 +-
 .../action/SetUserAuthoritiesActionTest.java    |   18 +-
 .../web/api/dto/AccessConfigurationDTO.java     |   61 +
 .../nifi/web/api/dto/AccessStatusDTO.java       |  101 +
 .../api/entity/AccessConfigurationEntity.java   |   43 +
 .../nifi/web/api/entity/AccessStatusEntity.java |   43 +
 .../nifi/web/api/entity/IdentityEntity.java     |   52 +
 .../org/apache/nifi/nar/ExtensionManager.java   |   17 +-
 .../nifi/nar/NarThreadContextClassLoader.java   |    2 +
 .../resources/conf/login-identity-providers.xml |   92 +
 .../src/main/resources/conf/nifi.properties     |    4 +
 .../org/apache/nifi/web/server/JettyServer.java |    8 +-
 .../apache/nifi/audit/ControllerAuditor.java    |    8 +-
 .../nifi/audit/ControllerServiceAuditor.java    |   12 +-
 .../org/apache/nifi/audit/FunnelAuditor.java    |    2 +-
 .../java/org/apache/nifi/audit/PortAuditor.java |    6 +-
 .../apache/nifi/audit/ProcessGroupAuditor.java  |    6 +-
 .../org/apache/nifi/audit/ProcessorAuditor.java |    6 +-
 .../apache/nifi/audit/RelationshipAuditor.java  |    4 +-
 .../nifi/audit/RemoteProcessGroupAuditor.java   |    6 +-
 .../apache/nifi/audit/ReportingTaskAuditor.java |    6 +-
 .../org/apache/nifi/audit/SnippetAuditor.java   |    2 +-
 .../org/apache/nifi/web/NiFiServiceFacade.java  |    7 +
 .../nifi/web/NiFiWebApiConfiguration.java       |   40 +
 .../web/NiFiWebApiSecurityConfiguration.java    |  194 ++
 .../nifi/web/StandardNiFiServiceFacade.java     |   28 +-
 .../StandardNiFiWebConfigurationContext.java    |    8 +-
 .../apache/nifi/web/StandardNiFiWebContext.java |    8 +-
 .../org/apache/nifi/web/api/AccessResource.java |  442 ++++
 .../nifi/web/api/ApplicationResource.java       |   18 +-
 .../apache/nifi/web/api/ControllerResource.java |   65 +-
 .../org/apache/nifi/web/api/UserResource.java   |   49 +-
 .../api/config/AccessDeniedExceptionMapper.java |    2 +-
 .../InvalidAuthenticationExceptionMapper.java   |   44 +
 .../org/apache/nifi/web/api/dto/DtoFactory.java |    2 +-
 .../nifi/web/controller/ControllerFacade.java   |   17 +-
 .../web/dao/impl/StandardConnectionDAO.java     |    2 +-
 .../apache/nifi/web/filter/RequestLogger.java   |    6 +-
 .../src/main/resources/nifi-web-api-context.xml |    9 +
 .../src/main/webapp/WEB-INF/web.xml             |   13 +-
 .../accesscontrol/AccessTokenEndpointTest.java  |  292 +++
 .../util/NiFiTestAuthorizationProvider.java     |    3 +-
 .../util/NiFiTestLoginIdentityProvider.java     |   75 +
 .../nifi/integration/util/NiFiTestServer.java   |    9 +-
 .../nifi/integration/util/NiFiTestUser.java     |  232 ++-
 ...he.nifi.authentication.LoginIdentityProvider |   15 +
 .../access-control/controller-services.xml      |   18 -
 .../access-control/login-identity-providers.xml |   24 +
 .../resources/access-control/nifi.properties    |    8 +-
 .../access-control/reporting-tasks.xml          |   17 -
 .../nifi-web/nifi-web-security/pom.xml          |   43 +
 .../org/apache/nifi/web/security/DnUtils.java   |   85 -
 .../InvalidAuthenticationException.java         |   35 +
 .../web/security/NiFiAuthenticationFilter.java  |  231 +++
 .../security/NiFiAuthenticationProvider.java    |   73 +
 .../nifi/web/security/ProxiedEntitiesUtils.java |  147 ++
 .../anonymous/NiFiAnonymousUserFilter.java      |   56 +-
 .../NiFiAuthenticationEntryPoint.java           |   69 -
 .../authorization/NiFiAuthorizationService.java |   46 +-
 .../authorization/NodeAuthorizedUserFilter.java |  128 --
 .../security/jwt/JwtAuthenticationFilter.java   |   83 +
 .../nifi/web/security/jwt/JwtService.java       |  162 ++
 .../security/node/NodeAuthorizedUserFilter.java |  127 ++
 .../LoginIdentityProviderFactoryBean.java       |  312 +++
 ...ginIdentityProviderConfigurationContext.java |   51 +
 ...inIdentityProviderInitializationContext.java |   45 +
 .../token/LoginAuthenticationToken.java         |  123 ++
 .../NewAccountAuthorizationRequestToken.java    |   40 +
 .../token/NewAccountAuthorizationToken.java     |   46 +
 .../security/token/NiFiAuthorizationToken.java  |   50 +
 .../token/NiFiAuthortizationRequestToken.java   |   54 +
 .../web/security/user/NewAccountRequest.java    |   47 +
 .../nifi/web/security/user/NiFiUserDetails.java |    3 +-
 .../nifi/web/security/user/NiFiUserUtils.java   |   27 +-
 .../security/x509/X509AuthenticationFilter.java |  305 +--
 .../security/x509/X509CertificateExtractor.java |    4 +-
 .../security/x509/X509CertificateValidator.java |   58 +
 .../web/security/x509/X509IdentityProvider.java |   94 +
 .../x509/ocsp/OcspCertificateValidator.java     |   20 +-
 .../resources/nifi-web-security-context.xml     |   83 +-
 .../src/main/xsd/login-identity-providers.xsd   |   49 +
 .../NiFiAuthorizationServiceTest.java           |   64 +-
 .../nifi/web/security/jwt/JwtServiceTest.java   |  445 ++++
 .../src/test/resources/logback-test.xml         |   36 +
 .../nifi-framework/nifi-web/nifi-web-ui/pom.xml |   53 +-
 .../src/main/resources/META-INF/LICENSE         |   22 +
 .../resources/filters/bulletin-board.properties |    1 +
 .../main/resources/filters/canvas.properties    |    3 +-
 .../main/resources/filters/cluster.properties   |    1 +
 .../main/resources/filters/counters.properties  |    1 +
 .../main/resources/filters/history.properties   |    1 +
 .../main/resources/filters/login-min.properties |   18 +
 .../src/main/resources/filters/login.properties |   25 +
 .../resources/filters/provenance.properties     |    1 +
 .../main/resources/filters/summary.properties   |    1 +
 .../main/resources/filters/templates.properties |    1 +
 .../src/main/resources/filters/users.properties |    1 +
 .../webapp/WEB-INF/pages/bulletin-board.jsp     |    1 +
 .../src/main/webapp/WEB-INF/pages/canvas.jsp    |    3 +-
 .../src/main/webapp/WEB-INF/pages/cluster.jsp   |    1 +
 .../src/main/webapp/WEB-INF/pages/counters.jsp  |    1 +
 .../src/main/webapp/WEB-INF/pages/history.jsp   |    1 +
 .../src/main/webapp/WEB-INF/pages/login.jsp     |   62 +
 .../main/webapp/WEB-INF/pages/message-page.jsp  |    4 +-
 .../main/webapp/WEB-INF/pages/provenance.jsp    |    1 +
 .../src/main/webapp/WEB-INF/pages/summary.jsp   |    1 +
 .../src/main/webapp/WEB-INF/pages/templates.jsp |    1 +
 .../src/main/webapp/WEB-INF/pages/users.jsp     |    1 +
 .../WEB-INF/partials/canvas/canvas-header.jsp   |   13 +-
 .../WEB-INF/partials/canvas/registration.jsp    |   44 -
 .../WEB-INF/partials/login/login-form.jsp       |   32 +
 .../WEB-INF/partials/login/login-message.jsp    |   21 +
 .../WEB-INF/partials/login/login-progress.jsp   |   22 +
 .../WEB-INF/partials/login/login-submission.jsp |   20 +
 .../partials/login/nifi-registration-form.jsp   |   38 +
 .../webapp/WEB-INF/partials/message-pane.jsp    |   14 +-
 .../partials/users/user-details-dialog.jsp      |    2 +-
 .../nifi-web-ui/src/main/webapp/WEB-INF/web.xml |   22 +-
 .../nifi-web-ui/src/main/webapp/css/canvas.css  |    1 -
 .../nifi-web-ui/src/main/webapp/css/header.css  |   38 +-
 .../nifi-web-ui/src/main/webapp/css/login.css   |  110 +
 .../nifi-web-ui/src/main/webapp/css/main.css    |   25 +-
 .../src/main/webapp/css/registration.css        |   45 -
 .../src/main/webapp/js/jquery/jquery.base64.js  |  123 ++
 .../js/nf/bulletin-board/nf-bulletin-board.js   |    2 +
 .../webapp/js/nf/canvas/nf-canvas-header.js     |   48 +-
 .../src/main/webapp/js/nf/canvas/nf-canvas.js   |  248 ++-
 .../main/webapp/js/nf/canvas/nf-registration.js |   71 -
 .../src/main/webapp/js/nf/canvas/nf-storage.js  |  139 --
 .../src/main/webapp/js/nf/cluster/nf-cluster.js |    2 +
 .../main/webapp/js/nf/counters/nf-counters.js   |    2 +
 .../src/main/webapp/js/nf/history/nf-history.js |    2 +
 .../src/main/webapp/js/nf/login/nf-login.js     |  309 +++
 .../src/main/webapp/js/nf/nf-common.js          | 1933 ++++++++++--------
 .../src/main/webapp/js/nf/nf-dialog.js          |   23 +-
 .../src/main/webapp/js/nf/nf-storage.js         |  172 ++
 .../webapp/js/nf/provenance/nf-provenance.js    |    2 +
 .../src/main/webapp/js/nf/summary/nf-summary.js |    2 +
 .../main/webapp/js/nf/templates/nf-templates.js |    2 +
 .../src/main/webapp/js/nf/users/nf-users.js     |    2 +
 .../nifi-ldap-iaa-providers-nar/pom.xml         |   32 +
 .../nifi-ldap-iaa-providers/pom.xml             |   60 +
 .../nifi/ldap/LdapAuthenticationStrategy.java   |   27 +
 .../java/org/apache/nifi/ldap/LdapProvider.java |  284 +++
 .../org/apache/nifi/ldap/ReferralStrategy.java  |   39 +
 ...he.nifi.authentication.LoginIdentityProvider |   15 +
 .../nifi-ldap-iaa-providers-bundle/pom.xml      |   38 +
 nifi-nar-bundles/pom.xml                        |    1 +
 pom.xml                                         |   67 +-
 203 files changed, 8952 insertions(+), 2634 deletions(-)
----------------------------------------------------------------------



[37/51] [abbrv] nifi git commit: NIFI-655: - Updating available links during login, registration, and account status review.

Posted by mc...@apache.org.
NIFI-655:
- Updating available links during login, registration, and account status review.

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

Branch: refs/heads/master
Commit: 1312bde498aadbafc706f4de7b955d20c6d2d036
Parents: f2d82ee
Author: Matt Gilman <ma...@gmail.com>
Authored: Tue Nov 24 00:37:47 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Tue Nov 24 00:37:47 2015 -0500

----------------------------------------------------------------------
 .../src/main/webapp/WEB-INF/pages/login.jsp     | 12 ++++-
 .../WEB-INF/partials/canvas/canvas-header.jsp   |  2 +-
 .../webapp/WEB-INF/partials/message-pane.jsp    | 11 ++++-
 .../nifi-web-ui/src/main/webapp/css/header.css  | 19 +------
 .../nifi-web-ui/src/main/webapp/css/login.css   | 15 ++++++
 .../nifi-web-ui/src/main/webapp/css/main.css    | 18 +++++--
 .../src/main/webapp/js/nf/login/nf-login.js     | 52 +++++++++-----------
 .../src/main/webapp/js/nf/nf-common.js          | 15 ++++--
 8 files changed, 87 insertions(+), 57 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/1312bde4/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 978d019..9f876e1 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
@@ -37,8 +37,16 @@
         ${nf.login.script.tags}
     </head>
     <body class="login-body">
-        <div id="user-logout-container" class="hidden">
-            <span id="user-logout" class="link">logout</span>
+        <div id="login-user-links-container">
+            <ul id="login-user-links" class="links">
+                <li id="user-logout-container" style="display: none;">
+                    <span id="user-logout" class="link">logout</span>
+                </li>
+                <li>
+                    <span id="user-home" class="link">home</span>
+                </li>
+            </ul>
+            <div class="clear"></div>
         </div>
         <div id="login-contents-container">
             <jsp:include page="/WEB-INF/partials/login/login-message.jsp"/>

http://git-wip-us.apache.org/repos/asf/nifi/blob/1312bde4/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 2ea7ca6..7a1d22d 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
@@ -43,7 +43,7 @@
         </div>
     </div>
     <div id="header-links-container">
-        <ul>
+        <ul class="links">
             <li id="current-user-container">
                 <div id="anonymous-user-alert" class="hidden"></div>
                 <div id="current-user"></div>

http://git-wip-us.apache.org/repos/asf/nifi/blob/1312bde4/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/message-pane.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/message-pane.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/message-pane.jsp
index db5dece..4fdc9c6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/message-pane.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/message-pane.jsp
@@ -16,8 +16,15 @@
 --%>
 <%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
 <div id="message-pane" class="message-pane hidden">
-    <div id="user-logout-container" class="hidden">
-        <span id="user-logout" class="link">logout</span>
+    <div id="user-links-container">
+        <ul class="links">
+            <li id="user-logout-container" style="display: none;">
+                <span id="user-logout" class="link">logout</span>
+            </li>
+            <li>
+                <span id="user-home" class="link">home</span>
+            </li>
+        </ul>
     </div>
     <div class="message-pane-message-box">
         <div id="message-title" class="message-pane-title"></div>

http://git-wip-us.apache.org/repos/asf/nifi/blob/1312bde4/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 49dd3a0..e0f8347 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
@@ -658,27 +658,10 @@ div.bulletin-board-hover {
 
 #header-links-container {
     position: absolute;
-    top: 1px;
-    right: 0px;
+    right: 10px;
     z-index: 100;
 }
 
-#header-links-container li {
-    float: left;
-    display: block;
-    margin-right: 10px;
-    padding: 4px;
-    text-align: center;
-    font-size: .8em;
-    font-weight: normal;
-    text-decoration: none;
-}
-
-#header-links-container span.header-link-over {
-    color: #264c58;
-    text-decoration: underline;
-}
-
 #has-pending-accounts {
     background-image: url(../images/starburst.png);
     width: 9px;

http://git-wip-us.apache.org/repos/asf/nifi/blob/1312bde4/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 68086a7..b4c8268 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
@@ -48,6 +48,21 @@
     color: #000;
 }
 
+#login-user-links-container {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    padding-top: 100px;
+    padding-left: 100px;
+    font-family: Verdana, Geneva, sans-serif;
+    z-index: 1300;
+    width: 412px;
+}
+
+#login-user-links {
+    float: right;
+}
+
 /*
     Login
 */

http://git-wip-us.apache.org/repos/asf/nifi/blob/1312bde4/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
index deadcd5..3a6e9d1 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
@@ -77,14 +77,26 @@ div.context-menu-provenance {
     background-position: top left;
 }
 
-#user-logout-container {
+#user-links-container {
     position: absolute;
-    left: 478px;
+    left: 550px;
     top: 100px;
     z-index: 1300;
 }
 
-#user-logout {
+ul.links li {
+    float: left;
+    display: block;
+    margin-left: 10px;
+    padding: 4px;
+    text-align: center;
+    font-size: 11px;
+    font-weight: normal;
+    text-decoration: none;
+}
+
+ul.links span.header-link-over {
+    color: #264c58;
     text-decoration: underline;
 }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/1312bde4/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 6cefd4f..92712e6 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
@@ -109,12 +109,25 @@ nf.Login = (function () {
             // check to see if they actually have access now
             $.ajax({
                 type: 'GET',
-                url: config.urls.identity,
+                url: config.urls.accessStatus,
                 dataType: 'json'
             }).done(function (response) {
-                if (response.identity === 'anonymous') {
-                    showLogoutLink();
+                var accessStatus = response.accessStatus;
+                
+                // update the logout link appropriately
+                showLogoutLink();
+                
+                // update according to the access status
+                if (accessStatus.status === 'UNKNOWN' || accessStatus.status === 'NOT_ACTIVE') {
+                    $('#login-message-title').text('Unable to log in');
+                    $('#login-message').text(accessStatus.message);
 
+                    // update visibility
+                    $('#login-container').hide();
+                    $('#login-submission-container').hide();
+                    $('#login-progress-container').hide();
+                    $('#login-message-container').show();
+                } else if (accessStatus.status === 'UNREGISTERED') {
                     // schedule automatic token refresh
                     nf.Common.scheduleTokenRefresh();
             
@@ -128,7 +141,7 @@ nf.Login = (function () {
                     // update the form visibility
                     $('#login-submission-container').show();
                     $('#login-progress-container').hide();
-                } else {
+                } else if (accessStatus.status === 'ACTIVE') {
                     // reload as appropriate - no need to schedule token refresh as the page is reloading
                     if (top !== window) {
                         parent.window.location = '/nifi';
@@ -137,31 +150,14 @@ nf.Login = (function () {
                     }
                 }
             }).fail(function (xhr, status, error) {
-                showLogoutLink();
-
-                // schedule automatic token refresh
-                nf.Common.scheduleTokenRefresh();
-
-                // show the user
-                $('#nifi-user-submit-justification').text(token['preferred_username']);
-
-                if (xhr.status === 401) {
-                    initializeNiFiRegistration();
-                    showNiFiRegistration();
-                    
-                    // update the form visibility
-                    $('#login-submission-container').show();
-                    $('#login-progress-container').hide();
-                } else {
-                    $('#login-message-title').text('Unable to log in');
-                    $('#login-message').text(xhr.responseText);
+                $('#login-message-title').text('Unable to log in');
+                $('#login-message').text(xhr.responseText);
 
-                    // update visibility
-                    $('#login-container').hide();
-                    $('#login-submission-container').hide();
-                    $('#login-progress-container').hide();
-                    $('#login-message-container').show();
-                }
+                // update visibility
+                $('#login-container').hide();
+                $('#login-submission-container').hide();
+                $('#login-progress-container').hide();
+                $('#login-message-container').show();
             });
         }).fail(function (xhr, status, error) {
             nf.Dialog.showOkDialog({

http://git-wip-us.apache.org/repos/asf/nifi/blob/1312bde4/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 321044f..cf83e16 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
@@ -74,7 +74,7 @@ $(document).ready(function () {
     
     // shows the logout link in the message-pane when appropriate and schedule token refresh
     if (nf.Storage.getItem('jwt') !== null) {
-        $('#user-logout-container').show();
+        $('#user-logout-container').css('display', 'block');
         nf.Common.scheduleTokenRefresh();
     }
     
@@ -83,6 +83,15 @@ $(document).ready(function () {
         nf.Storage.removeItem('jwt');
         window.location = '/nifi/login';
     });
+    
+    // handle home
+    $('#user-home').on('click', function () {
+        if (top !== window) {
+            parent.window.location = '/nifi';
+        } else {
+            window.location = '/nifi';
+        }
+    });
 });
 
 // Define a common utility class used across the entire application.
@@ -447,9 +456,9 @@ nf.Common = (function () {
          */
         showLogoutLink: function () {
             if (nf.Storage.getItem('jwt') === null) {
-                $('#user-logout-container').hide();
+                $('#user-logout-container').css('display', 'none');
             } else {
-                $('#user-logout-container').show();
+                $('#user-logout-container').css('display', 'block');
             }
         },
 


[22/51] [abbrv] nifi git commit: NIFI-655: - Ensuring the user identity is not too long.

Posted by mc...@apache.org.
NIFI-655:
- Ensuring the user identity is not too long.

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

Branch: refs/heads/master
Commit: 5ef53b6fe349c1eb2a1f5d5b91435a5745f57571
Parents: ec50a2d
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 23 12:14:01 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 23 12:14:01 2015 -0500

----------------------------------------------------------------------
 .../java/org/apache/nifi/admin/dao/impl/StandardUserDAO.java    | 5 +++++
 1 file changed, 5 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/5ef53b6f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardUserDAO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardUserDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardUserDAO.java
index 8292cac..7648f22 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardUserDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardUserDAO.java
@@ -466,6 +466,11 @@ public class StandardUserDAO implements UserDAO {
         if (user.getIdentity() == null) {
             throw new IllegalArgumentException("User identity must be specified.");
         }
+        
+        // ensure the user identity is not too lengthy
+        if (user.getIdentity().length() > 4096) {
+            throw new IllegalArgumentException("User identity must be less than 4096 characters.");
+        }
 
         PreparedStatement statement = null;
         ResultSet rs = null;


[16/51] [abbrv] nifi git commit: NIFI-655: - Removing unnecessary class.

Posted by mc...@apache.org.
NIFI-655:
- Removing unnecessary class.

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

Branch: refs/heads/master
Commit: 9f60411b1501bb5faf9c2f1812f6e349329dd9b8
Parents: 242949e
Author: Matt Gilman <ma...@gmail.com>
Authored: Wed Nov 18 18:37:12 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Nov 18 18:37:12 2015 -0500

----------------------------------------------------------------------
 .../src/main/webapp/WEB-INF/partials/login/login-progress.jsp      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/9f60411b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-progress.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-progress.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-progress.jsp
index 874a0cb..cefc73c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-progress.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/login-progress.jsp
@@ -16,7 +16,7 @@
 --%>
 <%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
 <div id="login-progress-container" class="login-container hidden">
-    <div id="login-progress-spinner" class="loading-container ajax-loading"></div>
+    <div id="login-progress-spinner" class="loading-container"></div>
     <div id="login-progress-label">Logging in...</div>
     <div class="clear"></div>
 </div>
\ No newline at end of file


[14/51] [abbrv] nifi git commit: NIFI-655: - Fixing typo when loading the ldap connect timeout. - Providing a better experience for session expiration. - Using ellipsis for lengthly user name. - Adding an issuer to the authentication response so the LIP

Posted by mc...@apache.org.
NIFI-655:
- Fixing typo when loading the ldap connect timeout.
- Providing a better experience for session expiration.
- Using ellipsis for lengthly user name.
- Adding an issuer to the authentication response so the LIP can specify the appropriate value.

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

Branch: refs/heads/master
Commit: 3da198135e432341eba36b072dfd0f7f6395b4c6
Parents: 0fa68a5
Author: Matt Gilman <ma...@gmail.com>
Authored: Wed Nov 18 15:44:47 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Nov 18 15:44:47 2015 -0500

----------------------------------------------------------------------
 .../authentication/AuthenticationResponse.java  |  9 +++++++-
 .../org/apache/nifi/web/api/AccessResource.java |  6 ++---
 .../util/NiFiTestLoginIdentityProvider.java     |  2 +-
 .../web/security/x509/X509IdentityProvider.java |  6 +++--
 .../nifi-web-ui/src/main/webapp/css/header.css  |  3 +++
 .../src/main/webapp/js/nf/nf-common.js          | 23 ++++++++++----------
 .../java/org/apache/nifi/ldap/LdapProvider.java |  6 +++--
 7 files changed, 33 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/3da19813/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java
----------------------------------------------------------------------
diff --git a/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java b/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java
index a64947b..e9999fc 100644
--- a/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java
+++ b/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java
@@ -24,6 +24,7 @@ public class AuthenticationResponse {
     private final String identity;
     private final String username;
     private final long expiration;
+    private final String issuer;
 
     /**
      * Creates an authentication response. The username and how long the authentication is valid in milliseconds
@@ -31,11 +32,13 @@ public class AuthenticationResponse {
      * @param identity The user identity
      * @param username The username
      * @param expiration The expiration in milliseconds
+     * @param issuer The issuer of the token
      */
-    public AuthenticationResponse(final String identity, final String username, final long expiration) {
+    public AuthenticationResponse(final String identity, final String username, final long expiration, final String issuer) {
         this.identity = identity;
         this.username = username;
         this.expiration = expiration;
+        this.issuer = issuer;
     }
 
     public String getIdentity() {
@@ -46,6 +49,10 @@ public class AuthenticationResponse {
         return username;
     }
 
+    public String getIssuer() {
+        return issuer;
+    }
+
     /**
      * Returns the expiration of a given authentication in milliseconds.
      *

http://git-wip-us.apache.org/repos/asf/nifi/blob/3da19813/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
index 5e52186..b486d74 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -336,8 +336,7 @@ public class AccessResource extends ApplicationResource {
                 }
 
                 // create the authentication token
-                // TODO: Some Spring beans return "" for getClass().getSimpleName(). Using getName() temporarily, the way that NAR loader works, this value will always be an anonymous inner class
-                loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getIdentity(), expiration, loginIdentityProvider.getClass().getName());
+                loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getIdentity(), expiration, authenticationResponse.getIssuer());
             } catch (final InvalidLoginCredentialsException ilce) {
                 throw new IllegalArgumentException("The supplied username and password are not valid.", ilce);
             } catch (final IdentityAccessException iae) {
@@ -358,8 +357,7 @@ public class AccessResource extends ApplicationResource {
             authorizeProxyIfNecessary(proxyChain);
 
             // create the authentication token
-            // TODO: Some Spring beans return "" for getClass().getSimpleName(). Using getName() temporarilyy, the way that NAR loader works, this value will always be an anonymous inner class
-            loginAuthenticationToken = new LoginAuthenticationToken(proxyChain.get(0), authenticationResponse.getExpiration(), certificateIdentityProvider.getClass().getName());
+            loginAuthenticationToken = new LoginAuthenticationToken(proxyChain.get(0), authenticationResponse.getExpiration(), authenticationResponse.getIssuer());
         }
 
         // generate JWT for response

http://git-wip-us.apache.org/repos/asf/nifi/blob/3da19813/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java
index 8ee51d9..c023ce1 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java
@@ -57,7 +57,7 @@ public class NiFiTestLoginIdentityProvider implements LoginIdentityProvider {
     @Override
     public AuthenticationResponse authenticate(LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException {
         checkUser(credentials.getUsername(), credentials.getPassword());
-        return new AuthenticationResponse(credentials.getUsername(), credentials.getUsername(), TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS));
+        return new AuthenticationResponse(credentials.getUsername(), credentials.getUsername(), TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS), getClass().getSimpleName());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/nifi/blob/3da19813/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.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/x509/X509IdentityProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
index 75a94d3..cae1134 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
@@ -31,7 +31,9 @@ import org.springframework.security.web.authentication.preauth.x509.X509Principa
 public class X509IdentityProvider {
 
     private static final Logger logger = LoggerFactory.getLogger(X509IdentityProvider.class);
-
+    
+    private final String issuer = getClass().getSimpleName();
+    
     private X509CertificateValidator certificateValidator;
     private X509PrincipalExtractor principalExtractor;
 
@@ -77,7 +79,7 @@ public class X509IdentityProvider {
         }
 
         // build the authentication response
-        return new AuthenticationResponse(principal, principal, TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS));
+        return new AuthenticationResponse(principal, principal, TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS), issuer);
     }
 
     /* setters */

http://git-wip-us.apache.org/repos/asf/nifi/blob/3da19813/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 8f2450c..49dd3a0 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
@@ -519,6 +519,9 @@ div.search-glass-pane {
     float: left;
     margin-right: 8px;
     font-weight: bold;
+    max-width: 250px;
+    text-overflow: ellipsis;
+    overflow: hidden;
 }
 
 #utilities-container {

http://git-wip-us.apache.org/repos/asf/nifi/blob/3da19813/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 9202819..d71c8ef 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
@@ -155,7 +155,7 @@ nf.Common = (function () {
             }
             
             // set the interval to one hour
-            var interval = 10 * nf.Common.MILLIS_PER_MINUTE;
+            var interval = nf.Common.MILLIS_PER_MINUTE;
             
             var checkExpiration = function () {
                 var expiration = nf.Storage.getItemExpiration('jwt');
@@ -166,7 +166,7 @@ nf.Common = (function () {
                     var now = new Date();
 
                     // get the time remainging plus a little bonus time to reload the token
-                    var timeRemaining = expirationDate.valueOf() - now.valueOf() - nf.Common.MILLIS_PER_MINUTE;
+                    var timeRemaining = expirationDate.valueOf() - now.valueOf() - (30 * nf.Common.MILLIS_PER_SECOND);
                     if (timeRemaining < interval) {
                         if ($('#current-user').text() !== nf.Common.ANONYMOUS_USER_TEXT && !$('#anonymous-user-alert').is(':visible')) {
                             // if the token will expire before the next interval minus some bonus time, notify the user to re-login
@@ -320,9 +320,6 @@ nf.Common = (function () {
 
                     // show the error pane
                     $('#message-pane').show();
-
-                    // close the canvas
-                    nf.Common.closeCanvas();
                 } else {
                     nf.Dialog.showOkDialog({
                         dialogContent: 'Your session has expired. Please press Ok to log in again.',
@@ -332,6 +329,9 @@ nf.Common = (function () {
                         }
                     });
                 }
+                
+                // close the canvas
+                nf.Common.closeCanvas();
                 return;
             }
             
@@ -424,19 +424,18 @@ nf.Common = (function () {
          * Closes the canvas by removing the splash screen and stats poller.
          */
         closeCanvas: function () {
+            if (nf.Storage.getItem('jwt') === null) {
+                $('#user-logout-container').hide();
+            } else {
+                $('#user-logout-container').show();
+            }
+            
             // ensure this javascript has been loaded in the nf canvas page
             if (nf.Common.isDefinedAndNotNull(nf.Canvas)) {
                 // hide the splash screen if required
                 if ($('#splash').is(':visible')) {
                     nf.Canvas.hideSplash();
                 }
-                
-                // update the log out link accordingly
-                if (nf.Storage.getItem('jwt') === null) {
-                    $('#user-logout-container').hide();
-                } else {
-                    $('#user-logout-container').show();
-                }
 
                 // hide the context menu
                 nf.ContextMenu.hide();

http://git-wip-us.apache.org/repos/asf/nifi/blob/3da19813/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
index cbd5ea4..f3abdb0 100644
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/LdapProvider.java
@@ -66,10 +66,12 @@ public class LdapProvider implements LoginIdentityProvider {
     private static final String TLS = "TLS";
 
     private AbstractLdapAuthenticationProvider provider;
+    private String issuer;
     private long expiration;
 
     @Override
     public final void initialize(final LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException {
+        this.issuer = getClass().getSimpleName();
     }
 
     @Override
@@ -251,9 +253,9 @@ public class LdapProvider implements LoginIdentityProvider {
             // attempt to get the ldap user details to get the DN
             if (authentication.getPrincipal() instanceof LdapUserDetails) {
                 final LdapUserDetails userDetails = (LdapUserDetails) authentication.getPrincipal();
-                return new AuthenticationResponse(userDetails.getDn(), credentials.getUsername(), expiration);
+                return new AuthenticationResponse(userDetails.getDn(), credentials.getUsername(), expiration, issuer);
             } else {
-                return new AuthenticationResponse(authentication.getName(), credentials.getUsername(), expiration);
+                return new AuthenticationResponse(authentication.getName(), credentials.getUsername(), expiration, issuer);
             }
         } catch (final CommunicationException | AuthenticationServiceException e) {
             logger.error(e.getMessage());


[19/51] [abbrv] nifi git commit: NIFI-655: - Fixing issue detecting the presence of DN column

Posted by mc...@apache.org.
NIFI-655:
- Fixing issue detecting the presence of DN column

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

Branch: refs/heads/master
Commit: 91573cb807f6399cebb8ce8d19fad1758578811c
Parents: 48c65e0
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 23 08:45:24 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 23 08:45:24 2015 -0500

----------------------------------------------------------------------
 .../nifi/admin/AuditDataSourceFactoryBean.java    | 18 ++++--------------
 .../nifi/admin/UserDataSourceFactoryBean.java     | 18 ++++--------------
 2 files changed, 8 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/91573cb8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/AuditDataSourceFactoryBean.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/AuditDataSourceFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/AuditDataSourceFactoryBean.java
index 7e68799..87cd420 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/AuditDataSourceFactoryBean.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/AuditDataSourceFactoryBean.java
@@ -19,7 +19,6 @@ package org.apache.nifi.admin;
 import java.io.File;
 import java.sql.Connection;
 import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
 import org.apache.commons.lang3.StringUtils;
@@ -176,10 +175,11 @@ public class AuditDataSourceFactoryBean implements FactoryBean {
                     statement.execute(CREATE_PURGE_DETAILS_TABLE);
                 } else {
                     logger.info("Existing database found and connected to at: " + databaseUrl);
+                    RepositoryUtils.closeQuietly(rs);
 
-                    // get the RS metadata to see if we need to transform the table
-                    final ResultSetMetaData rsMetadata = rs.getMetaData();
-                    if (hasDnColumn(rsMetadata)) {
+                    // check if the DN column exists to see if we need to transform the table
+                    rs = connection.getMetaData().getColumns(null, null, "ACTION", "USER_DN");
+                    if (rs.next()) {
                         statement.execute(RENAME_DN_COLUMN);
                         statement.execute(RESIZE_IDENTITY_COLUMN);
                         statement.execute(RESIZE_USER_NAME_COLUMN);
@@ -201,16 +201,6 @@ public class AuditDataSourceFactoryBean implements FactoryBean {
         return connectionPool;
     }
 
-    private boolean hasDnColumn(final ResultSetMetaData rsMetadata) throws SQLException {
-        boolean hasDn = false;
-        for (int i = 1; i <= rsMetadata.getColumnCount() && !hasDn; i++) {
-            if ("USER_DN".equals(rsMetadata.getColumnName(i))) {
-                hasDn = true;
-            }
-        }
-        return hasDn;
-    }
-
     @Override
     public Class getObjectType() {
         return JdbcConnectionPool.class;

http://git-wip-us.apache.org/repos/asf/nifi/blob/91573cb8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/UserDataSourceFactoryBean.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/UserDataSourceFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/UserDataSourceFactoryBean.java
index acd3449..6d8566e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/UserDataSourceFactoryBean.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/UserDataSourceFactoryBean.java
@@ -19,7 +19,6 @@ package org.apache.nifi.admin;
 import java.io.File;
 import java.sql.Connection;
 import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.util.HashSet;
@@ -151,10 +150,11 @@ public class UserDataSourceFactoryBean implements FactoryBean {
                     statement.execute(INSERT_ANONYMOUS_USER);
                 } else {
                     logger.info("Existing database found and connected to at: " + databaseUrl);
+                    RepositoryUtils.closeQuietly(rs);
 
-                    // get the RS metadata to see if we need to transform the table
-                    final ResultSetMetaData rsMetadata = rs.getMetaData();
-                    if (hasDnColumn(rsMetadata)) {
+                    // if the DN column exists, transform the table
+                    rs = connection.getMetaData().getColumns(null, null, "USER", "DN");
+                    if (rs.next()) {
                         statement.execute(RENAME_DN_COLUMN);
                         statement.execute(RESIZE_IDENTITY_COLUMN);
                         statement.execute(RESIZE_USER_NAME_COLUMN);
@@ -184,16 +184,6 @@ public class UserDataSourceFactoryBean implements FactoryBean {
         return connectionPool;
     }
 
-    private boolean hasDnColumn(final ResultSetMetaData rsMetadata) throws SQLException {
-        boolean hasDn = false;
-        for (int i = 1; i <= rsMetadata.getColumnCount() && !hasDn; i++) {
-            if ("DN".equals(rsMetadata.getColumnName(i))) {
-                hasDn = true;
-            }
-        }
-        return hasDn;
-    }
-
     private String getDatabaseUrl(File databaseFile) {
         String databaseUrl = "jdbc:h2:" + databaseFile + ";AUTOCOMMIT=OFF;DB_CLOSE_ON_EXIT=FALSE;LOCK_MODE=3";
         String databaseUrlAppend = properties.getProperty(NiFiProperties.H2_URL_APPEND);


[05/51] [abbrv] nifi git commit: NIFI-655: - Keeping token expiration between 1 minute and 12 hours.

Posted by mc...@apache.org.
NIFI-655:
- Keeping token expiration between 1 minute and 12 hours.

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

Branch: refs/heads/master
Commit: 4bb8b137f09219ecc7fbc81a25a3079140745b7e
Parents: a196207
Author: Matt Gilman <ma...@gmail.com>
Authored: Tue Nov 17 18:58:22 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Tue Nov 17 18:58:22 2015 -0500

----------------------------------------------------------------------
 .../org/apache/nifi/web/api/AccessResource.java | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/4bb8b137/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
index 67eb8b4..57de41d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -33,6 +33,7 @@ import java.net.URI;
 import java.security.cert.X509Certificate;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.DefaultValue;
 import javax.ws.rs.FormParam;
@@ -316,9 +317,24 @@ public class AccessResource extends ApplicationResource {
             try {
                 // attempt to authenticate
                 final AuthenticationResponse authenticationResponse = loginIdentityProvider.authenticate(new LoginCredentials(username, password));
-
+                final long maxExpiration = TimeUnit.MILLISECONDS.convert(12, TimeUnit.HOURS);
+                final long minExpiration = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES);
+                
+                long expiration = authenticationResponse.getExpiration();
+                if (expiration > maxExpiration) {
+                    expiration = maxExpiration;
+                    
+                    logger.warn(String.format("Max token expiration exceeded. Setting expiration to %s from %s for %s", expiration, 
+                            authenticationResponse.getExpiration(), authenticationResponse.getIdentity()));
+                } else if (expiration < minExpiration) {
+                    expiration = minExpiration;
+                    
+                    logger.warn(String.format("Min token expiration not met. Setting expiration to %s from %s for %s", expiration, 
+                            authenticationResponse.getExpiration(), authenticationResponse.getIdentity()));
+                }
+                
                 // create the authentication token
-                loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getUsername(), authenticationResponse.getExpiration());
+                loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getUsername(), expiration);
             } catch (final InvalidLoginCredentialsException ilce) {
                 throw new IllegalArgumentException("The supplied username and password are not valid.", ilce);
             } catch (final IdentityAccessException iae) {


[21/51] [abbrv] nifi git commit: NIFI-655: - Updating documentation for the failure response codes.

Posted by mc...@apache.org.
NIFI-655:
- Updating documentation for the failure response codes.

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

Branch: refs/heads/master
Commit: ec50a2de8c6901bff366539ace328443a2664209
Parents: e61a369
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 23 09:37:27 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 23 09:37:27 2015 -0500

----------------------------------------------------------------------
 .../main/java/org/apache/nifi/web/api/AccessResource.java    | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/ec50a2de/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
index b486d74..e198438 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -155,7 +155,10 @@ public class AccessResource extends ApplicationResource {
     @ApiResponses(
             value = {
                 @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
-                @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
+                @ApiResponse(code = 401, message = "Unable to determine access status because the client could not be authenticated."),
+                @ApiResponse(code = 403, message = "Unable to determine access status because the client is not authorized to make this request."),
+                @ApiResponse(code = 409, message = "Unable to determine access status because NiFi is not in the appropriate state."),
+                @ApiResponse(code = 500, message = "Unable to determine access status because an unexpected error occurred.")
             }
     )
     public Response getAccessStatus(
@@ -287,7 +290,8 @@ public class AccessResource extends ApplicationResource {
             value = {
                 @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
                 @ApiResponse(code = 403, message = "Client is not authorized to make this request."),
-                @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
+                @ApiResponse(code = 409, message = "Unable to create access token because NiFi is not in the appropriate state. (i.e. may not be configured to support username/password login."),
+                @ApiResponse(code = 500, message = "Unable to create access token because an unexpected error occurred.")
             }
     )
     public Response createAccessToken(


[04/51] [abbrv] nifi git commit: NIFI-655: - Refactoring certificate extraction and validation. - Refactoring how expiration is specified in the login identity providers. - Adding unit tests for the access endpoints. - Code clean up.

Posted by mc...@apache.org.
NIFI-655:
- Refactoring certificate extraction and validation.
- Refactoring how expiration is specified in the login identity providers.
- Adding unit tests for the access endpoints.
- Code clean up.

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

Branch: refs/heads/master
Commit: a196207725a37aecaccc8c7858cec3a4d1c4ff55
Parents: 7529694
Author: Matt Gilman <ma...@gmail.com>
Authored: Tue Nov 17 17:02:41 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Tue Nov 17 17:02:41 2015 -0500

----------------------------------------------------------------------
 .../authentication/AuthenticationResponse.java  |  14 +-
 .../authentication/LoginIdentityProvider.java   |   7 -
 .../web/NiFiWebApiSecurityConfiguration.java    |  25 +-
 .../org/apache/nifi/web/api/AccessResource.java | 188 ++++++-----
 .../nifi/web/api/ApplicationResource.java       |   8 +-
 .../nifi/web/controller/ControllerFacade.java   |   2 +-
 .../src/main/resources/nifi-web-api-context.xml |   3 +-
 .../accesscontrol/AccessTokenEndpointTest.java  | 292 +++++++++++++++++
 .../util/NiFiTestAuthorizationProvider.java     |   3 +-
 .../util/NiFiTestLoginIdentityProvider.java     |  75 +++++
 .../nifi/integration/util/NiFiTestServer.java   |   9 +-
 .../nifi/integration/util/NiFiTestUser.java     | 229 ++++++++++++--
 ...he.nifi.authentication.LoginIdentityProvider |  15 +
 .../access-control/controller-services.xml      |  18 --
 .../access-control/login-identity-providers.xml |  24 ++
 .../resources/access-control/nifi.properties    |   9 +-
 .../access-control/reporting-tasks.xml          |  17 -
 .../web/security/NiFiAuthenticationFilter.java  |  14 +-
 .../security/NiFiAuthenticationProvider.java    |   8 +-
 .../nifi/web/security/ProxiedEntitiesUtils.java | 117 +++----
 .../authorization/NiFiAuthorizationService.java |  15 +-
 .../security/jwt/JwtAuthenticationFilter.java   |   3 +-
 .../nifi/web/security/jwt/JwtService.java       |   2 +-
 .../security/node/NodeAuthorizedUserFilter.java |  68 ++--
 .../LoginIdentityProviderFactoryBean.java       |   7 -
 .../security/x509/X509AuthenticationFilter.java |  63 +---
 .../x509/X509AuthenticationFilterOld.java       | 317 -------------------
 .../security/x509/X509CertificateExtractor.java |   4 +-
 .../security/x509/X509CertificateValidator.java |  10 +-
 .../web/security/x509/X509IdentityProvider.java |  92 ++++++
 .../x509/ocsp/OcspCertificateValidator.java     |  20 +-
 .../resources/nifi-web-security-context.xml     |   6 +
 .../NiFiAuthorizationServiceTest.java           |   2 +-
 .../src/main/webapp/js/nf/login/nf-login.js     |   2 -
 .../apache/nifi/ldap/AbstractLdapProvider.java  |   9 +-
 35 files changed, 985 insertions(+), 712 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java
----------------------------------------------------------------------
diff --git a/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java b/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java
index 8f57810..a64947b 100644
--- a/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java
+++ b/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java
@@ -23,16 +23,19 @@ public class AuthenticationResponse {
 
     private final String identity;
     private final String username;
+    private final long expiration;
 
     /**
      * Creates an authentication response. The username and how long the authentication is valid in milliseconds
      *
      * @param identity The user identity
      * @param username The username
+     * @param expiration The expiration in milliseconds
      */
-    public AuthenticationResponse(final String identity, final String username) {
+    public AuthenticationResponse(final String identity, final String username, final long expiration) {
         this.identity = identity;
         this.username = username;
+        this.expiration = expiration;
     }
 
     public String getIdentity() {
@@ -43,4 +46,13 @@ public class AuthenticationResponse {
         return username;
     }
 
+    /**
+     * Returns the expiration of a given authentication in milliseconds.
+     *
+     * @return The expiration in milliseconds
+     */
+    public long getExpiration() {
+        return expiration;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/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 5f4db40..54becb3 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
@@ -37,13 +37,6 @@ public interface LoginIdentityProvider {
     AuthenticationResponse authenticate(LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException;
 
     /**
-     * Returns the expiration of a given authentication in milliseconds.
-     *
-     * @return The expiration in milliseconds
-     */
-    long getExpiration();
-
-    /**
      * Called immediately after instance creation for implementers to perform additional setup
      *
      * @param initializationContext in which to initialize

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/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 216e311..e8ed267 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
@@ -28,7 +28,7 @@ import org.apache.nifi.web.security.node.NodeAuthorizedUserFilter;
 import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
 import org.apache.nifi.web.security.x509.X509AuthenticationFilter;
 import org.apache.nifi.web.security.x509.X509CertificateExtractor;
-import org.apache.nifi.web.security.x509.X509CertificateValidator;
+import org.apache.nifi.web.security.x509.X509IdentityProvider;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -42,7 +42,6 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
 import org.springframework.security.config.http.SessionCreationPolicy;
 import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
 import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
-import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
 
 /**
  * NiFi Web Api Spring security
@@ -56,9 +55,8 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
     private UserService userService;
     private AuthenticationUserDetailsService userDetailsService;
     private JwtService jwtService;
-    private X509CertificateValidator certificateValidator;
     private X509CertificateExtractor certificateExtractor;
-    private X509PrincipalExtractor principalExtractor;
+    private X509IdentityProvider certificateIdentityProvider;
     private LoginIdentityProvider loginIdentityProvider;
 
     public NiFiWebApiSecurityConfiguration() {
@@ -113,7 +111,11 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
     }
 
     private NodeAuthorizedUserFilter buildNodeAuthorizedUserFilter() {
-        return new NodeAuthorizedUserFilter(properties);
+        final NodeAuthorizedUserFilter nodeFilter = new NodeAuthorizedUserFilter();
+        nodeFilter.setProperties(properties);
+        nodeFilter.setCertificateExtractor(certificateExtractor);
+        nodeFilter.setCertificateIdentityProvider(certificateIdentityProvider);
+        return nodeFilter;
     }
 
     private JwtAuthenticationFilter buildJwtFilter() throws Exception {
@@ -127,9 +129,8 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
     private X509AuthenticationFilter buildX509Filter() throws Exception {
         final X509AuthenticationFilter x509Filter = new X509AuthenticationFilter();
         x509Filter.setProperties(properties);
-        x509Filter.setPrincipalExtractor(principalExtractor);
         x509Filter.setCertificateExtractor(certificateExtractor);
-        x509Filter.setCertificateValidator(certificateValidator);
+        x509Filter.setCertificateIdentityProvider(certificateIdentityProvider);
         x509Filter.setAuthenticationManager(authenticationManager());
         return x509Filter;
     }
@@ -166,17 +167,13 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
     }
 
     @Autowired
-    public void setCertificateValidator(X509CertificateValidator certificateValidator) {
-        this.certificateValidator = certificateValidator;
-    }
-
-    @Autowired
     public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
         this.certificateExtractor = certificateExtractor;
     }
 
     @Autowired
-    public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) {
-        this.principalExtractor = principalExtractor;
+    public void setCertificateIdentityProvider(X509IdentityProvider certificateIdentityProvider) {
+        this.certificateIdentityProvider = certificateIdentityProvider;
     }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
index 9cb4141..67eb8b4 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -30,8 +30,6 @@ import com.wordnik.swagger.annotations.ApiParam;
 import com.wordnik.swagger.annotations.ApiResponse;
 import com.wordnik.swagger.annotations.ApiResponses;
 import java.net.URI;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.X509Certificate;
 import java.util.Arrays;
 import java.util.List;
@@ -41,6 +39,7 @@ import javax.ws.rs.FormParam;
 import javax.ws.rs.POST;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.admin.service.AdministrationException;
 import org.apache.nifi.authentication.AuthenticationResponse;
 import org.apache.nifi.authentication.LoginCredentials;
@@ -48,8 +47,6 @@ import org.apache.nifi.authentication.LoginIdentityProvider;
 import org.apache.nifi.authentication.exception.IdentityAccessException;
 import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException;
 import org.apache.nifi.security.util.CertificateUtils;
-import org.apache.nifi.util.StringUtils;
-import static org.apache.nifi.web.api.ApplicationResource.CLIENT_ID;
 import org.apache.nifi.web.api.dto.AccessStatusDTO;
 import org.apache.nifi.web.api.dto.AccessConfigurationDTO;
 import org.apache.nifi.web.api.dto.RevisionDTO;
@@ -62,15 +59,15 @@ import org.apache.nifi.web.security.jwt.JwtService;
 import org.apache.nifi.web.security.token.LoginAuthenticationToken;
 import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
 import org.apache.nifi.web.security.x509.X509CertificateExtractor;
-import org.apache.nifi.web.security.x509.X509CertificateValidator;
+import org.apache.nifi.web.security.x509.X509IdentityProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.authentication.AccountStatusException;
-import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
 import org.springframework.security.authentication.AuthenticationServiceException;
 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.preauth.x509.X509PrincipalExtractor;
 
 /**
  * RESTful endpoint for managing a cluster.
@@ -82,13 +79,15 @@ import org.springframework.security.web.authentication.preauth.x509.X509Principa
 )
 public class AccessResource extends ApplicationResource {
 
-    private NiFiProperties properties;
+    private static final Logger logger = LoggerFactory.getLogger(AccessResource.class);
 
-    private X509CertificateValidator certificateValidator;
-    private X509CertificateExtractor certificateExtractor;
-    private X509PrincipalExtractor principalExtractor;
+    private static final String AUTHORIZATION = "Authorization";
+
+    private NiFiProperties properties;
 
     private LoginIdentityProvider loginIdentityProvider;
+    private X509CertificateExtractor certificateExtractor;
+    private X509IdentityProvider certificateIdentityProvider;
     private JwtService jwtService;
 
     private AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService;
@@ -172,57 +171,64 @@ public class AccessResource extends ApplicationResource {
         final AccessStatusDTO accessStatus = new AccessStatusDTO();
 
         try {
-            // look for a certificate
-            final X509Certificate certificate = certificateExtractor.extractClientCertificate(httpServletRequest);
+            final X509Certificate[] certificates = certificateExtractor.extractClientCertificate(httpServletRequest);
 
-            // if no certificate, just check the credentials
-            if (certificate == null) {
-                final String principal = jwtService.getAuthentication(httpServletRequest);
+            // if there is not certificate, consider a token
+            if (certificates == null) {
+                // look for an authorization token
+                final String authorization = httpServletRequest.getHeader(AUTHORIZATION);
 
-                // ensure we have something we can work with (certificate or crendentials)
-                if (principal == null) {
+                // if there is no authorization header, we don't know the user
+                if (authorization == null) {
                     accessStatus.setStatus(AccessStatusDTO.Status.UNKNOWN.name());
                     accessStatus.setMessage("No credentials supplied, unknown user.");
                 } else {
-                    // set the user identity
-                    accessStatus.setIdentity(principal);
-                    accessStatus.setUsername(CertificateUtils.extractUsername(principal));
-
-                    // without a certificate, this is not a proxied request
-                    final List<String> chain = Arrays.asList(principal);
-
-                    // check authorization for this user
-                    checkAuthorization(chain);
-
-                    // no issues with authorization
-                    accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
-                    accessStatus.setStatus("Account is active and authorized");
+                    // TODO - use this token with the JWT service
+                    final String token = StringUtils.substringAfterLast(authorization, " ");
+
+                    // TODO - do not call this method of the jwt service
+                    final String principal = jwtService.getAuthentication(httpServletRequest);
+
+                    // TODO - catch jwt exception?
+                    // ensure we have something we can work with (certificate or crendentials)
+                    if (principal == null) {
+                        throw new IllegalArgumentException("The specific token is not valid.");
+                    } else {
+                        // set the user identity
+                        accessStatus.setIdentity(principal);
+                        accessStatus.setUsername(CertificateUtils.extractUsername(principal));
+
+                        // without a certificate, this is not a proxied request
+                        final List<String> chain = Arrays.asList(principal);
+
+                        // check authorization for this user
+                        checkAuthorization(chain);
+
+                        // no issues with authorization
+                        accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
+                        accessStatus.setMessage("Account is active and authorized");
+                    }
                 }
             } else {
-                // we have a certificate so let's consider a proxy chain
-                final String principal = principalExtractor.extractPrincipal(certificate).toString();
-
-                try {
-                    // validate the certificate
-                    certificateValidator.validateClientCertificate(httpServletRequest, certificate);
-                } catch (CertificateExpiredException cee) {
-                    throw new IllegalArgumentException(String.format("Client certificate for (%s) is expired.", principal), cee);
-                } catch (CertificateNotYetValidException cnyve) {
-                    throw new IllegalArgumentException(String.format("Client certificate for (%s) is not yet valid.", principal), cnyve);
-                } catch (final Exception e) {
-                    throw new IllegalArgumentException(e.getMessage(), e);
-                }
+                final AuthenticationResponse authenticationResponse = certificateIdentityProvider.authenticate(certificates);
 
-                // set the user identity
-                accessStatus.setIdentity(principal);
-                accessStatus.setUsername(CertificateUtils.extractUsername(principal));
+                // get the proxy chain and ensure its populated
+                final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(httpServletRequest, authenticationResponse.getIdentity());
+                if (proxyChain.isEmpty()) {
+                    logger.error(String.format("Unable to parse the proxy chain %s from the incoming request.", authenticationResponse.getIdentity()));
+                    throw new IllegalArgumentException("Unable to determine the user from the incoming request.");
+                }
 
                 // ensure the proxy chain is authorized
-                checkAuthorization(ProxiedEntitiesUtils.buildProxyChain(httpServletRequest, principal));
+                checkAuthorization(proxyChain);
+
+                // set the user identity
+                accessStatus.setIdentity(proxyChain.get(0));
+                accessStatus.setUsername(CertificateUtils.extractUsername(proxyChain.get(0)));
 
                 // no issues with authorization
                 accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
-                accessStatus.setStatus("Account is active and authorized");
+                accessStatus.setMessage("Account is active and authorized");
             }
         } catch (final UsernameNotFoundException unfe) {
             accessStatus.setStatus(AccessStatusDTO.Status.UNREGISTERED.name());
@@ -277,6 +283,7 @@ public class AccessResource extends ApplicationResource {
     @ApiResponses(
             value = {
                 @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
+                @ApiResponse(code = 403, message = "Client is not authorized to make this request."),
                 @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
             }
     )
@@ -297,58 +304,42 @@ public class AccessResource extends ApplicationResource {
 
         final LoginAuthenticationToken loginAuthenticationToken;
 
-        // if we don't have username/password, consider JWT or x509
-        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
-            // look for a certificate
-            final X509Certificate certificate = certificateExtractor.extractClientCertificate(httpServletRequest);
-
-            // if there is no certificate, look for an existing token
-            if (certificate == null) {
-                // if not configured for login, don't consider existing tokens
-                if (loginIdentityProvider == null) {
-                    throw new IllegalStateException("Login not supported.");
-                }
+        final X509Certificate[] certificates = certificateExtractor.extractClientCertificate(httpServletRequest);
 
-                // look for the principal
-                final String principal = jwtService.getAuthentication(httpServletRequest);
-                if (principal == null) {
-                    throw new AuthenticationCredentialsNotFoundException("Unable to issue token as issue token as no credentials were found in the request.");
-                }
-
-                // create the authentication token
-                loginAuthenticationToken = new LoginAuthenticationToken(principal, loginIdentityProvider.getExpiration());
-            } else {
-                // extract the principal
-                final String principal = principalExtractor.extractPrincipal(certificate).toString();
-
-                try {
-                    certificateValidator.validateClientCertificate(httpServletRequest, certificate);
-                } catch (CertificateExpiredException cee) {
-                    throw new IllegalArgumentException(String.format("Client certificate for (%s) is expired.", principal), cee);
-                } catch (CertificateNotYetValidException cnyve) {
-                    throw new IllegalArgumentException(String.format("Client certificate for (%s) is not yet valid.", principal), cnyve);
-                } catch (final Exception e) {
-                    throw new IllegalArgumentException(e.getMessage(), e);
-                }
-
-                // authorize the proxy if necessary
-                authorizeProxyIfNecessary(ProxiedEntitiesUtils.buildProxyChain(httpServletRequest, principal));
-
-                // create the authentication token
-                loginAuthenticationToken = new LoginAuthenticationToken(principal, loginIdentityProvider.getExpiration());
+        // if there is not certificate, consider login credentials
+        if (certificates == null) {
+            // ensure we have login credentials
+            if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
+                throw new IllegalArgumentException("The username and password must be specified.");
             }
-        } else {
+
             try {
                 // attempt to authenticate
                 final AuthenticationResponse authenticationResponse = loginIdentityProvider.authenticate(new LoginCredentials(username, password));
 
                 // create the authentication token
-                loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getUsername(), loginIdentityProvider.getExpiration());
+                loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getUsername(), authenticationResponse.getExpiration());
             } catch (final InvalidLoginCredentialsException ilce) {
                 throw new IllegalArgumentException("The supplied username and password are not valid.", ilce);
             } catch (final IdentityAccessException iae) {
                 throw new AdministrationException(iae.getMessage(), iae);
             }
+        } else {
+            // consider a certificate
+            final AuthenticationResponse authenticationResponse = certificateIdentityProvider.authenticate(certificates);
+
+            // get the proxy chain and ensure its populated
+            final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(httpServletRequest, authenticationResponse.getIdentity());
+            if (proxyChain.isEmpty()) {
+                logger.error(String.format("Unable to parse the proxy chain %s from the incoming request.", authenticationResponse.getIdentity()));
+                throw new IllegalArgumentException("Unable to determine the user from the incoming request.");
+            }
+
+            // authorize the proxy if necessary
+            authorizeProxyIfNecessary(proxyChain);
+
+            // create the authentication token
+            loginAuthenticationToken = new LoginAuthenticationToken(proxyChain.get(0), authenticationResponse.getExpiration());
         }
 
         // generate JWT for response
@@ -371,11 +362,14 @@ public class AccessResource extends ApplicationResource {
                 userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
             } 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
+                // we can issue a new token to the end user which they will use to identify themselves
+                // when they enter a new account request
+            } catch (final AuthenticationServiceException ase) {
+                // throw an administration exception which will return a 500
+                throw new AdministrationException(ase.getMessage(), ase);
             } catch (final Exception e) {
-                // any other issue we're going to treat as an authentication exception which will return 401
-                throw new AdministrationException(e.getMessage(), e) {
-                };
+                // any other issue we're going to treat as access denied exception which will return 403
+                throw new AccessDeniedException(e.getMessage(), e);
             }
         }
     }
@@ -393,16 +387,12 @@ public class AccessResource extends ApplicationResource {
         this.jwtService = jwtService;
     }
 
-    public void setCertificateValidator(X509CertificateValidator certificateValidator) {
-        this.certificateValidator = certificateValidator;
-    }
-
     public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
         this.certificateExtractor = certificateExtractor;
     }
 
-    public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) {
-        this.principalExtractor = principalExtractor;
+    public void setCertificateIdentityProvider(X509IdentityProvider certificateIdentityProvider) {
+        this.certificateIdentityProvider = certificateIdentityProvider;
     }
 
     public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
index 2e15d30..d0c36d4 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
@@ -53,6 +53,8 @@ import org.apache.nifi.web.util.WebUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.nifi.user.NiFiUser;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.core.Authentication;
@@ -363,9 +365,9 @@ public abstract class ApplicationResource {
         if (httpServletRequest.isSecure()) {
 
             // add the certificate DN to the proxy chain
-            final String xProxiedEntitiesChain = ProxiedEntitiesUtils.getXProxiedEntitiesChain(httpServletRequest);
-            if (StringUtils.isNotBlank(xProxiedEntitiesChain)) {
-                result.put(PROXIED_ENTITIES_CHAIN_HTTP_HEADER, xProxiedEntitiesChain);
+            final NiFiUser user = NiFiUserUtils.getNiFiUser();
+            if (user != null) {
+                result.put(PROXIED_ENTITIES_CHAIN_HTTP_HEADER, ProxiedEntitiesUtils.buildProxiedEntitiesChainString(user));
             }
 
             // add the user's authorities (if any) to the headers

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
index 0e1b20b..a02d1fd 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
@@ -823,7 +823,7 @@ public class ControllerFacade {
             final Map<String, String> attributes = event.getAttributes();
 
             // calculate the dn chain
-            final List<String> dnChain = ProxiedEntitiesUtils.getXProxiedEntitiesChain(user);
+            final List<String> dnChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(user);
 
             // ensure the users in this chain are allowed to download this content
             final DownloadAuthorization downloadAuthorization = userService.authorizeDownload(dnChain, attributes);

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
index e992dc9..73929d8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
@@ -243,9 +243,8 @@
     </bean>
     <bean id="accessResource" class="org.apache.nifi.web.api.AccessResource" scope="singleton">
         <property name="properties" ref="nifiProperties"/>
-        <property name="certificateValidator" ref="certificateValidator"/>
         <property name="certificateExtractor" ref="certificateExtractor"/>
-        <property name="principalExtractor" ref="principalExtractor"/>
+        <property name="certificateIdentityProvider" ref="certificateIdentityProvider"/>
         <property name="loginIdentityProvider" ref="loginIdentityProvider"/>
         <property name="jwtService" ref="jwtService"/>
         <property name="userDetailsService" ref="userDetailsService"/>

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java
new file mode 100644
index 0000000..82fe73a
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java
@@ -0,0 +1,292 @@
+/*
+ * 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.integration.accesscontrol;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import javax.net.ssl.SSLContext;
+import org.apache.commons.io.FileUtils;
+import org.apache.nifi.integration.util.NiFiTestServer;
+import org.apache.nifi.integration.util.NiFiTestUser;
+import org.apache.nifi.integration.util.SourceTestProcessor;
+import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.NarClassLoaders;
+import org.apache.nifi.security.util.SslContextFactory;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.api.dto.AccessConfigurationDTO;
+import org.apache.nifi.web.api.dto.AccessStatusDTO;
+import org.apache.nifi.web.api.dto.ProcessorDTO;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.entity.AccessConfigurationEntity;
+import org.apache.nifi.web.api.entity.AccessStatusEntity;
+import org.apache.nifi.web.api.entity.ProcessorEntity;
+import org.apache.nifi.web.util.WebUtils;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Access token endpoint test.
+ */
+public class AccessTokenEndpointTest {
+
+    private static final String CLIENT_ID = "token-endpoint-id";
+    private static final String CONTEXT_PATH = "/nifi-api";
+    private static final String FLOW_XML_PATH = "target/test-classes/access-control/flow-admin.xml";
+
+    private static NiFiTestServer SERVER;
+    private static NiFiTestUser TOKEN_USER;
+    private static String BASE_URL;
+
+    @BeforeClass
+    public static void setup() throws Exception {
+        // configure the location of the nifi properties
+        File nifiPropertiesFile = new File("src/test/resources/access-control/nifi.properties");
+        System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, nifiPropertiesFile.getAbsolutePath());
+
+        // update the flow.xml property
+        NiFiProperties props = NiFiProperties.getInstance();
+        props.setProperty("nifi.flow.configuration.file", FLOW_XML_PATH);
+
+        // delete the database directory to avoid issues with re-registration in testRequestAccessUsingToken
+        FileUtils.deleteDirectory(props.getDatabaseRepositoryPath().toFile());
+
+        // load extensions
+        NarClassLoaders.load(props);
+        ExtensionManager.discoverExtensions();
+
+        // start the server
+        SERVER = new NiFiTestServer("src/main/webapp", CONTEXT_PATH);
+        SERVER.startServer();
+        SERVER.loadFlow();
+
+        // get the base url
+        BASE_URL = SERVER.getBaseUrl() + CONTEXT_PATH;
+
+        // create the user
+        final Client client = WebUtils.createClient(null, createTrustContext(props));
+        TOKEN_USER = new NiFiTestUser(client, null);
+    }
+
+    private static SSLContext createTrustContext(final NiFiProperties props) throws Exception {
+        return SslContextFactory.createTrustSslContext(props.getProperty(NiFiProperties.SECURITY_TRUSTSTORE),
+                props.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD).toCharArray(),
+                props.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE), "TLS");
+    }
+
+    // -----------
+    // LOGIN CONIG
+    // -----------
+    /**
+     * Test getting access configuration.
+     *
+     * @throws Exception ex
+     */
+    @Test
+    public void testGetAccessConfig() throws Exception {
+        String url = BASE_URL + "/access/config";
+
+        ClientResponse response = TOKEN_USER.testGet(url);
+
+        // ensure the request is successful
+        Assert.assertEquals(200, response.getStatus());
+
+        // extract the process group
+        AccessConfigurationEntity accessConfigEntity = response.getEntity(AccessConfigurationEntity.class);
+
+        // ensure there is content
+        Assert.assertNotNull(accessConfigEntity);
+
+        // extract the process group dto
+        AccessConfigurationDTO accessConfig = accessConfigEntity.getConfig();
+
+        // verify config
+        Assert.assertTrue(accessConfig.getSupportsLogin());
+        Assert.assertFalse(accessConfig.getSupportsAnonymous());
+    }
+
+    /**
+     * Obtains a token and creates a processor using it.
+     *
+     * @throws Exception ex
+     */
+    @Test
+    public void testCreateProcessorUsingToken() throws Exception {
+        String url = BASE_URL + "/access/token";
+
+        ClientResponse response = TOKEN_USER.testCreateToken(url, "user@nifi", "whateve");
+
+        // ensure the request is successful
+        Assert.assertEquals(201, response.getStatus());
+
+        // get the token
+        String token = response.getEntity(String.class);
+
+        // attempt to create a processor with it
+        createProcessor(token);
+    }
+
+    private ProcessorDTO createProcessor(final String token) throws Exception {
+        String url = BASE_URL + "/controller/process-groups/root/processors";
+
+        // authorization header
+        Map<String, String> headers = new HashMap<>();
+        headers.put("Authorization", "Bearer " + token);
+
+        // create the processor
+        ProcessorDTO processor = new ProcessorDTO();
+        processor.setName("Copy");
+        processor.setType(SourceTestProcessor.class.getName());
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(CLIENT_ID);
+        revision.setVersion(NiFiTestUser.REVISION);
+
+        // create the entity body
+        ProcessorEntity entity = new ProcessorEntity();
+        entity.setRevision(revision);
+        entity.setProcessor(processor);
+
+        // perform the request
+        ClientResponse response = TOKEN_USER.testPostWithHeaders(url, entity, headers);
+
+        // ensure the request is successful
+        Assert.assertEquals(201, response.getStatus());
+
+        // get the entity body
+        entity = response.getEntity(ProcessorEntity.class);
+
+        // verify creation
+        processor = entity.getProcessor();
+        Assert.assertEquals("Copy", processor.getName());
+        Assert.assertEquals("org.apache.nifi.integration.util.SourceTestProcessor", processor.getType());
+
+        return processor;
+    }
+
+    /**
+     * Verifies the response when bad credentials are specified.
+     *
+     * @throws Exception ex
+     */
+    @Test
+    public void testInvalidCredentials() throws Exception {
+        String url = BASE_URL + "/access/token";
+
+        ClientResponse response = TOKEN_USER.testCreateToken(url, "user@nifi", "not a real password");
+
+        // ensure the request is successful
+        Assert.assertEquals(400, response.getStatus());
+    }
+
+    /**
+     * Verifies the response when the user is known.
+     *
+     * @throws Exception ex
+     */
+    @Test
+    public void testUnkownUser() throws Exception {
+        String url = BASE_URL + "/access/token";
+
+        ClientResponse response = TOKEN_USER.testCreateToken(url, "not a real user", "not a real password");
+
+        // ensure the request is successful
+        Assert.assertEquals(400, response.getStatus());
+    }
+
+    /**
+     * Request access using access token.
+     *
+     * @throws Exception ex
+     */
+    @Test
+    public void testRequestAccessUsingToken() throws Exception {
+        String accessStatusUrl = BASE_URL + "/access";
+        String accessTokenUrl = BASE_URL + "/access/token";
+        String registrationUrl = BASE_URL + "/controller/users";
+
+        ClientResponse response = TOKEN_USER.testGet(accessStatusUrl);
+
+        // ensure the request is successful
+        Assert.assertEquals(200, response.getStatus());
+
+        AccessStatusEntity accessStatusEntity = response.getEntity(AccessStatusEntity.class);
+        AccessStatusDTO accessStatus = accessStatusEntity.getAccessStatus();
+
+        // verify unknown
+        Assert.assertEquals("UNKNOWN", accessStatus.getStatus());
+
+        response = TOKEN_USER.testCreateToken(accessTokenUrl, "unregistered-user@nifi", "password");
+
+        // ensure the request is successful
+        Assert.assertEquals(201, response.getStatus());
+
+        // get the token
+        String token = response.getEntity(String.class);
+
+        // authorization header
+        Map<String, String> headers = new HashMap<>();
+        headers.put("Authorization", "Bearer " + token);
+
+        // check the status with the token
+        response = TOKEN_USER.testGetWithHeaders(accessStatusUrl, null, headers);
+
+        // ensure the request is successful
+        Assert.assertEquals(200, response.getStatus());
+
+        accessStatusEntity = response.getEntity(AccessStatusEntity.class);
+        accessStatus = accessStatusEntity.getAccessStatus();
+
+        // verify unregistered
+        Assert.assertEquals("UNREGISTERED", accessStatus.getStatus());
+
+        response = TOKEN_USER.testRegisterUser(registrationUrl, "Gimme access", headers);
+
+        // ensure the request is successful
+        Assert.assertEquals(201, response.getStatus());
+
+        // check the status with the token
+        response = TOKEN_USER.testGetWithHeaders(accessStatusUrl, null, headers);
+
+        // ensure the request is successful
+        Assert.assertEquals(200, response.getStatus());
+
+        accessStatusEntity = response.getEntity(AccessStatusEntity.class);
+        accessStatus = accessStatusEntity.getAccessStatus();
+
+        // verify unregistered
+        Assert.assertEquals("NOT_ACTIVE", accessStatus.getStatus());
+    }
+
+    @AfterClass
+    public static void cleanup() throws Exception {
+        // shutdown the server
+        SERVER.shutdownServer();
+        SERVER = null;
+
+        // look for the flow.xml
+        File flow = new File(FLOW_XML_PATH);
+        if (flow.exists()) {
+            flow.delete();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizationProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizationProvider.java
index d51b7df..d29be92 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizationProvider.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizationProvider.java
@@ -37,7 +37,7 @@ import org.apache.nifi.authorization.DownloadAuthorization;
  */
 public class NiFiTestAuthorizationProvider implements AuthorityProvider {
 
-    private Map<String, Set<Authority>> users;
+    private final Map<String, Set<Authority>> users;
 
     /**
      * Creates a new FileAuthorizationProvider.
@@ -48,6 +48,7 @@ public class NiFiTestAuthorizationProvider implements AuthorityProvider {
         users.put("CN=Lastname Firstname Middlename monitor, OU=Unknown, OU=Unknown, OU=Unknown, O=Unknown, C=Unknown", EnumSet.of(Authority.ROLE_MONITOR));
         users.put("CN=Lastname Firstname Middlename dfm, OU=Unknown, OU=Unknown, OU=Unknown, O=Unknown, C=Unknown", EnumSet.of(Authority.ROLE_DFM));
         users.put("CN=Lastname Firstname Middlename admin, OU=Unknown, OU=Unknown, OU=Unknown, O=Unknown, C=Unknown", EnumSet.of(Authority.ROLE_ADMIN));
+        users.put("user@nifi", EnumSet.of(Authority.ROLE_DFM));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java
new file mode 100644
index 0000000..8ee51d9
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java
@@ -0,0 +1,75 @@
+/*
+ * 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.integration.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import org.apache.nifi.authorization.exception.ProviderCreationException;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.apache.nifi.authentication.LoginCredentials;
+import org.apache.nifi.authentication.LoginIdentityProvider;
+import org.apache.nifi.authentication.LoginIdentityProviderConfigurationContext;
+import org.apache.nifi.authentication.LoginIdentityProviderInitializationContext;
+import org.apache.nifi.authentication.exception.IdentityAccessException;
+import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException;
+
+/**
+ *
+ */
+public class NiFiTestLoginIdentityProvider implements LoginIdentityProvider {
+
+    private final Map<String, String> users;
+
+    /**
+     * Creates a new FileAuthorizationProvider.
+     */
+    public NiFiTestLoginIdentityProvider() {
+        users = new HashMap<>();
+        users.put("user@nifi", "whateve");
+        users.put("unregistered-user@nifi", "password");
+    }
+
+    private void checkUser(final String user, final String password) {
+        if (!users.containsKey(user)) {
+            throw new InvalidLoginCredentialsException("Unknown user");
+        }
+
+        if (!users.get(user).equals(password)) {
+            throw new InvalidLoginCredentialsException("Invalid password");
+        }
+    }
+
+    @Override
+    public AuthenticationResponse authenticate(LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException {
+        checkUser(credentials.getUsername(), credentials.getPassword());
+        return new AuthenticationResponse(credentials.getUsername(), credentials.getUsername(), TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS));
+    }
+
+    @Override
+    public void initialize(LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException {
+    }
+
+    @Override
+    public void onConfigured(LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException {
+    }
+
+    @Override
+    public void preDestruction() {
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java
index 42b0aab..38c2d41 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java
@@ -78,8 +78,12 @@ public class NiFiTestServer {
     private void createSecureConnector() {
         org.eclipse.jetty.util.ssl.SslContextFactory contextFactory = new org.eclipse.jetty.util.ssl.SslContextFactory();
 
-        // need client auth
-        contextFactory.setNeedClientAuth(properties.getNeedClientAuth());
+        // require client auth when not supporting login or anonymous access
+        if (StringUtils.isBlank(properties.getProperty(NiFiProperties.SECURITY_USER_LOGIN_IDENTITY_PROVIDER)) && properties.getAnonymousAuthorities().isEmpty()) {
+            contextFactory.setNeedClientAuth(true);
+        } else {
+            contextFactory.setWantClientAuth(true);
+        }
 
         /* below code sets JSSE system properties when values are provided */
         // keystore properties
@@ -163,7 +167,6 @@ public class NiFiTestServer {
     }
 
     public Client getClient() {
-        // create the client
         return WebUtils.createClient(null, SslContextFactory.createSslContext(properties));
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
index c0e9246..621dc09 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
@@ -34,9 +34,27 @@ public class NiFiTestUser {
     private final Client client;
     private final String proxyDn;
 
-    public NiFiTestUser(Client client, String dn) {
+    public NiFiTestUser(Client client, String proxyDn) {
         this.client = client;
-        this.proxyDn = ProxiedEntitiesUtils.formatProxyDn(dn);
+        if (proxyDn != null) {
+            this.proxyDn = ProxiedEntitiesUtils.formatProxyDn(proxyDn);
+        } else {
+            this.proxyDn = null;
+        }
+    }
+
+    /**
+     * Conditionally adds the proxied entities chain.
+     *
+     * @param builder the resource builder
+     * @return the resource builder
+     */
+    private WebResource.Builder addProxiedEntities(final WebResource.Builder builder) {
+        if (proxyDn == null) {
+            return builder;
+        } else {
+            return builder.header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn);
+        }
     }
 
     /**
@@ -58,6 +76,18 @@ public class NiFiTestUser {
      * @return response
      */
     public ClientResponse testGet(String url, Map<String, String> queryParams) {
+        return testGetWithHeaders(url, queryParams, null);
+    }
+
+    /**
+     * Performs a GET using the specified url and query parameters.
+     *
+     * @param url url
+     * @param queryParams params
+     * @param headers http headers
+     * @return response
+     */
+    public ClientResponse testGetWithHeaders(String url, Map<String, String> queryParams, Map<String, String> headers) {
         // get the resource
         WebResource resource = client.resource(url);
 
@@ -68,8 +98,18 @@ public class NiFiTestUser {
             }
         }
 
+        // get the builder
+        WebResource.Builder builder = addProxiedEntities(resource.accept(MediaType.APPLICATION_JSON));
+
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                builder = builder.header(key, headers.get(key));
+            }
+        }
+
         // perform the query
-        return resource.accept(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn).get(ClientResponse.class);
+        return builder.get(ClientResponse.class);
     }
 
     /**
@@ -92,14 +132,34 @@ public class NiFiTestUser {
      * @throws Exception ex
      */
     public ClientResponse testPost(String url, Object entity) throws Exception {
+        return testPostWithHeaders(url, entity, null);
+    }
+
+    /**
+     * Performs a POST using the specified url and entity body.
+     *
+     * @param url url
+     * @param entity entity
+     * @param headers http headers
+     * @return response
+     * @throws Exception ex
+     */
+    public ClientResponse testPostWithHeaders(String url, Object entity, Map<String, String> headers) throws Exception {
         // get the resource
-        WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn);
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON));
 
         // include the request entity
         if (entity != null) {
             resourceBuilder = resourceBuilder.entity(entity);
         }
 
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                resourceBuilder = resourceBuilder.header(key, headers.get(key));
+            }
+        }
+
         // perform the request
         return resourceBuilder.post(ClientResponse.class);
     }
@@ -109,18 +169,38 @@ public class NiFiTestUser {
      *
      * @param url url
      * @param entity entity
-     * @return repsonse
+     * @return response
      * @throws Exception ex
      */
     public ClientResponse testPostMultiPart(String url, Object entity) throws Exception {
+        return testPostMultiPartWithHeaders(url, entity, null);
+    }
+
+    /**
+     * Performs a POST using the specified url and entity body.
+     *
+     * @param url url
+     * @param entity entity
+     * @param headers http headers
+     * @return response
+     * @throws Exception ex
+     */
+    public ClientResponse testPostMultiPartWithHeaders(String url, Object entity, Map<String, String> headers) throws Exception {
         // get the resource
-        WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_XML).type(MediaType.MULTIPART_FORM_DATA).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn);
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_XML).type(MediaType.MULTIPART_FORM_DATA));
 
         // include the request entity
         if (entity != null) {
             resourceBuilder = resourceBuilder.entity(entity);
         }
 
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                resourceBuilder = resourceBuilder.header(key, headers.get(key));
+            }
+        }
+
         // perform the request
         return resourceBuilder.post(ClientResponse.class);
     }
@@ -134,6 +214,19 @@ public class NiFiTestUser {
      * @throws java.lang.Exception ex
      */
     public ClientResponse testPost(String url, Map<String, String> formData) throws Exception {
+        return testPostWithHeaders(url, formData, null);
+    }
+
+    /**
+     * Performs a POST using the specified url and form data.
+     *
+     * @param url url
+     * @param formData form data
+     * @param headers http headers
+     * @return response
+     * @throws java.lang.Exception ex
+     */
+    public ClientResponse testPostWithHeaders(String url, Map<String, String> formData, Map<String, String> headers) throws Exception {
         // convert the form data
         MultivaluedMapImpl entity = new MultivaluedMapImpl();
         for (String key : formData.keySet()) {
@@ -141,14 +234,20 @@ public class NiFiTestUser {
         }
 
         // get the resource
-        WebResource.Builder resourceBuilder
-                = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn);
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED));
 
         // add the form data if necessary
         if (!entity.isEmpty()) {
             resourceBuilder = resourceBuilder.entity(entity);
         }
 
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                resourceBuilder = resourceBuilder.header(key, headers.get(key));
+            }
+        }
+
         // perform the request
         return resourceBuilder.post(ClientResponse.class);
     }
@@ -162,14 +261,34 @@ public class NiFiTestUser {
      * @throws java.lang.Exception ex
      */
     public ClientResponse testPut(String url, Object entity) throws Exception {
+        return testPutWithHeaders(url, entity, null);
+    }
+
+    /**
+     * Performs a PUT using the specified url and entity body.
+     *
+     * @param url url
+     * @param entity entity
+     * @param headers http headers
+     * @return response
+     * @throws java.lang.Exception ex
+     */
+    public ClientResponse testPutWithHeaders(String url, Object entity, Map<String, String> headers) throws Exception {
         // get the resource
-        WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn);
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON));
 
         // include the request entity
         if (entity != null) {
             resourceBuilder = resourceBuilder.entity(entity);
         }
 
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                resourceBuilder = resourceBuilder.header(key, headers.get(key));
+            }
+        }
+
         // perform the request
         return resourceBuilder.put(ClientResponse.class);
     }
@@ -183,6 +302,19 @@ public class NiFiTestUser {
      * @throws java.lang.Exception ex
      */
     public ClientResponse testPut(String url, Map<String, String> formData) throws Exception {
+        return testPutWithHeaders(url, formData, null);
+    }
+
+    /**
+     * Performs a PUT using the specified url and form data.
+     *
+     * @param url url
+     * @param formData form data
+     * @param headers http headers
+     * @return response
+     * @throws java.lang.Exception ex
+     */
+    public ClientResponse testPutWithHeaders(String url, Map<String, String> formData, Map<String, String> headers) throws Exception {
         // convert the form data
         MultivaluedMapImpl entity = new MultivaluedMapImpl();
         for (String key : formData.keySet()) {
@@ -190,14 +322,20 @@ public class NiFiTestUser {
         }
 
         // get the resource
-        WebResource.Builder resourceBuilder
-                = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn);
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED));
 
         // add the form data if necessary
         if (!entity.isEmpty()) {
             resourceBuilder = resourceBuilder.entity(entity);
         }
 
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                resourceBuilder = resourceBuilder.header(key, headers.get(key));
+            }
+        }
+
         // perform the request
         return resourceBuilder.put(ClientResponse.class);
     }
@@ -210,24 +348,26 @@ public class NiFiTestUser {
      * @throws java.lang.Exception ex
      */
     public ClientResponse testDelete(String url) throws Exception {
-        return testDelete(url, (Object) null);
+        return testDelete(url, null);
     }
 
     /**
      * Performs a DELETE using the specified url and entity.
      *
      * @param url url
-     * @param entity entity
-     * @return repsonse
+     * @param headers http headers
+     * @return response
      * @throws java.lang.Exception ex
      */
-    public ClientResponse testDelete(String url, Object entity) throws Exception {
+    public ClientResponse testDeleteWithHeaders(String url, Map<String, String> headers) throws Exception {
         // get the resource
-        WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn);
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_JSON));
 
-        // append any query parameters
-        if (entity != null) {
-            resourceBuilder = resourceBuilder.entity(entity);
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                resourceBuilder = resourceBuilder.header(key, headers.get(key));
+            }
         }
 
         // perform the query
@@ -254,7 +394,56 @@ public class NiFiTestUser {
         }
 
         // perform the request
-        return resource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn).delete(ClientResponse.class);
+        return addProxiedEntities(resource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED)).delete(ClientResponse.class);
     }
 
+    /**
+     * Attempts to create a token with the specified username and password.
+     *
+     * @param url the url
+     * @param username the username
+     * @param password the password
+     * @return response
+     * @throws Exception ex
+     */
+    public ClientResponse testCreateToken(String url, String username, String password) throws Exception {
+        // convert the form data
+        MultivaluedMapImpl entity = new MultivaluedMapImpl();
+        entity.add("username", username);
+        entity.add("password", password);
+
+        // get the resource
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.TEXT_PLAIN).type(MediaType.APPLICATION_FORM_URLENCODED)).entity(entity);
+
+        // perform the request
+        return resourceBuilder.post(ClientResponse.class);
+    }
+
+    /**
+     * Attempts to create a token with the specified username and password.
+     *
+     * @param url the url
+     * @param justification justification
+     * @param headers http headers
+     * @return response
+     * @throws Exception ex
+     */
+    public ClientResponse testRegisterUser(String url, String justification, Map<String, String> headers) throws Exception {
+        // convert the form data
+        MultivaluedMapImpl entity = new MultivaluedMapImpl();
+        entity.add("justification", justification);
+
+        // get the resource
+        WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.TEXT_PLAIN).type(MediaType.APPLICATION_FORM_URLENCODED)).entity(entity);
+
+        // append any headers
+        if (headers != null && !headers.isEmpty()) {
+            for (String key : headers.keySet()) {
+                resourceBuilder = resourceBuilder.header(key, headers.get(key));
+            }
+        }
+
+        // perform the request
+        return resourceBuilder.post(ClientResponse.class);
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider
new file mode 100644
index 0000000..4b42e4f
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider
@@ -0,0 +1,15 @@
+# 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.
+org.apache.nifi.integration.util.NiFiTestLoginIdentityProvider
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/controller-services.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/controller-services.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/controller-services.xml
deleted file mode 100644
index f5bd96a..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/controller-services.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
-  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.
--->
-<services>
-
-</services>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/login-identity-providers.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/login-identity-providers.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/login-identity-providers.xml
new file mode 100644
index 0000000..04120c9
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/login-identity-providers.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+  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.
+-->
+<!--
+    This file lists all authority providers to use when running securely.
+-->
+<loginIdentityProviders>
+    <provider>
+        <identifier>test-provider</identifier>
+        <class>org.apache.nifi.integration.util.NiFiTestLoginIdentityProvider</class>
+    </provider>
+</loginIdentityProviders>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties
index fc20d78..10db651 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties
@@ -14,16 +14,15 @@
 # limitations under the License.
 
 # Core Properties #
-nifi.version=nifi 0.2.1-SNAPSHOT 
+nifi.version=nifi version 
 nifi.flow.configuration.file=
 nifi.flow.configuration.archive.dir=target/archive
 nifi.flowcontroller.autoResumeState=true
 nifi.flowcontroller.graceful.shutdown.period=10 sec
 nifi.flowservice.writedelay.interval=2 sec
 
-nifi.reporting.task.configuration.file=target/test-classes/access-control/reporting-tasks.xml
-nifi.controller.service.configuration.file=target/test-classes/access-control/controller-services.xml
 nifi.authority.provider.configuration.file=target/test-classes/access-control/authority-providers.xml
+nifi.login.identity.provider.configuration.file=target/test-classes/access-control/login-identity-providers.xml
 nifi.templates.directory=target/test-classes/access-control/templates
 nifi.ui.banner.text=TEST BANNER
 nifi.ui.autorefresh.interval=30 sec
@@ -93,11 +92,11 @@ nifi.security.truststoreType=JKS
 nifi.security.truststorePasswd=localtest
 nifi.security.needClientAuth=true
 nifi.security.user.authority.provider=test-provider
-nifi.security.user.login.identity.provider=
+nifi.security.user.login.identity.provider=test-provider
 nifi.security.authorizedUsers.file=target/test-classes/access-control/users.xml
 nifi.security.user.credential.cache.duration=1 hr
 nifi.security.support.new.account.requests=
-nifi.security.default.user.roles=
+nifi.security.anonymous.authorities=
 
 # cluster common properties (cluster manager and nodes must have same values) #
 nifi.cluster.protocol.heartbeat.interval=5 sec

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/reporting-tasks.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/reporting-tasks.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/reporting-tasks.xml
deleted file mode 100644
index 251735e..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/reporting-tasks.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<!--
-  Licensed to the Apache Software Foundation (ASF) under one or more
-  contributor license agreements.  See the NOTICE file distributed with
-  this work for additional information regarding copyright ownership.
-  The ASF licenses this file to You under the Apache License, Version 2.0
-  (the "License"); you may not use this file except in compliance with
-  the License.  You may obtain a copy of the License at
-      http://www.apache.org/licenses/LICENSE-2.0
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-<tasks>
-</tasks>

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.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/NiFiAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
index e5e5c04..ec34ace 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
@@ -29,12 +29,14 @@ import javax.servlet.http.HttpServletResponse;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
 import org.apache.nifi.web.security.user.NiFiUserUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.authentication.AccountStatusException;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -85,8 +87,13 @@ public abstract class NiFiAuthenticationFilter implements Filter {
 
     private void authenticate(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
         try {
-            final Authentication authenticated = attemptAuthentication(request, response);
+            final NiFiAuthenticationRequestToken authenticated = attemptAuthentication(request, response);
             if (authenticated != null) {
+                // log the request attempt - response details will be logged later
+                logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)",
+                        ProxiedEntitiesUtils.formatProxyDn(StringUtils.join(authenticated.getChain(), "><")), request.getMethod(),
+                        request.getRequestURL().toString(), request.getRemoteAddr()));
+
                 final Authentication authorized = authenticationManager.authenticate(authenticated);
                 successfulAuthorization(request, response, authorized);
             }
@@ -97,7 +104,7 @@ public abstract class NiFiAuthenticationFilter implements Filter {
         }
     }
 
-    public abstract Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response);
+    public abstract NiFiAuthenticationRequestToken attemptAuthentication(HttpServletRequest request, HttpServletResponse response);
 
     protected void successfulAuthorization(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
         if (logger.isDebugEnabled()) {
@@ -127,6 +134,9 @@ public abstract class NiFiAuthenticationFilter implements Filter {
                 response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                 out.println("Access is denied.");
             }
+        } else if (ae instanceof BadCredentialsException) {
+            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+            out.println(ae.getMessage());
         } else if (ae instanceof AccountStatusException) {
             response.setStatus(HttpServletResponse.SC_FORBIDDEN);
             out.println(ae.getMessage());

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.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/NiFiAuthenticationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
index 79e8eb2..eb0684b 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
@@ -47,10 +47,12 @@ public class NiFiAuthenticationProvider implements AuthenticationProvider {
             final UserDetails userDetails = userDetailsService.loadUserDetails(request);
 
             // build an authentication for accesing nifi
-            return new NiFiAuthorizationToken(userDetails);
+            final NiFiAuthorizationToken result = new NiFiAuthorizationToken(userDetails);
+            result.setDetails(request.getDetails());
+            return result;
         } catch (final UsernameNotFoundException unfe) {
-            // if the result was an authenticated new account request and it could not be authorized because the user was not found,
-            // return the token so the new account could be created. this must go here to ensure that any proxies have been authorized
+            // if the authentication request is for a new account and it could not be authorized because the user was not found,
+            // return the token so the new account could be created. this must go here toe nsure that any proxies have been authorized
             if (isNewAccountAuthenticationToken(request)) {
                 return new NewAccountAuthenticationToken(((NewAccountAuthenticationRequestToken) authentication).getNewAccountRequest());
             } else {