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:44:16 UTC

[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

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