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; }