NIFI-4885:
- Introducing more granular restricted component access policies.

This closes #2515.

Signed-off-by: Mark Payne <marka...@hotmail.com>


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

Branch: refs/heads/master
Commit: b1217f529bfc5ea9296d1d55c6b0fe92a881a485
Parents: d4632bd
Author: Matt Gilman <matt.c.gil...@gmail.com>
Authored: Fri Feb 16 16:21:47 2018 -0500
Committer: Mark Payne <marka...@hotmail.com>
Committed: Mon Mar 12 14:19:42 2018 -0400

----------------------------------------------------------------------
 .../nifi/annotation/behavior/Restricted.java    |  12 +-
 .../nifi/annotation/behavior/Restriction.java   |  47 +++++
 .../nifi/components/RequiredPermission.java     |  52 +++++
 .../src/main/asciidoc/administration-guide.adoc |   5 +-
 nifi-docs/src/main/asciidoc/user-guide.adoc     |  23 ++-
 .../nifi/processors/flume/ExecuteFlumeSink.java |  10 +-
 .../processors/flume/ExecuteFlumeSource.java    |  10 +-
 .../dto/ComponentRestrictionPermissionDTO.java  |  82 ++++++++
 .../nifi/web/api/dto/DocumentedTypeDTO.java     |  32 ++-
 .../web/api/dto/ExplicitRestrictionDTO.java     |  59 ++++++
 .../nifi/web/api/dto/RequiredPermissionDTO.java |  82 ++++++++
 .../nifi/web/api/entity/CurrentUserEntity.java  |  15 ++
 .../html/HtmlDocumentationWriter.java           |  28 ++-
 .../example/FullyDocumentedProcessor.java       |  17 +-
 .../html/ProcessorDocumentationWriterTest.java  |   3 +
 .../authorization/resource/ResourceFactory.java |  26 +++
 .../RestrictedComponentsAuthorizable.java       |  33 ----
 ...RestrictedComponentsAuthorizableFactory.java | 119 ++++++++++++
 .../endpoints/CurrentUserEndpointMerger.java    |  18 ++
 .../CurrentUserEndpointMergerTest.java          | 126 ++++++++++++
 .../nifi/controller/ConfiguredComponent.java    |  28 ++-
 .../nifi/controller/StandardProcessorNode.java  |   5 +
 .../reporting/StandardReportingTaskNode.java    |   5 +
 .../service/StandardControllerServiceNode.java  |   5 +
 .../nifi/authorization/AuthorizableLookup.java  |   9 +
 .../authorization/ComponentAuthorizable.java    |   8 +
 .../StandardAuthorizableLookup.java             |  48 ++++-
 .../nifi/web/StandardNiFiServiceFacade.java     |  22 ++-
 .../nifi/web/api/ApplicationResource.java       |  11 ++
 .../apache/nifi/web/api/ControllerResource.java |   6 +-
 .../nifi/web/api/ProcessGroupResource.java      |  17 +-
 .../org/apache/nifi/web/api/dto/DtoFactory.java |  45 ++++-
 .../nifi/web/controller/ControllerFacade.java   |   7 +-
 .../accesscontrol/AccessControlHelper.java      |   6 +
 .../accesscontrol/ITProcessorAccessControl.java | 194 +++++++++++++++++--
 .../util/ExecuteCodeRestrictedProcessor.java    |  59 ++++++
 .../integration/util/NiFiTestAuthorizer.java    |  19 +-
 .../integration/util/RestrictedProcessor.java   |   4 +-
 .../org.apache.nifi.processor.Processor         |   3 +-
 .../partials/canvas/policy-management.jsp       |   2 +
 .../src/main/webapp/css/policy-management.css   |   4 +-
 .../components/nf-ng-processor-component.js     |  74 ++++++-
 .../main/webapp/js/nf/canvas/nf-canvas-utils.js |  38 ++++
 .../js/nf/canvas/nf-controller-services.js      |  74 ++++++-
 .../webapp/js/nf/canvas/nf-policy-management.js | 153 ++++++++++++++-
 .../src/main/webapp/js/nf/canvas/nf-settings.js |  74 ++++++-
 .../src/main/webapp/js/nf/nf-common.js          |  56 +++++-
 .../main/webapp/js/nf/users/nf-users-table.js   |  61 ++++--
 .../processors/groovyx/ExecuteGroovyScript.java |  53 ++---
 .../nifi/processors/hadoop/DeleteHDFS.java      |  36 ++--
 .../nifi/processors/hadoop/FetchHDFS.java       |  13 +-
 .../apache/nifi/processors/hadoop/GetHDFS.java  |  16 +-
 .../apache/nifi/processors/hadoop/MoveHDFS.java |  48 +++--
 .../apache/nifi/processors/hadoop/PutHDFS.java  |  13 +-
 .../nifi/processors/parquet/FetchParquet.java   |  13 +-
 .../nifi/processors/parquet/PutParquet.java     |  13 +-
 .../lookup/script/ScriptedLookupService.java    |  12 +-
 .../nifi/processors/script/ExecuteScript.java   |  18 +-
 .../script/InvokeScriptedProcessor.java         |  10 +-
 .../nifi/record/script/ScriptedReader.java      |  10 +-
 .../record/script/ScriptedRecordSetWriter.java  |  29 +--
 .../reporting/script/ScriptedReportingTask.java |  12 +-
 .../SiteToSiteBulletinReportingTask.java        |  49 +++--
 .../SiteToSiteProvenanceReportingTask.java      |  10 +-
 .../processors/standard/ExecuteProcess.java     |  10 +-
 .../standard/ExecuteStreamCommand.java          |  10 +-
 .../nifi/processors/standard/FetchFile.java     |  13 +-
 .../nifi/processors/standard/GetFile.java       |  13 +-
 .../nifi/processors/standard/PutFile.java       |  10 +-
 .../nifi/processors/standard/TailFile.java      |  72 ++++---
 70 files changed, 2042 insertions(+), 277 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-api/src/main/java/org/apache/nifi/annotation/behavior/Restricted.java
----------------------------------------------------------------------
diff --git 
a/nifi-api/src/main/java/org/apache/nifi/annotation/behavior/Restricted.java 
b/nifi-api/src/main/java/org/apache/nifi/annotation/behavior/Restricted.java
index 07d729d..3237f0d 100644
--- a/nifi-api/src/main/java/org/apache/nifi/annotation/behavior/Restricted.java
+++ b/nifi-api/src/main/java/org/apache/nifi/annotation/behavior/Restricted.java
@@ -45,7 +45,15 @@ import java.lang.annotation.Target;
 @Inherited
 public @interface Restricted {
     /**
-     * Provides a description of why the component usage is restricted
+     * Provides a description of why the component usage is restricted. If 
using granular
+     * restrictions, specific explanations should be set in the Restriction.
      */
-    String value();
+    String value() default "";
+
+    /**
+     * Provides a listing of specific Restrictions. If unspecified, this 
component will
+     * require access to restricted components regardless of restrictions.
+     */
+    Restriction[] restrictions() default {};
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-api/src/main/java/org/apache/nifi/annotation/behavior/Restriction.java
----------------------------------------------------------------------
diff --git 
a/nifi-api/src/main/java/org/apache/nifi/annotation/behavior/Restriction.java 
b/nifi-api/src/main/java/org/apache/nifi/annotation/behavior/Restriction.java
new file mode 100644
index 0000000..2a07108
--- /dev/null
+++ 
b/nifi-api/src/main/java/org/apache/nifi/annotation/behavior/Restriction.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.annotation.behavior;
+
+import org.apache.nifi.components.RequiredPermission;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specific restriction for a component. Indicates what the required 
permission is and why the restriction exists.
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface Restriction {
+
+    /**
+     * Provides a listing of RequiredPermissions.
+     */
+    RequiredPermission requiredPermission();
+
+    /**
+     * Provides a explanation of why the component usage is restricted
+     */
+    String explanation();
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-api/src/main/java/org/apache/nifi/components/RequiredPermission.java
----------------------------------------------------------------------
diff --git 
a/nifi-api/src/main/java/org/apache/nifi/components/RequiredPermission.java 
b/nifi-api/src/main/java/org/apache/nifi/components/RequiredPermission.java
new file mode 100644
index 0000000..cd4a376
--- /dev/null
+++ b/nifi-api/src/main/java/org/apache/nifi/components/RequiredPermission.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.components;
+
+import java.util.Arrays;
+
+/**
+ *
+ */
+public enum RequiredPermission {
+    READ_FILESYSTEM("read-filesystem", "read filesystem"),
+    WRITE_FILESYSTEM("write-filesystem", "write filesystem"),
+    EXECUTE_CODE("execute-code", "execute code"),
+    ACCESS_KEYTAB("access-keytab", "access keytab"),
+    EXPORT_NIFI_DETAILS("export-nifi-details", "export nifi details");
+
+    private String permissionIdentifier;
+    private String permissionLabel;
+
+    RequiredPermission(String permissionIdentifier, String permissionLabel) {
+        this.permissionIdentifier = permissionIdentifier;
+        this.permissionLabel = permissionLabel;
+    }
+
+    public String getPermissionIdentifier() {
+        return permissionIdentifier;
+    }
+
+    public String getPermissionLabel() {
+        return permissionLabel;
+    }
+
+    public static RequiredPermission valueOfPermissionIdentifier(final String 
permissionIdentifier) {
+        return Arrays.stream(RequiredPermission.values())
+                .filter(candidate -> 
candidate.getPermissionIdentifier().equals(permissionIdentifier))
+                .findFirst().orElse(null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/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 81a667a..ea60e0a 100644
--- a/nifi-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/administration-guide.adoc
@@ -1044,7 +1044,10 @@ Global access policies govern the following system level 
authorizations:
 |Data Provenance
 
 |access restricted components
-|Allows users to create/modify restricted components assuming otherwise 
sufficient permissions
+|Allows users to create/modify restricted components assuming other 
permissions are sufficient. The restricted
+components may indicate which specific permissions are required. Permissions 
can be granted for specific
+restrictions or be granted regardless of restrictions. If permission is 
granted regardless of restrictions,
+the user can create/modify all restricted components.
 |N/A
 
 |access all policies

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-docs/src/main/asciidoc/user-guide.adoc
----------------------------------------------------------------------
diff --git a/nifi-docs/src/main/asciidoc/user-guide.adoc 
b/nifi-docs/src/main/asciidoc/user-guide.adoc
index 7b414cf..3e55ffe 100644
--- a/nifi-docs/src/main/asciidoc/user-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/user-guide.adoc
@@ -191,7 +191,9 @@ The available global access policies are:
 |view the UI   |Allows users to view the UI
 |access the controller  |Allows users to view and modify the controller 
including reporting tasks, Controller Services, and nodes in the cluster
 |query provenance     |Allows users to submit a provenance search and request 
even lineage
-|access restricted components     |Allows users to create/modify restricted 
components assuming otherwise sufficient permissions
+|access restricted components     |Allows users to create/modify restricted 
components assuming other permissions are sufficient. The restricted
+components may indicate which specific permissions are required. Permissions 
can be granted for specific restrictions or be granted regardless
+of restrictions. If permission is granted regardless of restrictions, the user 
can create/modify all restricted components.
 |access all policies   |Allows users to view and modify the policies for all 
components
 |access users/groups   |Allows users view and modify the users and user groups
 |retrieve site-to-site details | Allows other NiFi instances to retrieve 
Site-To-Site details
@@ -267,13 +269,18 @@ image::add-processor-with-tag-cloud.png["Add Processor 
with Tag Cloud"]
 
 Restricted components will be marked with a
 image:restricted.png["Restricted"]
-icon next to their name. These are components that can be used to execute 
arbitrary unsanitized code provided by the operator
-through the NiFi REST API/UI or can be used to obtain or alter data on the 
NiFi host system using the NiFi OS credentials.
-These components could be used by an otherwise authorized NiFi user to go 
beyond the intended use of the application, escalate
-privilege, or could expose data about the internals of the NiFi process or the 
host system. All of these capabilities should
-be considered privileged, and admins should be aware of these capabilities and 
explicitly enable them for a subset of trusted users.
-
-Before a user is allowed to create and modify restricted components they must 
be granted access to restricted components. For more information refer to
+icon next to their name. Hovering over the tooltip will display the specific 
restrictions this component requires. If the component
+does not list any specific restrictions it will require access to restricted 
components regardless of restrictions. These are components
+that can be used to execute arbitrary unsanitized code provided by the 
operator through the NiFi REST API/UI or can be used to obtain
+or alter data on the NiFi host system using the NiFi OS credentials. These 
components could be used by an otherwise authorized NiFi
+user to go beyond the intended use of the application, escalate privilege, or 
could expose data about the internals of the NiFi process
+or the host system. All of these capabilities should be considered privileged, 
and admins should be aware of these capabilities and
+explicitly enable them for a subset of trusted users.
+
+Before a user is allowed to create and modify restricted components they must 
be granted access to restricted components. This can be
+assigned regardless of restrictions. In this case, the user will have access 
to all restricted components. Alternatively, users can
+be assigned access to specific restrictions. If the user has been granted 
access to all restrictions a component requires, they will
+have access to that component assuming otherwise sufficient permissions. For 
more information refer to
 <<UI-with-multi-tenant-authorization>>.
 
 Clicking the `Add` button or double-clicking on a Processor Type will add the 
selected Processor to the canvas at the

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-flume-bundle/nifi-flume-processors/src/main/java/org/apache/nifi/processors/flume/ExecuteFlumeSink.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-flume-bundle/nifi-flume-processors/src/main/java/org/apache/nifi/processors/flume/ExecuteFlumeSink.java
 
b/nifi-nar-bundles/nifi-flume-bundle/nifi-flume-processors/src/main/java/org/apache/nifi/processors/flume/ExecuteFlumeSink.java
index ec99acb..70bcd12 100644
--- 
a/nifi-nar-bundles/nifi-flume-bundle/nifi-flume-processors/src/main/java/org/apache/nifi/processors/flume/ExecuteFlumeSink.java
+++ 
b/nifi-nar-bundles/nifi-flume-bundle/nifi-flume-processors/src/main/java/org/apache/nifi/processors/flume/ExecuteFlumeSink.java
@@ -25,12 +25,14 @@ import org.apache.flume.conf.Configurables;
 import org.apache.nifi.annotation.behavior.InputRequirement;
 import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
 import org.apache.nifi.annotation.behavior.Restricted;
+import org.apache.nifi.annotation.behavior.Restriction;
 import org.apache.nifi.annotation.behavior.TriggerSerially;
 import org.apache.nifi.annotation.documentation.CapabilityDescription;
 import org.apache.nifi.annotation.documentation.Tags;
 import org.apache.nifi.annotation.lifecycle.OnScheduled;
 import org.apache.nifi.annotation.lifecycle.OnStopped;
 import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.RequiredPermission;
 import org.apache.nifi.components.Validator;
 import org.apache.nifi.processor.ProcessContext;
 import org.apache.nifi.processor.ProcessSession;
@@ -49,7 +51,13 @@ import java.util.Set;
 @Tags({"flume", "hadoop", "put", "sink", "restricted"})
 @InputRequirement(Requirement.INPUT_REQUIRED)
 @CapabilityDescription("Execute a Flume sink. Each input FlowFile is converted 
into a Flume Event for processing by the sink.")
-@Restricted("Provides operator the ability to execute arbitrary Flume 
configurations assuming all permissions that NiFi has.")
+@Restricted(
+        restrictions = {
+                @Restriction(
+                        requiredPermission = RequiredPermission.EXECUTE_CODE,
+                        explanation = "Provides operator the ability to 
execute arbitrary Flume configurations assuming all permissions that NiFi has.")
+        }
+)
 public class ExecuteFlumeSink extends AbstractFlumeProcessor {
 
     public static final PropertyDescriptor SINK_TYPE = new 
PropertyDescriptor.Builder()

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-flume-bundle/nifi-flume-processors/src/main/java/org/apache/nifi/processors/flume/ExecuteFlumeSource.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-flume-bundle/nifi-flume-processors/src/main/java/org/apache/nifi/processors/flume/ExecuteFlumeSource.java
 
b/nifi-nar-bundles/nifi-flume-bundle/nifi-flume-processors/src/main/java/org/apache/nifi/processors/flume/ExecuteFlumeSource.java
index 63ed190..4daed70 100644
--- 
a/nifi-nar-bundles/nifi-flume-bundle/nifi-flume-processors/src/main/java/org/apache/nifi/processors/flume/ExecuteFlumeSource.java
+++ 
b/nifi-nar-bundles/nifi-flume-bundle/nifi-flume-processors/src/main/java/org/apache/nifi/processors/flume/ExecuteFlumeSource.java
@@ -29,12 +29,14 @@ import org.apache.flume.source.EventDrivenSourceRunner;
 import org.apache.nifi.annotation.behavior.InputRequirement;
 import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
 import org.apache.nifi.annotation.behavior.Restricted;
+import org.apache.nifi.annotation.behavior.Restriction;
 import org.apache.nifi.annotation.behavior.TriggerSerially;
 import org.apache.nifi.annotation.documentation.CapabilityDescription;
 import org.apache.nifi.annotation.documentation.Tags;
 import org.apache.nifi.annotation.lifecycle.OnScheduled;
 import org.apache.nifi.annotation.lifecycle.OnStopped;
 import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.RequiredPermission;
 import org.apache.nifi.components.Validator;
 import org.apache.nifi.processor.ProcessContext;
 import org.apache.nifi.processor.ProcessSession;
@@ -55,7 +57,13 @@ import java.util.concurrent.atomic.AtomicReference;
 @Tags({"flume", "hadoop", "get", "source", "restricted"})
 @InputRequirement(Requirement.INPUT_FORBIDDEN)
 @CapabilityDescription("Execute a Flume source. Each Flume Event is sent to 
the success relationship as a FlowFile")
-@Restricted("Provides operator the ability to execute arbitrary Flume 
configurations assuming all permissions that NiFi has.")
+@Restricted(
+        restrictions = {
+                @Restriction(
+                        requiredPermission = RequiredPermission.EXECUTE_CODE,
+                        explanation = "Provides operator the ability to 
execute arbitrary Flume configurations assuming all permissions that NiFi has.")
+        }
+)
 public class ExecuteFlumeSource extends AbstractFlumeProcessor {
 
     public static final PropertyDescriptor SOURCE_TYPE = new 
PropertyDescriptor.Builder()

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ComponentRestrictionPermissionDTO.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ComponentRestrictionPermissionDTO.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ComponentRestrictionPermissionDTO.java
new file mode 100644
index 0000000..6fb5799
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ComponentRestrictionPermissionDTO.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.api.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.xml.bind.annotation.XmlType;
+import java.util.Objects;
+
+/**
+ * Class used for providing details about a components usage restriction.
+ */
+@XmlType(name = "componentRestrictionPermission")
+public class ComponentRestrictionPermissionDTO {
+
+    private RequiredPermissionDTO requiredPermission;
+    private PermissionsDTO permissions;
+
+    /**
+     * @return The required permission necessary for this restriction.
+     */
+    @ApiModelProperty(
+            value = "The required permission necessary for this restriction."
+    )
+    public RequiredPermissionDTO getRequiredPermission() {
+        return requiredPermission;
+    }
+
+    public void setRequiredPermission(RequiredPermissionDTO 
requiredPermission) {
+        this.requiredPermission = requiredPermission;
+    }
+
+    /**
+     * @return The permissions for this component restriction.
+     */
+    @ApiModelProperty(
+            value = "The permissions for this component restriction. Note: the 
read permission are not used and will always be false."
+    )
+    public PermissionsDTO getPermissions() {
+        return permissions;
+    }
+
+    public void setPermissions(PermissionsDTO permissions) {
+        this.permissions = permissions;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(requiredPermission);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+
+        if (obj == this) {
+            return true;
+        }
+
+        if (obj.getClass() != getClass()) {
+            return false;
+        }
+
+        return Objects.equals(requiredPermission, 
((ComponentRestrictionPermissionDTO)obj).requiredPermission);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DocumentedTypeDTO.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DocumentedTypeDTO.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DocumentedTypeDTO.java
index 7f38a08..386f3a5 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DocumentedTypeDTO.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DocumentedTypeDTO.java
@@ -33,7 +33,9 @@ public class DocumentedTypeDTO {
     private BundleDTO bundle;
     private List<ControllerServiceApiDTO> controllerServiceApis;
     private String description;
+    private boolean restricted;
     private String usageRestriction;
+    private Set<ExplicitRestrictionDTO> explicitRestrictions;
     private String deprecationReason;
     private Set<String> tags;
 
@@ -52,10 +54,38 @@ public class DocumentedTypeDTO {
     }
 
     /**
+     * @return Whether this type is restricted
+     */
+    @ApiModelProperty(
+            value = "Whether this type is restricted."
+    )
+    public boolean isRestricted() {
+        return restricted;
+    }
+
+    public void setRestricted(boolean restricted) {
+        this.restricted = restricted;
+    }
+
+    /**
+     * @return An optional collection of explicit restrictions
+     */
+    @ApiModelProperty(
+            value = "An optional collection of explicit restrictions. If 
specified, these explicit restrictions will be enfored."
+    )
+    public Set<ExplicitRestrictionDTO> getExplicitRestrictions() {
+        return explicitRestrictions;
+    }
+
+    public void setExplicitRestrictions(Set<ExplicitRestrictionDTO> 
explicitRestrictions) {
+        this.explicitRestrictions = explicitRestrictions;
+    }
+
+    /**
      * @return An optional description of why the usage of this component is 
restricted
      */
     @ApiModelProperty(
-            value = "The description of why the usage of this component is 
restricted."
+            value = "The optional description of why the usage of this 
component is restricted."
     )
     public String getUsageRestriction() {
         return usageRestriction;

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ExplicitRestrictionDTO.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ExplicitRestrictionDTO.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ExplicitRestrictionDTO.java
new file mode 100644
index 0000000..87c336d
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ExplicitRestrictionDTO.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.api.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * Class used for providing details about a components usage restriction.
+ */
+@XmlType(name = "explicitRestriction")
+public class ExplicitRestrictionDTO {
+
+    private RequiredPermissionDTO requiredPermission;
+    private String explanation;
+
+    /**
+     * @return The required permission necessary for this restriction.
+     */
+    @ApiModelProperty(
+            value = "The required permission necessary for this restriction."
+    )
+    public RequiredPermissionDTO getRequiredPermission() {
+        return requiredPermission;
+    }
+
+    public void setRequiredPermission(RequiredPermissionDTO 
requiredPermission) {
+        this.requiredPermission = requiredPermission;
+    }
+
+    /**
+     * @return The description of why the usage of this component is 
restricted for this required permission.
+     */
+    @ApiModelProperty(
+            value = "The description of why the usage of this component is 
restricted for this required permission."
+    )
+    public String getExplanation() {
+        return explanation;
+    }
+
+    public void setExplanation(String explanation) {
+        this.explanation = explanation;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/RequiredPermissionDTO.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/RequiredPermissionDTO.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/RequiredPermissionDTO.java
new file mode 100644
index 0000000..1fa3ba4
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/RequiredPermissionDTO.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.api.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.xml.bind.annotation.XmlType;
+import java.util.Objects;
+
+/**
+ * Class used for providing details about a components usage restriction.
+ */
+@XmlType(name = "requiredPermission")
+public class RequiredPermissionDTO {
+
+    private String id;
+    private String label;
+
+    /**
+     * @return The required sub-permission necessary for this restriction.
+     */
+    @ApiModelProperty(
+            value = "The required sub-permission necessary for this 
restriction."
+    )
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /**
+     * @return The label for the required sub-permission necessary for this 
restriction.
+     */
+    @ApiModelProperty(
+            value = "The label for the required sub-permission necessary for 
this restriction."
+    )
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+
+        if (obj == this) {
+            return true;
+        }
+
+        if (obj.getClass() != getClass()) {
+            return false;
+        }
+
+        return Objects.equals(id, ((RequiredPermissionDTO)obj).id);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java
index 8121ce4..c157c8b 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java
@@ -17,9 +17,11 @@
 package org.apache.nifi.web.api.entity;
 
 import io.swagger.annotations.ApiModelProperty;
+import org.apache.nifi.web.api.dto.ComponentRestrictionPermissionDTO;
 import org.apache.nifi.web.api.dto.PermissionsDTO;
 
 import javax.xml.bind.annotation.XmlRootElement;
+import java.util.Set;
 
 /**
  * A serialized representation of this class can be placed in the entity body 
of a response to the API. This particular entity holds the users identity.
@@ -37,6 +39,7 @@ public class CurrentUserEntity extends Entity {
     private PermissionsDTO policiesPermissions;
     private PermissionsDTO systemPermissions;
     private PermissionsDTO restrictedComponentsPermissions;
+    private Set<ComponentRestrictionPermissionDTO> 
componentRestrictionPermissions;
 
     private boolean canVersionFlows;
 
@@ -149,6 +152,18 @@ public class CurrentUserEntity extends Entity {
     }
 
     /**
+     * @return permissions for specific component restrictions
+     */
+    @ApiModelProperty("Permissions for specific component restrictions.")
+    public Set<ComponentRestrictionPermissionDTO> 
getComponentRestrictionPermissions() {
+        return componentRestrictionPermissions;
+    }
+
+    public void 
setComponentRestrictionPermissions(Set<ComponentRestrictionPermissionDTO> 
componentRestrictionPermissions) {
+        this.componentRestrictionPermissions = componentRestrictionPermissions;
+    }
+
+    /**
      * @return whether the current user can version flows
      */
     @ApiModelProperty("Whether the current user can version flows.")

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
index e96fe1c..9294906 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
@@ -18,6 +18,7 @@ package org.apache.nifi.documentation.html;
 
 import org.apache.nifi.annotation.behavior.DynamicProperties;
 import org.apache.nifi.annotation.behavior.DynamicProperty;
+import org.apache.nifi.annotation.behavior.Restriction;
 import org.apache.nifi.annotation.behavior.SystemResourceConsideration;
 import org.apache.nifi.annotation.behavior.InputRequirement;
 import org.apache.nifi.annotation.behavior.Restricted;
@@ -248,7 +249,32 @@ public class HtmlDocumentationWriter implements 
DocumentationWriter {
         writeSimpleElement(xmlStreamWriter, "h3", "Restricted: ");
 
         if(restricted != null) {
-            xmlStreamWriter.writeCharacters(restricted.value());
+            final String value = restricted.value();
+
+            if (!StringUtils.isBlank(value)) {
+                xmlStreamWriter.writeCharacters(restricted.value());
+            }
+
+            final Restriction[] restrictions = restricted.restrictions();
+            if (restrictions != null && restrictions.length > 0) {
+                xmlStreamWriter.writeStartElement("table");
+                xmlStreamWriter.writeAttribute("id", "restrictions");
+                xmlStreamWriter.writeStartElement("tr");
+                writeSimpleElement(xmlStreamWriter, "th", "Required 
Permission");
+                writeSimpleElement(xmlStreamWriter, "th", "Explanation");
+                xmlStreamWriter.writeEndElement();
+
+                for (Restriction restriction : restrictions) {
+                    xmlStreamWriter.writeStartElement("tr");
+                    writeSimpleElement(xmlStreamWriter, "td", 
restriction.requiredPermission().getPermissionLabel());
+                    writeSimpleElement(xmlStreamWriter, "td", 
restriction.explanation());
+                    xmlStreamWriter.writeEndElement();
+                }
+
+                xmlStreamWriter.writeEndElement();
+            } else {
+                xmlStreamWriter.writeCharacters("This component requires 
access to restricted components regardless of restriction.");
+            }
         } else {
             xmlStreamWriter.writeCharacters("This component is not 
restricted.");
         }

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedProcessor.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedProcessor.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedProcessor.java
index 999040d..8442a33 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedProcessor.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedProcessor.java
@@ -18,15 +18,16 @@ package org.apache.nifi.documentation.example;
 
 import org.apache.nifi.annotation.behavior.DynamicProperty;
 import org.apache.nifi.annotation.behavior.DynamicRelationship;
-import org.apache.nifi.annotation.behavior.SystemResourceConsideration;
-import org.apache.nifi.annotation.behavior.SystemResource;
 import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
 import org.apache.nifi.annotation.behavior.ReadsAttribute;
 import org.apache.nifi.annotation.behavior.Restricted;
+import org.apache.nifi.annotation.behavior.Restriction;
 import org.apache.nifi.annotation.behavior.Stateful;
+import org.apache.nifi.annotation.behavior.SystemResource;
+import org.apache.nifi.annotation.behavior.SystemResourceConsideration;
 import org.apache.nifi.annotation.behavior.WritesAttribute;
 import org.apache.nifi.annotation.behavior.WritesAttributes;
-import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
 import org.apache.nifi.annotation.documentation.CapabilityDescription;
 import org.apache.nifi.annotation.documentation.SeeAlso;
 import org.apache.nifi.annotation.documentation.Tags;
@@ -34,6 +35,7 @@ import org.apache.nifi.annotation.lifecycle.OnRemoved;
 import org.apache.nifi.annotation.lifecycle.OnShutdown;
 import org.apache.nifi.components.AllowableValue;
 import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.RequiredPermission;
 import org.apache.nifi.components.state.Scope;
 import org.apache.nifi.processor.AbstractProcessor;
 import org.apache.nifi.processor.ProcessContext;
@@ -59,7 +61,14 @@ import java.util.Set;
 @DynamicProperty(name = "Relationship Name", supportsExpressionLanguage = 
true, value = "some XPath", description = "Routes FlowFiles to relationships 
based on XPath")
 @DynamicRelationship(name = "name from dynamic property", description = "all 
files that match the properties XPath")
 @Stateful(scopes = {Scope.CLUSTER, Scope.LOCAL}, description = "state 
management description")
-@Restricted("processor restriction description")
+@Restricted(
+        value = "processor restriction description",
+        restrictions = {
+                @Restriction(
+                        requiredPermission = 
RequiredPermission.READ_FILESYSTEM,
+                        explanation = "Requires read filesystem permission")
+        }
+)
 @InputRequirement(Requirement.INPUT_FORBIDDEN)
 @SystemResourceConsideration(resource = SystemResource.CPU)
 @SystemResourceConsideration(resource = SystemResource.DISK, description = 
"Customized disk usage description")

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/ProcessorDocumentationWriterTest.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/ProcessorDocumentationWriterTest.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/ProcessorDocumentationWriterTest.java
index 325f18b..10333d0 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/ProcessorDocumentationWriterTest.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/ProcessorDocumentationWriterTest.java
@@ -19,6 +19,7 @@ package org.apache.nifi.documentation.html;
 import org.apache.nifi.annotation.behavior.SystemResourceConsideration;
 import org.apache.nifi.annotation.behavior.SystemResource;
 import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.components.RequiredPermission;
 import org.apache.nifi.documentation.DocumentationWriter;
 import org.apache.nifi.documentation.example.DeprecatedProcessor;
 import org.apache.nifi.documentation.example.FullyDocumentedProcessor;
@@ -73,6 +74,8 @@ public class ProcessorDocumentationWriterTest {
         assertContains(results, "state management description");
 
         assertContains(results, "processor restriction description");
+        assertContains(results, 
RequiredPermission.READ_FILESYSTEM.getPermissionLabel());
+        assertContains(results, "Requires read filesystem permission");
 
         assertNotContains(results, "iconSecure.png");
         assertContains(results, 
FullyDocumentedProcessor.class.getAnnotation(CapabilityDescription.class)

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/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 79392b7..c18598b 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
@@ -17,6 +17,7 @@
 package org.apache.nifi.authorization.resource;
 
 import org.apache.nifi.authorization.Resource;
+import org.apache.nifi.components.RequiredPermission;
 
 import java.util.Objects;
 
@@ -328,6 +329,31 @@ public final class ResourceFactory {
     }
 
     /**
+     * Gets a Resource for accessing certain kinds of restricted components.
+     *
+     * @param requiredPermission The required permission
+     * @return The restricted components resource
+     */
+    public static Resource getRestrictedComponentsResource(final 
RequiredPermission requiredPermission) {
+        return new Resource() {
+            @Override
+            public String getIdentifier() {
+                return String.format("%s/%s", 
RESTRICTED_COMPONENTS_RESOURCE.getIdentifier(), 
requiredPermission.getPermissionIdentifier());
+            }
+
+            @Override
+            public String getName() {
+                return requiredPermission.getPermissionLabel();
+            }
+
+            @Override
+            public String getSafeDescription() {
+                return "Components requiring additional permission: " + 
requiredPermission.getPermissionLabel();
+            }
+        };
+    }
+
+    /**
      * Gets the Resource for accessing Tenants which includes creating, 
modifying, and deleting Users and UserGroups.
      *
      * @return The Resource for accessing Tenants

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/RestrictedComponentsAuthorizable.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/RestrictedComponentsAuthorizable.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/RestrictedComponentsAuthorizable.java
deleted file mode 100644
index 92a905b..0000000
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/RestrictedComponentsAuthorizable.java
+++ /dev/null
@@ -1,33 +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.authorization.resource;
-
-import org.apache.nifi.authorization.Resource;
-
-public class RestrictedComponentsAuthorizable implements Authorizable {
-
-    @Override
-    public Authorizable getParentAuthorizable() {
-        return null;
-    }
-
-    @Override
-    public Resource getResource() {
-        return ResourceFactory.getRestrictedComponentsResource();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/RestrictedComponentsAuthorizableFactory.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/RestrictedComponentsAuthorizableFactory.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/RestrictedComponentsAuthorizableFactory.java
new file mode 100644
index 0000000..95ab7fc
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/RestrictedComponentsAuthorizableFactory.java
@@ -0,0 +1,119 @@
+/*
+ * 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.authorization.resource;
+
+import org.apache.nifi.annotation.behavior.Restricted;
+import org.apache.nifi.annotation.behavior.Restriction;
+import org.apache.nifi.authorization.AccessDeniedException;
+import org.apache.nifi.authorization.AuthorizationResult;
+import org.apache.nifi.authorization.AuthorizationResult.Result;
+import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.RequestAction;
+import org.apache.nifi.authorization.Resource;
+import org.apache.nifi.authorization.user.NiFiUser;
+import org.apache.nifi.components.RequiredPermission;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class RestrictedComponentsAuthorizableFactory {
+
+    private static final Authorizable RESTRICTED_COMPONENTS_AUTHORIZABLE = new 
Authorizable() {
+        @Override
+        public Authorizable getParentAuthorizable() {
+            return null;
+        }
+
+        @Override
+        public Resource getResource() {
+            return ResourceFactory.getRestrictedComponentsResource();
+        }
+    };
+
+    public static Authorizable getRestrictedComponentsAuthorizable() {
+        return RESTRICTED_COMPONENTS_AUTHORIZABLE;
+    }
+
+    public static Authorizable getRestrictedComponentsAuthorizable(final 
RequiredPermission requiredPermission) {
+        return new Authorizable() {
+            @Override
+            public Authorizable getParentAuthorizable() {
+                return RESTRICTED_COMPONENTS_AUTHORIZABLE;
+            }
+
+            @Override
+            public Resource getResource() {
+                return 
ResourceFactory.getRestrictedComponentsResource(requiredPermission);
+            }
+
+            @Override
+            public AuthorizationResult checkAuthorization(Authorizer 
authorizer, RequestAction action, NiFiUser user, Map<String, String> 
resourceContext) {
+                if (user == null) {
+                    throw new AccessDeniedException("Unknown user.");
+                }
+
+                final AuthorizationResult resourceResult = 
Authorizable.super.checkAuthorization(authorizer, action, user, 
resourceContext);
+
+                // if we're denied from the resource try inheriting
+                if (Result.Denied.equals(resourceResult.getResult())) {
+                    return 
getParentAuthorizable().checkAuthorization(authorizer, action, user, 
resourceContext);
+                } else {
+                    return resourceResult;
+                }
+            }
+
+            @Override
+            public void authorize(Authorizer authorizer, RequestAction action, 
NiFiUser user, Map<String, String> resourceContext) throws 
AccessDeniedException {
+                if (user == null) {
+                    throw new AccessDeniedException("Unknown user.");
+                }
+
+                try {
+                    Authorizable.super.authorize(authorizer, action, user, 
resourceContext);
+                } catch (final AccessDeniedException resourceDenied) {
+                    // if we're denied from the resource try inheriting
+                    try {
+                        getParentAuthorizable().authorize(authorizer, action, 
user, resourceContext);
+                    } catch (final AccessDeniedException policiesDenied) {
+                        throw resourceDenied;
+                    }
+                }
+            }
+        };
+    }
+
+    public static Set<Authorizable> getRestrictedComponentsAuthorizable(final 
Class<?> configurableComponentClass) {
+        final Set<Authorizable> authorizables = new HashSet<>();
+
+        final Restricted restricted = 
configurableComponentClass.getAnnotation(Restricted.class);
+
+        if (restricted != null) {
+            final Restriction[] restrictions = restricted.restrictions();
+
+            if (restrictions != null && restrictions.length > 0) {
+                Arrays.stream(restrictions).forEach(restriction -> 
authorizables.add(getRestrictedComponentsAuthorizable(restriction.requiredPermission())));
+            } else {
+                authorizables.add(getRestrictedComponentsAuthorizable());
+            }
+        }
+
+        return authorizables;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/CurrentUserEndpointMerger.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/CurrentUserEndpointMerger.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/CurrentUserEndpointMerger.java
index 4a97d7a..03691c4 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/CurrentUserEndpointMerger.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/CurrentUserEndpointMerger.java
@@ -19,6 +19,7 @@ package org.apache.nifi.cluster.coordination.http.endpoints;
 
 import org.apache.nifi.cluster.manager.NodeResponse;
 import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.web.api.dto.ComponentRestrictionPermissionDTO;
 import org.apache.nifi.web.api.dto.PermissionsDTO;
 import org.apache.nifi.web.api.entity.CurrentUserEntity;
 
@@ -53,6 +54,23 @@ public class CurrentUserEndpointMerger extends 
AbstractSingleEntityEndpoint<Curr
                 mergePermissions(clientEntity.getPoliciesPermissions(), 
entity.getPoliciesPermissions());
                 mergePermissions(clientEntity.getProvenancePermissions(), 
entity.getProvenancePermissions());
                 mergePermissions(clientEntity.getTenantsPermissions(), 
entity.getTenantsPermissions());
+                mergePermissions(clientEntity.getSystemPermissions(), 
entity.getSystemPermissions());
+                mergePermissions(clientEntity.getTenantsPermissions(), 
entity.getTenantsPermissions());
+
+                final Set<ComponentRestrictionPermissionDTO> 
clientEntityComponentRestrictionsPermissions = 
clientEntity.getComponentRestrictionPermissions();
+                final Set<ComponentRestrictionPermissionDTO> 
entityComponentRestrictionsPermissions = 
entity.getComponentRestrictionPermissions();
+
+                // only retain the component restriction permissions in common
+                
clientEntityComponentRestrictionsPermissions.retainAll(entityComponentRestrictionsPermissions);
+
+                // merge the component restriction permissions
+                
clientEntityComponentRestrictionsPermissions.forEach(clientEntityPermission -> {
+                    final ComponentRestrictionPermissionDTO entityPermission = 
entityComponentRestrictionsPermissions.stream().filter(entityComponentRestrictionsPermission
 -> {
+                        return 
entityComponentRestrictionsPermission.getRequiredPermission().getId().equals(clientEntityPermission.getRequiredPermission().getId());
+                    }).findFirst().orElse(null);
+
+                    mergePermissions(clientEntityPermission.getPermissions(), 
entityPermission.getPermissions());
+                });
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/coordination/http/endpoints/CurrentUserEndpointMergerTest.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/coordination/http/endpoints/CurrentUserEndpointMergerTest.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/coordination/http/endpoints/CurrentUserEndpointMergerTest.java
new file mode 100644
index 0000000..c1cfdf8
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/coordination/http/endpoints/CurrentUserEndpointMergerTest.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nifi.cluster.coordination.http.endpoints;
+
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.components.RequiredPermission;
+import org.apache.nifi.web.api.dto.ComponentRestrictionPermissionDTO;
+import org.apache.nifi.web.api.dto.PermissionsDTO;
+import org.apache.nifi.web.api.dto.RequiredPermissionDTO;
+import org.apache.nifi.web.api.entity.CurrentUserEntity;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class CurrentUserEndpointMergerTest {
+
+    @Test
+    public void testMergeUserPermissions() {
+        final NodeIdentifier nodeId1 = new NodeIdentifier("1", "localhost", 
9000, "localhost", 9001, "localhost", 9002, 9003, false);
+        final CurrentUserEntity userNode1 = new CurrentUserEntity();
+        userNode1.setControllerPermissions(buildPermissions(true, false));
+        userNode1.setCountersPermissions(buildPermissions(true, true));
+        userNode1.setPoliciesPermissions(buildPermissions(true, true));
+        userNode1.setProvenancePermissions(buildPermissions(false, false));
+        userNode1.setRestrictedComponentsPermissions(buildPermissions(false, 
false));
+        userNode1.setSystemPermissions(buildPermissions(true, true));
+        userNode1.setTenantsPermissions(buildPermissions(false, true));
+
+        final Set<ComponentRestrictionPermissionDTO> 
componentRestrictionsNode1 = new HashSet<>();
+        
componentRestrictionsNode1.add(buildComponentRestriction(RequiredPermission.ACCESS_KEYTAB,
 true, true));
+        
componentRestrictionsNode1.add(buildComponentRestriction(RequiredPermission.WRITE_FILESYSTEM,
 false, true));
+        
componentRestrictionsNode1.add(buildComponentRestriction(RequiredPermission.READ_FILESYSTEM,
 true, true));
+        
userNode1.setComponentRestrictionPermissions(componentRestrictionsNode1);
+
+        final NodeIdentifier nodeId2 = new NodeIdentifier("2", "localhost", 
8000, "localhost", 8001, "localhost", 8002, 8003, false);
+        final CurrentUserEntity userNode2 = new CurrentUserEntity();
+        userNode2.setControllerPermissions(buildPermissions(false, true));
+        userNode2.setCountersPermissions(buildPermissions(true, false));
+        userNode2.setPoliciesPermissions(buildPermissions(true, true));
+        userNode2.setProvenancePermissions(buildPermissions(false, false));
+        userNode2.setRestrictedComponentsPermissions(buildPermissions(true, 
true));
+        userNode2.setSystemPermissions(buildPermissions(false, false));
+        userNode2.setTenantsPermissions(buildPermissions(true, true));
+
+        final Set<ComponentRestrictionPermissionDTO> 
componentRestrictionsNode2 = new HashSet<>();
+        
componentRestrictionsNode2.add(buildComponentRestriction(RequiredPermission.ACCESS_KEYTAB,
 true, false));
+        
componentRestrictionsNode2.add(buildComponentRestriction(RequiredPermission.WRITE_FILESYSTEM,
 true, false));
+        
componentRestrictionsNode2.add(buildComponentRestriction(RequiredPermission.EXECUTE_CODE,
 true, true));
+        
userNode2.setComponentRestrictionPermissions(componentRestrictionsNode2);
+
+        final Map<NodeIdentifier, CurrentUserEntity> entityMap = new 
HashMap<>();
+        entityMap.put(nodeId1, userNode1);
+        entityMap.put(nodeId2, userNode2);
+
+        final CurrentUserEndpointMerger merger = new 
CurrentUserEndpointMerger();
+        merger.mergeResponses(userNode1, entityMap, Collections.emptySet(), 
Collections.emptySet());
+
+        assertFalse(userNode1.getControllerPermissions().getCanRead());
+        assertFalse(userNode1.getControllerPermissions().getCanWrite());
+        assertTrue(userNode1.getCountersPermissions().getCanRead());
+        assertFalse(userNode1.getCountersPermissions().getCanWrite());
+        assertTrue(userNode1.getPoliciesPermissions().getCanRead());
+        assertTrue(userNode1.getPoliciesPermissions().getCanWrite());
+        assertFalse(userNode1.getProvenancePermissions().getCanRead());
+        assertFalse(userNode1.getProvenancePermissions().getCanWrite());
+        
assertFalse(userNode1.getRestrictedComponentsPermissions().getCanRead());
+        
assertFalse(userNode1.getRestrictedComponentsPermissions().getCanWrite());
+        assertFalse(userNode1.getSystemPermissions().getCanRead());
+        assertFalse(userNode1.getSystemPermissions().getCanWrite());
+        assertFalse(userNode1.getTenantsPermissions().getCanRead());
+        assertTrue(userNode1.getTenantsPermissions().getCanWrite());
+
+        
userNode1.getComponentRestrictionPermissions().forEach(componentRestriction -> {
+            if 
(RequiredPermission.ACCESS_KEYTAB.getPermissionIdentifier().equals(componentRestriction.getRequiredPermission().getId()))
 {
+                assertTrue(componentRestriction.getPermissions().getCanRead());
+                
assertFalse(componentRestriction.getPermissions().getCanWrite());
+            } else if 
(RequiredPermission.WRITE_FILESYSTEM.getPermissionIdentifier().equals(componentRestriction.getRequiredPermission().getId()))
 {
+                
assertFalse(componentRestriction.getPermissions().getCanRead());
+                
assertFalse(componentRestriction.getPermissions().getCanWrite());
+            } else {
+                fail();
+            }
+        });
+    }
+
+    private PermissionsDTO buildPermissions(final boolean canRead, final 
boolean canWrite) {
+        final PermissionsDTO permissionsDto = new PermissionsDTO();
+        permissionsDto.setCanRead(canRead);
+        permissionsDto.setCanWrite(canWrite);
+        return permissionsDto;
+    }
+
+    private ComponentRestrictionPermissionDTO buildComponentRestriction(final 
RequiredPermission requiredPermission, final boolean canRead, final boolean 
canWrite) {
+        final RequiredPermissionDTO requiredPermissionDto = new 
RequiredPermissionDTO();
+        
requiredPermissionDto.setId(requiredPermission.getPermissionIdentifier());
+        
requiredPermissionDto.setLabel(requiredPermission.getPermissionLabel());
+
+        final ComponentRestrictionPermissionDTO 
componentRestrictionPermissionDto = new ComponentRestrictionPermissionDTO();
+        
componentRestrictionPermissionDto.setRequiredPermission(requiredPermissionDto);
+        
componentRestrictionPermissionDto.setPermissions(buildPermissions(canRead, 
canWrite));
+        return componentRestrictionPermissionDto;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ConfiguredComponent.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ConfiguredComponent.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ConfiguredComponent.java
index b0f65a7..34bf575 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ConfiguredComponent.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ConfiguredComponent.java
@@ -21,8 +21,9 @@ import org.apache.nifi.authorization.AuthorizationResult;
 import org.apache.nifi.authorization.AuthorizationResult.Result;
 import org.apache.nifi.authorization.Authorizer;
 import org.apache.nifi.authorization.RequestAction;
+import org.apache.nifi.authorization.resource.Authorizable;
 import org.apache.nifi.authorization.resource.ComponentAuthorizable;
-import org.apache.nifi.authorization.resource.RestrictedComponentsAuthorizable;
+import 
org.apache.nifi.authorization.resource.RestrictedComponentsAuthorizableFactory;
 import org.apache.nifi.authorization.user.NiFiUser;
 import org.apache.nifi.bundle.BundleCoordinate;
 import org.apache.nifi.components.ConfigurableComponent;
@@ -91,12 +92,17 @@ public interface ConfiguredComponent extends 
ComponentAuthorizable {
     String getComponentType();
 
     /**
+     * @return the class of the underlying
+     */
+    Class<?> getComponentClass();
+
+    /**
      * @return the Canonical Class Name of the component
      */
     String getCanonicalClassName();
 
     /**
-     * @return whether or not the underlying implementation is restricted
+     * @return whether or not the underlying implementation has any 
restrictions
      */
     boolean isRestricted();
 
@@ -115,10 +121,13 @@ public interface ConfiguredComponent extends 
ComponentAuthorizable {
         // if this is a modification request and the reporting task is 
restricted ensure the user has elevated privileges. if this
         // is not a modification request, we just want to use the normal rules
         if (RequestAction.WRITE.equals(action) && isRestricted()) {
-            final RestrictedComponentsAuthorizable 
restrictedComponentsAuthorizable = new RestrictedComponentsAuthorizable();
-            final AuthorizationResult result = 
restrictedComponentsAuthorizable.checkAuthorization(authorizer, 
RequestAction.WRITE, user, resourceContext);
-            if (Result.Denied.equals(result.getResult())) {
-                return result;
+            final Set<Authorizable> restrictedComponentsAuthorizables = 
RestrictedComponentsAuthorizableFactory.getRestrictedComponentsAuthorizable(getComponentClass());
+
+            for (final Authorizable restrictedComponentsAuthorizable : 
restrictedComponentsAuthorizables) {
+                final AuthorizationResult result = 
restrictedComponentsAuthorizable.checkAuthorization(authorizer, 
RequestAction.WRITE, user, resourceContext);
+                if (Result.Denied.equals(result.getResult())) {
+                    return result;
+                }
             }
         }
 
@@ -131,8 +140,11 @@ public interface ConfiguredComponent extends 
ComponentAuthorizable {
         // if this is a modification request and the reporting task is 
restricted ensure the user has elevated privileges. if this
         // is not a modification request, we just want to use the normal rules
         if (RequestAction.WRITE.equals(action) && isRestricted()) {
-            final RestrictedComponentsAuthorizable 
restrictedComponentsAuthorizable = new RestrictedComponentsAuthorizable();
-            restrictedComponentsAuthorizable.authorize(authorizer, 
RequestAction.WRITE, user, resourceContext);
+            final Set<Authorizable> restrictedComponentsAuthorizables = 
RestrictedComponentsAuthorizableFactory.getRestrictedComponentsAuthorizable(getComponentClass());
+
+            for (final Authorizable restrictedComponentsAuthorizable : 
restrictedComponentsAuthorizables) {
+                restrictedComponentsAuthorizable.authorize(authorizer, 
RequestAction.WRITE, user, resourceContext);
+            }
         }
 
         // defer to the base authorization check

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardProcessorNode.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardProcessorNode.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardProcessorNode.java
index 12f4b1e..c6d62a2 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardProcessorNode.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardProcessorNode.java
@@ -245,6 +245,11 @@ public class StandardProcessorNode extends ProcessorNode 
implements Connectable
     }
 
     @Override
+    public Class<?> getComponentClass() {
+        return getProcessor().getClass();
+    }
+
+    @Override
     public boolean isDeprecated() {
         return 
getProcessor().getClass().isAnnotationPresent(DeprecationNotice.class);
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingTaskNode.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingTaskNode.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingTaskNode.java
index 40bdf8b..dbe2f51 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingTaskNode.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingTaskNode.java
@@ -68,6 +68,11 @@ public class StandardReportingTaskNode extends 
AbstractReportingTaskNode impleme
     }
 
     @Override
+    public Class<?> getComponentClass() {
+        return getReportingContext().getClass();
+    }
+
+    @Override
     public boolean isDeprecated() {
         return 
getReportingTask().getClass().isAnnotationPresent(DeprecationNotice.class);
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
index 53fd166..b51faa8 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
@@ -151,6 +151,11 @@ public class StandardControllerServiceNode extends 
AbstractConfiguredComponent i
     }
 
     @Override
+    public Class<?> getComponentClass() {
+        return getControllerServiceImplementation().getClass();
+    }
+
+    @Override
     public boolean isDeprecated() {
         return 
getControllerServiceImplementation().getClass().isAnnotationPresent(DeprecationNotice.class);
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizableLookup.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizableLookup.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizableLookup.java
index 95c5539..3f95656 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizableLookup.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizableLookup.java
@@ -17,6 +17,7 @@
 package org.apache.nifi.authorization;
 
 import org.apache.nifi.authorization.resource.Authorizable;
+import org.apache.nifi.components.RequiredPermission;
 import org.apache.nifi.web.api.dto.BundleDTO;
 import org.apache.nifi.web.api.dto.FlowSnippetDTO;
 
@@ -263,4 +264,12 @@ public interface AuthorizableLookup {
      * @return authorizable
      */
     Authorizable getRestrictedComponents();
+
+    /**
+     * Get the authorizable for accessing restricted components with a 
specific required permission.
+     *
+     * @param requiredPermission required permission
+     * @return authorizable
+     */
+    Authorizable getRestrictedComponents(RequiredPermission 
requiredPermission);
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/ComponentAuthorizable.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/ComponentAuthorizable.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/ComponentAuthorizable.java
index 1b7f8cd..4e29b30 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/ComponentAuthorizable.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/ComponentAuthorizable.java
@@ -20,6 +20,7 @@ import org.apache.nifi.authorization.resource.Authorizable;
 import org.apache.nifi.components.PropertyDescriptor;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * Authorizable for a component that references a ControllerService.
@@ -40,6 +41,13 @@ public interface ComponentAuthorizable {
     boolean isRestricted();
 
     /**
+     * Returns all component restriction authorizables for this component.
+     *
+     * @return all component restriction authorizables
+     */
+    Set<Authorizable> getRestrictedAuthorizables();
+
+    /**
      * Returns the property descriptor for the specified property.
      *
      * @param propertyName property name

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/StandardAuthorizableLookup.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/StandardAuthorizableLookup.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/StandardAuthorizableLookup.java
index 8cad740..42b3a55 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/StandardAuthorizableLookup.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/StandardAuthorizableLookup.java
@@ -24,12 +24,13 @@ import 
org.apache.nifi.authorization.resource.DataAuthorizable;
 import org.apache.nifi.authorization.resource.DataTransferAuthorizable;
 import org.apache.nifi.authorization.resource.ResourceFactory;
 import org.apache.nifi.authorization.resource.ResourceType;
-import org.apache.nifi.authorization.resource.RestrictedComponentsAuthorizable;
+import 
org.apache.nifi.authorization.resource.RestrictedComponentsAuthorizableFactory;
 import org.apache.nifi.authorization.resource.TenantAuthorizable;
 import org.apache.nifi.authorization.user.NiFiUser;
 import org.apache.nifi.bundle.BundleCoordinate;
 import org.apache.nifi.components.ConfigurableComponent;
 import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.RequiredPermission;
 import org.apache.nifi.connectable.Connectable;
 import org.apache.nifi.connectable.Connection;
 import org.apache.nifi.connectable.Port;
@@ -70,7 +71,6 @@ import java.util.stream.Collectors;
 class StandardAuthorizableLookup implements AuthorizableLookup {
 
     private static final TenantAuthorizable TENANT_AUTHORIZABLE = new 
TenantAuthorizable();
-    private static final Authorizable RESTRICTED_COMPONENTS_AUTHORIZABLE = new 
RestrictedComponentsAuthorizable();
 
     private static final Authorizable POLICIES_AUTHORIZABLE = new 
Authorizable() {
         @Override
@@ -500,6 +500,20 @@ class StandardAuthorizableLookup implements 
AuthorizableLookup {
             } else {
                 return new 
DataTransferAuthorizable(getAccessPolicy(resourceType, resource));
             }
+        } else if (ResourceType.RestrictedComponents.equals(resourceType)) {
+            final String slashRequiredPermission = 
StringUtils.substringAfter(resource, resourceType.getValue());
+
+            if (slashRequiredPermission.startsWith("/")) {
+                final RequiredPermission requiredPermission = 
RequiredPermission.valueOfPermissionIdentifier(slashRequiredPermission.substring(1));
+
+                if (requiredPermission == null) {
+                    throw new ResourceNotFoundException("Unrecognized 
resource: " + resource);
+                }
+
+                return getRestrictedComponents(requiredPermission);
+            } else {
+                return getRestrictedComponents();
+            }
         } else {
             return getAccessPolicy(resourceType, resource);
         }
@@ -629,9 +643,6 @@ class StandardAuthorizableLookup implements 
AuthorizableLookup {
             case Tenant:
                 authorizable = getTenant();
                 break;
-            case RestrictedComponents:
-                authorizable = getRestrictedComponents();
-                break;
         }
 
         if (authorizable == null) {
@@ -724,7 +735,12 @@ class StandardAuthorizableLookup implements 
AuthorizableLookup {
 
     @Override
     public Authorizable getRestrictedComponents() {
-        return RESTRICTED_COMPONENTS_AUTHORIZABLE;
+        return 
RestrictedComponentsAuthorizableFactory.getRestrictedComponentsAuthorizable();
+    }
+
+    @Override
+    public Authorizable getRestrictedComponents(final RequiredPermission 
requiredPermission) {
+        return 
RestrictedComponentsAuthorizableFactory.getRestrictedComponentsAuthorizable(requiredPermission);
     }
 
     @Override
@@ -754,6 +770,11 @@ class StandardAuthorizableLookup implements 
AuthorizableLookup {
         }
 
         @Override
+        public Set<Authorizable> getRestrictedAuthorizables() {
+            return 
RestrictedComponentsAuthorizableFactory.getRestrictedComponentsAuthorizable(configurableComponent.getClass());
+        }
+
+        @Override
         public String getValue(PropertyDescriptor propertyDescriptor) {
             return null;
         }
@@ -795,6 +816,11 @@ class StandardAuthorizableLookup implements 
AuthorizableLookup {
         }
 
         @Override
+        public Set<Authorizable> getRestrictedAuthorizables() {
+            return 
RestrictedComponentsAuthorizableFactory.getRestrictedComponentsAuthorizable(processorNode.getComponentClass());
+        }
+
+        @Override
         public String getValue(PropertyDescriptor propertyDescriptor) {
             return processorNode.getProperty(propertyDescriptor);
         }
@@ -836,6 +862,11 @@ class StandardAuthorizableLookup implements 
AuthorizableLookup {
         }
 
         @Override
+        public Set<Authorizable> getRestrictedAuthorizables() {
+            return 
RestrictedComponentsAuthorizableFactory.getRestrictedComponentsAuthorizable(controllerServiceNode.getComponentClass());
+        }
+
+        @Override
         public String getValue(PropertyDescriptor propertyDescriptor) {
             return controllerServiceNode.getProperty(propertyDescriptor);
         }
@@ -877,6 +908,11 @@ class StandardAuthorizableLookup implements 
AuthorizableLookup {
         }
 
         @Override
+        public Set<Authorizable> getRestrictedAuthorizables() {
+            return 
RestrictedComponentsAuthorizableFactory.getRestrictedComponentsAuthorizable(reportingTaskNode.getComponentClass());
+        }
+
+        @Override
         public String getValue(PropertyDescriptor propertyDescriptor) {
             return reportingTaskNode.getProperty(propertyDescriptor);
         }

http://git-wip-us.apache.org/repos/asf/nifi/blob/b1217f52/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 f57f628..2044559 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
@@ -55,6 +55,7 @@ import 
org.apache.nifi.cluster.manager.exception.UnknownNodeException;
 import org.apache.nifi.cluster.protocol.NodeIdentifier;
 import org.apache.nifi.components.ConfigurableComponent;
 import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.RequiredPermission;
 import org.apache.nifi.components.ValidationResult;
 import org.apache.nifi.components.Validator;
 import org.apache.nifi.components.state.Scope;
@@ -136,6 +137,7 @@ import org.apache.nifi.web.api.dto.ComponentDTO;
 import org.apache.nifi.web.api.dto.ComponentDifferenceDTO;
 import org.apache.nifi.web.api.dto.ComponentHistoryDTO;
 import org.apache.nifi.web.api.dto.ComponentReferenceDTO;
+import org.apache.nifi.web.api.dto.ComponentRestrictionPermissionDTO;
 import org.apache.nifi.web.api.dto.ComponentStateDTO;
 import org.apache.nifi.web.api.dto.ConnectionDTO;
 import org.apache.nifi.web.api.dto.ControllerConfigurationDTO;
@@ -168,6 +170,7 @@ import org.apache.nifi.web.api.dto.RegistryDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO;
 import org.apache.nifi.web.api.dto.ReportingTaskDTO;
+import org.apache.nifi.web.api.dto.RequiredPermissionDTO;
 import org.apache.nifi.web.api.dto.ResourceDTO;
 import org.apache.nifi.web.api.dto.RevisionDTO;
 import org.apache.nifi.web.api.dto.SnippetDTO;
@@ -3506,9 +3509,26 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
         
entity.setControllerPermissions(dtoFactory.createPermissionsDto(authorizableLookup.getController()));
         
entity.setPoliciesPermissions(dtoFactory.createPermissionsDto(authorizableLookup.getPolicies()));
         
entity.setSystemPermissions(dtoFactory.createPermissionsDto(authorizableLookup.getSystem()));
-        
entity.setRestrictedComponentsPermissions(dtoFactory.createPermissionsDto(authorizableLookup.getRestrictedComponents()));
         
entity.setCanVersionFlows(CollectionUtils.isNotEmpty(flowRegistryClient.getRegistryIdentifiers()));
 
+        
entity.setRestrictedComponentsPermissions(dtoFactory.createPermissionsDto(authorizableLookup.getRestrictedComponents()));
+
+        final Set<ComponentRestrictionPermissionDTO> 
componentRestrictionPermissions = new HashSet<>();
+        Arrays.stream(RequiredPermission.values()).forEach(requiredPermission 
-> {
+            final PermissionsDTO restrictionPermissions = 
dtoFactory.createPermissionsDto(authorizableLookup.getRestrictedComponents(requiredPermission));
+
+            final RequiredPermissionDTO requiredPermissionDto = new 
RequiredPermissionDTO();
+            
requiredPermissionDto.setId(requiredPermission.getPermissionIdentifier());
+            
requiredPermissionDto.setLabel(requiredPermission.getPermissionLabel());
+
+            final ComponentRestrictionPermissionDTO 
componentRestrictionPermissionDto = new ComponentRestrictionPermissionDTO();
+            
componentRestrictionPermissionDto.setRequiredPermission(requiredPermissionDto);
+            
componentRestrictionPermissionDto.setPermissions(restrictionPermissions);
+
+            
componentRestrictionPermissions.add(componentRestrictionPermissionDto);
+        });
+        
entity.setComponentRestrictionPermissions(componentRestrictionPermissions);
+
         return entity;
     }
 

Reply via email to