From 7d8dd27027b42134d4825f3d5a5da6aedb962b1a Mon Sep 17 00:00:00 2001 From: Matt Gilman Date: Mon, 15 Aug 2016 11:49:05 -0400 Subject: [PATCH] NIFI-2554: - Requiring READ permissions on the referenced controller service when creating/updating processors, controller services, and reporting tasks. - Preventing client side selection of unauthorized controller services unless they were the previously configured value. This closes #860. Signed-off-by: Bryan Bende --- .../authorization/AuthorizableLookup.java | 36 ++++- .../AuthorizeControllerServiceReference.java | 74 +++++++++ ...rviceReferencingComponentAuthorizable.java | 48 ++++++ .../StandardAuthorizableLookup.java | 144 ++++++++++++++++-- .../nifi/web/StandardNiFiServiceFacade.java | 16 +- .../StandardNiFiWebConfigurationContext.java | 38 +++-- .../nifi/web/api/ApplicationResource.java | 4 +- .../nifi/web/api/ControllerResource.java | 46 ++++-- .../web/api/ControllerServiceResource.java | 25 +-- .../nifi/web/api/ProcessGroupResource.java | 56 ++++--- .../nifi/web/api/ProcessorResource.java | 27 +++- .../nifi/web/api/ReportingTaskResource.java | 23 ++- .../nifi/web/controller/ControllerFacade.java | 35 +++++ .../webapp/js/jquery/combo/jquery.combo.css | 4 + .../webapp/js/jquery/combo/jquery.combo.js | 26 +--- .../propertytable/jquery.propertytable.js | 1 + 16 files changed, 489 insertions(+), 114 deletions(-) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeControllerServiceReference.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/ControllerServiceReferencingComponentAuthorizable.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 69104db590..0cd0548ae4 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 @@ -34,7 +34,17 @@ public interface AuthorizableLookup { * @param id processor id * @return authorizable */ - Authorizable getProcessor(String id); + ControllerServiceReferencingComponentAuthorizable getProcessor(String id); + + /** + * Get the authorizable for this Processor. This will create a dummy instance of the + * processor. The intent of this method is to provide access to the PropertyDescriptors + * prior to the component being created. + * + * @param type processor type + * @return authorizable + */ + ControllerServiceReferencingComponentAuthorizable getProcessorByType(String type); /** * Get the authorizable for querying Provenance. @@ -130,7 +140,17 @@ public interface AuthorizableLookup { * @param id controller service id * @return authorizable */ - Authorizable getControllerService(String id); + ControllerServiceReferencingComponentAuthorizable getControllerService(String id); + + /** + * Get the authorizable for this Controller Service. This will create a dummy instance of the + * controller service. The intent of this method is to provide access to the PropertyDescriptors + * prior to the component being created. + * + * @param type processor type + * @return authorizable + */ + ControllerServiceReferencingComponentAuthorizable getControllerServiceByType(String type); /** * Get the authorizable referencing component. @@ -147,7 +167,17 @@ public interface AuthorizableLookup { * @param id reporting task id * @return authorizable */ - Authorizable getReportingTask(String id); + ControllerServiceReferencingComponentAuthorizable getReportingTask(String id); + + /** + * Get the authorizable for this Reporting Task. This will create a dummy instance of the + * reporting task. The intent of this method is to provide access to the PropertyDescriptors + * prior to the component being created. + * + * @param type processor type + * @return authorizable + */ + ControllerServiceReferencingComponentAuthorizable getReportingTaskByType(String type); /** * Get the authorizable Template. diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeControllerServiceReference.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeControllerServiceReference.java new file mode 100644 index 0000000000..8617135f22 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeControllerServiceReference.java @@ -0,0 +1,74 @@ +/* + * 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; + +import org.apache.nifi.authorization.resource.Authorizable; +import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.authorization.user.NiFiUserUtils; +import org.apache.nifi.components.PropertyDescriptor; + +import java.util.Map; +import java.util.Objects; + +/** + * Authorizes references to Controller Services. Utilizes when Processors, Controller Services, and Reporting Tasks are created and updated. + */ +public final class AuthorizeControllerServiceReference { + + /** + * Authorizes the proposed properties for the specified authorizable. + * + * @param proposedProperties proposed properties + * @param authorizable authorizable that may reference a controller service + * @param authorizer authorizer + * @param lookup lookup + */ + public static void authorizeControllerServiceReferences(final Map proposedProperties, final ControllerServiceReferencingComponentAuthorizable authorizable, + final Authorizer authorizer, final AuthorizableLookup lookup) { + + // only attempt to authorize if properties are changing + if (proposedProperties != null) { + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + + for (final Map.Entry entry : proposedProperties.entrySet()) { + final String propertyName = entry.getKey(); + final PropertyDescriptor propertyDescriptor = authorizable.getPropertyDescriptor(propertyName); + + // if this descriptor identifies a controller service + if (propertyDescriptor.getControllerServiceDefinition() != null) { + final String currentValue = authorizable.getValue(propertyDescriptor); + final String proposedValue = entry.getValue(); + + // if the value is changing + if (!Objects.equals(currentValue, proposedValue)) { + // ensure access to the old service + if (currentValue != null) { + final Authorizable currentServiceAuthorizable = lookup.getControllerService(currentValue).getAuthorizable(); + currentServiceAuthorizable.authorize(authorizer, RequestAction.READ, user); + } + + // ensure access to the new service + if (proposedValue != null) { + final Authorizable newServiceAuthorizable = lookup.getControllerService(proposedValue).getAuthorizable(); + newServiceAuthorizable.authorize(authorizer, RequestAction.READ, user); + } + } + } + } + } + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/ControllerServiceReferencingComponentAuthorizable.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/ControllerServiceReferencingComponentAuthorizable.java new file mode 100644 index 0000000000..abffc94d7f --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/ControllerServiceReferencingComponentAuthorizable.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.authorization; + +import org.apache.nifi.authorization.resource.Authorizable; +import org.apache.nifi.components.PropertyDescriptor; + +/** + * Authorizable for a component that references a ControllerService. + */ +public interface ControllerServiceReferencingComponentAuthorizable { + /** + * Returns the base authorizable for this ControllerServiceReference. Non null + * + * @return authorizable + */ + Authorizable getAuthorizable(); + + /** + * Returns the property descriptor for the specified property. + * + * @param propertyName property name + * @return property descriptor + */ + PropertyDescriptor getPropertyDescriptor(String propertyName); + + /** + * Returns the current value of the specified property. + * + * @param propertyDescriptor property descriptor + * @return value + */ + String getValue(PropertyDescriptor propertyDescriptor); +} 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 58e79665d0..58808082ff 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,9 +24,12 @@ 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.TenantAuthorizable; +import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.connectable.Connectable; import org.apache.nifi.connectable.Connection; import org.apache.nifi.controller.ConfiguredComponent; +import org.apache.nifi.controller.ProcessorNode; +import org.apache.nifi.controller.ReportingTaskNode; import org.apache.nifi.controller.Snippet; import org.apache.nifi.controller.service.ControllerServiceNode; import org.apache.nifi.controller.service.ControllerServiceReference; @@ -115,8 +118,49 @@ class StandardAuthorizableLookup implements AuthorizableLookup { } @Override - public Authorizable getProcessor(final String id) { - return processorDAO.getProcessor(id); + public ControllerServiceReferencingComponentAuthorizable getProcessor(final String id) { + final ProcessorNode processorNode = processorDAO.getProcessor(id); + return new ControllerServiceReferencingComponentAuthorizable() { + @Override + public Authorizable getAuthorizable() { + return processorNode; + } + + @Override + public String getValue(PropertyDescriptor propertyDescriptor) { + return processorNode.getProperty(propertyDescriptor); + } + + @Override + public PropertyDescriptor getPropertyDescriptor(String propertyName) { + return processorNode.getPropertyDescriptor(propertyName); + } + }; + } + + @Override + public ControllerServiceReferencingComponentAuthorizable getProcessorByType(String type) { + try { + final ProcessorNode processorNode = controllerFacade.createTemporaryProcessor(type); + return new ControllerServiceReferencingComponentAuthorizable() { + @Override + public Authorizable getAuthorizable() { + return processorNode; + } + + @Override + public String getValue(PropertyDescriptor propertyDescriptor) { + return processorNode.getProperty(propertyDescriptor); + } + + @Override + public PropertyDescriptor getPropertyDescriptor(String propertyName) { + return processorNode.getPropertyDescriptor(propertyName); + } + }; + } catch (final Exception e) { + throw new AccessDeniedException("Unable to create processor to verify if it references any Controller Services."); + } } @Override @@ -212,8 +256,49 @@ class StandardAuthorizableLookup implements AuthorizableLookup { } @Override - public Authorizable getControllerService(final String id) { - return controllerServiceDAO.getControllerService(id); + public ControllerServiceReferencingComponentAuthorizable getControllerService(final String id) { + final ControllerServiceNode controllerService = controllerServiceDAO.getControllerService(id); + return new ControllerServiceReferencingComponentAuthorizable() { + @Override + public Authorizable getAuthorizable() { + return controllerService; + } + + @Override + public String getValue(PropertyDescriptor propertyDescriptor) { + return controllerService.getProperty(propertyDescriptor); + } + + @Override + public PropertyDescriptor getPropertyDescriptor(String propertyName) { + return controllerService.getControllerServiceImplementation().getPropertyDescriptor(propertyName); + } + }; + } + + @Override + public ControllerServiceReferencingComponentAuthorizable getControllerServiceByType(String type) { + try { + final ControllerServiceNode controllerService = controllerFacade.createTemporaryControllerService(type); + return new ControllerServiceReferencingComponentAuthorizable() { + @Override + public Authorizable getAuthorizable() { + return controllerService; + } + + @Override + public String getValue(PropertyDescriptor propertyDescriptor) { + return controllerService.getProperty(propertyDescriptor); + } + + @Override + public PropertyDescriptor getPropertyDescriptor(String propertyName) { + return controllerService.getControllerServiceImplementation().getPropertyDescriptor(propertyName); + } + }; + } catch (final Exception e) { + throw new AccessDeniedException("Unable to create controller service to verify if it references any Controller Services."); + } } @Override @@ -260,8 +345,49 @@ class StandardAuthorizableLookup implements AuthorizableLookup { } @Override - public Authorizable getReportingTask(final String id) { - return reportingTaskDAO.getReportingTask(id); + public ControllerServiceReferencingComponentAuthorizable getReportingTask(final String id) { + final ReportingTaskNode reportingTaskNode = reportingTaskDAO.getReportingTask(id); + return new ControllerServiceReferencingComponentAuthorizable() { + @Override + public Authorizable getAuthorizable() { + return reportingTaskNode; + } + + @Override + public String getValue(PropertyDescriptor propertyDescriptor) { + return reportingTaskNode.getProperty(propertyDescriptor); + } + + @Override + public PropertyDescriptor getPropertyDescriptor(String propertyName) { + return reportingTaskNode.getReportingTask().getPropertyDescriptor(propertyName); + } + }; + } + + @Override + public ControllerServiceReferencingComponentAuthorizable getReportingTaskByType(String type) { + try { + final ReportingTaskNode reportingTask = controllerFacade.createTemporaryReportingTask(type); + return new ControllerServiceReferencingComponentAuthorizable() { + @Override + public Authorizable getAuthorizable() { + return reportingTask; + } + + @Override + public String getValue(PropertyDescriptor propertyDescriptor) { + return reportingTask.getProperty(propertyDescriptor); + } + + @Override + public PropertyDescriptor getPropertyDescriptor(String propertyName) { + return reportingTask.getReportingTask().getPropertyDescriptor(propertyName); + } + }; + } catch (final Exception e) { + throw new AccessDeniedException("Unable to create reporting to verify if it references any Controller Services."); + } } @Override @@ -357,7 +483,7 @@ class StandardAuthorizableLookup implements AuthorizableLookup { Authorizable authorizable = null; switch (resourceType) { case ControllerService: - authorizable = getControllerService(componentId); + authorizable = getControllerService(componentId).getAuthorizable(); break; case Funnel: authorizable = getFunnel(componentId); @@ -372,7 +498,7 @@ class StandardAuthorizableLookup implements AuthorizableLookup { authorizable = getOutputPort(componentId); break; case Processor: - authorizable = getProcessor(componentId); + authorizable = getProcessor(componentId).getAuthorizable(); break; case ProcessGroup: authorizable = getProcessGroup(componentId).getAuthorizable(); @@ -381,7 +507,7 @@ class StandardAuthorizableLookup implements AuthorizableLookup { authorizable = getRemoteProcessGroup(componentId); break; case ReportingTask: - authorizable = getReportingTask(componentId); + authorizable = getReportingTask(componentId).getAuthorizable(); break; case Template: authorizable = getTemplate(componentId); 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 34890adc71..d336b51fdd 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 @@ -2275,13 +2275,13 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { try { switch (type) { case PROCESSOR: - authorizable = authorizableLookup.getProcessor(sourceId); + authorizable = authorizableLookup.getProcessor(sourceId).getAuthorizable(); break; case REPORTING_TASK: - authorizable = authorizableLookup.getReportingTask(sourceId); + authorizable = authorizableLookup.getReportingTask(sourceId).getAuthorizable(); break; case CONTROLLER_SERVICE: - authorizable = authorizableLookup.getControllerService(sourceId); + authorizable = authorizableLookup.getControllerService(sourceId).getAuthorizable(); break; case FLOW_CONTROLLER: authorizable = controllerFacade; @@ -2465,7 +2465,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { final List authorizedControllerServiceBulletins = new ArrayList<>(); for (final Bulletin bulletin : allControllerServiceBulletins) { try { - final Authorizable controllerServiceAuthorizable = authorizableLookup.getControllerService(bulletin.getSourceId()); + final Authorizable controllerServiceAuthorizable = authorizableLookup.getControllerService(bulletin.getSourceId()).getAuthorizable(); if (controllerServiceAuthorizable.isAuthorized(authorizer, RequestAction.READ, user)) { authorizedControllerServiceBulletins.add(bulletin); } @@ -2481,7 +2481,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { final List authorizedReportingTaskBulletins = new ArrayList<>(); for (final Bulletin bulletin : allReportingTaskBulletins) { try { - final Authorizable reportingTaskAuthorizable = authorizableLookup.getReportingTask(bulletin.getSourceId()); + final Authorizable reportingTaskAuthorizable = authorizableLookup.getReportingTask(bulletin.getSourceId()).getAuthorizable(); if (reportingTaskAuthorizable.isAuthorized(authorizer, RequestAction.READ, user)) { authorizedReportingTaskBulletins.add(bulletin); } @@ -2957,13 +2957,13 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { try { switch (type) { case Processor: - authorizable = authorizableLookup.getProcessor(sourceId); + authorizable = authorizableLookup.getProcessor(sourceId).getAuthorizable(); break; case ReportingTask: - authorizable = authorizableLookup.getReportingTask(sourceId); + authorizable = authorizableLookup.getReportingTask(sourceId).getAuthorizable(); break; case ControllerService: - authorizable = authorizableLookup.getControllerService(sourceId); + authorizable = authorizableLookup.getControllerService(sourceId).getAuthorizable(); break; case Controller: authorizable = controllerFacade; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java index 6a931f9cf6..8269d7925a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java @@ -29,7 +29,9 @@ import org.apache.nifi.authorization.AccessDeniedException; import org.apache.nifi.authorization.AuthorizationRequest; import org.apache.nifi.authorization.AuthorizationResult; import org.apache.nifi.authorization.AuthorizationResult.Result; +import org.apache.nifi.authorization.AuthorizeControllerServiceReference; import org.apache.nifi.authorization.Authorizer; +import org.apache.nifi.authorization.ControllerServiceReferencingComponentAuthorizable; import org.apache.nifi.authorization.RequestAction; import org.apache.nifi.authorization.UserContextKeys; import org.apache.nifi.authorization.resource.Authorizable; @@ -145,7 +147,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration case ProcessorConfiguration: // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable authorizable = lookup.getProcessor(requestContext.getId()); + final Authorizable authorizable = lookup.getProcessor(requestContext.getId()).getAuthorizable(); authorizable.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }); @@ -154,7 +156,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration case ControllerServiceConfiguration: // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable authorizable = lookup.getControllerService(requestContext.getId()); + final Authorizable authorizable = lookup.getControllerService(requestContext.getId()).getAuthorizable(); authorizable.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }); @@ -163,7 +165,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration case ReportingTaskConfiguration: // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable authorizable = lookup.getReportingTask(requestContext.getId()); + final Authorizable authorizable = lookup.getReportingTask(requestContext.getId()).getAuthorizable(); authorizable.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }); @@ -336,7 +338,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable authorizable = lookup.getProcessor(id); + final Authorizable authorizable = lookup.getProcessor(id).getAuthorizable(); authorizable.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }); @@ -387,8 +389,12 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable authorizable = lookup.getProcessor(id); - authorizable.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + // authorize the processor + final ControllerServiceReferencingComponentAuthorizable authorizable = lookup.getProcessor(id); + authorizable.getAuthorizable().authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + + // authorize any referenced service + AuthorizeControllerServiceReference.authorizeControllerServiceReferences(properties, authorizable, authorizer, lookup); }); final ProcessorDTO processor; @@ -516,7 +522,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable authorizable = lookup.getControllerService(id); + final Authorizable authorizable = lookup.getControllerService(id).getAuthorizable(); authorizable.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }); @@ -574,8 +580,12 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable authorizable = lookup.getControllerService(id); - authorizable.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + // authorize the controller service + final ControllerServiceReferencingComponentAuthorizable authorizable = lookup.getControllerService(id); + authorizable.getAuthorizable().authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + + // authorize any referenced service + AuthorizeControllerServiceReference.authorizeControllerServiceReferences(properties, authorizable, authorizer, lookup); }); final ControllerServiceDTO controllerService; @@ -677,7 +687,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable authorizable = lookup.getReportingTask(id); + final Authorizable authorizable = lookup.getReportingTask(id).getAuthorizable(); authorizable.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }); @@ -735,8 +745,12 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable authorizable = lookup.getReportingTask(id); - authorizable.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + // authorize the reporting task + final ControllerServiceReferencingComponentAuthorizable authorizable = lookup.getReportingTask(id); + authorizable.getAuthorizable().authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + + // authorize any referenced service + AuthorizeControllerServiceReference.authorizeControllerServiceReferences(properties, authorizable, authorizer, lookup); }); final ReportingTaskDTO reportingTask; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java index aeffa89cdb..880186de7d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java @@ -426,7 +426,7 @@ public abstract class ApplicationResource { processGroupAuthorizable.getEncapsulatedAuthorizables().forEach(authorize); }); snippet.getRemoteProcessGroups().keySet().stream().map(id -> lookup.getRemoteProcessGroup(id)).forEach(authorize); - snippet.getProcessors().keySet().stream().map(id -> lookup.getProcessor(id)).forEach(authorize); + snippet.getProcessors().keySet().stream().map(id -> lookup.getProcessor(id).getAuthorizable()).forEach(authorize); snippet.getInputPorts().keySet().stream().map(id -> lookup.getInputPort(id)).forEach(authorize); snippet.getOutputPorts().keySet().stream().map(id -> lookup.getOutputPort(id)).forEach(authorize); snippet.getConnections().keySet().stream().map(id -> lookup.getConnection(id)).forEach(connAuth -> authorize.accept(connAuth.getAuthorizable())); @@ -451,7 +451,7 @@ public abstract class ApplicationResource { processGroupAuthorizable.getEncapsulatedAuthorizables().forEach(authorize); }); snippet.getRemoteProcessGroups().keySet().stream().map(id -> lookup.getRemoteProcessGroup(id)).forEach(authorize); - snippet.getProcessors().keySet().stream().map(id -> lookup.getProcessor(id)).forEach(authorize); + snippet.getProcessors().keySet().stream().map(id -> lookup.getProcessor(id).getAuthorizable()).forEach(authorize); snippet.getInputPorts().keySet().stream().map(id -> lookup.getInputPort(id)).forEach(authorize); snippet.getOutputPorts().keySet().stream().map(id -> lookup.getOutputPort(id)).forEach(authorize); snippet.getConnections().keySet().stream().map(id -> lookup.getConnection(id)).forEach(connAuth -> authorize.accept(connAuth.getAuthorizable())); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java index 4063a72b73..8971aed2fc 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java @@ -28,7 +28,9 @@ import org.apache.nifi.authorization.AccessDeniedException; import org.apache.nifi.authorization.AuthorizationRequest; import org.apache.nifi.authorization.AuthorizationResult; import org.apache.nifi.authorization.AuthorizationResult.Result; +import org.apache.nifi.authorization.AuthorizeControllerServiceReference; import org.apache.nifi.authorization.Authorizer; +import org.apache.nifi.authorization.ControllerServiceReferencingComponentAuthorizable; import org.apache.nifi.authorization.RequestAction; import org.apache.nifi.authorization.UserContextKeys; import org.apache.nifi.authorization.resource.ResourceFactory; @@ -39,7 +41,9 @@ import org.apache.nifi.web.IllegalClusterResourceRequestException; import org.apache.nifi.web.NiFiServiceFacade; import org.apache.nifi.web.Revision; import org.apache.nifi.web.api.dto.ClusterDTO; +import org.apache.nifi.web.api.dto.ControllerServiceDTO; import org.apache.nifi.web.api.dto.NodeDTO; +import org.apache.nifi.web.api.dto.ReportingTaskDTO; import org.apache.nifi.web.api.entity.ClusterEntity; import org.apache.nifi.web.api.entity.ControllerConfigurationEntity; import org.apache.nifi.web.api.entity.ControllerServiceEntity; @@ -230,7 +234,8 @@ public class ControllerResource extends ApplicationResource { value = "Creates a new reporting task", response = ReportingTaskEntity.class, authorizations = { - @Authorization(value = "Write - /controller", type = "") + @Authorization(value = "Write - /controller", type = ""), + @Authorization(value = "Read - any referenced Controller Services - /controller-services/{uuid}", type = "") } ) @ApiResponses( @@ -256,11 +261,12 @@ public class ControllerResource extends ApplicationResource { throw new IllegalArgumentException("A revision of 0 must be specified when creating a new Reporting task."); } - if (reportingTaskEntity.getComponent().getId() != null) { + final ReportingTaskDTO requestReportingTask = reportingTaskEntity.getComponent(); + if (requestReportingTask.getId() != null) { throw new IllegalArgumentException("Reporting task ID cannot be specified."); } - if (StringUtils.isBlank(reportingTaskEntity.getComponent().getType())) { + if (StringUtils.isBlank(requestReportingTask.getType())) { throw new IllegalArgumentException("The type of reporting task to create must be specified."); } @@ -274,6 +280,11 @@ public class ControllerResource extends ApplicationResource { // authorize access serviceFacade.authorizeAccess(lookup -> { authorizeController(RequestAction.WRITE); + + if (requestReportingTask.getProperties() != null) { + final ControllerServiceReferencingComponentAuthorizable authorizable = lookup.getReportingTaskByType(requestReportingTask.getType()); + AuthorizeControllerServiceReference.authorizeControllerServiceReferences(requestReportingTask.getProperties(), authorizable, authorizer, lookup); + } }); } if (validationPhase) { @@ -281,11 +292,11 @@ public class ControllerResource extends ApplicationResource { } // set the processor id as appropriate - reportingTaskEntity.getComponent().setId(generateUuid()); + requestReportingTask.setId(generateUuid()); // create the reporting task and generate the json - final Revision revision = getRevision(reportingTaskEntity, reportingTaskEntity.getComponent().getId()); - final ReportingTaskEntity entity = serviceFacade.createReportingTask(revision, reportingTaskEntity.getComponent()); + final Revision revision = getRevision(reportingTaskEntity, requestReportingTask.getId()); + final ReportingTaskEntity entity = serviceFacade.createReportingTask(revision, requestReportingTask); reportingTaskResource.populateRemainingReportingTaskEntityContent(entity); // build the response @@ -311,7 +322,8 @@ public class ControllerResource extends ApplicationResource { value = "Creates a new controller service", response = ControllerServiceEntity.class, authorizations = { - @Authorization(value = "Write - /controller", type = "") + @Authorization(value = "Write - /controller", type = ""), + @Authorization(value = "Read - any referenced Controller Services - /controller-services/{uuid}", type = "") } ) @ApiResponses( @@ -337,11 +349,16 @@ public class ControllerResource extends ApplicationResource { throw new IllegalArgumentException("A revision of 0 must be specified when creating a new Controller service."); } - if (controllerServiceEntity.getComponent().getId() != null) { + final ControllerServiceDTO requestControllerService = controllerServiceEntity.getComponent(); + if (requestControllerService.getId() != null) { throw new IllegalArgumentException("Controller service ID cannot be specified."); } - if (StringUtils.isBlank(controllerServiceEntity.getComponent().getType())) { + if (requestControllerService.getParentGroupId() != null) { + throw new IllegalArgumentException("Parent process group ID cannot be specified."); + } + + if (StringUtils.isBlank(requestControllerService.getType())) { throw new IllegalArgumentException("The type of controller service to create must be specified."); } @@ -355,6 +372,11 @@ public class ControllerResource extends ApplicationResource { // authorize access serviceFacade.authorizeAccess(lookup -> { authorizeController(RequestAction.WRITE); + + if (requestControllerService.getProperties() != null) { + final ControllerServiceReferencingComponentAuthorizable authorizable = lookup.getControllerServiceByType(requestControllerService.getType()); + AuthorizeControllerServiceReference.authorizeControllerServiceReferences(requestControllerService.getProperties(), authorizable, authorizer, lookup); + } }); } if (validationPhase) { @@ -362,11 +384,11 @@ public class ControllerResource extends ApplicationResource { } // set the processor id as appropriate - controllerServiceEntity.getComponent().setId(generateUuid()); + requestControllerService.setId(generateUuid()); // create the controller service and generate the json - final Revision revision = getRevision(controllerServiceEntity, controllerServiceEntity.getComponent().getId()); - final ControllerServiceEntity entity = serviceFacade.createControllerService(revision, null, controllerServiceEntity.getComponent()); + final Revision revision = getRevision(controllerServiceEntity, requestControllerService.getId()); + final ControllerServiceEntity entity = serviceFacade.createControllerService(revision, null, requestControllerService); controllerServiceResource.populateRemainingControllerServiceEntityContent(entity); // build the response diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java index 89b441f089..0b9434a9f1 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java @@ -23,7 +23,9 @@ import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; import com.wordnik.swagger.annotations.Authorization; import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.authorization.AuthorizeControllerServiceReference; import org.apache.nifi.authorization.Authorizer; +import org.apache.nifi.authorization.ControllerServiceReferencingComponentAuthorizable; import org.apache.nifi.authorization.RequestAction; import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.user.NiFiUserUtils; @@ -175,7 +177,7 @@ public class ControllerServiceResource extends ApplicationResource { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable controllerService = lookup.getControllerService(id); + final Authorizable controllerService = lookup.getControllerService(id).getAuthorizable(); controllerService.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }); @@ -236,7 +238,7 @@ public class ControllerServiceResource extends ApplicationResource { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable controllerService = lookup.getControllerService(id); + final Authorizable controllerService = lookup.getControllerService(id).getAuthorizable(); controllerService.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }); @@ -290,7 +292,7 @@ public class ControllerServiceResource extends ApplicationResource { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable controllerService = lookup.getControllerService(id); + final Authorizable controllerService = lookup.getControllerService(id).getAuthorizable(); controllerService.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }); @@ -348,7 +350,7 @@ public class ControllerServiceResource extends ApplicationResource { if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable controllerService = lookup.getControllerService(id); + final Authorizable controllerService = lookup.getControllerService(id).getAuthorizable(); controllerService.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }); } @@ -406,7 +408,7 @@ public class ControllerServiceResource extends ApplicationResource { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable controllerService = lookup.getControllerService(id); + final Authorizable controllerService = lookup.getControllerService(id).getAuthorizable(); controllerService.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }); @@ -545,7 +547,8 @@ public class ControllerServiceResource extends ApplicationResource { value = "Updates a controller service", response = ControllerServiceEntity.class, authorizations = { - @Authorization(value = "Write - /controller-services/{uuid}", type = "") + @Authorization(value = "Write - /controller-services/{uuid}", type = ""), + @Authorization(value = "Read - any referenced Controller Services - /controller-services/{uuid}", type = "") } ) @ApiResponses( @@ -594,8 +597,12 @@ public class ControllerServiceResource extends ApplicationResource { serviceFacade, revision, lookup -> { - Authorizable authorizable = lookup.getControllerService(id); - authorizable.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + // authorize the service + final ControllerServiceReferencingComponentAuthorizable authorizable = lookup.getControllerService(id); + authorizable.getAuthorizable().authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + + // authorize any referenced services + AuthorizeControllerServiceReference.authorizeControllerServiceReferences(requestControllerServiceDTO.getProperties(), authorizable, authorizer, lookup); }, () -> serviceFacade.verifyUpdateControllerService(requestControllerServiceDTO), () -> { @@ -668,7 +675,7 @@ public class ControllerServiceResource extends ApplicationResource { serviceFacade, revision, lookup -> { - final Authorizable controllerService = lookup.getControllerService(id); + final Authorizable controllerService = lookup.getControllerService(id).getAuthorizable(); controllerService.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }, () -> serviceFacade.verifyDeleteControllerService(id), diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java index d9055c93b6..00fe7d212c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java @@ -26,7 +26,9 @@ import com.wordnik.swagger.annotations.ApiResponses; import com.wordnik.swagger.annotations.Authorization; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.authorization.AuthorizableLookup; +import org.apache.nifi.authorization.AuthorizeControllerServiceReference; import org.apache.nifi.authorization.Authorizer; +import org.apache.nifi.authorization.ControllerServiceReferencingComponentAuthorizable; import org.apache.nifi.authorization.ProcessGroupAuthorizable; import org.apache.nifi.authorization.RequestAction; import org.apache.nifi.authorization.resource.Authorizable; @@ -38,7 +40,10 @@ import org.apache.nifi.web.NiFiServiceFacade; import org.apache.nifi.web.ResourceNotFoundException; import org.apache.nifi.web.Revision; import org.apache.nifi.web.api.dto.ConnectionDTO; +import org.apache.nifi.web.api.dto.ControllerServiceDTO; import org.apache.nifi.web.api.dto.ProcessGroupDTO; +import org.apache.nifi.web.api.dto.ProcessorConfigDTO; +import org.apache.nifi.web.api.dto.ProcessorDTO; import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO; import org.apache.nifi.web.api.dto.TemplateDTO; import org.apache.nifi.web.api.dto.flow.FlowDTO; @@ -553,7 +558,8 @@ public class ProcessGroupResource extends ApplicationResource { value = "Creates a new processor", response = ProcessorEntity.class, authorizations = { - @Authorization(value = "Write - /process-groups/{uuid}", type = "") + @Authorization(value = "Write - /process-groups/{uuid}", type = ""), + @Authorization(value = "Read - any referenced Controller Services - /controller-services/{uuid}", type = "") } ) @ApiResponses( @@ -585,19 +591,20 @@ public class ProcessGroupResource extends ApplicationResource { throw new IllegalArgumentException("A revision of 0 must be specified when creating a new Processor."); } - if (processorEntity.getComponent().getId() != null) { + final ProcessorDTO requestProcessor = processorEntity.getComponent(); + if (requestProcessor.getId() != null) { throw new IllegalArgumentException("Processor ID cannot be specified."); } - if (StringUtils.isBlank(processorEntity.getComponent().getType())) { + if (StringUtils.isBlank(requestProcessor.getType())) { throw new IllegalArgumentException("The type of processor to create must be specified."); } - if (processorEntity.getComponent().getParentGroupId() != null && !groupId.equals(processorEntity.getComponent().getParentGroupId())) { + if (requestProcessor.getParentGroupId() != null && !groupId.equals(requestProcessor.getParentGroupId())) { throw new IllegalArgumentException(String.format("If specified, the parent process group id %s must be the same as specified in the URI %s", - processorEntity.getComponent().getParentGroupId(), groupId)); + requestProcessor.getParentGroupId(), groupId)); } - processorEntity.getComponent().setParentGroupId(groupId); + requestProcessor.setParentGroupId(groupId); if (isReplicateRequest()) { return replicate(HttpMethod.POST, processorEntity); @@ -610,6 +617,12 @@ public class ProcessGroupResource extends ApplicationResource { serviceFacade.authorizeAccess(lookup -> { final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable(); processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + + final ProcessorConfigDTO config = requestProcessor.getConfig(); + if (config != null && config.getProperties() != null) { + final ControllerServiceReferencingComponentAuthorizable authorizable = lookup.getProcessorByType(requestProcessor.getType()); + AuthorizeControllerServiceReference.authorizeControllerServiceReferences(config.getProperties(), authorizable, authorizer, lookup); + } }); } if (validationPhase) { @@ -617,11 +630,11 @@ public class ProcessGroupResource extends ApplicationResource { } // set the processor id as appropriate - processorEntity.getComponent().setId(generateUuid()); + requestProcessor.setId(generateUuid()); // create the new processor - final Revision revision = getRevision(processorEntity, processorEntity.getComponent().getId()); - final ProcessorEntity entity = serviceFacade.createProcessor(revision, groupId, processorEntity.getComponent()); + final Revision revision = getRevision(processorEntity, requestProcessor.getId()); + final ProcessorEntity entity = serviceFacade.createProcessor(revision, groupId, requestProcessor); processorResource.populateRemainingProcessorEntityContent(entity); // generate a 201 created response @@ -2074,7 +2087,8 @@ public class ProcessGroupResource extends ApplicationResource { value = "Creates a new controller service", response = ControllerServiceEntity.class, authorizations = { - @Authorization(value = "Write - /process-groups/{uuid}", type = "") + @Authorization(value = "Write - /process-groups/{uuid}", type = ""), + @Authorization(value = "Read - any referenced Controller Services - /controller-services/{uuid}", type = "") } ) @ApiResponses( @@ -2105,19 +2119,20 @@ public class ProcessGroupResource extends ApplicationResource { throw new IllegalArgumentException("A revision of 0 must be specified when creating a new Controller service."); } - if (controllerServiceEntity.getComponent().getId() != null) { + final ControllerServiceDTO requestControllerService = controllerServiceEntity.getComponent(); + if (requestControllerService.getId() != null) { throw new IllegalArgumentException("Controller service ID cannot be specified."); } - if (StringUtils.isBlank(controllerServiceEntity.getComponent().getType())) { + if (StringUtils.isBlank(requestControllerService.getType())) { throw new IllegalArgumentException("The type of controller service to create must be specified."); } - if (controllerServiceEntity.getComponent().getParentGroupId() != null && !groupId.equals(controllerServiceEntity.getComponent().getParentGroupId())) { + if (requestControllerService.getParentGroupId() != null && !groupId.equals(requestControllerService.getParentGroupId())) { throw new IllegalArgumentException(String.format("If specified, the parent process group id %s must be the same as specified in the URI %s", - controllerServiceEntity.getComponent().getParentGroupId(), groupId)); + requestControllerService.getParentGroupId(), groupId)); } - controllerServiceEntity.getComponent().setParentGroupId(groupId); + requestControllerService.setParentGroupId(groupId); if (isReplicateRequest()) { return replicate(HttpMethod.POST, controllerServiceEntity); @@ -2130,6 +2145,11 @@ public class ProcessGroupResource extends ApplicationResource { serviceFacade.authorizeAccess(lookup -> { final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable(); processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + + if (requestControllerService.getProperties() != null) { + final ControllerServiceReferencingComponentAuthorizable authorizable = lookup.getControllerServiceByType(requestControllerService.getType()); + AuthorizeControllerServiceReference.authorizeControllerServiceReferences(requestControllerService.getProperties(), authorizable, authorizer, lookup); + } }); } if (validationPhase) { @@ -2137,11 +2157,11 @@ public class ProcessGroupResource extends ApplicationResource { } // set the processor id as appropriate - controllerServiceEntity.getComponent().setId(generateUuid()); + requestControllerService.setId(generateUuid()); // create the controller service and generate the json - final Revision revision = getRevision(controllerServiceEntity, controllerServiceEntity.getComponent().getId()); - final ControllerServiceEntity entity = serviceFacade.createControllerService(revision, groupId, controllerServiceEntity.getComponent()); + final Revision revision = getRevision(controllerServiceEntity, requestControllerService.getId()); + final ControllerServiceEntity entity = serviceFacade.createControllerService(revision, groupId, requestControllerService); controllerServiceResource.populateRemainingControllerServiceEntityContent(entity); // build the response diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java index fae20aaf24..246ba707a1 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java @@ -23,9 +23,12 @@ import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; import com.wordnik.swagger.annotations.Authorization; import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.authorization.AuthorizeControllerServiceReference; import org.apache.nifi.authorization.Authorizer; +import org.apache.nifi.authorization.ControllerServiceReferencingComponentAuthorizable; import org.apache.nifi.authorization.RequestAction; import org.apache.nifi.authorization.resource.Authorizable; +import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.ui.extension.UiExtension; import org.apache.nifi.ui.extension.UiExtensionMapping; @@ -173,7 +176,7 @@ public class ProcessorResource extends ApplicationResource { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable processor = lookup.getProcessor(id); + final Authorizable processor = lookup.getProcessor(id).getAuthorizable(); processor.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }); @@ -241,7 +244,7 @@ public class ProcessorResource extends ApplicationResource { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable processor = lookup.getProcessor(id); + final Authorizable processor = lookup.getProcessor(id).getAuthorizable(); processor.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }); @@ -296,7 +299,7 @@ public class ProcessorResource extends ApplicationResource { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable processor = lookup.getProcessor(id); + final Authorizable processor = lookup.getProcessor(id).getAuthorizable(); processor.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }); @@ -355,7 +358,7 @@ public class ProcessorResource extends ApplicationResource { if (isValidationPhase || !isTwoPhaseRequest(httpServletRequest)) { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable processor = lookup.getProcessor(id); + final Authorizable processor = lookup.getProcessor(id).getAuthorizable(); processor.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }); } @@ -391,7 +394,8 @@ public class ProcessorResource extends ApplicationResource { value = "Updates a processor", response = ProcessorEntity.class, authorizations = { - @Authorization(value = "Write - /processors/{uuid}", type = "") + @Authorization(value = "Write - /processors/{uuid}", type = ""), + @Authorization(value = "Read - any referenced Controller Services - /controller-services/{uuid}", type = "") } ) @ApiResponses( @@ -440,8 +444,15 @@ public class ProcessorResource extends ApplicationResource { serviceFacade, revision, lookup -> { - Authorizable authorizable = lookup.getProcessor(id); - authorizable.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + + final ControllerServiceReferencingComponentAuthorizable authorizable = lookup.getProcessor(id); + authorizable.getAuthorizable().authorize(authorizer, RequestAction.WRITE, user); + + final ProcessorConfigDTO config = requestProcessorDTO.getConfig(); + if (config != null) { + AuthorizeControllerServiceReference.authorizeControllerServiceReferences(config.getProperties(), authorizable, authorizer, lookup); + } }, () -> serviceFacade.verifyUpdateProcessor(requestProcessorDTO), () -> { @@ -511,7 +522,7 @@ public class ProcessorResource extends ApplicationResource { serviceFacade, revision, lookup -> { - final Authorizable processor = lookup.getProcessor(id); + final Authorizable processor = lookup.getProcessor(id).getAuthorizable(); processor.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }, () -> serviceFacade.verifyDeleteProcessor(id), diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java index 0920220a38..0c2ad6873c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java @@ -23,7 +23,9 @@ import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; import com.wordnik.swagger.annotations.Authorization; import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.authorization.AuthorizeControllerServiceReference; import org.apache.nifi.authorization.Authorizer; +import org.apache.nifi.authorization.ControllerServiceReferencingComponentAuthorizable; import org.apache.nifi.authorization.RequestAction; import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.user.NiFiUserUtils; @@ -162,7 +164,7 @@ public class ReportingTaskResource extends ApplicationResource { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable reportingTask = lookup.getReportingTask(id); + final Authorizable reportingTask = lookup.getReportingTask(id).getAuthorizable(); reportingTask.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }); @@ -223,7 +225,7 @@ public class ReportingTaskResource extends ApplicationResource { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable reportingTask = lookup.getReportingTask(id); + final Authorizable reportingTask = lookup.getReportingTask(id).getAuthorizable(); reportingTask.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }); @@ -277,7 +279,7 @@ public class ReportingTaskResource extends ApplicationResource { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable reportingTask = lookup.getReportingTask(id); + final Authorizable reportingTask = lookup.getReportingTask(id).getAuthorizable(); reportingTask.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }); @@ -335,7 +337,7 @@ public class ReportingTaskResource extends ApplicationResource { if (isValidationPhase || !isTwoPhaseRequest(httpServletRequest)) { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable processor = lookup.getReportingTask(id); + final Authorizable processor = lookup.getReportingTask(id).getAuthorizable(); processor.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }); } @@ -370,7 +372,8 @@ public class ReportingTaskResource extends ApplicationResource { value = "Updates a reporting task", response = ReportingTaskEntity.class, authorizations = { - @Authorization(value = "Write - /reporting-tasks/{uuid}", type = "") + @Authorization(value = "Write - /reporting-tasks/{uuid}", type = ""), + @Authorization(value = "Read - any referenced Controller Services - /controller-services/{uuid}", type = "") } ) @ApiResponses( @@ -419,8 +422,12 @@ public class ReportingTaskResource extends ApplicationResource { serviceFacade, revision, lookup -> { - Authorizable authorizable = lookup.getReportingTask(id); - authorizable.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + // authorize reporting task + final ControllerServiceReferencingComponentAuthorizable authorizable = lookup.getReportingTask(id); + authorizable.getAuthorizable().authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + + // authorize any referenced services + AuthorizeControllerServiceReference.authorizeControllerServiceReferences(requestReportingTaskDTO.getProperties(), authorizable, authorizer, lookup); }, () -> serviceFacade.verifyUpdateReportingTask(requestReportingTaskDTO), () -> { @@ -493,7 +500,7 @@ public class ReportingTaskResource extends ApplicationResource { serviceFacade, revision, lookup -> { - final Authorizable reportingTask = lookup.getReportingTask(id); + final Authorizable reportingTask = lookup.getReportingTask(id).getAuthorizable(); reportingTask.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }, () -> serviceFacade.verifyDeleteReportingTask(id), diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java index d64956d95a..cbdc2da770 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java @@ -43,9 +43,11 @@ import org.apache.nifi.controller.ProcessorNode; import org.apache.nifi.controller.ReportingTaskNode; import org.apache.nifi.controller.ScheduledState; import org.apache.nifi.controller.Template; +import org.apache.nifi.controller.exception.ProcessorInstantiationException; import org.apache.nifi.controller.label.Label; import org.apache.nifi.controller.queue.FlowFileQueue; import org.apache.nifi.controller.queue.QueueSize; +import org.apache.nifi.controller.reporting.ReportingTaskInstantiationException; import org.apache.nifi.controller.repository.ContentNotFoundException; import org.apache.nifi.controller.repository.claim.ContentDirection; import org.apache.nifi.controller.service.ControllerServiceNode; @@ -126,6 +128,7 @@ import java.util.Set; import java.util.SortedSet; import java.util.TimeZone; import java.util.TreeSet; +import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -189,6 +192,38 @@ public class ControllerFacade implements Authorizable { flowController.setComments(comments); } + /** + * Create a temporary Processor used for extracting PropertyDescriptor's for ControllerService reference authorization. + * + * @param type type of processor + * @return processor + * @throws ProcessorInstantiationException when unable to instantiate the processor + */ + public ProcessorNode createTemporaryProcessor(String type) throws ProcessorInstantiationException { + return flowController.createProcessor(type, UUID.randomUUID().toString(), false); + } + + /** + * Create a temporary ReportingTask used for extracting PropertyDescriptor's for ControllerService reference authorization. + * + * @param type type of reporting task + * @return reporting task + * @throws ReportingTaskInstantiationException when unable to instantiate the reporting task + */ + public ReportingTaskNode createTemporaryReportingTask(String type) throws ReportingTaskInstantiationException { + return flowController.createReportingTask(type, UUID.randomUUID().toString(), false); + } + + /** + * Create a temporary ControllerService used for extracting PropertyDescriptor's for ControllerService reference authorization. + * + * @param type type of controller service + * @return controller service + */ + public ControllerServiceNode createTemporaryControllerService(String type) { + return flowController.createControllerService(type, UUID.randomUUID().toString(), false); + } + /** * Sets the max timer driven thread count of this controller. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/combo/jquery.combo.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/combo/jquery.combo.css index 67ad4990a0..1494a16dc9 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/combo/jquery.combo.css +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/combo/jquery.combo.css @@ -77,6 +77,10 @@ div.selected-disabled-option { padding: 0px 10px; } +.combo-options ul > li.disabled { + font-style: italic; +} + .combo-option-text { overflow: hidden; white-space: nowrap; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/combo/jquery.combo.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/combo/jquery.combo.js index b79ea16306..2bc406b5de 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/combo/jquery.combo.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/combo/jquery.combo.js @@ -118,30 +118,6 @@ }; - var setDisabled = function (combo, optionElement, option, disabled) { - // reset the option element - optionElement.removeClass('unset').off('click'); - - if (disabled === true) { - optionElement.addClass('unset'); - } else { - optionElement.on('click', function () { - //remove active styles - $('.combo').removeClass('combo-open'); - - // select the option - selectOption(combo, option.text, option.value); - - // click the glass pane which will hide the options - $('.combo-glass-pane').click(); - }).hover(function () { - $(this).addClass('pointer').css('background', '#eaeef0'); - }, function () { - $(this).removeClass('pointer').css('background', '#ffffff'); - }); - } - }; - var methods = { /** @@ -212,7 +188,7 @@ // this is option is enabled register appropriate listeners if (option.disabled === true) { - optionElement.addClass('unset'); + optionElement.addClass('unset disabled'); } else { optionElement.click(function () { //remove active styles diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js index 67d7efc4e9..b914482653 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js @@ -530,6 +530,7 @@ options.push({ text: allowableValue.displayName, value: allowableValue.value, + disabled: allowableValueEntity.canRead === false && allowableValue.value !== args.item['previousValue'], description: nf.Common.escapeHtml(allowableValue.description) }); });