You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by bb...@apache.org on 2017/01/04 18:06:45 UTC

nifi git commit: NIFI-2695: - Providing more granular and meaningful authorization error messages.

Repository: nifi
Updated Branches:
  refs/heads/master 41189b055 -> b1c9f0e76


NIFI-2695: - Providing more granular and meaningful authorization error messages.

This closes #1309.

Signed-off-by: Bryan Bende <bb...@apache.org>


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

Branch: refs/heads/master
Commit: b1c9f0e76428d69e7ee39448ef182ad0a23327ec
Parents: 41189b0
Author: Matt Gilman <ma...@gmail.com>
Authored: Thu Dec 8 11:29:32 2016 -0500
Committer: Bryan Bende <bb...@apache.org>
Committed: Wed Jan 4 13:06:19 2017 -0500

----------------------------------------------------------------------
 .../AbstractPolicyBasedAuthorizer.java          |   3 +-
 .../authorization/AuthorizationRequest.java     |  31 ++++
 .../nifi/authorization/AuthorizationResult.java |   8 +-
 .../org/apache/nifi/authorization/Resource.java |   7 +
 .../authorization/resource/Authorizable.java    | 103 +++++++++++--
 .../TestAbstractPolicyBasedAuthorizer.java      |   6 +-
 .../nifi/authorization/FileAuthorizer.java      |   8 +-
 .../nifi/authorization/FileAuthorizerTest.java  |  12 +-
 .../resource/AccessPolicyAuthorizable.java      |   4 +-
 .../resource/DataAuthorizable.java              |   4 +-
 .../authorization/resource/ResourceFactory.java | 153 +++++++++++++++----
 .../nifi/connectable/StandardConnection.java    |   9 +-
 .../nifi/web/StandardNiFiServiceFacade.java     |  36 ++++-
 .../StandardNiFiWebConfigurationContext.java    |   4 +-
 .../org/apache/nifi/web/api/AccessResource.java |   4 +-
 .../apache/nifi/web/api/ControllerResource.java |  15 +-
 .../apache/nifi/web/api/CountersResource.java   |  15 +-
 .../org/apache/nifi/web/api/FlowResource.java   |   4 +-
 .../apache/nifi/web/api/ProvenanceResource.java |   4 +-
 .../apache/nifi/web/api/ResourceResource.java   |   4 +-
 .../apache/nifi/web/api/SiteToSiteResource.java |   4 +-
 .../nifi/web/api/SystemDiagnosticsResource.java |   4 +-
 .../api/config/AccessDeniedExceptionMapper.java |   7 +-
 .../nifi/web/controller/ControllerFacade.java   |   4 +-
 .../src/main/webapp/js/nf/nf-common.js          |  11 +-
 .../PersistentProvenanceRepository.java         |   8 +-
 .../VolatileProvenanceRepository.java           |   8 +-
 .../authorization/RangerNiFiAuthorizer.java     |  10 +-
 .../authorization/TestRangerNiFiAuthorizer.java |  10 ++
 29 files changed, 400 insertions(+), 100 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AbstractPolicyBasedAuthorizer.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AbstractPolicyBasedAuthorizer.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AbstractPolicyBasedAuthorizer.java
index 98b8102..0d047f1 100644
--- a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AbstractPolicyBasedAuthorizer.java
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AbstractPolicyBasedAuthorizer.java
@@ -158,8 +158,7 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
             return AuthorizationResult.approved();
         }
 
-
-        return AuthorizationResult.denied();
+        return AuthorizationResult.denied(request.getExplanationSupplier().get());
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java
index da0a276..4f5a8f8 100644
--- a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java
@@ -20,12 +20,15 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
+import java.util.function.Supplier;
 
 /**
  * Represents an authorization request for a given user/entity performing an action against a resource within some userContext.
  */
 public class AuthorizationRequest {
 
+    public static final String DEFAULT_EXPLANATION = "Unable to perform the desired action.";
+
     private final Resource resource;
     private final String identity;
     private final RequestAction action;
@@ -33,6 +36,7 @@ public class AuthorizationRequest {
     private final boolean isAnonymous;
     private final Map<String, String> userContext;
     private final Map<String, String> resourceContext;
+    private final Supplier<String> explanationSupplier;
 
     private AuthorizationRequest(final Builder builder) {
         Objects.requireNonNull(builder.resource, "The resource is required when creating an authorization request");
@@ -47,6 +51,16 @@ public class AuthorizationRequest {
         this.isAnonymous = builder.isAnonymous;
         this.userContext = builder.userContext == null ? null : Collections.unmodifiableMap(builder.userContext);
         this.resourceContext = builder.resourceContext == null ? null : Collections.unmodifiableMap(builder.resourceContext);
+        this.explanationSupplier = () -> {
+            final String explanation = builder.explanationSupplier.get();
+
+            // ensure the specified supplier returns non null
+            if (explanation == null) {
+                return DEFAULT_EXPLANATION;
+            } else {
+                return explanation;
+            }
+        };
     }
 
     /**
@@ -113,6 +127,15 @@ public class AuthorizationRequest {
     }
 
     /**
+     * A supplier for the explanation if access is denied. Non null.
+     *
+     * @return The explanation supplier if access is denied
+     */
+    public Supplier<String> getExplanationSupplier() {
+        return explanationSupplier;
+    }
+
+    /**
      * AuthorizationRequest builder.
      */
     public static final class Builder {
@@ -124,6 +147,7 @@ public class AuthorizationRequest {
         private RequestAction action;
         private Map<String, String> userContext;
         private Map<String, String> resourceContext;
+        private Supplier<String> explanationSupplier = () -> DEFAULT_EXPLANATION;
 
         public Builder resource(final Resource resource) {
             this.resource = resource;
@@ -164,6 +188,13 @@ public class AuthorizationRequest {
             return this;
         }
 
+        public Builder explanationSupplier(final Supplier<String> explanationSupplier) {
+            if (explanationSupplier != null) {
+                this.explanationSupplier = explanationSupplier;
+            }
+            return this;
+        }
+
         public AuthorizationRequest build() {
             return new AuthorizationRequest(this);
         }

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationResult.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationResult.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationResult.java
index a3f520c..acfdc02 100644
--- a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationResult.java
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationResult.java
@@ -28,7 +28,7 @@ public class AuthorizationResult {
     }
 
     private static final AuthorizationResult APPROVED = new AuthorizationResult(Result.Approved, null);
-    private static final AuthorizationResult RESOURCE_NOT_FOUND = new AuthorizationResult(Result.ResourceNotFound, null);
+    private static final AuthorizationResult RESOURCE_NOT_FOUND = new AuthorizationResult(Result.ResourceNotFound, "Not authorized for the requested resource.");
 
     private final Result result;
     private final String explanation;
@@ -44,6 +44,10 @@ public class AuthorizationResult {
             throw new IllegalArgumentException("An explanation is required when the authorization request is denied.");
         }
 
+        if (Result.ResourceNotFound.equals(result) && explanation == null) {
+            throw new IllegalArgumentException("An explanation is required when the authorization request is resource not found.");
+        }
+
         this.result = result;
         this.explanation = explanation;
     }
@@ -83,7 +87,7 @@ public class AuthorizationResult {
      * @return a new denied AuthorizationResult
      */
     public static AuthorizationResult denied() {
-        return denied("Access is denied");
+        return denied(AuthorizationRequest.DEFAULT_EXPLANATION);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-framework-api/src/main/java/org/apache/nifi/authorization/Resource.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/Resource.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/Resource.java
index 661b326..2aae7b2 100644
--- a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/Resource.java
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/Resource.java
@@ -34,4 +34,11 @@ public interface Resource {
      * @return name of this resource
      */
     String getName();
+
+    /**
+     * The description of this resource that may be safely used in messages to the client.
+     *
+     * @return safe description
+     */
+    String getSafeDescription();
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-framework-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java
index 39e9642..3219ac2 100644
--- a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java
@@ -26,8 +26,8 @@ import org.apache.nifi.authorization.Resource;
 import org.apache.nifi.authorization.UserContextKeys;
 import org.apache.nifi.authorization.user.NiFiUser;
 
-import java.util.Map;
 import java.util.HashMap;
+import java.util.Map;
 
 public interface Authorizable {
 
@@ -70,7 +70,7 @@ public interface Authorizable {
      */
     default AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) {
         if (user == null) {
-            return AuthorizationResult.denied("Unknown user");
+            return AuthorizationResult.denied("Unknown user.");
         }
 
         final Map<String,String> userContext;
@@ -81,15 +81,28 @@ public interface Authorizable {
             userContext = null;
         }
 
-        // build the request
+        final Resource resource = getResource();
         final AuthorizationRequest request = new AuthorizationRequest.Builder()
                 .identity(user.getIdentity())
                 .anonymous(user.isAnonymous())
                 .accessAttempt(false)
                 .action(action)
-                .resource(getResource())
+                .resource(resource)
                 .resourceContext(resourceContext)
                 .userContext(userContext)
+                .explanationSupplier(() -> {
+                    // build the safe explanation
+                    final StringBuilder safeDescription = new StringBuilder("Unable to ");
+
+                    if (RequestAction.READ.equals(action)) {
+                        safeDescription.append("view ");
+                    } else {
+                        safeDescription.append("modify ");
+                    }
+                    safeDescription.append(resource.getSafeDescription()).append(".");
+
+                    return safeDescription.toString();
+                })
                 .build();
 
         // perform the authorization
@@ -99,9 +112,37 @@ public interface Authorizable {
         if (Result.ResourceNotFound.equals(result.getResult())) {
             final Authorizable parent = getParentAuthorizable();
             if (parent == null) {
-                return AuthorizationResult.denied();
+                return AuthorizationResult.denied("No applicable policies could be found.");
             } else {
-                return parent.checkAuthorization(authorizer, action, user, resourceContext);
+                // create a custom authorizable to override the safe description but still defer to the parent authorizable
+                final Authorizable parentProxy = new Authorizable() {
+                    @Override
+                    public Authorizable getParentAuthorizable() {
+                        return parent.getParentAuthorizable();
+                    }
+
+                    @Override
+                    public Resource getResource() {
+                        final Resource parentResource = parent.getResource();
+                        return new Resource() {
+                            @Override
+                            public String getIdentifier() {
+                                return parentResource.getIdentifier();
+                            }
+
+                            @Override
+                            public String getName() {
+                                return parentResource.getName();
+                            }
+
+                            @Override
+                            public String getSafeDescription() {
+                                return resource.getSafeDescription();
+                            }
+                        };
+                    }
+                };
+                return parentProxy.checkAuthorization(authorizer, action, user, resourceContext);
             }
         } else {
             return result;
@@ -133,7 +174,7 @@ public interface Authorizable {
      */
     default void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException {
         if (user == null) {
-            throw new AccessDeniedException("Unknown user");
+            throw new AccessDeniedException("Unknown user.");
         }
 
         final Map<String,String> userContext;
@@ -144,23 +185,65 @@ public interface Authorizable {
             userContext = null;
         }
 
+        final Resource resource = getResource();
         final AuthorizationRequest request = new AuthorizationRequest.Builder()
                 .identity(user.getIdentity())
                 .anonymous(user.isAnonymous())
                 .accessAttempt(true)
                 .action(action)
-                .resource(getResource())
+                .resource(resource)
                 .resourceContext(resourceContext)
                 .userContext(userContext)
+                .explanationSupplier(() -> {
+                    // build the safe explanation
+                    final StringBuilder safeDescription = new StringBuilder("Unable to ");
+
+                    if (RequestAction.READ.equals(action)) {
+                        safeDescription.append("view ");
+                    } else {
+                        safeDescription.append("modify ");
+                    }
+                    safeDescription.append(resource.getSafeDescription()).append(".");
+
+                    return safeDescription.toString();
+                })
                 .build();
 
         final AuthorizationResult result = authorizer.authorize(request);
         if (Result.ResourceNotFound.equals(result.getResult())) {
             final Authorizable parent = getParentAuthorizable();
             if (parent == null) {
-                throw new AccessDeniedException("Access is denied");
+                throw new AccessDeniedException("No applicable policies could be found.");
             } else {
-                parent.authorize(authorizer, action, user, resourceContext);
+                // create a custom authorizable to override the safe description but still defer to the parent authorizable
+                final Authorizable parentProxy = new Authorizable() {
+                    @Override
+                    public Authorizable getParentAuthorizable() {
+                        return parent.getParentAuthorizable();
+                    }
+
+                    @Override
+                    public Resource getResource() {
+                        final Resource parentResource = parent.getResource();
+                        return new Resource() {
+                            @Override
+                            public String getIdentifier() {
+                                return parentResource.getIdentifier();
+                            }
+
+                            @Override
+                            public String getName() {
+                                return parentResource.getName();
+                            }
+
+                            @Override
+                            public String getSafeDescription() {
+                                return resource.getSafeDescription();
+                            }
+                        };
+                    }
+                };
+                parentProxy.authorize(authorizer, action, user, resourceContext);
             }
         } else if (Result.Denied.equals(result.getResult())) {
             throw new AccessDeniedException(result.getExplanation());

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-framework-api/src/test/java/org/apache/nifi/authorization/TestAbstractPolicyBasedAuthorizer.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/test/java/org/apache/nifi/authorization/TestAbstractPolicyBasedAuthorizer.java b/nifi-framework-api/src/test/java/org/apache/nifi/authorization/TestAbstractPolicyBasedAuthorizer.java
index a5cf351..cda2ac5 100644
--- a/nifi-framework-api/src/test/java/org/apache/nifi/authorization/TestAbstractPolicyBasedAuthorizer.java
+++ b/nifi-framework-api/src/test/java/org/apache/nifi/authorization/TestAbstractPolicyBasedAuthorizer.java
@@ -16,7 +16,6 @@
  */
 package org.apache.nifi.authorization;
 
-import org.apache.nifi.authorization.MockPolicyBasedAuthorizer;
 import org.apache.nifi.authorization.exception.AuthorizerCreationException;
 import org.junit.Assert;
 import org.junit.Test;
@@ -42,6 +41,11 @@ public class TestAbstractPolicyBasedAuthorizer {
         public String getName() {
             return "resource1";
         }
+
+        @Override
+        public String getSafeDescription() {
+            return "description1";
+        }
     };
 
     @Test

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java
index 7f89ddd..be20b60 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java
@@ -447,8 +447,12 @@ public class FileAuthorizer extends AbstractPolicyBasedAuthorizer {
 
         // convert any access controls on ports to the appropriate policies
         for (PortDTO portDTO : ports) {
-            final boolean isInputPort = portDTO.getType() != null && portDTO.getType().equals("inputPort");
-            final Resource resource = ResourceFactory.getDataTransferResource(isInputPort, portDTO.getId(), portDTO.getName());
+            final Resource resource;
+            if (portDTO.getType() != null && portDTO.getType().equals("inputPort")) {
+                resource = ResourceFactory.getDataTransferResource(ResourceFactory.getComponentResource(ResourceType.InputPort, portDTO.getId(), portDTO.getName()));
+            } else {
+                resource = ResourceFactory.getDataTransferResource(ResourceFactory.getComponentResource(ResourceType.OutputPort, portDTO.getId(), portDTO.getName()));
+            }
 
             if (portDTO.getUserAccessControl() != null) {
                 for (String userAccessControl : portDTO.getUserAccessControl()) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/test/java/org/apache/nifi/authorization/FileAuthorizerTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/test/java/org/apache/nifi/authorization/FileAuthorizerTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/test/java/org/apache/nifi/authorization/FileAuthorizerTest.java
index c335e19..55a1839 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/test/java/org/apache/nifi/authorization/FileAuthorizerTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/test/java/org/apache/nifi/authorization/FileAuthorizerTest.java
@@ -358,7 +358,8 @@ public class FileAuthorizerTest {
         assertEquals(2, user6Policies.get(ResourceType.SiteToSite.getValue()).size());
         assertTrue(user6Policies.get(ResourceType.SiteToSite.getValue()).contains(RequestAction.WRITE));
 
-        final Resource inputPortResource = ResourceFactory.getDataTransferResource(true, "2f7d1606-b090-4be7-a592-a5b70fb55531", "TCP Input");
+        final Resource inputPortResource = ResourceFactory.getDataTransferResource(
+                ResourceFactory.getComponentResource(ResourceType.InputPort, "2f7d1606-b090-4be7-a592-a5b70fb55531", "TCP Input"));
         final AccessPolicy inputPortPolicy = authorizer.getUsersAndAccessPolicies().getAccessPolicy(inputPortResource.getIdentifier(), RequestAction.WRITE);
         assertNotNull(inputPortPolicy);
         assertEquals(1, inputPortPolicy.getUsers().size());
@@ -366,7 +367,8 @@ public class FileAuthorizerTest {
         assertEquals(1, inputPortPolicy.getGroups().size());
         assertTrue(inputPortPolicy.getGroups().contains(group1.getIdentifier()));
 
-        final Resource outputPortResource = ResourceFactory.getDataTransferResource(false, "2f7d1606-b090-4be7-a592-a5b70fb55532", "TCP Output");
+        final Resource outputPortResource = ResourceFactory.getDataTransferResource(
+                ResourceFactory.getComponentResource(ResourceType.OutputPort, "2f7d1606-b090-4be7-a592-a5b70fb55532", "TCP Output"));
         final AccessPolicy outputPortPolicy = authorizer.getUsersAndAccessPolicies().getAccessPolicy(outputPortResource.getIdentifier(), RequestAction.WRITE);
         assertNotNull(outputPortPolicy);
         assertEquals(1, outputPortPolicy.getUsers().size());
@@ -432,7 +434,8 @@ public class FileAuthorizerTest {
         final Group group1 = groups.iterator().next();
         assertEquals("group1", group1.getName());
 
-        final Resource inputPortResource = ResourceFactory.getDataTransferResource(true, "2f7d1606-b090-4be7-a592-a5b70fb55531", "TCP Input");
+        final Resource inputPortResource = ResourceFactory.getDataTransferResource(
+                ResourceFactory.getComponentResource(ResourceType.InputPort, "2f7d1606-b090-4be7-a592-a5b70fb55531", "TCP Input"));
         final AccessPolicy inputPortPolicy = authorizer.getUsersAndAccessPolicies().getAccessPolicy(inputPortResource.getIdentifier(), RequestAction.WRITE);
         assertNotNull(inputPortPolicy);
         assertEquals(1, inputPortPolicy.getUsers().size());
@@ -440,7 +443,8 @@ public class FileAuthorizerTest {
         assertEquals(1, inputPortPolicy.getGroups().size());
         assertTrue(inputPortPolicy.getGroups().contains(group1.getIdentifier()));
 
-        final Resource outputPortResource = ResourceFactory.getDataTransferResource(false, "2f7d1606-b090-4be7-a592-a5b70fb55532", "TCP Output");
+        final Resource outputPortResource = ResourceFactory.getDataTransferResource(
+                ResourceFactory.getComponentResource(ResourceType.OutputPort, "2f7d1606-b090-4be7-a592-a5b70fb55532", "TCP Output"));
         final AccessPolicy outputPortPolicy = authorizer.getUsersAndAccessPolicies().getAccessPolicy(outputPortResource.getIdentifier(), RequestAction.WRITE);
         assertNotNull(outputPortPolicy);
         assertEquals(1, outputPortPolicy.getUsers().size());

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/AccessPolicyAuthorizable.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/AccessPolicyAuthorizable.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/AccessPolicyAuthorizable.java
index 041b982..81f8a13 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/AccessPolicyAuthorizable.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/AccessPolicyAuthorizable.java
@@ -89,7 +89,7 @@ public class AccessPolicyAuthorizable implements Authorizable, EnforcePolicyPerm
     @Override
     public AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) {
         if (user == null) {
-            throw new AccessDeniedException("Unknown user");
+            throw new AccessDeniedException("Unknown user.");
         }
 
         final AuthorizationResult resourceResult = Authorizable.super.checkAuthorization(authorizer, action, user, resourceContext);
@@ -105,7 +105,7 @@ public class AccessPolicyAuthorizable implements Authorizable, EnforcePolicyPerm
     @Override
     public void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException {
         if (user == null) {
-            throw new AccessDeniedException("Unknown user");
+            throw new AccessDeniedException("Unknown user.");
         }
 
         try {

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/DataAuthorizable.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/DataAuthorizable.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/DataAuthorizable.java
index cb0d0f1..7269560 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/DataAuthorizable.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/DataAuthorizable.java
@@ -62,7 +62,7 @@ public class DataAuthorizable implements Authorizable, EnforcePolicyPermissionsT
     @Override
     public AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) {
         if (user == null) {
-            return AuthorizationResult.denied("Unknown user");
+            return AuthorizationResult.denied("Unknown user.");
         }
 
         AuthorizationResult result = null;
@@ -100,7 +100,7 @@ public class DataAuthorizable implements Authorizable, EnforcePolicyPermissionsT
     @Override
     public void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException {
         if (user == null) {
-            throw new AccessDeniedException("Unknown user");
+            throw new AccessDeniedException("Unknown user.");
         }
 
         // calculate the dn chain

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceFactory.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceFactory.java
index 83c5bb9..79392b7 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceFactory.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceFactory.java
@@ -32,6 +32,11 @@ public final class ResourceFactory {
         public String getName() {
             return "Controller";
         }
+
+        @Override
+        public String getSafeDescription() {
+            return "the controller";
+        }
     };
 
     private final static Resource FLOW_RESOURCE = new Resource() {
@@ -44,6 +49,11 @@ public final class ResourceFactory {
         public String getName() {
             return "NiFi Flow";
         }
+
+        @Override
+        public String getSafeDescription() {
+            return "the user interface";
+        }
     };
 
     private final static Resource POLICY_RESOURCE = new Resource() {
@@ -54,7 +64,12 @@ public final class ResourceFactory {
 
         @Override
         public String getName() {
-            return "Policy";
+            return "Policies for ";
+        }
+
+        @Override
+        public String getSafeDescription() {
+            return "the policies for ";
         }
     };
 
@@ -68,6 +83,11 @@ public final class ResourceFactory {
         public String getName() {
             return "Counters";
         }
+
+        @Override
+        public String getSafeDescription() {
+            return "counters";
+        }
     };
 
     private final static Resource PROVENANCE_RESOURCE = new Resource() {
@@ -80,6 +100,11 @@ public final class ResourceFactory {
         public String getName() {
             return "Provenance";
         }
+
+        @Override
+        public String getSafeDescription() {
+            return "provenance";
+        }
     };
 
     private final static Resource DATA_RESOURCE = new Resource() {
@@ -90,7 +115,12 @@ public final class ResourceFactory {
 
         @Override
         public String getName() {
-            return "Data";
+            return "Data for ";
+        }
+
+        @Override
+        public String getSafeDescription() {
+            return "the data for ";
         }
     };
 
@@ -104,6 +134,11 @@ public final class ResourceFactory {
         public String getName() {
             return "Proxy User Requests";
         }
+
+        @Override
+        public String getSafeDescription() {
+            return "proxy requests on behalf of users";
+        }
     };
 
     private final static Resource RESOURCE_RESOURCE = new Resource() {
@@ -116,6 +151,11 @@ public final class ResourceFactory {
         public String getName() {
             return "NiFi Resources";
         }
+
+        @Override
+        public String getSafeDescription() {
+            return "resources";
+        }
     };
 
     private final static Resource SITE_TO_SITE_RESOURCE = new Resource() {
@@ -128,6 +168,11 @@ public final class ResourceFactory {
         public String getName() {
             return "Site to Site";
         }
+
+        @Override
+        public String getSafeDescription() {
+            return "site-to-site details";
+        }
     };
 
     private final static Resource SYSTEM_RESOURCE = new Resource() {
@@ -140,6 +185,11 @@ public final class ResourceFactory {
         public String getName() {
             return "System";
         }
+
+        @Override
+        public String getSafeDescription() {
+            return "system diagnostics";
+        }
     };
 
     private final static Resource RESTRICTED_COMPONENTS_RESOURCE = new Resource() {
@@ -152,6 +202,11 @@ public final class ResourceFactory {
         public String getName() {
             return "Restricted Components";
         }
+
+        @Override
+        public String getSafeDescription() {
+            return "restricted components";
+        }
     };
 
     private final static Resource TENANT_RESOURCE = new Resource() {
@@ -164,6 +219,11 @@ public final class ResourceFactory {
         public String getName() {
             return "Tenant";
         }
+
+        @Override
+        public String getSafeDescription() {
+            return "users/user groups";
+        }
     };
 
     private final static Resource POLICIES_RESOURCE = new Resource() {
@@ -177,6 +237,11 @@ public final class ResourceFactory {
         public String getName() {
             return "Access Policies";
         }
+
+        @Override
+        public String getSafeDescription() {
+            return "policies";
+        }
     };
 
     /**
@@ -274,30 +339,6 @@ public final class ResourceFactory {
     /**
      * Gets a Resource for performing transferring data to a port.
      *
-     * @param isInputPort   Whether this port is an input port or an output port
-     * @param identifier    The identifier of the component being accessed
-     * @param name          The name of the component being accessed
-     * @return              The resource
-     */
-    public static Resource getDataTransferResource(final boolean isInputPort, final String identifier, final String name) {
-        Objects.requireNonNull(identifier, "The component identifier must be specified.");
-
-        return new Resource() {
-            @Override
-            public String getIdentifier() {
-                return String.format("%s/%s/%s", ResourceType.DataTransfer.getValue(), isInputPort ? "input-ports" : "output-ports", identifier);
-            }
-
-            @Override
-            public String getName() {
-                return name;
-            }
-        };
-    }
-
-    /**
-     * Gets a Resource for performing transferring data to a port.
-     *
      * @param resource      The resource to transfer data to
      * @return              The resource
      */
@@ -314,6 +355,11 @@ public final class ResourceFactory {
             public String getName() {
                 return "Transfer data to " + resource.getName();
             }
+
+            @Override
+            public String getSafeDescription() {
+                return "data transfers to " + resource.getSafeDescription();
+            }
         };
     }
 
@@ -342,7 +388,12 @@ public final class ResourceFactory {
 
             @Override
             public String getName() {
-                return "Policies for " + resource.getName();
+                return POLICY_RESOURCE.getName() + resource.getName();
+            }
+
+            @Override
+            public String getSafeDescription() {
+                return POLICY_RESOURCE.getSafeDescription() + resource.getSafeDescription();
             }
         };
     }
@@ -369,6 +420,47 @@ public final class ResourceFactory {
             public String getName() {
                 return name;
             }
+
+            @Override
+            public String getSafeDescription() {
+                final String componentType;
+                switch (resourceType) {
+                    case ControllerService:
+                        componentType = "Controller Service";
+                        break;
+                    case ProcessGroup:
+                        componentType = "Process Group";
+                        break;
+                    case Template:
+                        componentType = "Template";
+                        break;
+                    case Funnel:
+                        componentType = "Funnel";
+                        break;
+                    case InputPort:
+                        componentType = "Input Port";
+                        break;
+                    case OutputPort:
+                        componentType = "Output Port";
+                        break;
+                    case Processor:
+                        componentType = "Processor";
+                        break;
+                    case RemoteProcessGroup:
+                        componentType = "Remote Process Group";
+                        break;
+                    case ReportingTask:
+                        componentType = "Reporting Task";
+                        break;
+                    case Label:
+                        componentType = "Label";
+                        break;
+                    default:
+                        componentType = "Component";
+                        break;
+                }
+                return componentType + " with ID " + identifier;
+            }
         };
     }
 
@@ -387,7 +479,12 @@ public final class ResourceFactory {
 
             @Override
             public String getName() {
-                return "Data for " + resource.getName();
+                return DATA_RESOURCE.getName() + resource.getName();
+            }
+
+            @Override
+            public String getSafeDescription() {
+                return DATA_RESOURCE.getSafeDescription() + resource.getSafeDescription();
             }
         };
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/connectable/StandardConnection.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/connectable/StandardConnection.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/connectable/StandardConnection.java
index 3d5efed..ddb4523 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/connectable/StandardConnection.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/connectable/StandardConnection.java
@@ -136,6 +136,11 @@ public final class StandardConnection implements Connection {
 
                 return name;
             }
+
+            @Override
+            public String getSafeDescription() {
+                return "Connection " + StandardConnection.this.getIdentifier();
+            }
         };
     }
 
@@ -172,7 +177,7 @@ public final class StandardConnection implements Connection {
     @Override
     public AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) {
         if (user == null) {
-            return AuthorizationResult.denied("Unknown user");
+            return AuthorizationResult.denied("Unknown user.");
         }
 
         // check the source
@@ -188,7 +193,7 @@ public final class StandardConnection implements Connection {
     @Override
     public void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException {
         if (user == null) {
-            throw new AccessDeniedException("Unknown user");
+            throw new AccessDeniedException("Unknown user.");
         }
 
         getSourceAuthorizable().authorize(authorizer, action, user, resourceContext);

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/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 67f768a..c8aad31 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
@@ -963,6 +963,11 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
                     public String getName() {
                         return resourceIdentifier;
                     }
+
+                    @Override
+                    public String getSafeDescription() {
+                        return "User " + userId;
+                    }
                 },
                 () -> userDAO.deleteUser(userId),
                 false, // no user specific policies to remove
@@ -993,6 +998,11 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
                     public String getName() {
                         return resourceIdentifier;
                     }
+
+                    @Override
+                    public String getSafeDescription() {
+                        return "User Group " + userGroupId;
+                    }
                 },
                 () -> userGroupDAO.deleteUserGroup(userGroupId),
                 false, // no user group specific policies to remove
@@ -1020,6 +1030,11 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
                     public String getName() {
                         return accessPolicy.getResource();
                     }
+
+                    @Override
+                    public String getSafeDescription() {
+                        return "Policy " + accessPolicyId;
+                    }
                 },
                 () -> accessPolicyDAO.deleteAccessPolicy(accessPolicyId),
                 false, // no need to clean up any policies as it's already been removed above
@@ -2514,6 +2529,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
                 .accessAttempt(false)
                 .action(RequestAction.WRITE)
                 .userContext(userContext)
+                .explanationSupplier(() -> "Unable to retrieve port details.")
                 .build();
 
         final AuthorizationResult result = authorizer.authorize(request);
@@ -2680,6 +2696,11 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
                         public String getName() {
                             return resource;
                         }
+
+                        @Override
+                        public String getSafeDescription() {
+                            return "Policy " + resource;
+                        }
                     };
                 }
             };
@@ -3100,7 +3121,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
         return entityFactory.createStatusHistoryEntity(dto, permissions);
     }
 
-    private boolean authorizeAction(final Action action) {
+    private AuthorizationResult authorizeAction(final Action action) {
         final String sourceId = action.getSourceId();
         final Component type = action.getSourceType();
 
@@ -3149,12 +3170,11 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
             }
         } catch (final ResourceNotFoundException e) {
             // if the underlying component is gone, disallow
-            return false;
+            return AuthorizationResult.denied("The component of this action is no longer in the data flow.");
         }
 
         // perform the authorization
-        final AuthorizationResult result = authorizable.checkAuthorization(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
-        return Result.Approved.equals(result.getResult());
+        return authorizable.checkAuthorization(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
     }
 
     @Override
@@ -3178,7 +3198,8 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
         if (history.getActions() != null) {
             final List<ActionEntity> actionEntities = new ArrayList<>();
             for (final Action action : history.getActions()) {
-                actionEntities.add(entityFactory.createActionEntity(dtoFactory.createActionDto(action), authorizeAction(action)));
+                final AuthorizationResult result = authorizeAction(action);
+                actionEntities.add(entityFactory.createActionEntity(dtoFactory.createActionDto(action), Result.Approved.equals(result.getResult())));
             }
             historyDto.setActions(actionEntities);
         }
@@ -3197,9 +3218,10 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
             throw new ResourceNotFoundException(String.format("Unable to find action with id '%s'.", actionId));
         }
 
-        final boolean authorized = authorizeAction(action);
+        final AuthorizationResult result = authorizeAction(action);
+        final boolean authorized = Result.Approved.equals(result.getResult());
         if (!authorized) {
-            throw new AccessDeniedException("Access is denied.");
+            throw new AccessDeniedException(result.getExplanation());
         }
 
         // return the action

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/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 177e557..8146a39 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
@@ -115,12 +115,12 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
                     .accessAttempt(true)
                     .action(RequestAction.READ)
                     .userContext(userContext)
+                    .explanationSupplier(() -> "Unable to view the user interface.")
                     .build();
 
             final AuthorizationResult result = authorizer.authorize(request);
             if (!Result.Approved.equals(result.getResult())) {
-                final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied";
-                throw new AccessDeniedException(message);
+                throw new AccessDeniedException(result.getExplanation());
             }
         });
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/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 5c108a4..529f049 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
@@ -252,7 +252,7 @@ public class AccessResource extends ApplicationResource {
 
         final NiFiUser user = NiFiUserUtils.getNiFiUser();
         if (user == null) {
-            throw new AccessDeniedException("Unable to determine user details.");
+            throw new AccessDeniedException("No user authenticated in the request.");
         }
 
         final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(user.getIdentity());
@@ -297,7 +297,7 @@ public class AccessResource extends ApplicationResource {
 
         final NiFiUser user = NiFiUserUtils.getNiFiUser();
         if (user == null) {
-            throw new AccessDeniedException("Unable to determine user details.");
+            throw new AccessDeniedException("No user authenticated in the request.");
         }
 
         final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(user.getIdentity());

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/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 20b9f87..86ca792 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
@@ -112,12 +112,23 @@ public class ControllerResource extends ApplicationResource {
                 .accessAttempt(true)
                 .action(action)
                 .userContext(userContext)
+                .explanationSupplier(() -> {
+                    final StringBuilder explanation = new StringBuilder("Unable to ");
+
+                    if (RequestAction.READ.equals(action)) {
+                        explanation.append("view ");
+                    } else {
+                        explanation.append("modify ");
+                    }
+                    explanation.append("the controller.");
+
+                    return explanation.toString();
+                })
                 .build();
 
         final AuthorizationResult result = authorizer.authorize(request);
         if (!Result.Approved.equals(result.getResult())) {
-            final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied";
-            throw new AccessDeniedException(message);
+            throw new AccessDeniedException(result.getExplanation());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/CountersResource.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/CountersResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/CountersResource.java
index e3a2b9e..3d6c3be 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/CountersResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/CountersResource.java
@@ -98,12 +98,23 @@ public class CountersResource extends ApplicationResource {
                 .accessAttempt(true)
                 .action(action)
                 .userContext(userContext)
+                .explanationSupplier(() -> {
+                    final StringBuilder explanation = new StringBuilder("Unable to ");
+
+                    if (RequestAction.READ.equals(action)) {
+                        explanation.append("view ");
+                    } else {
+                        explanation.append("modify ");
+                    }
+                    explanation.append("counters.");
+
+                    return explanation.toString();
+                })
                 .build();
 
         final AuthorizationResult result = authorizer.authorize(request);
         if (!Result.Approved.equals(result.getResult())) {
-            final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied";
-            throw new AccessDeniedException(message);
+            throw new AccessDeniedException(result.getExplanation());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.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/FlowResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java
index 1b9bc99..a031725 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java
@@ -218,12 +218,12 @@ public class FlowResource extends ApplicationResource {
                 .accessAttempt(true)
                 .action(RequestAction.READ)
                 .userContext(userContext)
+                .explanationSupplier(() -> "Unable to view the user interface.")
                 .build();
 
         final AuthorizationResult result = authorizer.authorize(request);
         if (!Result.Approved.equals(result.getResult())) {
-            final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied";
-            throw new AccessDeniedException(message);
+            throw new AccessDeniedException(result.getExplanation());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.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/ProvenanceResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java
index 4e7a171..9044bbe 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java
@@ -108,12 +108,12 @@ public class ProvenanceResource extends ApplicationResource {
                 .accessAttempt(true)
                 .action(RequestAction.READ)
                 .userContext(userContext)
+                .explanationSupplier(() -> "Unable to query provenance.")
                 .build();
 
         final AuthorizationResult result = authorizer.authorize(request);
         if (!Result.Approved.equals(result.getResult())) {
-            final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied";
-            throw new AccessDeniedException(message);
+            throw new AccessDeniedException(result.getExplanation());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ResourceResource.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/ResourceResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ResourceResource.java
index 67c1b22..cd41ed9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ResourceResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ResourceResource.java
@@ -78,12 +78,12 @@ public class ResourceResource extends ApplicationResource {
                 .accessAttempt(true)
                 .action(RequestAction.READ)
                 .userContext(userContext)
+                .explanationSupplier(() -> "Unable to retrieve resources.")
                 .build();
 
         final AuthorizationResult result = authorizer.authorize(request);
         if (!Result.Approved.equals(result.getResult())) {
-            final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied";
-            throw new AccessDeniedException(message);
+            throw new AccessDeniedException(result.getExplanation());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SiteToSiteResource.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/SiteToSiteResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SiteToSiteResource.java
index efb1c26..744a9f4 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SiteToSiteResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SiteToSiteResource.java
@@ -116,12 +116,12 @@ public class SiteToSiteResource extends ApplicationResource {
                 .accessAttempt(true)
                 .action(RequestAction.READ)
                 .userContext(userContext)
+                .explanationSupplier(() -> "Unable to retrieve site to site details.")
                 .build();
 
         final AuthorizationResult result = authorizer.authorize(request);
         if (!Result.Approved.equals(result.getResult())) {
-            final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied";
-            throw new AccessDeniedException(message);
+            throw new AccessDeniedException(result.getExplanation());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.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/SystemDiagnosticsResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.java
index 0be9c49..04e8683 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.java
@@ -81,12 +81,12 @@ public class SystemDiagnosticsResource extends ApplicationResource {
                 .accessAttempt(true)
                 .action(RequestAction.READ)
                 .userContext(userContext)
+                .explanationSupplier(() -> "Unable to view system diagnostics.")
                 .build();
 
         final AuthorizationResult result = authorizer.authorize(request);
         if (!Result.Approved.equals(result.getResult())) {
-            final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied";
-            throw new AccessDeniedException(message);
+            throw new AccessDeniedException(result.getExplanation());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/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 d55a386..9c5ba4d 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
@@ -58,13 +58,16 @@ public class AccessDeniedExceptionMapper implements ExceptionMapper<AccessDenied
             identity = user.getIdentity();
         }
 
-        logger.info(String.format("%s does not have permission to access the requested resource. Returning %s response.", identity, status));
+        logger.info(String.format("%s does not have permission to access the requested resource. %s Returning %s response.", identity, exception.getMessage(), status));
 
         if (logger.isDebugEnabled()) {
             logger.debug(StringUtils.EMPTY, exception);
         }
 
-        return Response.status(status).entity("Unable to perform the desired action due to insufficient permissions. Contact the system administrator.").type("text/plain").build();
+        return Response.status(status)
+                .entity(String.format("%s Contact the system administrator.", exception.getMessage()))
+                .type("text/plain")
+                .build();
     }
 
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/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 55a0235..93d86c9 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
@@ -1241,7 +1241,7 @@ public class ControllerFacade implements Authorizable {
     private AuthorizationResult checkAuthorizationForReplay(final ProvenanceEventRecord event) {
         // if the connection id isn't specified, then the replay wouldn't be available anyways and we have nothing to authorize against so deny it`
         if (event.getSourceQueueIdentifier() == null) {
-            return AuthorizationResult.denied();
+            return AuthorizationResult.denied("The connection id in the provenance event is unknown.");
         }
 
         final NiFiUser user = NiFiUserUtils.getNiFiUser();
@@ -1272,7 +1272,7 @@ public class ControllerFacade implements Authorizable {
     private void authorizeReplay(final ProvenanceEventRecord event) {
         // if the connection id isn't specified, then the replay wouldn't be available anyways and we have nothing to authorize against so deny it`
         if (event.getSourceQueueIdentifier() == null) {
-            throw new AccessDeniedException("The connection id is unknown.");
+            throw new AccessDeniedException("The connection id in the provenance event is unknown.");
         }
 
         final NiFiUser user = NiFiUserUtils.getNiFiUser();

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/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 0b0e614..faecda7 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
@@ -514,7 +514,7 @@ nf.Common = (function () {
                 if (xhr.status === 401) {
                     $('#message-title').text('Unauthorized');
                 } else if (xhr.status === 403) {
-                    $('#message-title').text('Access Denied');
+                    $('#message-title').text('Insufficient Permissions');
                 } else if (xhr.status === 409) {
                     $('#message-title').text('Invalid State');
                 } else {
@@ -535,12 +535,17 @@ nf.Common = (function () {
                 return;
             }
 
-            // status code 400, 403, 404, and 409 are expected response codes for common errors.
-            if (xhr.status === 400 || xhr.status === 403 || xhr.status === 404 || xhr.status === 409 || xhr.status === 503) {
+            // status code 400, 404, and 409 are expected response codes for common errors.
+            if (xhr.status === 400 || xhr.status === 404 || xhr.status === 409 || xhr.status === 503) {
                 nf.Dialog.showOkDialog({
                     headerText: 'Error',
                     dialogContent: nf.Common.escapeHtml(xhr.responseText)
                 });
+            } else if (xhr.status === 403) {
+                nf.Dialog.showOkDialog({
+                    headerText: 'Insufficient Permissions',
+                    dialogContent: nf.Common.escapeHtml(xhr.responseText)
+                });
             } 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.';

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/PersistentProvenanceRepository.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/PersistentProvenanceRepository.java b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/PersistentProvenanceRepository.java
index 03cc3b7..282fabc 100644
--- a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/PersistentProvenanceRepository.java
+++ b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/PersistentProvenanceRepository.java
@@ -2384,14 +2384,14 @@ public class PersistentProvenanceRepository implements ProvenanceRepository {
         }
 
         if (user == null) {
-            throw new AccessDeniedException("Cannot retrieve Provenance Lineage Submission because no user id was provided");
+            throw new AccessDeniedException("Cannot retrieve Provenance Lineage Submission because no user id was provided in the lineage request.");
         }
 
         if (userId == null || userId.equals(user.getIdentity())) {
             return submission;
         }
 
-        throw new AccessDeniedException("Cannot retrieve Provenance Lineage Submission because " + user.getIdentity() + " is not the user who submitted the request");
+        throw new AccessDeniedException("Cannot retrieve Provenance Lineage Submission because " + user.getIdentity() + " is not the user who submitted the request.");
     }
 
     @Override
@@ -2405,14 +2405,14 @@ public class PersistentProvenanceRepository implements ProvenanceRepository {
         }
 
         if (user == null) {
-            throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because no user id was provided");
+            throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because no user id was provided in the provenance request.");
         }
 
         if (userId == null || userId.equals(user.getIdentity())) {
             return submission;
         }
 
-        throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because " + user.getIdentity() + " is not the user who submitted the request");
+        throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because " + user.getIdentity() + " is not the user who submitted the request.");
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-volatile-provenance-repository/src/main/java/org/apache/nifi/provenance/VolatileProvenanceRepository.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-volatile-provenance-repository/src/main/java/org/apache/nifi/provenance/VolatileProvenanceRepository.java b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-volatile-provenance-repository/src/main/java/org/apache/nifi/provenance/VolatileProvenanceRepository.java
index f889e8b..e467676 100644
--- a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-volatile-provenance-repository/src/main/java/org/apache/nifi/provenance/VolatileProvenanceRepository.java
+++ b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-volatile-provenance-repository/src/main/java/org/apache/nifi/provenance/VolatileProvenanceRepository.java
@@ -460,14 +460,14 @@ public class VolatileProvenanceRepository implements ProvenanceRepository {
         }
 
         if (user == null) {
-            throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because no user id was provided");
+            throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because no user id was provided in the provenance request.");
         }
 
         if (userId == null || userId.equals(user.getIdentity())) {
             return submission;
         }
 
-        throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because " + user.getIdentity() + " is not the user who submitted the request");
+        throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because " + user.getIdentity() + " is not the user who submitted the request.");
     }
 
     public Lineage computeLineage(final String flowFileUUID, final NiFiUser user) throws IOException {
@@ -520,14 +520,14 @@ public class VolatileProvenanceRepository implements ProvenanceRepository {
         }
 
         if (user == null) {
-            throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because no user id was provided");
+            throw new AccessDeniedException("Cannot retrieve Provenance Lineage Submission because no user id was provided in the lineage request.");
         }
 
         if (userId == null || userId.equals(user.getIdentity())) {
             return submission;
         }
 
-        throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because " + user.getIdentity() + " is not the user who submitted the request");
+        throw new AccessDeniedException("Cannot retrieve Provenance Lineage Submission because " + user.getIdentity() + " is not the user who submitted the request.");
     }
 
     public Lineage expandSpawnEventParents(String identifier) throws IOException {

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerNiFiAuthorizer.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerNiFiAuthorizer.java b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerNiFiAuthorizer.java
index a86423c..41b8106 100644
--- a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerNiFiAuthorizer.java
+++ b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerNiFiAuthorizer.java
@@ -179,13 +179,13 @@ public class RangerNiFiAuthorizer implements Authorizer {
             final boolean doesPolicyExist = nifiPlugin.doesPolicyExist(request.getResource().getIdentifier());
 
             if (doesPolicyExist) {
-                // a policy does exist for the resource so we were really denied access here
                 final String reason = result == null ? null : result.getReason();
-                if (reason == null) {
-                    return AuthorizationResult.denied();
-                } else {
-                    return AuthorizationResult.denied(result.getReason());
+                if (reason != null) {
+                    logger.debug(String.format("Unable to authorize %s due to %s", identity, reason));
                 }
+
+                // a policy does exist for the resource so we were really denied access here
+                return AuthorizationResult.denied(request.getExplanationSupplier().get());
             } else {
                 // a policy doesn't exist so return resource not found so NiFi can work back up the resource hierarchy
                 return AuthorizationResult.resourceNotFound();

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1c9f0e7/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerNiFiAuthorizer.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerNiFiAuthorizer.java b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerNiFiAuthorizer.java
index 46084c4..679d2cb 100644
--- a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerNiFiAuthorizer.java
+++ b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerNiFiAuthorizer.java
@@ -467,6 +467,11 @@ public class TestRangerNiFiAuthorizer {
                         public String getName() {
                             return "/system";
                         }
+
+                        @Override
+                        public String getSafeDescription() {
+                            return "system";
+                        }
                     })
                     .action(RequestAction.WRITE)
                     .identity("admin")
@@ -526,6 +531,11 @@ public class TestRangerNiFiAuthorizer {
         public String getName() {
             return name;
         }
+
+        @Override
+        public String getSafeDescription() {
+            return name;
+        }
     }
 
     /**