mirror of https://github.com/apache/nifi.git
NIFI-2347: - Ensuring component specific policies are retained when using copy/paste. - This includes the policies for the component, data of the component, data transfers of the component, and policies of the component.
This closes #730 Signed-off-by: jpercivall <joepercivall@yahoo.com>
This commit is contained in:
parent
25cadf5db1
commit
09b124714e
|
@ -92,10 +92,19 @@ public class StandardSnippetDAO implements SnippetDAO {
|
|||
org.apache.nifi.util.SnippetUtils.moveSnippet(snippetContents, originX, originY);
|
||||
}
|
||||
|
||||
// instantiate the snippet
|
||||
try {
|
||||
// instantiate the snippet and return the contents
|
||||
flowController.instantiateSnippet(processGroup, snippetContents);
|
||||
|
||||
return snippetContents;
|
||||
} catch (IllegalStateException ise) {
|
||||
// illegal state will be thrown from instantiateSnippet when there is an issue with the snippet _before_ any of the
|
||||
// components are actually created. if we've received this exception we want to attempt to roll back any of the
|
||||
// policies that we've already cloned for this request
|
||||
snippetUtils.rollbackClonedPolicies(snippetContents);
|
||||
|
||||
// rethrow the same exception
|
||||
throw ise;
|
||||
}
|
||||
} catch (ProcessorInstantiationException pie) {
|
||||
throw new NiFiCoreException(String.format("Unable to copy snippet because processor type '%s' is unknown to this NiFi.",
|
||||
StringUtils.substringAfterLast(pie.getMessage(), ".")));
|
||||
|
|
|
@ -16,19 +16,12 @@
|
|||
*/
|
||||
package org.apache.nifi.web.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.authorization.AccessPolicy;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.Resource;
|
||||
import org.apache.nifi.authorization.resource.ResourceFactory;
|
||||
import org.apache.nifi.authorization.resource.ResourceType;
|
||||
import org.apache.nifi.components.PropertyDescriptor;
|
||||
import org.apache.nifi.connectable.ConnectableType;
|
||||
import org.apache.nifi.connectable.Connection;
|
||||
|
@ -44,6 +37,7 @@ import org.apache.nifi.controller.service.ControllerServiceState;
|
|||
import org.apache.nifi.groups.ProcessGroup;
|
||||
import org.apache.nifi.groups.RemoteProcessGroup;
|
||||
import org.apache.nifi.util.TypeOneUUIDGenerator;
|
||||
import org.apache.nifi.web.api.dto.AccessPolicyDTO;
|
||||
import org.apache.nifi.web.api.dto.ConnectableDTO;
|
||||
import org.apache.nifi.web.api.dto.ConnectionDTO;
|
||||
import org.apache.nifi.web.api.dto.ControllerServiceDTO;
|
||||
|
@ -59,15 +53,34 @@ import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
|
|||
import org.apache.nifi.web.api.dto.RemoteProcessGroupContentsDTO;
|
||||
import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
|
||||
import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO;
|
||||
import org.apache.nifi.web.api.entity.TenantEntity;
|
||||
import org.apache.nifi.web.dao.AccessPolicyDAO;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Template utilities.
|
||||
*/
|
||||
public final class SnippetUtils {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SnippetUtils.class);
|
||||
|
||||
private FlowController flowController;
|
||||
private DtoFactory dtoFactory;
|
||||
|
||||
private AccessPolicyDAO accessPolicyDAO;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -262,8 +275,7 @@ public final class SnippetUtils {
|
|||
}
|
||||
|
||||
|
||||
public FlowSnippetDTO copy(final FlowSnippetDTO snippetContents, final ProcessGroup group,
|
||||
final String idGenerationSeed, boolean isCopy) {
|
||||
public FlowSnippetDTO copy(final FlowSnippetDTO snippetContents, final ProcessGroup group, final String idGenerationSeed, boolean isCopy) {
|
||||
final FlowSnippetDTO snippetCopy = copyContentsForGroup(snippetContents, group.getIdentifier(), null, null, idGenerationSeed, isCopy);
|
||||
resolveNameConflicts(snippetCopy, group);
|
||||
return snippetCopy;
|
||||
|
@ -319,10 +331,11 @@ public final class SnippetUtils {
|
|||
}
|
||||
}
|
||||
|
||||
private FlowSnippetDTO copyContentsForGroup(final FlowSnippetDTO snippetContents, final String groupId, final Map<String, ConnectableDTO> parentConnectableMap, Map<String, String> serviceIdMap,
|
||||
final String idGenerationSeed, boolean isCopy) {
|
||||
final FlowSnippetDTO snippetContentsCopy = new FlowSnippetDTO();
|
||||
private FlowSnippetDTO copyContentsForGroup(final FlowSnippetDTO snippetContents, final String groupId, final Map<String, ConnectableDTO> parentConnectableMap,
|
||||
Map<String, String> serviceIdMap, final String idGenerationSeed, boolean isCopy) {
|
||||
|
||||
final FlowSnippetDTO snippetContentsCopy = new FlowSnippetDTO();
|
||||
try {
|
||||
//
|
||||
// Copy the Controller Services
|
||||
//
|
||||
|
@ -340,6 +353,13 @@ public final class SnippetUtils {
|
|||
|
||||
// Map old service ID to new service ID so that we can make sure that we reference the new ones.
|
||||
serviceIdMap.put(serviceDTO.getId(), service.getId());
|
||||
|
||||
// clone policies as appropriate
|
||||
if (isCopy) {
|
||||
cloneComponentSpecificPolicies(
|
||||
ResourceFactory.getComponentResource(ResourceType.ControllerService, serviceDTO.getId(), serviceDTO.getName()),
|
||||
ResourceFactory.getComponentResource(ResourceType.ControllerService, service.getId(), service.getName()), idGenerationSeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -373,6 +393,13 @@ public final class SnippetUtils {
|
|||
label.setId(generateId(labelDTO.getId(), idGenerationSeed, isCopy));
|
||||
label.setParentGroupId(groupId);
|
||||
labels.add(label);
|
||||
|
||||
// clone policies as appropriate
|
||||
if (isCopy) {
|
||||
cloneComponentSpecificPolicies(
|
||||
ResourceFactory.getComponentResource(ResourceType.Label, labelDTO.getId(), labelDTO.getLabel()),
|
||||
ResourceFactory.getComponentResource(ResourceType.Label, label.getId(), label.getLabel()), idGenerationSeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
snippetContentsCopy.setLabels(labels);
|
||||
|
@ -395,6 +422,13 @@ public final class SnippetUtils {
|
|||
funnels.add(cp);
|
||||
|
||||
connectableMap.put(funnelDTO.getParentGroupId() + "-" + funnelDTO.getId(), dtoFactory.createConnectableDto(cp));
|
||||
|
||||
// clone policies as appropriate
|
||||
if (isCopy) {
|
||||
cloneComponentSpecificPolicies(
|
||||
ResourceFactory.getComponentResource(ResourceType.Funnel, funnelDTO.getId(), funnelDTO.getId()),
|
||||
ResourceFactory.getComponentResource(ResourceType.Funnel, cp.getId(), cp.getId()), idGenerationSeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
snippetContentsCopy.setFunnels(funnels);
|
||||
|
@ -413,6 +447,13 @@ public final class SnippetUtils {
|
|||
if (parentConnectableMap != null) {
|
||||
parentConnectableMap.put(portDTO.getParentGroupId() + "-" + portDTO.getId(), portConnectable);
|
||||
}
|
||||
|
||||
// clone policies as appropriate
|
||||
if (isCopy) {
|
||||
cloneComponentSpecificPolicies(
|
||||
ResourceFactory.getComponentResource(ResourceType.InputPort, portDTO.getId(), portDTO.getName()),
|
||||
ResourceFactory.getComponentResource(ResourceType.InputPort, cp.getId(), cp.getName()), idGenerationSeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
snippetContentsCopy.setInputPorts(inputPorts);
|
||||
|
@ -431,6 +472,13 @@ public final class SnippetUtils {
|
|||
if (parentConnectableMap != null) {
|
||||
parentConnectableMap.put(portDTO.getParentGroupId() + "-" + portDTO.getId(), portConnectable);
|
||||
}
|
||||
|
||||
// clone policies as appropriate
|
||||
if (isCopy) {
|
||||
cloneComponentSpecificPolicies(
|
||||
ResourceFactory.getComponentResource(ResourceType.OutputPort, portDTO.getId(), portDTO.getName()),
|
||||
ResourceFactory.getComponentResource(ResourceType.OutputPort, cp.getId(), cp.getName()), idGenerationSeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
snippetContentsCopy.setOutputPorts(outputPorts);
|
||||
|
@ -448,6 +496,13 @@ public final class SnippetUtils {
|
|||
processors.add(cp);
|
||||
|
||||
connectableMap.put(processorDTO.getParentGroupId() + "-" + processorDTO.getId(), dtoFactory.createConnectableDto(cp));
|
||||
|
||||
// clone policies as appropriate
|
||||
if (isCopy) {
|
||||
cloneComponentSpecificPolicies(
|
||||
ResourceFactory.getComponentResource(ResourceType.Processor, processorDTO.getId(), processorDTO.getName()),
|
||||
ResourceFactory.getComponentResource(ResourceType.Processor, cp.getId(), cp.getName()), idGenerationSeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
snippetContentsCopy.setProcessors(processors);
|
||||
|
@ -470,6 +525,13 @@ public final class SnippetUtils {
|
|||
final FlowSnippetDTO contentsCopy = copyContentsForGroup(groupDTO.getContents(), cp.getId(), connectableMap, serviceIdMap, idGenerationSeed, isCopy);
|
||||
cp.setContents(contentsCopy);
|
||||
groups.add(cp);
|
||||
|
||||
// clone policies as appropriate
|
||||
if (isCopy) {
|
||||
cloneComponentSpecificPolicies(
|
||||
ResourceFactory.getComponentResource(ResourceType.ProcessGroup, groupDTO.getId(), groupDTO.getName()),
|
||||
ResourceFactory.getComponentResource(ResourceType.ProcessGroup, cp.getId(), cp.getName()), idGenerationSeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
snippetContentsCopy.setProcessGroups(groups);
|
||||
|
@ -496,6 +558,13 @@ public final class SnippetUtils {
|
|||
}
|
||||
|
||||
remoteGroups.add(cp);
|
||||
|
||||
// clone policies as appropriate
|
||||
if (isCopy) {
|
||||
cloneComponentSpecificPolicies(
|
||||
ResourceFactory.getComponentResource(ResourceType.RemoteProcessGroup, remoteGroupDTO.getId(), remoteGroupDTO.getName()),
|
||||
ResourceFactory.getComponentResource(ResourceType.RemoteProcessGroup, cp.getId(), cp.getName()), idGenerationSeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
snippetContentsCopy.setRemoteProcessGroups(remoteGroups);
|
||||
|
@ -518,11 +587,136 @@ public final class SnippetUtils {
|
|||
cp.setDestination(destination);
|
||||
cp.setParentGroupId(groupId);
|
||||
connections.add(cp);
|
||||
|
||||
// note - no need to copy policies of a connection as their permissions are inferred through the source and destination
|
||||
}
|
||||
}
|
||||
snippetContentsCopy.setConnections(connections);
|
||||
|
||||
return snippetContentsCopy;
|
||||
} catch (Exception e) {
|
||||
// attempt to role back any policies of the copies that were created in preparation for the clone
|
||||
rollbackClonedPolicies(snippetContentsCopy);
|
||||
|
||||
// rethrow the original exception
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones all the component specified policies for the specified original component. This will include the component resource, data resource
|
||||
* for the component, data transfer resource for the component, and policy resource for the component.
|
||||
*
|
||||
* @param originalComponentResource original component resource
|
||||
* @param clonedComponentResource cloned component resource
|
||||
* @param idGenerationSeed id generation seed
|
||||
*/
|
||||
private void cloneComponentSpecificPolicies(final Resource originalComponentResource, final Resource clonedComponentResource, final String idGenerationSeed) {
|
||||
final Map<Resource, Resource> resources = new HashMap<>();
|
||||
resources.put(originalComponentResource, clonedComponentResource);
|
||||
resources.put(ResourceFactory.getDataResource(originalComponentResource), ResourceFactory.getDataResource(clonedComponentResource));
|
||||
resources.put(ResourceFactory.getDataTransferResource(originalComponentResource), ResourceFactory.getDataTransferResource(clonedComponentResource));
|
||||
resources.put(ResourceFactory.getPolicyResource(originalComponentResource), ResourceFactory.getPolicyResource(clonedComponentResource));
|
||||
|
||||
for (final Entry<Resource, Resource> entry : resources.entrySet()) {
|
||||
final Resource originalResource = entry.getKey();
|
||||
final Resource cloneResource = entry.getValue();
|
||||
|
||||
for (final RequestAction action : RequestAction.values()) {
|
||||
final AccessPolicy accessPolicy = accessPolicyDAO.getAccessPolicy(action, originalResource.getIdentifier());
|
||||
|
||||
// if there is a component specific policy we want to clone it for the new component
|
||||
if (accessPolicy != null) {
|
||||
final AccessPolicyDTO cloneAccessPolicy = new AccessPolicyDTO();
|
||||
cloneAccessPolicy.setId(generateId(accessPolicy.getIdentifier(), idGenerationSeed, true));
|
||||
cloneAccessPolicy.setAction(accessPolicy.getAction().toString());
|
||||
cloneAccessPolicy.setResource(cloneResource.getIdentifier());
|
||||
|
||||
final Set<TenantEntity> users = new HashSet<>();
|
||||
accessPolicy.getUsers().forEach(userId -> {
|
||||
final TenantEntity entity = new TenantEntity();
|
||||
entity.setId(userId);
|
||||
users.add(entity);
|
||||
});
|
||||
cloneAccessPolicy.setUsers(users);
|
||||
|
||||
final Set<TenantEntity> groups = new HashSet<>();
|
||||
accessPolicy.getGroups().forEach(groupId -> {
|
||||
final TenantEntity entity = new TenantEntity();
|
||||
entity.setId(groupId);
|
||||
groups.add(entity);
|
||||
});
|
||||
cloneAccessPolicy.setUserGroups(groups);
|
||||
|
||||
// create the access policy for the cloned policy
|
||||
accessPolicyDAO.createAccessPolicy(cloneAccessPolicy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to roll back and in the specified snippet.
|
||||
*
|
||||
* @param snippet snippet
|
||||
*/
|
||||
public void rollbackClonedPolicies(final FlowSnippetDTO snippet) {
|
||||
snippet.getControllerServices().forEach(controllerServiceDTO -> {
|
||||
rollbackClonedPolicy(ResourceFactory.getComponentResource(ResourceType.ControllerService, controllerServiceDTO.getId(), controllerServiceDTO.getName()));
|
||||
});
|
||||
snippet.getFunnels().forEach(funnelDTO -> {
|
||||
rollbackClonedPolicy(ResourceFactory.getComponentResource(ResourceType.Funnel, funnelDTO.getId(), funnelDTO.getId()));
|
||||
});
|
||||
snippet.getInputPorts().forEach(inputPortDTO -> {
|
||||
rollbackClonedPolicy(ResourceFactory.getComponentResource(ResourceType.InputPort, inputPortDTO.getId(), inputPortDTO.getName()));
|
||||
});
|
||||
snippet.getLabels().forEach(labelDTO -> {
|
||||
rollbackClonedPolicy(ResourceFactory.getComponentResource(ResourceType.Label, labelDTO.getId(), labelDTO.getLabel()));
|
||||
});
|
||||
snippet.getOutputPorts().forEach(outputPortDTO -> {
|
||||
rollbackClonedPolicy(ResourceFactory.getComponentResource(ResourceType.OutputPort, outputPortDTO.getId(), outputPortDTO.getName()));
|
||||
});
|
||||
snippet.getProcessors().forEach(processorDTO -> {
|
||||
rollbackClonedPolicy(ResourceFactory.getComponentResource(ResourceType.Processor, processorDTO.getId(), processorDTO.getName()));
|
||||
});
|
||||
snippet.getRemoteProcessGroups().forEach(remoteProcessGroupDTO -> {
|
||||
rollbackClonedPolicy(ResourceFactory.getComponentResource(ResourceType.RemoteProcessGroup, remoteProcessGroupDTO.getId(), remoteProcessGroupDTO.getName()));
|
||||
});
|
||||
snippet.getProcessGroups().forEach(processGroupDTO -> {
|
||||
rollbackClonedPolicy(ResourceFactory.getComponentResource(ResourceType.ProcessGroup, processGroupDTO.getId(), processGroupDTO.getName()));
|
||||
|
||||
// consider all descendant components
|
||||
if (processGroupDTO.getContents() != null) {
|
||||
rollbackClonedPolicies(processGroupDTO.getContents());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to roll back all policies for the specified component. This includes the component resource, data resource
|
||||
* for the component, data transfer resource for the component, and policy resource for the component.
|
||||
*
|
||||
* @param componentResource component resource
|
||||
*/
|
||||
private void rollbackClonedPolicy(final Resource componentResource) {
|
||||
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()) {
|
||||
final AccessPolicy accessPolicy = accessPolicyDAO.getAccessPolicy(action, resource.getIdentifier());
|
||||
if (accessPolicy != null) {
|
||||
try {
|
||||
accessPolicyDAO.deleteAccessPolicy(accessPolicy.getIdentifier());
|
||||
} catch (final Exception e) {
|
||||
logger.warn(String.format("Unable to clean up cloned access policy for %s %s after failed copy/paste action.", action, componentResource.getIdentifier()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateControllerServiceIdentifiers(final FlowSnippetDTO snippet, final Map<String, String> serviceIdMap) {
|
||||
|
@ -585,6 +779,10 @@ public final class SnippetUtils {
|
|||
this.flowController = flowController;
|
||||
}
|
||||
|
||||
public void setAccessPolicyDAO(AccessPolicyDAO accessPolicyDAO) {
|
||||
this.accessPolicyDAO = accessPolicyDAO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will normalize the coordinates of the processors to ensure their
|
||||
* consistency across exports. It will do so by fist calculating the
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
<bean id="snippetUtils" class="org.apache.nifi.web.util.SnippetUtils">
|
||||
<property name="dtoFactory" ref="dtoFactory"/>
|
||||
<property name="flowController" ref="flowController"/>
|
||||
<property name="accessPolicyDAO" ref="policyBasedAuthorizerDAO"/>
|
||||
</bean>
|
||||
|
||||
<!-- nifi component dao initialization -->
|
||||
|
|
Loading…
Reference in New Issue