Repository: nifi
Updated Branches:
  refs/heads/master 25a2fac45 -> fa639e259


NIFI-2542: - Ensuring transitive referencing components are able to be 
returned. - Ensuring we can enable/disable services with transitive referencing 
components. - Ensuring we cannot enable/disable services with unauthorized 
referencing components.

NIFI-2543: - Ensuring we have permissions before attempting to reload a 
controller service.

This closes #837

Signed-off-by: jpercivall <[email protected]>


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

Branch: refs/heads/master
Commit: fa639e2596cc125af80615066f500cc908d4db6a
Parents: 25a2fac
Author: Matt Gilman <[email protected]>
Authored: Thu Aug 11 10:52:04 2016 -0400
Committer: jpercivall <[email protected]>
Committed: Thu Aug 11 20:42:35 2016 -0400

----------------------------------------------------------------------
 .../StandardAuthorizableLookup.java             |  23 ++-
 .../nifi/web/StandardNiFiServiceFacade.java     |  39 ++++-
 .../js/nf/canvas/nf-controller-service.js       | 163 ++++++++++++-------
 3 files changed, 150 insertions(+), 75 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/fa639e25/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 32862b2..58e7966 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
@@ -226,19 +226,32 @@ class StandardAuthorizableLookup implements 
AuthorizableLookup {
         return COUNTERS_AUTHORIZABLE;
     }
 
-    @Override
-    public Authorizable getControllerServiceReferencingComponent(String 
controllerSeriveId, String id) {
-        final ControllerServiceNode controllerService = 
controllerServiceDAO.getControllerService(controllerSeriveId);
-        final ControllerServiceReference referencingComponents = 
controllerService.getReferences();
-
+    private ConfiguredComponent 
findControllerServiceReferencingComponent(final ControllerServiceReference 
referencingComponents, final String id) {
         ConfiguredComponent reference = null;
         for (final ConfiguredComponent component : 
referencingComponents.getReferencingComponents()) {
             if (component.getIdentifier().equals(id)) {
                 reference = component;
                 break;
             }
+
+            if (component instanceof ControllerServiceNode) {
+                final ControllerServiceNode refControllerService = 
(ControllerServiceNode) component;
+                reference = 
findControllerServiceReferencingComponent(refControllerService.getReferences(), 
id);
+                if (reference != null) {
+                    break;
+                }
+            }
         }
 
+        return reference;
+    }
+
+    @Override
+    public Authorizable getControllerServiceReferencingComponent(String 
controllerSeriveId, String id) {
+        final ControllerServiceNode controllerService = 
controllerServiceDAO.getControllerService(controllerSeriveId);
+        final ControllerServiceReference referencingComponents = 
controllerService.getReferences();
+        final ConfiguredComponent reference = 
findControllerServiceReferencingComponent(referencingComponents, id);
+
         if (reference == null) {
             throw new ResourceNotFoundException("Unable to find referencing 
component with id " + id);
         }

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa639e25/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 9db15c0..56d6595 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
@@ -1676,13 +1676,32 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
         final RevisionUpdate<ControllerServiceDTO> snapshot = 
updateComponent(revision,
                 controllerService,
                 () -> 
controllerServiceDAO.updateControllerService(controllerServiceDTO),
-                cs -> dtoFactory.createControllerServiceDto(cs));
+                cs -> {
+                    final ControllerServiceDTO dto = 
dtoFactory.createControllerServiceDto(cs);
+                    final ControllerServiceReference ref = 
controllerService.getReferences();
+                    final ControllerServiceReferencingComponentsEntity 
referencingComponentsEntity =
+                            
createControllerServiceReferencingComponentsEntity(ref, 
Sets.newHashSet(controllerService.getIdentifier()));
+                    
dto.setReferencingComponents(referencingComponentsEntity.getControllerServiceReferencingComponents());
+                    return dto;
+                });
 
         final PermissionsDTO permissions = 
dtoFactory.createPermissionsDto(controllerService);
         final List<BulletinDTO> bulletins = 
dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(controllerServiceDTO.getId()));
         return 
entityFactory.createControllerServiceEntity(snapshot.getComponent(), 
dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, 
bulletins);
     }
 
+    private Set<ConfiguredComponent> findAllReferencingComponents(final 
ControllerServiceReference reference) {
+        final Set<ConfiguredComponent> referencingComponents = new 
HashSet<>(reference.getReferencingComponents());
+
+        for (final ConfiguredComponent referencingComponent : 
reference.getReferencingComponents()) {
+            if (referencingComponent instanceof ControllerServiceNode) {
+                
referencingComponents.addAll(findAllReferencingComponents(((ControllerServiceNode)
 referencingComponent).getReferences()));
+            }
+        }
+
+        return referencingComponents;
+    }
+
     @Override
     public ControllerServiceReferencingComponentsEntity 
updateControllerServiceReferencingComponents(
             final Map<String, Revision> referenceRevisions, final String 
controllerServiceId, final ScheduledState scheduledState, final 
ControllerServiceState controllerServiceState) {
@@ -1705,13 +1724,9 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
                             updatedRevisions.put(component.getIdentifier(), 
currentRevision.incrementRevision(requestRevision.getClientId()));
                         }
 
-                        // return the current revision if the component wasn't 
updated
-                        for (final Map.Entry<String, Revision> entry : 
referenceRevisions.entrySet()) {
-                            final String componentId = entry.getKey();
-                            if (!updatedRevisions.containsKey(componentId)) {
-                                final Revision currentRevision = 
revisionManager.getRevision(componentId);
-                                updatedRevisions.put(componentId, 
currentRevision);
-                            }
+                        // ensure the revision for all referencing components 
is included regardless of whether they were updated in this request
+                        for (final ConfiguredComponent component : 
findAllReferencingComponents(updatedReference)) {
+                            
updatedRevisions.putIfAbsent(component.getIdentifier(), 
revisionManager.getRevision(component.getIdentifier()));
                         }
 
                         final ControllerServiceReferencingComponentsEntity 
entity = createControllerServiceReferencingComponentsEntity(updatedReference, 
updatedRevisions);
@@ -1757,6 +1772,7 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
         for (final ConfiguredComponent component : 
reference.getReferencingComponents()) {
             referencingRevisions.put(component.getIdentifier(), 
revisionManager.getRevision(component.getIdentifier()));
         }
+
         return createControllerServiceReferencingComponentsEntity(reference, 
referencingRevisions);
     }
 
@@ -1806,7 +1822,12 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
 
                 // if we haven't encountered this service before include it's 
referencing components
                 if (!dto.getReferenceCycle()) {
-                    final ControllerServiceReferencingComponentsEntity 
references = 
createControllerServiceReferencingComponentsEntity(node.getReferences(), 
revisions, visited);
+                    final ControllerServiceReference refReferences = 
node.getReferences();
+                    final Map<String, Revision> referencingRevisions = new 
HashMap<>(revisions);
+                    for (final ConfiguredComponent component : 
refReferences.getReferencingComponents()) {
+                        
referencingRevisions.putIfAbsent(component.getIdentifier(), 
revisionManager.getRevision(component.getIdentifier()));
+                    }
+                    final ControllerServiceReferencingComponentsEntity 
references = createControllerServiceReferencingComponentsEntity(refReferences, 
referencingRevisions, visited);
                     
dto.setReferencingComponents(references.getControllerServiceReferencingComponents());
                 }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa639e25/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
index 1a32c8e..44ee3c0 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
@@ -109,7 +109,7 @@ nf.ControllerService = (function () {
     };
 
     /**
-     * Reloads the specified controller service. It's referencing and 
referenced
+     * Reloads the specified controller service if we have read permissions. 
It's referencing and referenced
      * components are NOT reloaded.
      *
      * @param {jQuery} serviceTable
@@ -123,8 +123,9 @@ nf.ControllerService = (function () {
 
         // this may happen if controller service A references another 
controller
         // service B that has been removed. attempting to 
enable/disable/remove A
-        // will attempt to reload B which is no longer a known service
-        if (nf.Common.isUndefined(controllerServiceEntity)) {
+        // will attempt to reload B which is no longer a known service. also 
ensure
+        // we have permissions to reload the service
+        if (nf.Common.isUndefined(controllerServiceEntity) || 
controllerServiceEntity.permissions.canRead === false) {
             return $.Deferred(function (deferred) {
                 deferred.reject();
             }).promise();
@@ -457,7 +458,8 @@ nf.ControllerService = (function () {
                         if (serviceTwist.hasClass('collapsed')) {
                             var controllerServiceGrid = 
serviceTable.data('gridInstance');
                             var controllerServiceData = 
controllerServiceGrid.getData();
-                            var referencingService = 
controllerServiceData.getItemById(referencingComponent.id);
+                            var referencingServiceEntity = 
controllerServiceData.getItemById(referencingComponent.id);
+                            var referencingService = 
referencingServiceEntity.component;
 
                             // create the markup for the references
                             createReferencingComponents(serviceTable, 
referencingServiceReferencesContainer, 
referencingService.referencingComponents);
@@ -674,6 +676,33 @@ nf.ControllerService = (function () {
     };
 
     /**
+     * Gathers all referencing component revisions.
+     *
+     * @param referencingComponents
+     * @param referencingComponentRevisions
+     * @param serviceOnly - true includes only services, false includes only 
schedulable components
+     */
+    var getReferencingComponentRevisions = function (referencingComponents, 
referencingComponentRevisions, serviceOnly) {
+        // include the revision of each referencing component
+        $.each(referencingComponents, function (_, referencingComponentEntity) 
{
+            var referencingComponent = referencingComponentEntity.component;
+
+            if (serviceOnly) {
+                if (referencingComponent.referenceType === 
'ControllerService') {
+                    
referencingComponentRevisions[referencingComponentEntity.id] = 
nf.Client.getRevision(referencingComponentEntity);
+                }
+            } else {
+                if (referencingComponent.referenceType !== 
'ControllerService') {
+                    
referencingComponentRevisions[referencingComponentEntity.id] = 
nf.Client.getRevision(referencingComponentEntity);
+                }
+            }
+
+            // recurse
+            
getReferencingComponentRevisions(referencingComponent.referencingComponents, 
referencingComponentRevisions, serviceOnly);
+        });
+    };
+
+    /**
      * Updates the scheduled state of the processors/reporting tasks 
referencing
      * the specified controller service.
      *
@@ -683,17 +712,15 @@ nf.ControllerService = (function () {
      * @param {function} pollCondition
      */
     var updateReferencingSchedulableComponents = function (serviceTable, 
controllerServiceEntity, running, pollCondition) {
+        var referencingRevisions = {};
+        
getReferencingComponentRevisions(controllerServiceEntity.component.referencingComponents,
 referencingRevisions, false);
+
         var referenceEntity = {
             'id': controllerServiceEntity.id,
             'state': running ? 'RUNNING' : 'STOPPED',
-            'referencingComponentRevisions': {}
+            'referencingComponentRevisions': referencingRevisions
         };
 
-        // include the revision of each referencing component
-        $.each(controllerServiceEntity.component.referencingComponents, 
function (_, referencingComponent) {
-            
referenceEntity.referencingComponentRevisions[referencingComponent.id] = 
nf.Client.getRevision(referencingComponent);
-        });
-
         // issue the request to update the referencing components
         var updated = $.ajax({
             type: 'PUT',
@@ -956,18 +983,16 @@ nf.ControllerService = (function () {
      * @param {function} pollCondition
      */
     var updateReferencingServices = function (serviceTable, 
controllerServiceEntity, enabled, pollCondition) {
+        var referencingRevisions = {};
+        
getReferencingComponentRevisions(controllerServiceEntity.component.referencingComponents,
 referencingRevisions, true);
+
         // build the reference entity
         var referenceEntity = {
             'id': controllerServiceEntity.id,
             'state': enabled ? 'ENABLED' : 'DISABLED',
-            'referencingComponentRevisions': {}
+            'referencingComponentRevisions': referencingRevisions
         };
 
-        // include the revision of each referencing component
-        $.each(controllerServiceEntity.component.referencingComponents, 
function (_, referencingComponent) {
-            
referenceEntity.referencingComponentRevisions[referencingComponent.id] = 
nf.Client.getRevision(referencingComponent);
-        });
-
         // issue the request to update the referencing components
         var updated = $.ajax({
             type: 'PUT',
@@ -1029,34 +1054,20 @@ nf.ControllerService = (function () {
         var referencingComponentsContainer = 
$('#disable-controller-service-referencing-components');
         createReferencingComponents(serviceTable, 
referencingComponentsContainer, controllerService.referencingComponents);
 
-        var hasUnauthorized = false;
-        $.each(controllerService.referencingComponents, function (_, 
referencingComponent) {
-            if (referencingComponent.permissions.canRead === false || 
referencingComponent.permissions.canWrite === false) {
-                hasUnauthorized = true;
-                return false;
-            }
-        });
-
         // build the button model
-        var buttons = [];
-
-        if (hasUnauthorized === false) {
-            buttons.push({
-                buttonText: 'Disable',
-                color: {
-                    base: '#728E9B',
-                    hover: '#004849',
-                    text: '#ffffff'
-                },
-                handler: {
-                    click: function () {
-                        disableHandler(serviceTable);
-                    }
+        var buttons = [{
+            buttonText: 'Disable',
+            color: {
+                base: '#728E9B',
+                hover: '#004849',
+                text: '#ffffff'
+            },
+            handler: {
+                click: function () {
+                    disableHandler(serviceTable);
                 }
-            });
-        }
-
-        buttons.push({
+            }
+        }, {
             buttonText: 'Cancel',
             color: {
                 base: '#E3E8EB',
@@ -1066,7 +1077,7 @@ nf.ControllerService = (function () {
             handler: {
                 click: closeModal
             }
-        });
+        }];
 
         // show the dialog
         $('#disable-controller-service-dialog').modal('setButtonModel', 
buttons).modal('show');
@@ -1197,6 +1208,17 @@ nf.ControllerService = (function () {
             }]);
         };
 
+        // ensure we have access to all referencing components before 
attempting the sequence
+        if 
(hasUnauthorizedReferencingComponent(controllerService.referencingComponents)) {
+            setCloseButton();
+
+            nf.Dialog.showOkDialog({
+                headerText: 'Controller Service',
+                dialogContent: 'Unable to disable due to unauthorized 
referencing components.'
+            });
+            return;
+        }
+
         $('#disable-progress-label').text('Steps to disable ' + 
controllerService.name);
         var disableReferencingSchedulable = 
$('#disable-referencing-schedulable').addClass('ajax-loading');
 
@@ -1248,6 +1270,31 @@ nf.ControllerService = (function () {
     };
 
     /**
+     * Determines if any of the specified referencing components are not 
authorized.
+     *
+     * @param referencingComponents referencing components
+     * @returns {boolean}
+     */
+    var hasUnauthorizedReferencingComponent = function (referencingComponents) 
{
+        var hasUnauthorized = false;
+
+        $.each(referencingComponents, function (_, referencingComponentEntity) 
{
+            if (referencingComponentEntity.permissions.canRead === false || 
referencingComponentEntity.permissions.canWrite === false) {
+                hasUnauthorized = true;
+                return false;
+            }
+
+            var referencingComponent = referencingComponentEntity.component;
+            if 
(hasUnauthorizedReferencingComponent(referencingComponent.referencingComponents))
 {
+                hasUnauthorized = true;
+                return false;
+            }
+        });
+
+        return hasUnauthorized;
+    };
+
+    /**
      * Handles the enable action of the enable controller service dialog.
      *
      * @param {jQuery} serviceTable
@@ -1263,26 +1310,9 @@ nf.ControllerService = (function () {
         var controllerServiceEntity = 
controllerServiceData.getItemById(controllerServiceId);
         var controllerService = controllerServiceEntity.component;
 
-        var hasUnauthorized = false;
-        $.each(controllerService.referencingComponents, function (_, 
referencingComponent) {
-            if (referencingComponent.permissions.canRead === false || 
referencingComponent.permissions.canWrite === false) {
-                hasUnauthorized = true;
-                return false;
-            }
-        });
-
         // determine if we want to also activate referencing components
         var scope = 
$('#enable-controller-service-scope').combo('getSelectedOption').value;
 
-        // ensure appropriate access
-        if (scope === config.serviceAndReferencingComponents && 
hasUnauthorized) {
-            nf.Dialog.showOkDialog({
-                headerText: 'Controller Service',
-                dialogContent: 'Unable to enable due to unauthorized 
referencing components.'
-            });
-            return;
-        }
-
         // update visibility
         if (scope === config.serviceOnly) {
             $('#enable-controller-service-progress 
li.referencing-component').hide();
@@ -1331,6 +1361,17 @@ nf.ControllerService = (function () {
             }]);
         };
 
+        // ensure appropriate access
+        if (scope === config.serviceAndReferencingComponents && 
hasUnauthorizedReferencingComponent(controllerService.referencingComponents)) {
+            setCloseButton();
+
+            nf.Dialog.showOkDialog({
+                headerText: 'Controller Service',
+                dialogContent: 'Unable to enable due to unauthorized 
referencing components.'
+            });
+            return;
+        }
+
         $('#enable-progress-label').text('Steps to enable ' + 
controllerService.name);
         var enableControllerService = 
$('#enable-controller-service').addClass('ajax-loading');
 
@@ -1504,7 +1545,7 @@ nf.ControllerService = (function () {
     };
 
     /**
-     * Identifies descritpors that reference controller services.
+     * Identifies descriptors that reference controller services.
      *
      * @param {object} component
      */

Reply via email to