diff --git a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java index 07a72056a0b9..beb18f56f6b0 100644 --- a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java +++ b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java @@ -50,6 +50,7 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.authorization.AuthorizableLookup; import org.apache.nifi.authorization.AuthorizeControllerServiceReference; import org.apache.nifi.authorization.AuthorizeParameterReference; import org.apache.nifi.authorization.Authorizer; @@ -719,49 +720,37 @@ public Response getProcessGroupOptions( final NiFiUser user = NiFiUserUtils.getNiFiUser(); final ComponentAuthorizable authorizableControllerService = lookup.getControllerService(controllerServiceId); - authorizableControllerService.getAuthorizable().authorize(authorizer, RequestAction.WRITE, user); + boolean authorized = authorizableControllerService.getAuthorizable().isAuthorized(authorizer, RequestAction.WRITE, user); final ControllerServiceDTO controllerServiceDTO = serviceFacade.getControllerService(controllerServiceId, true).getComponent(); final ProcessGroupAuthorizable authorizableProcessGroupCurrent = lookup.getProcessGroup(controllerServiceDTO.getParentGroupId()); - authorizableProcessGroupCurrent.getAuthorizable().authorize(authorizer, RequestAction.WRITE, user); - - if (authorizableProcessGroupCurrent.getProcessGroup().getParent() != null) { - final ProcessGroupAuthorizable authorizableProcessGroupParent = lookup.getProcessGroup(authorizableProcessGroupCurrent.getProcessGroup().getParent().getIdentifier()); - authorizableProcessGroupParent.getAuthorizable().authorize(authorizer, RequestAction.WRITE, user); + authorized = authorized && authorizableProcessGroupCurrent.getAuthorizable().isAuthorized(authorizer, RequestAction.WRITE, user); + + if (authorized) { + if (authorizableProcessGroupCurrent.getProcessGroup().getParent() != null) { + final ProcessGroupAuthorizable authorizableProcessGroupParent = lookup.getProcessGroup(authorizableProcessGroupCurrent.getProcessGroup().getParent().getIdentifier()); + if (authorizableProcessGroupParent.getAuthorizable().isAuthorized(authorizer, RequestAction.READ, user) + && authorizableProcessGroupParent.getAuthorizable().isAuthorized(authorizer, RequestAction.WRITE, user)) { + options.add(generateProcessGroupOption(controllerServiceDTO, authorizableProcessGroupParent, lookup, user)); + } + } - options.add(generateProcessGroupOption(controllerServiceDTO, authorizableProcessGroupParent)); + authorizableProcessGroupCurrent.getProcessGroup().getProcessGroups().forEach(processGroup -> { + final ProcessGroupAuthorizable authorizableProcessGroup = lookup.getProcessGroup(processGroup.getIdentifier()); + if (authorizableProcessGroup.getAuthorizable().isAuthorized(authorizer, RequestAction.READ, user) + && authorizableProcessGroup.getAuthorizable().isAuthorized(authorizer, RequestAction.WRITE, user)) { + options.add(generateProcessGroupOption(controllerServiceDTO, authorizableProcessGroup, lookup, user)); + } + }); } - - authorizableProcessGroupCurrent.getProcessGroup().getProcessGroups().forEach(processGroup -> { - final ProcessGroupAuthorizable authorizableProcessGroup = lookup.getProcessGroup(processGroup.getIdentifier()); - authorizableProcessGroup.getAuthorizable().authorize(authorizer, RequestAction.WRITE, user); - options.add(generateProcessGroupOption(controllerServiceDTO, authorizableProcessGroup)); - }); }); return generateOkResponse(options).build(); } - private ProcessGroupOptionEntity generateProcessGroupOption(ControllerServiceDTO controllerServiceDTO, ProcessGroupAuthorizable processGroup) { - List conflictingComponents = new ArrayList<>(); - controllerServiceDTO.getReferencingComponents().forEach(e -> { - if (processGroup.getProcessGroup().findProcessor(e.getId()) == null - && processGroup.getProcessGroup().findControllerService(e.getId(), true, false) == null) { - conflictingComponents.add("[" + e.getComponent().getName() + "]"); - } - }); - - controllerServiceDTO.getProperties().forEach((key, value) -> { - try { - ControllerServiceEntity refControllerService = serviceFacade.getControllerService(value, false); - if (refControllerService != null) { - if (processGroup.getProcessGroup().findControllerService(value, false, true) == null) { - conflictingComponents.add("[" + refControllerService.getComponent().getName() + "]"); - } - } - } catch (Exception ignored) { } - }); + private ProcessGroupOptionEntity generateProcessGroupOption(ControllerServiceDTO controllerServiceDTO, ProcessGroupAuthorizable processGroup, AuthorizableLookup lookup, NiFiUser user) { + List conflictingComponents = getConflictingComponents(controllerServiceDTO, processGroup, lookup, user); ProcessGroupOptionEntity option = new ProcessGroupOptionEntity(); option.setText(processGroup.getProcessGroup().getName()); @@ -769,7 +758,7 @@ private ProcessGroupOptionEntity generateProcessGroupOption(ControllerServiceDTO option.setDisabled(false); if (!conflictingComponents.isEmpty()) { - String errorMessage = "Could not move controller service because the following components would be out of scope: "; + String errorMessage = "Cannot move to this process group because the following components would be out of scope: "; errorMessage += String.join(" ", conflictingComponents); option.setDescription(errorMessage); option.setDisabled(true); @@ -778,6 +767,38 @@ private ProcessGroupOptionEntity generateProcessGroupOption(ControllerServiceDTO return option; } + private List getConflictingComponents(ControllerServiceDTO controllerServiceDTO, ProcessGroupAuthorizable processGroup, AuthorizableLookup lookup, NiFiUser user) { + List conflictingComponents = new ArrayList<>(); + controllerServiceDTO.getReferencingComponents().forEach(referencingComponent -> { + if (processGroup.getProcessGroup().findProcessor(referencingComponent.getId()) == null + && processGroup.getProcessGroup().findControllerService(referencingComponent.getId(), true, false) == null) { + final Authorizable componentAuthorizable = lookup.getControllerServiceReferencingComponent(controllerServiceDTO.getId(), referencingComponent.getId()); + if (componentAuthorizable.isAuthorized(authorizer, RequestAction.READ, user)) { + conflictingComponents.add("[" + referencingComponent.getComponent().getName() + "]"); + } else { + conflictingComponents.add("[Unauthorized Component]"); + } + } + }); + + controllerServiceDTO.getProperties().forEach((key, value) -> { + try { + ControllerServiceEntity refControllerService = serviceFacade.getControllerService(value, false); + if (refControllerService != null) { + if (processGroup.getProcessGroup().findControllerService(value, false, true) == null) { + ComponentAuthorizable componentAuthorizable = lookup.getControllerService(value); + if (componentAuthorizable.getAuthorizable().isAuthorized(authorizer, RequestAction.READ, user)) { + conflictingComponents.add("[" + refControllerService.getComponent().getName() + "]"); + } else { + conflictingComponents.add("[Unauthorized Component]"); + } + } + } + } catch (Exception ignored) { } + }); + return conflictingComponents; + } + /** * Moves the specified Controller Service to parent/child process groups. * @@ -831,47 +852,32 @@ public Response moveControllerServices( throw new IllegalArgumentException("ParentGroupId must be specified."); } - final ControllerServiceDTO requestControllerServiceDTO = serviceFacade.getControllerService(id, true).getComponent(); - final Revision requestRevision = getRevision(requestControllerServiceEntity, id); return withWriteLock( serviceFacade, serviceFacade.getControllerService(id, true), requestRevision, lookup -> { + NiFiUser user = NiFiUserUtils.getNiFiUser(); // authorize the service - final ComponentAuthorizable authorizable = lookup.getControllerService(requestControllerServiceDTO.getId()); - authorizable.getAuthorizable().authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + final ComponentAuthorizable authorizableControllerService = lookup.getControllerService(id); + + if (authorizableControllerService.getAuthorizable().isAuthorized(authorizer, RequestAction.WRITE, user)) { + throw new IllegalStateException("You do not have permission to perform this action."); + } + + authorizableControllerService.getAuthorizable().authorize(authorizer, RequestAction.WRITE, user); + final ControllerServiceDTO requestControllerServiceDTO = serviceFacade.getControllerService(id, true).getComponent(); // authorize the current and new process groups final ProcessGroupAuthorizable authorizableProcessGroupNew = lookup.getProcessGroup(requestControllerServiceEntity.getParentGroupId()); - authorizableProcessGroupNew.getAuthorizable().authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + authorizableProcessGroupNew.getAuthorizable().authorize(authorizer, RequestAction.WRITE, user); final ProcessGroupAuthorizable authorizableProcessGroupOld = lookup.getProcessGroup(requestControllerServiceDTO.getParentGroupId()); - authorizableProcessGroupOld.getAuthorizable().authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + authorizableProcessGroupOld.getAuthorizable().authorize(authorizer, RequestAction.WRITE, user); // Verify all referencing and referenced components are still within scope - List conflictingComponents = new ArrayList<>(); - requestControllerServiceDTO.getReferencingComponents().forEach(e -> { - if (authorizableProcessGroupNew.getProcessGroup().findProcessor(e.getId()) == null - && authorizableProcessGroupNew.getProcessGroup().findControllerService(e.getId(), true, false) == null) { - conflictingComponents.add("[" + e.getComponent().getName() + "]"); - } - - final Authorizable referencingComponent = lookup.getControllerServiceReferencingComponent(requestControllerServiceDTO.getId(), e.getId()); - OperationAuthorizable.authorizeOperation(referencingComponent, authorizer, NiFiUserUtils.getNiFiUser()); - }); - - requestControllerServiceDTO.getProperties().forEach((key, value) -> { - try { - ControllerServiceEntity refControllerService = serviceFacade.getControllerService(value, false); - if (refControllerService != null) { - if (authorizableProcessGroupNew.getProcessGroup().findControllerService(value, false, true) == null) { - conflictingComponents.add("[" + refControllerService.getComponent().getName() + "]"); - } - } - } catch (Exception ignored) { } - }); + List conflictingComponents = getConflictingComponents(requestControllerServiceDTO, authorizableProcessGroupNew, lookup, user); if (!conflictingComponents.isEmpty()) { String errorMessage = "Could not move controller service because the following components would be out of scope: "; diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.selectors.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.selectors.ts index 260b54a8b324..fda8e4da4f18 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.selectors.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.selectors.ts @@ -51,11 +51,6 @@ export const selectCurrentParameterContext = createSelector( export const selectCanvasPermissions = createSelector(selectFlowState, (state: FlowState) => state.flow.permissions); -export const selectProcessGroupFlow = createSelector( - selectFlowState, - (state: FlowState) => state.flow.processGroupFlow -); - export const selectBreadcrumbs = createSelector( selectFlowState, (state: FlowState) => state.flow.processGroupFlow.breadcrumb diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/controller-service/move-controller-service/move-controller-service.component.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/controller-service/move-controller-service/move-controller-service.component.ts index 42ba3911e991..8b073157db03 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/controller-service/move-controller-service/move-controller-service.component.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/controller-service/move-controller-service/move-controller-service.component.ts @@ -16,52 +16,45 @@ */ import { Component, Inject, Input } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; +import { + MAT_DIALOG_DATA, + MatDialogActions, + MatDialogClose, + MatDialogContent, + MatDialogTitle +} from '@angular/material/dialog'; import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; - -import { MatInputModule } from '@angular/material/input'; -import { MatCheckboxModule } from '@angular/material/checkbox'; -import { MatButtonModule } from '@angular/material/button'; -import { AsyncPipe, NgTemplateOutlet } from '@angular/common'; -import { MatTabsModule } from '@angular/material/tabs'; -import { MatOptionModule } from '@angular/material/core'; -import { MatSelectModule } from '@angular/material/select'; +import { MatFormField } from '@angular/material/input'; +import { MatButton } from '@angular/material/button'; +import { MatOption } from '@angular/material/core'; +import { MatLabel, MatSelect } from '@angular/material/select'; import { TextTip, NifiTooltipDirective, SelectOption } from '@nifi/shared'; import { Store } from '@ngrx/store'; import { CloseOnEscapeDialog } from '@nifi/shared'; import { NiFiState } from 'apps/nifi/src/app/state'; import { NgIf } from '@angular/common'; -import { - ControllerServiceEntity, - ControllerServiceReferencingComponent, - ControllerServiceReferencingComponentEntity -} from 'apps/nifi/src/app/state/shared'; -import { ControllerServiceApi } from 'apps/nifi/src/app/ui/common/controller-service/controller-service-api/controller-service-api.component'; +import { ControllerServiceEntity, ControllerServiceReferencingComponent } from 'apps/nifi/src/app/state/shared'; import { ControllerServiceReferences } from 'apps/nifi/src/app/ui/common/controller-service/controller-service-references/controller-service-references.component'; -import { NifiSpinnerDirective } from 'apps/nifi/src/app/ui/common/spinner/nifi-spinner.directive'; import { MoveControllerServiceDialogRequestSuccess } from '../../../state/controller-services'; import { moveControllerService } from '../../../state/controller-services/controller-services.actions'; -import { BreadcrumbEntity } from '../../../state/shared'; @Component({ selector: 'move-controller-service', standalone: true, templateUrl: './move-controller-service.component.html', imports: [ + MatDialogContent, ReactiveFormsModule, - MatDialogModule, - MatInputModule, - MatCheckboxModule, - MatButtonModule, - MatTabsModule, - MatOptionModule, - MatSelectModule, - ControllerServiceApi, - ControllerServiceReferences, - AsyncPipe, - NifiSpinnerDirective, + MatFormField, + MatSelect, + MatDialogTitle, + MatLabel, NifiTooltipDirective, - NgTemplateOutlet, + MatOption, + ControllerServiceReferences, + MatDialogActions, + MatButton, + MatDialogClose, NgIf ], styleUrls: ['./move-controller-service.component.scss']