NIFI-2301: - Ensure all component specific policies are removed when the component is removed. - Allowing snippets to be created if the user has read or write access as we don't know what the intended snippet usage. When used the snippet is still authorized accordingly. - Ensuring actions involving Process Groups correctly authorize encapsulated components. - Not requiring read permissions when showing the delete button for Controller Services and Reporting Tasks.

This closes #757

Signed-off-by: jpercivall <joepercivall@yahoo.com>
This commit is contained in:
Matt Gilman 2016-08-01 10:52:11 -04:00 committed by jpercivall
parent 9338f102cb
commit 1511887a68
15 changed files with 328 additions and 113 deletions

View File

@ -671,6 +671,13 @@ public interface ProcessGroup extends Authorizable, Positionable {
*/
void removeFunnel(Funnel funnel);
/**
* @return a List of all Funnel that are children or descendants of this
* ProcessGroup. This performs a recursive search of all descendant
* ProcessGroups
*/
List<Funnel> findAllFunnels();
/**
* Adds the given Controller Service to this group
*

View File

@ -1578,6 +1578,19 @@ public final class StandardProcessGroup implements ProcessGroup {
return allOutputPorts;
}
@Override
public List<Funnel> findAllFunnels() {
return findAllFunnels(this);
}
private List<Funnel> findAllFunnels(final ProcessGroup start) {
final List<Funnel> allFunnels = new ArrayList<>(start.getFunnels());
for (final ProcessGroup group : start.getProcessGroups()) {
allFunnels.addAll(findAllFunnels(group));
}
return allFunnels;
}
@Override
public Port getInputPortByName(final String name) {
return getPortByName(name, this, new InputPortRetriever());

View File

@ -461,6 +461,11 @@ public class MockProcessGroup implements ProcessGroup {
return null;
}
@Override
public List<Funnel> findAllFunnels() {
return null;
}
@Override
public void removeFunnel(final Funnel funnel) {

View File

@ -80,7 +80,7 @@ public interface AuthorizableLookup {
* @param id process group id
* @return authorizable
*/
Authorizable getProcessGroup(String id);
ProcessGroupAuthorizable getProcessGroup(String id);
/**
* Get the authorizable RemoteProcessGroup.

View File

@ -0,0 +1,40 @@
/*
* 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 java.util.Set;
/**
* Authorizable for a ProcessGroup and its encapsulated components.
*/
public interface ProcessGroupAuthorizable {
/**
* Returns the authorizable for this ProcessGroup. Non null
*
* @return authorizable
*/
Authorizable getAuthorizable();
/**
* The authorizables for all encapsulated components. Non null
*
* @return all encapsulated authorizables
*/
Set<Authorizable> getEncapsulatedAuthorizables();
}

View File

@ -19,8 +19,8 @@ package org.apache.nifi.authorization;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.resource.AccessPolicyAuthorizable;
import org.apache.nifi.authorization.resource.Authorizable;
import org.apache.nifi.authorization.resource.DataTransferAuthorizable;
import org.apache.nifi.authorization.resource.DataAuthorizable;
import org.apache.nifi.authorization.resource.DataTransferAuthorizable;
import org.apache.nifi.authorization.resource.ResourceFactory;
import org.apache.nifi.authorization.resource.ResourceType;
import org.apache.nifi.authorization.resource.TenantAuthorizable;
@ -47,6 +47,10 @@ import org.apache.nifi.web.dao.ReportingTaskDAO;
import org.apache.nifi.web.dao.SnippetDAO;
import org.apache.nifi.web.dao.TemplateDAO;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
class StandardAuthorizableLookup implements AuthorizableLookup {
@ -152,8 +156,32 @@ class StandardAuthorizableLookup implements AuthorizableLookup {
}
@Override
public Authorizable getProcessGroup(final String id) {
return processGroupDAO.getProcessGroup(id);
public ProcessGroupAuthorizable getProcessGroup(final String id) {
final ProcessGroup processGroup = processGroupDAO.getProcessGroup(id);
final Set<Authorizable> encapsulatedAuthorizables = new HashSet<>();
processGroup.findAllProcessors().forEach(processor -> encapsulatedAuthorizables.add(processor));
processGroup.findAllConnections().forEach(connection -> encapsulatedAuthorizables.add(connection));
processGroup.findAllInputPorts().forEach(inputPort -> encapsulatedAuthorizables.add(inputPort));
processGroup.findAllOutputPorts().forEach(outputPort -> encapsulatedAuthorizables.add(outputPort));
processGroup.findAllFunnels().forEach(funnel -> encapsulatedAuthorizables.add(funnel));
processGroup.findAllLabels().forEach(label -> encapsulatedAuthorizables.add(label));
processGroup.findAllProcessGroups().forEach(childGroup -> encapsulatedAuthorizables.add(childGroup));
processGroup.findAllRemoteProcessGroups().forEach(remoteProcessGroup -> encapsulatedAuthorizables.add(remoteProcessGroup));
processGroup.findAllTemplates().forEach(template -> encapsulatedAuthorizables.add(template));
processGroup.findAllControllerServices().forEach(controllerService -> encapsulatedAuthorizables.add(controllerService));
return new ProcessGroupAuthorizable() {
@Override
public Authorizable getAuthorizable() {
return processGroup;
}
@Override
public Set<Authorizable> getEncapsulatedAuthorizables() {
return Collections.unmodifiableSet(encapsulatedAuthorizables);
}
};
}
@Override
@ -334,7 +362,7 @@ class StandardAuthorizableLookup implements AuthorizableLookup {
authorizable = getProcessor(componentId);
break;
case ProcessGroup:
authorizable = getProcessGroup(componentId);
authorizable = getProcessGroup(componentId).getAuthorizable();
break;
case RemoteProcessGroup:
authorizable = getRemoteProcessGroup(componentId);

View File

@ -850,9 +850,9 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final Connection connection = connectionDAO.getConnection(connectionId);
final ConnectionDTO snapshot = deleteComponent(
revision,
connection.getResource().getIdentifier(),
connection.getResource(),
() -> connectionDAO.deleteConnection(connectionId),
false,
false, // no policies to remove
dtoFactory.createConnectionDto(connection));
return entityFactory.createConnectionEntity(snapshot, null, null, null);
@ -884,7 +884,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final ProcessorNode processor = processorDAO.getProcessor(processorId);
final ProcessorDTO snapshot = deleteComponent(
revision,
processor.getResource().getIdentifier(),
processor.getResource(),
() -> processorDAO.deleteProcessor(processorId),
true,
dtoFactory.createProcessorDto(processor));
@ -897,7 +897,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final Label label = labelDAO.getLabel(labelId);
final LabelDTO snapshot = deleteComponent(
revision,
label.getResource().getIdentifier(),
label.getResource(),
() -> labelDAO.deleteLabel(labelId),
true,
dtoFactory.createLabelDto(label));
@ -916,7 +916,17 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final String resourceIdentifier = ResourceFactory.getTenantResource().getIdentifier() + "/" + userId;
final UserDTO snapshot = deleteComponent(
revision,
resourceIdentifier,
new Resource() {
@Override
public String getIdentifier() {
return resourceIdentifier;
}
@Override
public String getName() {
return resourceIdentifier;
}
},
() -> userDAO.deleteUser(userId),
false, // no user specific policies to remove
dtoFactory.createUserDto(user, userGroups, policyEntities));
@ -934,7 +944,17 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final String resourceIdentifier = ResourceFactory.getTenantResource().getIdentifier() + "/" + userGroupId;
final UserGroupDTO snapshot = deleteComponent(
revision,
resourceIdentifier,
new Resource() {
@Override
public String getIdentifier() {
return resourceIdentifier;
}
@Override
public String getName() {
return resourceIdentifier;
}
},
() -> userGroupDAO.deleteUserGroup(userGroupId),
false, // no user group specific policies to remove
dtoFactory.createUserGroupDto(userGroup, users));
@ -949,7 +969,17 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final Set<TenantEntity> users = accessPolicy != null ? accessPolicy.getUsers().stream().map(mapUserIdToTenantEntity()).collect(Collectors.toSet()) : null;
final AccessPolicyDTO snapshot = deleteComponent(
revision,
accessPolicy.getResource(),
new Resource() {
@Override
public String getIdentifier() {
return accessPolicy.getResource();
}
@Override
public String getName() {
return accessPolicy.getResource();
}
},
() -> accessPolicyDAO.deleteAccessPolicy(accessPolicyId),
false, // no need to clean up any policies as it's already been removed above
dtoFactory.createAccessPolicyDto(accessPolicy, userGroups,
@ -963,7 +993,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final Funnel funnel = funnelDAO.getFunnel(funnelId);
final FunnelDTO snapshot = deleteComponent(
revision,
funnel.getResource().getIdentifier(),
funnel.getResource(),
() -> funnelDAO.deleteFunnel(funnelId),
true,
dtoFactory.createFunnelDto(funnel));
@ -975,50 +1005,30 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
* Deletes a component using the Optimistic Locking Manager
*
* @param revision the current revision
* @param resourceIdentifier the identifier of the resource being removed
* @param resource the resource being removed
* @param deleteAction the action that deletes the component via the appropriate DAO object
* @param cleanUpPolicies whether or not the policies for this resource should be removed as well - not necessary when there are
* no component specific policies or if the policies of the component are inherited
* @return a dto that represents the new configuration
*/
private <D, C> D deleteComponent(final Revision revision, final String resourceIdentifier, final Runnable deleteAction, final boolean cleanUpPolicies, final D dto) {
private <D, C> D deleteComponent(final Revision revision, final Resource resource, final Runnable deleteAction, final boolean cleanUpPolicies, final D dto) {
final RevisionClaim claim = new StandardRevisionClaim(revision);
final NiFiUser user = NiFiUserUtils.getNiFiUser();
return revisionManager.deleteRevision(claim, user, new DeleteRevisionTask<D>() {
@Override
public D performTask() {
logger.debug("Attempting to delete component {} with claim {}", resourceIdentifier, claim);
logger.debug("Attempting to delete component {} with claim {}", resource.getIdentifier(), claim);
// run the delete action
deleteAction.run();
// save the flow
controllerFacade.save();
logger.debug("Deletion of component {} was successful", resourceIdentifier);
logger.debug("Deletion of component {} was successful", resource.getIdentifier());
// clean up the policy if necessary and configured with a policy based authorizer
if (cleanUpPolicies && accessPolicyDAO.supportsConfigurableAuthorizer()) {
try {
// since the component is being deleted, also delete any relevant read access policies
final AccessPolicy readPolicy = accessPolicyDAO.getAccessPolicy(RequestAction.READ, resourceIdentifier);
if (readPolicy != null) {
accessPolicyDAO.deleteAccessPolicy(readPolicy.getIdentifier());
}
} catch (final Exception e) {
logger.warn(String.format("Unable to remove access policy for %s %s after component removal.", RequestAction.READ, resourceIdentifier), e);
}
try {
// since the component is being deleted, also delete any relevant write access policies
final AccessPolicy writePolicy = accessPolicyDAO.getAccessPolicy(RequestAction.WRITE, resourceIdentifier);
if (writePolicy != null) {
accessPolicyDAO.deleteAccessPolicy(writePolicy.getIdentifier());
}
} catch (final ResourceNotFoundException e) {
// no policy exists for this component... no worries
} catch (final Exception e) {
logger.warn(String.format("Unable to remove access policy for %s %s after component removal.", RequestAction.WRITE, resourceIdentifier), e);
}
if (cleanUpPolicies) {
cleanUpPolicies(resource);
}
return dto;
@ -1026,6 +1036,36 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
});
}
/**
* Clean up the policies for the specified component resource.
*
* @param componentResource the resource for the component
*/
private void cleanUpPolicies(final Resource componentResource) {
// ensure the authorizer supports configuration
if (accessPolicyDAO.supportsConfigurableAuthorizer()) {
final List<Resource> resources = new ArrayList<>();
resources.add(componentResource);
resources.add(ResourceFactory.getDataResource(componentResource));
resources.add(ResourceFactory.getDataTransferResource(componentResource));
resources.add(ResourceFactory.getPolicyResource(componentResource));
for (final Resource resource : resources) {
for (final RequestAction action : RequestAction.values()) {
try {
// since the component is being deleted, also delete any relevant access policies
final AccessPolicy readPolicy = accessPolicyDAO.getAccessPolicy(action, resource.getIdentifier());
if (readPolicy != null) {
accessPolicyDAO.deleteAccessPolicy(readPolicy.getIdentifier());
}
} catch (final Exception e) {
logger.warn(String.format("Unable to remove access policy for %s %s after component removal.", action, resource.getIdentifier()), e);
}
}
}
}
}
@Override
public void verifyDeleteSnippet(final String snippetId, final Set<String> affectedComponentIds) {
snippetDAO.verifyDeleteSnippetComponents(snippetId);
@ -1035,6 +1075,32 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
public SnippetEntity deleteSnippet(final Set<Revision> revisions, final String snippetId) {
final Snippet snippet = snippetDAO.getSnippet(snippetId);
// grab the resources in the snippet so we can delete the policies afterwards
final Set<Resource> snippetResources = new HashSet<>();
snippet.getProcessors().keySet().forEach(id -> snippetResources.add(processorDAO.getProcessor(id).getResource()));
snippet.getInputPorts().keySet().forEach(id -> snippetResources.add(inputPortDAO.getPort(id).getResource()));
snippet.getOutputPorts().keySet().forEach(id -> snippetResources.add(outputPortDAO.getPort(id).getResource()));
snippet.getFunnels().keySet().forEach(id -> snippetResources.add(funnelDAO.getFunnel(id).getResource()));
snippet.getLabels().keySet().forEach(id -> snippetResources.add(labelDAO.getLabel(id).getResource()));
snippet.getRemoteProcessGroups().keySet().forEach(id -> snippetResources.add(remoteProcessGroupDAO.getRemoteProcessGroup(id).getResource()));
snippet.getProcessGroups().keySet().forEach(id -> {
final ProcessGroup processGroup = processGroupDAO.getProcessGroup(id);
// add the process group
snippetResources.add(processGroup.getResource());
// add each encapsulated component
processGroup.findAllProcessors().forEach(processor -> snippetResources.add(processor.getResource()));
processGroup.findAllInputPorts().forEach(inputPort -> snippetResources.add(inputPort.getResource()));
processGroup.findAllOutputPorts().forEach(outputPort -> snippetResources.add(outputPort.getResource()));
processGroup.findAllFunnels().forEach(funnel -> snippetResources.add(funnel.getResource()));
processGroup.findAllLabels().forEach(label -> snippetResources.add(label.getResource()));
processGroup.findAllProcessGroups().forEach(childGroup -> snippetResources.add(childGroup.getResource()));
processGroup.findAllRemoteProcessGroups().forEach(remoteProcessGroup -> snippetResources.add(remoteProcessGroup.getResource()));
processGroup.findAllTemplates().forEach(template -> snippetResources.add(template.getResource()));
processGroup.findAllControllerServices().forEach(controllerService -> snippetResources.add(controllerService.getResource()));
});
final NiFiUser user = NiFiUserUtils.getNiFiUser();
final RevisionClaim claim = new StandardRevisionClaim(revisions);
final SnippetDTO dto = revisionManager.deleteRevision(claim, user, new DeleteRevisionTask<SnippetDTO>() {
@ -1054,6 +1120,9 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
}
});
// clean up component policies
snippetResources.forEach(resource -> cleanUpPolicies(resource));
return entityFactory.createSnippetEntity(dto);
}
@ -1062,7 +1131,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final Port port = inputPortDAO.getPort(inputPortId);
final PortDTO snapshot = deleteComponent(
revision,
port.getResource().getIdentifier(),
port.getResource(),
() -> inputPortDAO.deletePort(inputPortId),
true,
dtoFactory.createPortDto(port));
@ -1075,7 +1144,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final Port port = outputPortDAO.getPort(outputPortId);
final PortDTO snapshot = deleteComponent(
revision,
port.getResource().getIdentifier(),
port.getResource(),
() -> outputPortDAO.deletePort(outputPortId),
true,
dtoFactory.createPortDto(port));
@ -1086,13 +1155,29 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
@Override
public ProcessGroupEntity deleteProcessGroup(final Revision revision, final String groupId) {
final ProcessGroup processGroup = processGroupDAO.getProcessGroup(groupId);
// grab the resources in the snippet so we can delete the policies afterwards
final Set<Resource> groupResources = new HashSet<>();
processGroup.findAllProcessors().forEach(processor -> groupResources.add(processor.getResource()));
processGroup.findAllInputPorts().forEach(inputPort -> groupResources.add(inputPort.getResource()));
processGroup.findAllOutputPorts().forEach(outputPort -> groupResources.add(outputPort.getResource()));
processGroup.findAllFunnels().forEach(funnel -> groupResources.add(funnel.getResource()));
processGroup.findAllLabels().forEach(label -> groupResources.add(label.getResource()));
processGroup.findAllProcessGroups().forEach(childGroup -> groupResources.add(childGroup.getResource()));
processGroup.findAllRemoteProcessGroups().forEach(remoteProcessGroup -> groupResources.add(remoteProcessGroup.getResource()));
processGroup.findAllTemplates().forEach(template -> groupResources.add(template.getResource()));
processGroup.findAllControllerServices().forEach(controllerService -> groupResources.add(controllerService.getResource()));
final ProcessGroupDTO snapshot = deleteComponent(
revision,
processGroup.getResource().getIdentifier(),
processGroup.getResource(),
() -> processGroupDAO.deleteProcessGroup(groupId),
true,
dtoFactory.createProcessGroupDto(processGroup));
// delete all applicable component policies
groupResources.forEach(groupResource -> cleanUpPolicies(groupResource));
return entityFactory.createProcessGroupEntity(snapshot, null, null, null, null);
}
@ -1101,7 +1186,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final RemoteProcessGroup remoteProcessGroup = remoteProcessGroupDAO.getRemoteProcessGroup(remoteProcessGroupId);
final RemoteProcessGroupDTO snapshot = deleteComponent(
revision,
remoteProcessGroup.getResource().getIdentifier(),
remoteProcessGroup.getResource(),
() -> remoteProcessGroupDAO.deleteRemoteProcessGroup(remoteProcessGroupId),
true,
dtoFactory.createRemoteProcessGroupDto(remoteProcessGroup));
@ -1740,7 +1825,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final ControllerServiceNode controllerService = controllerServiceDAO.getControllerService(controllerServiceId);
final ControllerServiceDTO snapshot = deleteComponent(
revision,
controllerService.getResource().getIdentifier(),
controllerService.getResource(),
() -> controllerServiceDAO.deleteControllerService(controllerServiceId),
true,
dtoFactory.createControllerServiceDto(controllerService));
@ -1794,7 +1879,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final ReportingTaskNode reportingTask = reportingTaskDAO.getReportingTask(reportingTaskId);
final ReportingTaskDTO snapshot = deleteComponent(
revision,
reportingTask.getResource().getIdentifier(),
reportingTask.getResource(),
() -> reportingTaskDAO.deleteReportingTask(reportingTaskId),
true,
dtoFactory.createReportingTaskDto(reportingTask));
@ -2693,7 +2778,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
authorizable = authorizableLookup.getOutputPort(sourceId);
break;
case ProcessGroup:
authorizable = authorizableLookup.getProcessGroup(sourceId);
authorizable = authorizableLookup.getProcessGroup(sourceId).getAuthorizable();
break;
case RemoteProcessGroup:
authorizable = authorizableLookup.getRemoteProcessGroup(sourceId);

View File

@ -21,6 +21,8 @@ import com.sun.jersey.api.representation.Form;
import com.sun.jersey.core.util.MultivaluedMapImpl;
import com.sun.jersey.server.impl.model.method.dispatch.FormDispatchProvider;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.AuthorizableLookup;
import org.apache.nifi.authorization.AuthorizeAccess;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.RequestAction;
import org.apache.nifi.authorization.resource.Authorizable;
@ -40,10 +42,8 @@ import org.apache.nifi.remote.exception.HandshakeException;
import org.apache.nifi.remote.exception.NotAuthorizedException;
import org.apache.nifi.remote.protocol.ResponseCode;
import org.apache.nifi.remote.protocol.http.HttpHeaders;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.util.ComponentIdGenerator;
import org.apache.nifi.authorization.AuthorizableLookup;
import org.apache.nifi.authorization.AuthorizeAccess;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.NiFiServiceFacade;
import org.apache.nifi.web.Revision;
import org.apache.nifi.web.api.dto.RevisionDTO;
@ -418,7 +418,13 @@ public abstract class ApplicationResource {
protected void authorizeSnippet(final Snippet snippet, final Authorizer authorizer, final AuthorizableLookup lookup, final RequestAction action) {
final Consumer<Authorizable> authorize = authorizable -> authorizable.authorize(authorizer, action, NiFiUserUtils.getNiFiUser());
snippet.getProcessGroups().keySet().stream().map(id -> lookup.getProcessGroup(id)).forEach(authorize);
snippet.getProcessGroups().keySet().stream().map(id -> lookup.getProcessGroup(id)).forEach(processGroupAuthorizable -> {
// authorize the process group
authorize.accept(processGroupAuthorizable.getAuthorizable());
// authorize the contents of the group
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.getInputPorts().keySet().stream().map(id -> lookup.getInputPort(id)).forEach(authorize);
@ -437,7 +443,13 @@ public abstract class ApplicationResource {
protected void authorizeSnippet(final SnippetDTO snippet, final Authorizer authorizer, final AuthorizableLookup lookup, final RequestAction action) {
final Consumer<Authorizable> authorize = authorizable -> authorizable.authorize(authorizer, action, NiFiUserUtils.getNiFiUser());
snippet.getProcessGroups().keySet().stream().map(id -> lookup.getProcessGroup(id)).forEach(authorize);
snippet.getProcessGroups().keySet().stream().map(id -> lookup.getProcessGroup(id)).forEach(processGroupAuthorizable -> {
// authorize the process group
authorize.accept(processGroupAuthorizable.getAuthorizable());
// authorize the contents of the group
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.getInputPorts().keySet().stream().map(id -> lookup.getInputPort(id)).forEach(authorize);

View File

@ -96,9 +96,7 @@ public class ControllerServiceResource extends ApplicationResource {
*/
public Set<ControllerServiceEntity> populateRemainingControllerServiceEntitiesContent(final Set<ControllerServiceEntity> controllerServiceEntities) {
for (ControllerServiceEntity controllerServiceEntity : controllerServiceEntities) {
if (controllerServiceEntity.getComponent() != null) {
populateRemainingControllerServiceEntityContent(controllerServiceEntity);
}
populateRemainingControllerServiceEntityContent(controllerServiceEntity);
}
return controllerServiceEntities;
}

View File

@ -25,13 +25,15 @@ 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.AuthorizableLookup;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.ProcessGroupAuthorizable;
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.cluster.protocol.NodeIdentifier;
import org.apache.nifi.controller.Snippet;
import org.apache.nifi.authorization.AuthorizableLookup;
import org.apache.nifi.web.NiFiServiceFacade;
import org.apache.nifi.web.Revision;
import org.apache.nifi.web.api.dto.ConnectionDTO;
@ -208,7 +210,7 @@ public class ProcessGroupResource extends ApplicationResource {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
});
@ -285,17 +287,17 @@ public class ProcessGroupResource extends ApplicationResource {
// handle expects request (usually from the cluster manager)
final Revision revision = getRevision(processGroupEntity, id);
return withWriteLock(
serviceFacade,
revision,
lookup -> {
Authorizable authorizable = lookup.getProcessGroup(id);
authorizable.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
},
null,
() -> {
// update the process group
final ProcessGroupEntity entity = serviceFacade.updateProcessGroup(revision, requestProcessGroupDTO);
populateRemainingProcessGroupEntityContent(entity);
serviceFacade,
revision,
lookup -> {
Authorizable authorizable = lookup.getProcessGroup(id).getAuthorizable();
authorizable.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
},
null,
() -> {
// update the process group
final ProcessGroupEntity entity = serviceFacade.updateProcessGroup(revision, requestProcessGroupDTO);
populateRemainingProcessGroupEntityContent(entity);
return clusterContext(generateOkResponse(entity)).build();
}
@ -357,16 +359,25 @@ public class ProcessGroupResource extends ApplicationResource {
// handle expects request (usually from the cluster manager)
final Revision revision = new Revision(version == null ? null : version.getLong(), clientId.getClientId(), id);
return withWriteLock(
serviceFacade,
revision,
lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(id);
processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
},
() -> serviceFacade.verifyDeleteProcessGroup(id),
() -> {
// delete the process group
final ProcessGroupEntity entity = serviceFacade.deleteProcessGroup(revision, id);
serviceFacade,
revision,
lookup -> {
final NiFiUser user = NiFiUserUtils.getNiFiUser();
final ProcessGroupAuthorizable processGroupAuthorizable = lookup.getProcessGroup(id);
// ensure write to the process group
final Authorizable processGroup = processGroupAuthorizable.getAuthorizable();
processGroup.authorize(authorizer, RequestAction.WRITE, user);
// ensure write to all encapsulated components
processGroupAuthorizable.getEncapsulatedAuthorizables().forEach(encaupsulatedAuthorizable -> {
encaupsulatedAuthorizable.authorize(authorizer, RequestAction.WRITE, user);
});
},
() -> serviceFacade.verifyDeleteProcessGroup(id),
() -> {
// delete the process group
final ProcessGroupEntity entity = serviceFacade.deleteProcessGroup(revision, id);
// create the response
return clusterContext(generateOkResponse(entity)).build();
@ -441,7 +452,7 @@ public class ProcessGroupResource extends ApplicationResource {
if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
});
}
@ -500,7 +511,7 @@ public class ProcessGroupResource extends ApplicationResource {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
});
@ -597,7 +608,7 @@ public class ProcessGroupResource extends ApplicationResource {
if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
});
}
@ -657,7 +668,7 @@ public class ProcessGroupResource extends ApplicationResource {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
});
@ -743,7 +754,7 @@ public class ProcessGroupResource extends ApplicationResource {
if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
});
}
@ -801,7 +812,7 @@ public class ProcessGroupResource extends ApplicationResource {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
});
@ -886,7 +897,7 @@ public class ProcessGroupResource extends ApplicationResource {
if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
});
}
@ -944,7 +955,7 @@ public class ProcessGroupResource extends ApplicationResource {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
});
@ -1030,7 +1041,7 @@ public class ProcessGroupResource extends ApplicationResource {
if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
});
}
@ -1088,7 +1099,7 @@ public class ProcessGroupResource extends ApplicationResource {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
});
@ -1174,7 +1185,7 @@ public class ProcessGroupResource extends ApplicationResource {
if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
});
}
@ -1232,7 +1243,7 @@ public class ProcessGroupResource extends ApplicationResource {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
});
@ -1324,7 +1335,7 @@ public class ProcessGroupResource extends ApplicationResource {
if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
});
}
@ -1407,7 +1418,7 @@ public class ProcessGroupResource extends ApplicationResource {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
});
@ -1514,7 +1525,7 @@ public class ProcessGroupResource extends ApplicationResource {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
// ensure write access to the group
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
// ensure write access to the source
@ -1582,7 +1593,7 @@ public class ProcessGroupResource extends ApplicationResource {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
});
@ -1751,7 +1762,7 @@ public class ProcessGroupResource extends ApplicationResource {
if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
final Authorizable template = lookup.getTemplate(instantiateTemplateRequestEntity.getTemplateId());
@ -1786,7 +1797,7 @@ public class ProcessGroupResource extends ApplicationResource {
private void authorizeSnippetUsage(final AuthorizableLookup lookup, final String groupId, final String snippetId) {
// ensure write access to the target process group
lookup.getProcessGroup(groupId).authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
lookup.getProcessGroup(groupId).getAuthorizable().authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
// ensure read permission to every component in the snippet
final Snippet snippet = lookup.getSnippet(snippetId);
@ -2005,7 +2016,7 @@ public class ProcessGroupResource extends ApplicationResource {
if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
});
}
@ -2110,7 +2121,7 @@ public class ProcessGroupResource extends ApplicationResource {
if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processGroup = lookup.getProcessGroup(groupId);
final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
});
}

View File

@ -84,9 +84,7 @@ public class ReportingTaskResource extends ApplicationResource {
*/
public Set<ReportingTaskEntity> populateRemainingReportingTaskEntitiesContent(final Set<ReportingTaskEntity> reportingTaskEntities) {
for (ReportingTaskEntity reportingTaskEntity : reportingTaskEntities) {
if (reportingTaskEntity.getComponent() != null) {
populateRemainingReportingTaskEntityContent(reportingTaskEntity);
}
populateRemainingReportingTaskEntityContent(reportingTaskEntity);
}
return reportingTaskEntities;
}

View File

@ -22,6 +22,7 @@ import com.wordnik.swagger.annotations.ApiParam;
import com.wordnik.swagger.annotations.ApiResponse;
import com.wordnik.swagger.annotations.ApiResponses;
import com.wordnik.swagger.annotations.Authorization;
import org.apache.nifi.authorization.AccessDeniedException;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.RequestAction;
import org.apache.nifi.authorization.user.NiFiUserUtils;
@ -141,7 +142,18 @@ public class SnippetResource extends ApplicationResource {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final SnippetDTO snippet = snippetEntity.getSnippet();
authorizeSnippet(snippet, authorizer, lookup, RequestAction.READ);
// the snippet being created may be used later for batch component modifications,
// copy/paste, or template creation. during those subsequent actions, the snippet
// will again be authorized accordingly (read or write). at this point we do not
// know what the snippet will be used for so we need to attempt to authorize as
// read OR write
try {
authorizeSnippet(snippet, authorizer, lookup, RequestAction.READ);
} catch (final AccessDeniedException e) {
authorizeSnippet(snippet, authorizer, lookup, RequestAction.WRITE);
}
});
}
if (validationPhase) {
@ -218,13 +230,13 @@ public class SnippetResource extends ApplicationResource {
// get the revision from this snippet
final Set<Revision> revisions = serviceFacade.getRevisionsFromSnippet(snippetId);
return withWriteLock(
serviceFacade,
revisions,
lookup -> {
// ensure write access to the target process group
if (requestSnippetDTO.getParentGroupId() != null) {
lookup.getProcessGroup(requestSnippetDTO.getParentGroupId()).authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
}
serviceFacade,
revisions,
lookup -> {
// ensure write access to the target process group
if (requestSnippetDTO.getParentGroupId() != null) {
lookup.getProcessGroup(requestSnippetDTO.getParentGroupId()).getAuthorizable().authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
}
// ensure write permission to every component in the snippet
final Snippet snippet = lookup.getSnippet(snippetId);

View File

@ -2019,7 +2019,9 @@ nf.ControllerService = (function () {
controllerServiceData.deleteItem(controllerServiceEntity.id);
// reload the as necessary
reloadControllerServiceReferences(serviceTable, controllerServiceEntity.component);
if (controllerServiceEntity.permissions.canRead) {
reloadControllerServiceReferences(serviceTable, controllerServiceEntity.component);
}
}).fail(nf.Common.handleAjaxError);
}
};

View File

@ -596,8 +596,6 @@ nf.ControllerServices = (function () {
if (nf.Common.isEmpty(dataContext.component.validationErrors)) {
markup += '<div class="pointer enable-controller-service fa fa-flash" title="Enable" style="margin-top: 2px; margin-right: 3px;"></div>';
}
markup += '<div class="pointer delete-controller-service fa fa-trash" title="Remove" style="margin-top: 2px; margin-right: 3px;" ></div>';
}
if (dataContext.component.persistsState === true) {
@ -605,6 +603,10 @@ nf.ControllerServices = (function () {
}
}
if (dataContext.permissions.canWrite) {
markup += '<div class="pointer delete-controller-service fa fa-trash" title="Remove" style="margin-top: 2px; margin-right: 3px;" ></div>';
}
// allow policy configuration conditionally
if (nf.Canvas.isConfigurableAuthorizer() && nf.Common.canAccessTenants()) {
markup += '<div title="Access Policies" class="pointer edit-access-policies fa fa-key" style="margin-top: 2px;"></div>';

View File

@ -673,8 +673,6 @@ nf.Settings = (function () {
if (dataContext.component.state === 'STOPPED' && nf.Common.isEmpty(dataContext.component.validationErrors)) {
markup += '<div title="Start" class="pointer start-reporting-task fa fa-play" style="margin-top: 2px; margin-right: 3px;"></div>';
}
markup += '<div title="Remove" class="pointer delete-reporting-task fa fa-trash" style="margin-top: 2px; margin-right: 3px;" ></div>';
}
if (dataContext.component.persistsState === true) {
@ -682,6 +680,10 @@ nf.Settings = (function () {
}
}
if (dataContext.permissions.canWrite) {
markup += '<div title="Remove" class="pointer delete-reporting-task fa fa-trash" style="margin-top: 2px; margin-right: 3px;" ></div>';
}
// allow policy configuration conditionally
if (nf.Canvas.isConfigurableAuthorizer() && nf.Common.canAccessTenants()) {
markup += '<div title="Access Policies" class="pointer edit-access-policies fa fa-key" style="margin-top: 2px;"></div>';