blob: 26db5c8d6d61416ecc9e13cf42374594c15ab46c [file] [log] [blame]
/*
* 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.controller;
import org.apache.nifi.parameter.ParameterParser;
import org.apache.nifi.parameter.ParameterTokenList;
import org.apache.nifi.parameter.ExpressionLanguageAwareParameterParser;
import org.apache.nifi.persistence.TemplateDeserializer;
import org.apache.nifi.web.api.dto.ConnectableDTO;
import org.apache.nifi.web.api.dto.ConnectionDTO;
import org.apache.nifi.web.api.dto.ControllerServiceDTO;
import org.apache.nifi.web.api.dto.FlowSnippetDTO;
import org.apache.nifi.web.api.dto.ProcessGroupDTO;
import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
import org.apache.nifi.web.api.dto.ProcessorDTO;
import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
import org.apache.nifi.web.api.dto.RelationshipDTO;
import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
import org.apache.nifi.web.api.dto.TemplateDTO;
import org.w3c.dom.Element;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class TemplateUtils {
public static TemplateDTO parseDto(final Element templateElement) {
try {
final DOMSource domSource = new DOMSource(templateElement);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final StreamResult streamResult = new StreamResult(baos);
// need to stream the template element as the TemplateDeserializer.deserialize operation needs to re-parse
// in order to apply explicit properties on the XMLInputFactory
final TransformerFactory transformerFactory = TransformerFactory.newInstance();
final Transformer transformer = transformerFactory.newTransformer();
transformer.transform(domSource, streamResult);
return parseDto(baos.toByteArray());
} catch (final Exception e) {
throw new RuntimeException("Could not parse XML as a valid template", e);
}
}
public static TemplateDTO parseDto(final byte[] bytes) {
try (final InputStream in = new ByteArrayInputStream(bytes)) {
return TemplateDeserializer.deserialize(in);
} catch (final IOException ioe) {
throw new RuntimeException("Could not parse bytes as template", ioe);
}
}
/**
* If template was serialized in a version before Parameters were supported, ensures that any reference to a
* Parameter is escaped so that the value is treated as a literal value.
* @param templateDto the template
*/
public static void escapeParameterReferences(final TemplateDTO templateDto) {
final String encodingVersion = templateDto.getEncodingVersion();
if (encodingVersion == null) {
escapeParameterReferences(templateDto.getSnippet());
} else {
switch (encodingVersion) {
case "1.0":
case "1.1":
case "1.2":
escapeParameterReferences(templateDto.getSnippet());
break;
}
}
}
private static void escapeParameterReferences(final FlowSnippetDTO flowSnippetDTO) {
flowSnippetDTO.getProcessors().forEach(TemplateUtils::escapeParameterReferences);
flowSnippetDTO.getControllerServices().forEach(TemplateUtils::escapeParameterReferences);
for (final ProcessGroupDTO groupDto : flowSnippetDTO.getProcessGroups()) {
escapeParameterReferences(groupDto.getContents());
}
}
private static void escapeParameterReferences(final ProcessorDTO processorDto) {
final ProcessorConfigDTO config = processorDto.getConfig();
if (config == null) {
return;
}
final ParameterParser parameterParser = new ExpressionLanguageAwareParameterParser();
final Map<String, String> escapedPropertyValues = new HashMap<>();
for (final Map.Entry<String, String> entry : config.getProperties().entrySet()) {
final ParameterTokenList references = parameterParser.parseTokens(entry.getValue());
final String escaped = references.escape();
escapedPropertyValues.put(entry.getKey(), escaped);
}
config.setProperties(escapedPropertyValues);
}
private static void escapeParameterReferences(final ControllerServiceDTO controllerServiceDTO) {
final ParameterParser parameterParser = new ExpressionLanguageAwareParameterParser();
final Map<String, String> escapedPropertyValues = new HashMap<>();
for (final Map.Entry<String, String> entry : controllerServiceDTO.getProperties().entrySet()) {
final ParameterTokenList references = parameterParser.parseTokens(entry.getValue());
final String escaped = references.escape();
escapedPropertyValues.put(entry.getKey(), escaped);
}
controllerServiceDTO.setProperties(escapedPropertyValues);
}
/**
* Scrubs the template prior to persisting in order to remove fields that shouldn't be included or are unnecessary.
*
* @param templateDto template
*/
public static void scrubTemplate(final TemplateDTO templateDto) {
scrubSnippet(templateDto.getSnippet());
}
private static void scrubSnippet(final FlowSnippetDTO snippet) {
// ensure that contents have been specified
if (snippet != null) {
// go through each processor if specified
if (snippet.getProcessors() != null) {
scrubProcessors(snippet.getProcessors());
}
// go through each connection if specified
if (snippet.getConnections() != null) {
scrubConnections(snippet.getConnections());
}
// go through each remote process group if specified
if (snippet.getRemoteProcessGroups() != null) {
scrubRemoteProcessGroups(snippet.getRemoteProcessGroups());
}
// go through each process group if specified
if (snippet.getProcessGroups() != null) {
scrubProcessGroups(snippet.getProcessGroups());
}
// go through each controller service if specified
if (snippet.getControllerServices() != null) {
scrubControllerServices(snippet.getControllerServices());
}
}
}
/**
* Scrubs process groups prior to saving.
*
* @param processGroups groups
*/
private static void scrubProcessGroups(final Set<ProcessGroupDTO> processGroups) {
// go through each process group
for (final ProcessGroupDTO processGroupDTO : processGroups) {
processGroupDTO.setActiveRemotePortCount(null);
processGroupDTO.setDisabledCount(null);
processGroupDTO.setInactiveRemotePortCount(null);
processGroupDTO.setLocalInputPortCount(null);
processGroupDTO.setPublicInputPortCount(null);
processGroupDTO.setInvalidCount(null);
processGroupDTO.setLocalOutputPortCount(null);
processGroupDTO.setPublicOutputPortCount(null);
processGroupDTO.setRunningCount(null);
processGroupDTO.setStoppedCount(null);
processGroupDTO.setUpToDateCount(null);
processGroupDTO.setLocallyModifiedCount(null);
processGroupDTO.setStaleCount(null);
processGroupDTO.setLocallyModifiedAndStaleCount(null);
processGroupDTO.setSyncFailureCount(null);
processGroupDTO.setVersionControlInformation(null);
processGroupDTO.setParameterContext(null);
scrubSnippet(processGroupDTO.getContents());
}
}
/**
* Scrubs processors prior to saving. This includes removing sensitive properties, validation errors, property descriptors, etc.
*
* @param processors procs
*/
private static void scrubProcessors(final Set<ProcessorDTO> processors) {
// go through each processor
for (final ProcessorDTO processorDTO : processors) {
final ProcessorConfigDTO processorConfig = processorDTO.getConfig();
// ensure that some property configuration have been specified
if (processorConfig != null) {
// if properties have been specified, remove sensitive ones
if (processorConfig.getProperties() != null) {
Map<String, String> processorProperties = processorConfig.getProperties();
// look for sensitive properties and remove them
if (processorConfig.getDescriptors() != null) {
final Collection<PropertyDescriptorDTO> descriptors = processorConfig.getDescriptors().values();
for (PropertyDescriptorDTO descriptor : descriptors) {
if (Boolean.TRUE.equals(descriptor.isSensitive())) {
processorProperties.put(descriptor.getName(), null);
}
scrubPropertyDescriptor(descriptor);
}
}
}
processorConfig.setCustomUiUrl(null);
processorConfig.setDefaultConcurrentTasks(null);
processorConfig.setDefaultSchedulingPeriod(null);
processorConfig.setAutoTerminatedRelationships(null);
}
if (processorDTO.getRelationships() != null) {
for (final RelationshipDTO relationship : processorDTO.getRelationships()) {
relationship.setDescription(null);
}
}
processorDTO.setExtensionMissing(null);
processorDTO.setMultipleVersionsAvailable(null);
processorDTO.setValidationErrors(null);
processorDTO.setValidationStatus(null);
processorDTO.setInputRequirement(null);
processorDTO.setDescription(null);
processorDTO.setInputRequirement(null);
processorDTO.setPersistsState(null);
processorDTO.setSupportsBatching(null);
processorDTO.setSupportsEventDriven(null);
processorDTO.setSupportsParallelProcessing(null);
}
}
/**
* The only thing that we really need from the Property Descriptors in the templates is the
* flag that indicates whether or not the property identifies a controller service.
* Everything else is unneeded and makes templates very verbose and more importantly makes it
* so that if one of these things changes, the template itself changes, which makes it hard to
* use a CM tool for versioning. So we remove all that we don't need.
*
* @param descriptor the PropertyDescriptor to scrub
*/
private static void scrubPropertyDescriptor(final PropertyDescriptorDTO descriptor) {
descriptor.setAllowableValues(null);
descriptor.setDefaultValue(null);
descriptor.setDescription(null);
descriptor.setDisplayName(null);
descriptor.setDynamic(null);
descriptor.setRequired(null);
descriptor.setSensitive(null);
descriptor.setSupportsEl(null);
descriptor.setExpressionLanguageScope(null);
descriptor.setIdentifiesControllerServiceBundle(null);
}
private static void scrubControllerServices(final Set<ControllerServiceDTO> controllerServices) {
for (final ControllerServiceDTO serviceDTO : controllerServices) {
final Map<String, String> properties = serviceDTO.getProperties();
final Map<String, PropertyDescriptorDTO> descriptors = serviceDTO.getDescriptors();
if (properties != null && descriptors != null) {
for (final PropertyDescriptorDTO descriptor : descriptors.values()) {
if (Boolean.TRUE.equals(descriptor.isSensitive())) {
properties.put(descriptor.getName(), null);
}
scrubPropertyDescriptor(descriptor);
}
}
serviceDTO.setControllerServiceApis(null);
serviceDTO.setExtensionMissing(null);
serviceDTO.setMultipleVersionsAvailable(null);
serviceDTO.setCustomUiUrl(null);
serviceDTO.setValidationErrors(null);
serviceDTO.setValidationStatus(null);
}
}
/**
* Scrubs connections prior to saving. This includes removing available relationships.
*
* @param connections conns
*/
private static void scrubConnections(final Set<ConnectionDTO> connections) {
// go through each connection
for (final ConnectionDTO connectionDTO : connections) {
connectionDTO.setAvailableRelationships(null);
scrubConnectable(connectionDTO.getSource());
scrubConnectable(connectionDTO.getDestination());
}
}
/**
* Remove unnecessary fields in connectables prior to saving.
*
* @param connectable connectable
*/
private static void scrubConnectable(final ConnectableDTO connectable) {
if (connectable != null) {
connectable.setComments(null);
connectable.setExists(null);
connectable.setRunning(null);
connectable.setTransmitting(null);
connectable.setName(null);
}
}
/**
* Remove unnecessary fields in remote groups prior to saving.
*
* @param remoteGroups groups
*/
private static void scrubRemoteProcessGroups(final Set<RemoteProcessGroupDTO> remoteGroups) {
// go through each remote process group
for (final RemoteProcessGroupDTO remoteProcessGroupDTO : remoteGroups) {
remoteProcessGroupDTO.setFlowRefreshed(null);
remoteProcessGroupDTO.setInputPortCount(null);
remoteProcessGroupDTO.setOutputPortCount(null);
remoteProcessGroupDTO.setTransmitting(null);
remoteProcessGroupDTO.setProxyPassword(null);
remoteProcessGroupDTO.setActiveRemoteInputPortCount(null);
remoteProcessGroupDTO.setInactiveRemoteInputPortCount(null);
remoteProcessGroupDTO.setActiveRemoteOutputPortCount(null);
remoteProcessGroupDTO.setInactiveRemoteOutputPortCount(null);
remoteProcessGroupDTO.setAuthorizationIssues(null);
remoteProcessGroupDTO.setFlowRefreshed(null);
remoteProcessGroupDTO.setName(null);
remoteProcessGroupDTO.setTargetSecure(null);
remoteProcessGroupDTO.setTransmitting(null);
}
}
}