NIFI-1951:

- Removing deprecated NiFiWebContext and related classes.
- Adding authorization to Custom UIs.
- Fixing issue when creating ControllerService inline.
- Addressing contentType issue when attempting to clear component state.
- This closes #489
This commit is contained in:
Matt Gilman 2016-06-03 16:03:00 -04:00
parent df0e4e7960
commit 806f4d549d
19 changed files with 256 additions and 1072 deletions

View File

@ -1,116 +0,0 @@
/*
* 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.web;
import java.util.Collection;
import org.apache.nifi.controller.ControllerService;
/**
* NiFi web context providing limited access to dataflow configuration for
* processor custom UIs.
*/
@Deprecated
public interface NiFiWebContext {
/**
* @param serviceIdentifier identifier of the service
* @return the ControllerService for the specified identifier. If a
* corresponding service cannot be found, null is returned. If this NiFi is
* clustered, the ControllerService is loaded from the NCM
*/
ControllerService getControllerService(String serviceIdentifier);
/**
* Provides a mechanism for custom UIs to save actions to appear in NiFi
* configuration history. Note all fields within each Action must be
* populated. Null values will result in a failure to insert the audit
* record. Since the saving to these actions is separate from the actual
* configuration change, a failure to insert here will just generate a
* warning log message. The recording of these actions typically happens
* after a configuration change is applied. Since those changes have already
* been applied to the flow, we cannot revert them because of a failure to
* insert an audit record.
*
* @param actions to save
*/
void saveActions(Collection<ProcessorConfigurationAction> actions);
/**
* @return the current user identity. It may be a dn, an email, a username, or any string that identities the user. Returns null if no user is found
*/
String getCurrentUserDn();
/**
* @return the current user name. Returns null if no user is found
*/
String getCurrentUserName();
/**
* Gets the Processor configuration. The given configuration is expected to
* contain the following configuration:
*
* <ul>
* <li>revision -- the client identifier and optionally the version
* number</li>
* <li>processorId -- the id of the processor to retrieve information
* for</li>
* <li>X509Certificate -- the certificate if this is a secure request</li>
* </ul>
*
* When operating in a clustered environment, if the configuration contains
* a X509Certificate, then the certificate information will be forwarded to
* the nodes.
*
* @param config the configuration
* @return the processor info object
* @throws ResourceNotFoundException if the processor does not exit
* @throws ClusterRequestException if the processor was unable to be
* retrieved from the cluster. This exception will only be thrown when
* operating in a cluster.
*/
ProcessorInfo getProcessor(NiFiWebContextConfig config) throws ResourceNotFoundException, ClusterRequestException;
/**
* Sets the Processor annotation data. The given configuration is expected
* to contain the following configuration:
*
* <ul>
* <li>revision -- the client identifier and optionally the version
* number</li>
* <li>processorId -- the id of the processor to retrieve information
* for</li>
* <li>X509Certificate -- the certificate if this is a secure request</li>
* </ul>
*
* When operating in a clustered environment, if the configuration contains
* a X509Certificate, then the certificate information will be forwarded to
* the nodes.
*
* @param config the configuration
* @param annotationData the annotation data
* @throws ResourceNotFoundException if the processor does not exit
* @throws InvalidRevisionException if a revision other than the current
* revision is given
* @throws ClusterRequestException if the annotation data was unable to be
* set for the processor. This exception will only be thrown when operating
* in a cluster.
*/
void setProcessorAnnotationData(NiFiWebContextConfig config, String annotationData)
throws ResourceNotFoundException, InvalidRevisionException, ClusterRequestException;
}

View File

@ -1,55 +0,0 @@
/*
* 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.web;
/**
* Context configuration for methods invoked from the NiFiWebContext.
*/
@Deprecated
public interface NiFiWebContextConfig {
/**
* The request protocol scheme (http or https). When scheme is https, the
* X509Certificate can be used for subsequent remote requests.
*
* @return the protocol scheme
*/
String getScheme();
/**
* @return the processor ID
*/
String getProcessorId();
/**
* @return the revision
*/
Revision getRevision();
/**
* Returns the proxied entities chain. The format of the chain is as
* follows:
*
* <code>
* &lt;CN=original-proxied-entity&gt;&lt;CN=first-proxy&gt;&lt;CN=second-proxy&gt;...
* </code>
*
* @return the proxied entities chain or null if no chain
*/
String getProxiedEntitiesChain();
}

View File

@ -1,126 +0,0 @@
/*
* 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.web;
/**
*
*/
@Deprecated
public class ProcessorConfigurationAction {
private final String processorId;
private final String processorName;
private final String processorType;
private final String name;
private final String previousValue;
private final String value;
private ProcessorConfigurationAction(final Builder builder) {
this.processorId = builder.processorId;
this.processorName = builder.processorName;
this.processorType = builder.processorType;
this.name = builder.name;
this.previousValue = builder.previousValue;
this.value = builder.value;
}
/**
* @return the id of the processor
*/
public String getProcessorId() {
return processorId;
}
/**
* @return the name of the processor being modified
*/
public String getProcessorName() {
return processorName;
}
/**
* @return the type of the processor being modified
*/
public String getProcessorType() {
return processorType;
}
/**
* @return the name of the field, property, etc that has been modified.
*/
public String getName() {
return name;
}
/**
* @return the previous value
*/
public String getPreviousValue() {
return previousValue;
}
/**
* @return the new value
*/
public String getValue() {
return value;
}
public static class Builder {
private String processorId;
private String processorName;
private String processorType;
private String name;
private String previousValue;
private String value;
public Builder processorId(final String processorId) {
this.processorId = processorId;
return this;
}
public Builder processorName(final String processorName) {
this.processorName = processorName;
return this;
}
public Builder processorType(final String processorType) {
this.processorType = processorType;
return this;
}
public Builder name(final String name) {
this.name = name;
return this;
}
public Builder previousValue(final String previousValue) {
this.previousValue = previousValue;
return this;
}
public Builder value(final String value) {
this.value = value;
return this;
}
public ProcessorConfigurationAction build() {
return new ProcessorConfigurationAction(this);
}
}
}

View File

@ -1,111 +0,0 @@
/*
* 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.web;
import java.util.Collection;
import java.util.Map;
/**
*
*/
@Deprecated
public class ProcessorInfo {
private final String id;
private final String name;
private final String state;
private final String annotationData;
private final Map<String, String> properties;
private final Collection<String> validationErrors;
private ProcessorInfo(final Builder builder) {
this.id = builder.id;
this.name = builder.name;
this.state = builder.state;
this.annotationData = builder.annotationData;
this.properties = builder.properties;
this.validationErrors = builder.validationErrors;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getState() {
return state;
}
public String getAnnotationData() {
return annotationData;
}
public Map<String, String> getProperties() {
return properties;
}
public Collection<String> getValidationErrors() {
return validationErrors;
}
public static final class Builder {
private String id;
private String name;
private String state;
private String annotationData;
private Map<String, String> properties;
private Collection<String> validationErrors;
public Builder id(final String id) {
this.id = id;
return this;
}
public Builder name(final String name) {
this.name = name;
return this;
}
public Builder state(final String state) {
this.state = state;
return this;
}
public Builder annotationData(final String annotationData) {
this.annotationData = annotationData;
return this;
}
public Builder properties(final Map<String, String> properties) {
this.properties = properties;
return this;
}
public Builder validateErrors(final Collection<String> validationErrors) {
this.validationErrors = validationErrors;
return this;
}
public ProcessorInfo build() {
return new ProcessorInfo(this);
}
}
}

View File

@ -1,119 +0,0 @@
/*
* 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.web;
import java.security.cert.X509Certificate;
import javax.servlet.http.HttpServletRequest;
/**
* An implementation of the NiFiWebContextConfig that retrieves configuration
* from a HttpServletRequest instance.
*/
@Deprecated
public class HttpServletRequestContextConfig implements NiFiWebContextConfig {
public static final String PROCESSOR_ID_PARAM = "processorId";
public static final String CLIENT_ID_PARAM = "clientId";
public static final String REVISION_PARAM = "revision";
private final HttpServletRequest request;
public HttpServletRequestContextConfig(final HttpServletRequest request) {
this.request = request;
}
@Override
public String getProxiedEntitiesChain() {
String xProxiedEntitiesChain = request.getHeader("X-ProxiedEntitiesChain");
final X509Certificate cert = extractClientCertificate(request);
if (cert != null) {
final String extractedPrincipal = extractPrincipal(cert);
final String formattedPrincipal = formatProxyDn(extractedPrincipal);
if (xProxiedEntitiesChain == null || xProxiedEntitiesChain.trim().isEmpty()) {
xProxiedEntitiesChain = formattedPrincipal;
} else {
xProxiedEntitiesChain += formattedPrincipal;
}
}
return xProxiedEntitiesChain;
}
/**
* @return the protocol scheme of the HttpServletRequest instance.
*/
@Override
public String getScheme() {
return request.getScheme();
}
/**
* @return the processor ID retrieved from the request parameter with key
* equal to "processorId".
*/
@Override
public String getProcessorId() {
return request.getParameter(PROCESSOR_ID_PARAM);
}
/**
* @return the revision retrieved from the request parameters with keys
* equal to "clientId" and "revision".
*/
@Override
public Revision getRevision() {
final String revisionParamVal = request.getParameter(REVISION_PARAM);
Long revision;
try {
revision = Long.parseLong(revisionParamVal);
} catch (final Exception ex) {
revision = null;
}
final String clientId = request.getParameter(CLIENT_ID_PARAM);
return new Revision(revision, clientId);
}
/**
* Utility methods that have been copied into this class to reduce the
* dependency footprint of this artifact. These utility methods typically
* live in web-utilities but that would pull in spring, jersey, jackson,
* etc.
*/
private X509Certificate extractClientCertificate(HttpServletRequest request) {
X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
if (certs != null && certs.length > 0) {
return certs[0];
}
return null;
}
private String extractPrincipal(X509Certificate cert) {
return cert.getSubjectDN().getName().trim();
}
private String formatProxyDn(String dn) {
return "<" + dn + ">";
}
}

View File

@ -58,7 +58,6 @@ import org.apache.nifi.ui.extension.UiExtensionMapping;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.ContentAccess;
import org.apache.nifi.web.NiFiWebConfigurationContext;
import org.apache.nifi.web.NiFiWebContext;
import org.apache.nifi.web.UiExtensionType;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.server.Connector;
@ -121,9 +120,6 @@ public class JettyServer implements NiFiServer {
private UiExtensionMapping componentUiExtensions;
private Collection<WebAppContext> componentUiExtensionWebContexts;
@Deprecated
private Collection<WebAppContext> customUiWebContexts;
/**
* Creates and configures a new Jetty instance.
*
@ -222,29 +218,21 @@ public class JettyServer implements NiFiServer {
final ClassLoader frameworkClassLoader = getClass().getClassLoader();
final ClassLoader jettyClassLoader = frameworkClassLoader.getParent();
@Deprecated
final Map<String, String> customUiMappings = new HashMap<>();
// deploy the other wars
if (CollectionUtils.isNotEmpty(otherWars)) {
// hold onto to the web contexts for all ui extensions
customUiWebContexts = new ArrayList<>();
componentUiExtensionWebContexts = new ArrayList<>();
contentViewerWebContexts = new ArrayList<>();
// ui extension organized by component type
final Map<String, List<UiExtension>> componentUiExtensionsByType = new HashMap<>();
for (File war : otherWars) {
// see if this war is a custom processor ui
@Deprecated
List<String> customUiProcessorTypes = getWarExtensions(war, "META-INF/nifi-processor");
// identify all known extension types in the war
final Map<UiExtensionType, List<String>> uiExtensionInWar = new HashMap<>();
identifyUiExtensionsForComponents(uiExtensionInWar, war);
// only include wars that are for custom processor ui's
if (!customUiProcessorTypes.isEmpty() || !uiExtensionInWar.isEmpty()) {
if (!uiExtensionInWar.isEmpty()) {
// get the context path
String warName = StringUtils.substringBeforeLast(war.getName(), ".");
String warContextPath = String.format("/%s", warName);
@ -260,52 +248,41 @@ public class JettyServer implements NiFiServer {
// create the extension web app context
WebAppContext extensionUiContext = loadWar(war, warContextPath, narClassLoaderForWar);
// also store it by type so we can populate the appropriate initialization parameters
if (!customUiProcessorTypes.isEmpty()) {
customUiWebContexts.add(extensionUiContext);
// create the ui extensions
for (final Map.Entry<UiExtensionType, List<String>> entry : uiExtensionInWar.entrySet()) {
final UiExtensionType extensionType = entry.getKey();
final List<String> types = entry.getValue();
// @Deprecated - supported custom uis as init params to the web api
for (String customUiProcessorType : customUiProcessorTypes) {
// map the processor type to the custom ui path
customUiMappings.put(customUiProcessorType, warContextPath);
}
} else {
// create the ui extensions
for (final Map.Entry<UiExtensionType, List<String>> entry : uiExtensionInWar.entrySet()) {
final UiExtensionType extensionType = entry.getKey();
final List<String> types = entry.getValue();
if (UiExtensionType.ContentViewer.equals(extensionType)) {
// consider each content type identified
for (final String contentType : types) {
// map the content type to the context path
mimeMappings.put(contentType, warContextPath);
}
// this ui extension provides a content viewer
contentViewerWebContexts.add(extensionUiContext);
} else {
// consider each component type identified
for (final String componentType : types) {
logger.info(String.format("Loading UI extension [%s, %s] for %s", extensionType, warContextPath, types));
// record the extension definition
final UiExtension uiExtension = new UiExtension(extensionType, warContextPath);
// create if this is the first extension for this component type
List<UiExtension> componentUiExtensionsForType = componentUiExtensionsByType.get(componentType);
if (componentUiExtensionsForType == null) {
componentUiExtensionsForType = new ArrayList<>();
componentUiExtensionsByType.put(componentType, componentUiExtensionsForType);
}
// record this extension
componentUiExtensionsForType.add(uiExtension);
}
// this ui extension provides a component custom ui
componentUiExtensionWebContexts.add(extensionUiContext);
if (UiExtensionType.ContentViewer.equals(extensionType)) {
// consider each content type identified
for (final String contentType : types) {
// map the content type to the context path
mimeMappings.put(contentType, warContextPath);
}
// this ui extension provides a content viewer
contentViewerWebContexts.add(extensionUiContext);
} else {
// consider each component type identified
for (final String componentType : types) {
logger.info(String.format("Loading UI extension [%s, %s] for %s", extensionType, warContextPath, types));
// record the extension definition
final UiExtension uiExtension = new UiExtension(extensionType, warContextPath);
// create if this is the first extension for this component type
List<UiExtension> componentUiExtensionsForType = componentUiExtensionsByType.get(componentType);
if (componentUiExtensionsForType == null) {
componentUiExtensionsForType = new ArrayList<>();
componentUiExtensionsByType.put(componentType, componentUiExtensionsForType);
}
// record this extension
componentUiExtensionsForType.add(uiExtension);
}
// this ui extension provides a component custom ui
componentUiExtensionWebContexts.add(extensionUiContext);
}
}
@ -326,7 +303,6 @@ public class JettyServer implements NiFiServer {
// load the web api app
webApiContext = loadWar(webApiWar, "/nifi-api", frameworkClassLoader);
webApiContext.getInitParams().putAll(customUiMappings);
handlers.addHandler(webApiContext);
// load the content viewer app
@ -696,23 +672,6 @@ public class JettyServer implements NiFiServer {
// get the application context
final WebApplicationContext webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(webApiServletContext);
// @Deprecated
if (CollectionUtils.isNotEmpty(customUiWebContexts)) {
final NiFiWebContext niFiWebContext = webApplicationContext.getBean("nifiWebContext", NiFiWebContext.class);
for (final WebAppContext customUiContext : customUiWebContexts) {
// set the NiFi context in each custom ui servlet context
final ServletContext customUiServletContext = customUiContext.getServletHandler().getServletContext();
customUiServletContext.setAttribute("nifi-web-context", niFiWebContext);
// add the security filter to any custom ui wars
final FilterHolder securityFilter = webApiContext.getServletHandler().getFilter("springSecurityFilterChain");
if (securityFilter != null) {
customUiContext.addFilter(securityFilter, "/*", EnumSet.allOf(DispatcherType.class));
}
}
}
// component ui extensions
if (CollectionUtils.isNotEmpty(componentUiExtensionWebContexts)) {
final NiFiWebConfigurationContext configurationContext = webApplicationContext.getBean("nifiWebConfigurationContext", NiFiWebConfigurationContext.class);

View File

@ -16,28 +16,7 @@
*/
package org.apache.nifi.web;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.ws.rs.WebApplicationException;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.action.Action;
import org.apache.nifi.action.Component;
@ -194,6 +173,27 @@ import org.apache.nifi.web.util.SnippetUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.WebApplicationException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Implementation of NiFiServiceFacade that performs revision checking.
*/
@ -1612,6 +1612,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
@Override
public ControllerServiceEntity createControllerService(final String groupId, final ControllerServiceDTO controllerServiceDTO) {
// TODO - update once Controller Services can be scoped by Controller
final String normalizedGroupId = groupId == null ? controllerFacade.getRootGroupId() : groupId;
controllerServiceDTO.setParentGroupId(normalizedGroupId);
@ -1721,12 +1722,15 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
* @param reference ControllerServiceReference
* @return The entity
*/
private ControllerServiceReferencingComponentsEntity createControllerServiceReferencingComponentsEntity(final ControllerServiceReference reference) {
private ControllerServiceReferencingComponentsEntity createControllerServiceReferencingComponentsEntity(final ControllerServiceReference reference, final Set<String> lockedIds) {
final Set<String> referencingIds = new HashSet<>();
final Set<ControllerServiceNode> visited = new HashSet<>();
visited.add(reference.getReferencedComponent());
findControllerServiceReferencingComponentIdentifiers(reference, referencingIds, visited);
// TODO remove once we can update a read lock
referencingIds.removeAll(lockedIds);
return revisionManager.get(referencingIds, () -> {
final Map<String, Revision> referencingRevisions = new HashMap<>();
for (final ConfiguredComponent component : reference.getReferencingComponents()) {
@ -2614,8 +2618,9 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final Set<ControllerServiceNode> serviceNodes;
final Set<String> serviceIds;
if (groupId == null) {
// TODO - controller services scoped by the controller
serviceNodes = controllerServiceDAO.getControllerServices();
// TODO - update when controller services are scoped by the controller
final ProcessGroup group = processGroupDAO.getProcessGroup(controllerFacade.getRootGroupId());
serviceNodes = group.getControllerServices(true);
serviceIds = serviceNodes.stream().map(service -> service.getIdentifier()).collect(Collectors.toSet());
} else {
final ProcessGroup group = processGroupDAO.getProcessGroup(groupId);
@ -2629,7 +2634,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final ControllerServiceDTO dto = dtoFactory.createControllerServiceDto(serviceNode);
final ControllerServiceReference ref = serviceNode.getReferences();
final ControllerServiceReferencingComponentsEntity referencingComponentsEntity = createControllerServiceReferencingComponentsEntity(ref);
final ControllerServiceReferencingComponentsEntity referencingComponentsEntity = createControllerServiceReferencingComponentsEntity(ref, serviceIds);
dto.setReferencingComponents(referencingComponentsEntity.getControllerServiceReferencingComponents());
final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(serviceNode.getIdentifier()));
@ -2651,7 +2656,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final ControllerServiceDTO dto = dtoFactory.createControllerServiceDto(controllerService);
final ControllerServiceReference ref = controllerService.getReferences();
final ControllerServiceReferencingComponentsEntity referencingComponentsEntity = createControllerServiceReferencingComponentsEntity(ref);
final ControllerServiceReferencingComponentsEntity referencingComponentsEntity = createControllerServiceReferencingComponentsEntity(ref, Sets.newHashSet(controllerServiceId));
dto.setReferencingComponents(referencingComponentsEntity.getControllerServiceReferencingComponents());
return entityFactory.createControllerServiceEntity(dto, revision, accessPolicy);
@ -2678,7 +2683,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
return revisionManager.get(controllerServiceId, rev -> {
final ControllerServiceNode service = controllerServiceDAO.getControllerService(controllerServiceId);
final ControllerServiceReference ref = service.getReferences();
return createControllerServiceReferencingComponentsEntity(ref);
return createControllerServiceReferencingComponentsEntity(ref, Sets.newHashSet(controllerServiceId));
});
}

View File

@ -40,6 +40,14 @@ import org.apache.nifi.action.Operation;
import org.apache.nifi.action.component.details.FlowChangeExtensionDetails;
import org.apache.nifi.action.details.FlowChangeConfigureDetails;
import org.apache.nifi.admin.service.AuditService;
import org.apache.nifi.authorization.AccessDeniedException;
import org.apache.nifi.authorization.AuthorizationRequest;
import org.apache.nifi.authorization.AuthorizationResult;
import org.apache.nifi.authorization.AuthorizationResult.Result;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.RequestAction;
import org.apache.nifi.authorization.resource.Authorizable;
import org.apache.nifi.authorization.resource.ResourceFactory;
import org.apache.nifi.authorization.user.NiFiUser;
import org.apache.nifi.authorization.user.NiFiUserDetails;
import org.apache.nifi.authorization.user.NiFiUserUtils;
@ -74,8 +82,6 @@ import com.sun.jersey.core.util.MultivaluedMapImpl;
public class StandardNiFiWebConfigurationContext implements NiFiWebConfigurationContext {
private static final Logger logger = LoggerFactory.getLogger(StandardNiFiWebConfigurationContext.class);
public static final String CLIENT_ID_PARAM = "clientId";
public static final String REVISION_PARAM = "revision";
public static final String VERBOSE_PARAM = "verbose";
private NiFiProperties properties;
@ -85,15 +91,35 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
private ControllerServiceLookup controllerServiceLookup;
private ReportingTaskProvider reportingTaskProvider;
private AuditService auditService;
private Authorizer authorizer;
private void authorizeFlowAccess(final NiFiUser user) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final AuthorizationRequest request = new AuthorizationRequest.Builder()
.resource(ResourceFactory.getFlowResource())
.identity(user.getIdentity())
.anonymous(user.isAnonymous())
.accessAttempt(true)
.action(RequestAction.READ)
.build();
final AuthorizationResult result = authorizer.authorize(request);
if (!Result.Approved.equals(result.getResult())) {
final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied";
throw new AccessDeniedException(message);
}
});
}
@Override
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public ControllerService getControllerService(String serviceIdentifier) {
final NiFiUser user = NiFiUserUtils.getNiFiUser();
authorizeFlowAccess(user);
return controllerServiceLookup.getControllerService(serviceIdentifier);
}
@Override
// TODO - @PreAuthorize("hasAnyRole('ROLE_DFM')")
public void saveActions(final NiFiWebRequestContext requestContext, final Collection<ConfigurationAction> configurationActions) {
Objects.requireNonNull(configurationActions, "Actions cannot be null.");
@ -105,12 +131,30 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
Component componentType = null;
switch (requestContext.getExtensionType()) {
case ProcessorConfiguration:
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable authorizable = lookup.getProcessor(requestContext.getId());
authorizable.authorize(authorizer, RequestAction.WRITE);
});
componentType = Component.Processor;
break;
case ControllerServiceConfiguration:
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable authorizable = lookup.getControllerService(requestContext.getId());
authorizable.authorize(authorizer, RequestAction.WRITE);
});
componentType = Component.ControllerService;
break;
case ReportingTaskConfiguration:
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable authorizable = lookup.getReportingTask(requestContext.getId());
authorizable.authorize(authorizer, RequestAction.WRITE);
});
componentType = Component.ReportingTask;
break;
}
@ -159,33 +203,20 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
}
@Override
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public String getCurrentUserDn() {
String userIdentity = NiFiUser.ANONYMOUS.getIdentity();
final NiFiUser user = NiFiUserUtils.getNiFiUser();
if (user != null) {
userIdentity = user.getIdentity();
}
return userIdentity;
authorizeFlowAccess(user);
return user.getIdentity();
}
@Override
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public String getCurrentUserName() {
String userName = NiFiUser.ANONYMOUS.getIdentity();
final NiFiUser user = NiFiUserUtils.getNiFiUser();
if (user != null) {
userName = user.getUserName();
}
return userName;
authorizeFlowAccess(user);
return user.getUserName();
}
@Override
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public ComponentDetails getComponentDetails(final NiFiWebRequestContext requestContext) throws ResourceNotFoundException, ClusterRequestException {
final String id = requestContext.getId();
@ -220,7 +251,6 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
}
@Override
// TODO - @PreAuthorize("hasAnyRole('ROLE_DFM')")
public ComponentDetails setAnnotationData(final NiFiWebConfigurationRequestContext requestContext, final String annotationData)
throws ResourceNotFoundException, InvalidRevisionException, ClusterRequestException {
@ -288,6 +318,12 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
public ComponentDetails getComponentDetails(final NiFiWebRequestContext requestContext) {
final String id = requestContext.getId();
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable authorizable = lookup.getProcessor(id);
authorizable.authorize(authorizer, RequestAction.READ);
});
final ProcessorDTO processor;
if (properties.isClustered() && clusterCoordinator != null && clusterCoordinator.isConnected()) {
// create the request URL
@ -330,9 +366,16 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
@Override
public ComponentDetails setAnnotationData(final NiFiWebConfigurationRequestContext requestContext, final String annotationData) {
final NiFiUser user = NiFiUserUtils.getNiFiUser();
final Revision revision = requestContext.getRevision();
final String id = requestContext.getId();
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable authorizable = lookup.getProcessor(id);
authorizable.authorize(authorizer, RequestAction.WRITE);
});
final ProcessorDTO processor;
if (properties.isClustered()) {
// create the request URL
@ -385,8 +428,15 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
}
processor = entity.getComponent();
} else {
final ProcessorEntity entity = serviceFacade.setProcessorAnnotationData(revision, id, annotationData);
processor = entity.getComponent();
// claim the revision
serviceFacade.claimRevision(revision, user);
try {
final ProcessorEntity entity = serviceFacade.setProcessorAnnotationData(revision, id, annotationData);
processor = entity.getComponent();
} finally {
// ensure the revision is canceled.. if the operation succeed, this is a noop
serviceFacade.cancelRevision(revision);
}
}
// return the processor info
@ -416,6 +466,12 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
final String id = requestContext.getId();
final ControllerServiceDTO controllerService;
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable authorizable = lookup.getControllerService(id);
authorizable.authorize(authorizer, RequestAction.READ);
});
// if the lookup has the service that means we are either a node or
// the ncm and the service is available there only
if (controllerServiceLookup.getControllerService(id) != null) {
@ -464,17 +520,32 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
@Override
public ComponentDetails setAnnotationData(final NiFiWebConfigurationRequestContext requestContext, final String annotationData) {
final NiFiUser user = NiFiUserUtils.getNiFiUser();
final Revision revision = requestContext.getRevision();
final String id = requestContext.getId();
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable authorizable = lookup.getControllerService(id);
authorizable.authorize(authorizer, RequestAction.WRITE);
});
final ControllerServiceDTO controllerService;
if (controllerServiceLookup.getControllerService(id) != null) {
final ControllerServiceDTO controllerServiceDto = new ControllerServiceDTO();
controllerServiceDto.setId(id);
controllerServiceDto.setAnnotationData(annotationData);
final UpdateResult<ControllerServiceEntity> updateResult = serviceFacade.updateControllerService(revision, controllerServiceDto);
controllerService = updateResult.getResult().getComponent();
// claim the revision
serviceFacade.claimRevision(revision, user);
try {
// perform the update
final UpdateResult<ControllerServiceEntity> updateResult = serviceFacade.updateControllerService(revision, controllerServiceDto);
controllerService = updateResult.getResult().getComponent();
} finally {
// ensure the revision is canceled.. if the operation succeed, this is a noop
serviceFacade.cancelRevision(revision);
}
} else {
// if this is a standalone instance the service should have been found above... there should
// no cluster to replicate the request to
@ -561,6 +632,12 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
final String id = requestContext.getId();
final ReportingTaskDTO reportingTask;
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable authorizable = lookup.getReportingTask(id);
authorizable.authorize(authorizer, RequestAction.READ);
});
// if the provider has the service that means we are either a node or
// the ncm and the service is available there only
if (reportingTaskProvider.getReportingTaskNode(id) != null) {
@ -609,17 +686,31 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
@Override
public ComponentDetails setAnnotationData(final NiFiWebConfigurationRequestContext requestContext, final String annotationData) {
final NiFiUser user = NiFiUserUtils.getNiFiUser();
final Revision revision = requestContext.getRevision();
final String id = requestContext.getId();
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable authorizable = lookup.getReportingTask(id);
authorizable.authorize(authorizer, RequestAction.WRITE);
});
final ReportingTaskDTO reportingTask;
if (reportingTaskProvider.getReportingTaskNode(id) != null) {
final ReportingTaskDTO reportingTaskDto = new ReportingTaskDTO();
reportingTaskDto.setId(id);
reportingTaskDto.setAnnotationData(annotationData);
final UpdateResult<ReportingTaskEntity> updateResult = serviceFacade.updateReportingTask(revision, reportingTaskDto);
reportingTask = updateResult.getResult().getComponent();
// claim the revision
serviceFacade.claimRevision(revision, user);
try {
final UpdateResult<ReportingTaskEntity> updateResult = serviceFacade.updateReportingTask(revision, reportingTaskDto);
reportingTask = updateResult.getResult().getComponent();
} finally {
// ensure the revision is canceled.. if the operation succeed, this is a noop
serviceFacade.cancelRevision(revision);
}
} else {
// if this is a standalone instance the task should have been found above... there should
// no cluster to replicate the request to
@ -768,4 +859,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
this.reportingTaskProvider = reportingTaskProvider;
}
public void setAuthorizer(Authorizer authorizer) {
this.authorizer = authorizer;
}
}

View File

@ -1,331 +0,0 @@
/*
* 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.web;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.action.Action;
import org.apache.nifi.action.Component;
import org.apache.nifi.action.FlowChangeAction;
import org.apache.nifi.action.Operation;
import org.apache.nifi.action.component.details.FlowChangeExtensionDetails;
import org.apache.nifi.action.details.FlowChangeConfigureDetails;
import org.apache.nifi.admin.service.AuditService;
import org.apache.nifi.authorization.user.NiFiUser;
import org.apache.nifi.authorization.user.NiFiUserDetails;
import org.apache.nifi.authorization.user.NiFiUserUtils;
import org.apache.nifi.cluster.manager.NodeResponse;
import org.apache.nifi.controller.ControllerService;
import org.apache.nifi.controller.ControllerServiceLookup;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
import org.apache.nifi.web.api.dto.ProcessorDTO;
import org.apache.nifi.web.api.dto.RevisionDTO;
import org.apache.nifi.web.api.entity.ProcessorEntity;
import org.apache.nifi.web.util.ClientResponseUtils;
import org.apache.nifi.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import com.sun.jersey.core.util.MultivaluedMapImpl;
/**
* Implements the NiFiWebContext interface to support a context in both standalone and clustered environments.
*/
@Deprecated
public class StandardNiFiWebContext implements NiFiWebContext {
private static final Logger logger = LoggerFactory.getLogger(StandardNiFiWebContext.class);
public static final String CLIENT_ID_PARAM = "clientId";
public static final String REVISION_PARAM = "revision";
public static final String VERBOSE_PARAM = "verbose";
private NiFiProperties properties;
private NiFiServiceFacade serviceFacade;
private ControllerServiceLookup controllerServiceLookup;
private AuditService auditService;
@Override
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public ControllerService getControllerService(String serviceIdentifier) {
return controllerServiceLookup.getControllerService(serviceIdentifier);
}
@Override
// TODO - @PreAuthorize("hasRole('ROLE_DFM')")
public void saveActions(final Collection<ProcessorConfigurationAction> processorActions) {
Objects.requireNonNull(processorActions, "Actions cannot be null.");
// - when running standalone or cluster ncm - actions from custom UIs are stored locally
// - clustered nodes do not serve custom UIs directly to users so they should never be invoking this method
final Date now = new Date();
final Collection<Action> actions = new HashSet<>(processorActions.size());
for (final ProcessorConfigurationAction processorAction : processorActions) {
final FlowChangeExtensionDetails processorDetails = new FlowChangeExtensionDetails();
processorDetails.setType(processorAction.getProcessorType());
final FlowChangeConfigureDetails configureDetails = new FlowChangeConfigureDetails();
configureDetails.setName(processorAction.getName());
configureDetails.setPreviousValue(processorAction.getPreviousValue());
configureDetails.setValue(processorAction.getValue());
final FlowChangeAction action = new FlowChangeAction();
action.setTimestamp(now);
action.setSourceId(processorAction.getProcessorId());
action.setSourceName(processorAction.getProcessorName());
action.setSourceType(Component.Processor);
action.setOperation(Operation.Configure);
action.setUserIdentity(getCurrentUserDn());
action.setUserName(getCurrentUserName());
action.setComponentDetails(processorDetails);
action.setActionDetails(configureDetails);
actions.add(action);
}
try {
// record the operations
auditService.addActions(actions);
} catch (Throwable t) {
logger.warn("Unable to record actions: " + t.getMessage());
if (logger.isDebugEnabled()) {
logger.warn(StringUtils.EMPTY, t);
}
}
}
@Override
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public String getCurrentUserDn() {
String userIdentity = NiFiUser.ANONYMOUS.getIdentity();
final NiFiUser user = NiFiUserUtils.getNiFiUser();
if (user != null) {
userIdentity = user.getIdentity();
}
return userIdentity;
}
@Override
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public String getCurrentUserName() {
String userName = NiFiUser.ANONYMOUS.getIdentity();
final NiFiUser user = NiFiUserUtils.getNiFiUser();
if (user != null) {
userName = user.getUserName();
}
return userName;
}
@Override
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public ProcessorInfo getProcessor(final NiFiWebContextConfig config) throws ResourceNotFoundException, ClusterRequestException {
final Revision revision = config.getRevision();
final String processorId = config.getProcessorId();
if (StringUtils.isBlank(processorId)) {
throw new ResourceNotFoundException(String.format("Context config did not have a processor ID."));
}
final ProcessorDTO processor;
if (properties.isClustered()) {
// create the request URL
URI requestUrl;
try {
String path = "/nifi-api/processors/" + URLEncoder.encode(processorId, "UTF-8");
requestUrl = new URI(config.getScheme(), null, "localhost", 0, path, null, null);
} catch (final URISyntaxException | UnsupportedEncodingException use) {
throw new ClusterRequestException(use);
}
// set the request parameters
MultivaluedMap<String, String> parameters = new MultivaluedMapImpl();
parameters.add(CLIENT_ID_PARAM, revision.getClientId());
parameters.add(VERBOSE_PARAM, "true");
// replicate request
NodeResponse nodeResponse = null;
// check for issues replicating request
if (nodeResponse.hasThrowable()) {
ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
throw new ClusterRequestException(nodeResponse.getThrowable());
} else if (nodeResponse.getClientResponse().getStatus() == Response.Status.CONFLICT.getStatusCode()) {
ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
throw new InvalidRevisionException(String.format("Invalid revision specified %s", revision));
} else if (nodeResponse.getClientResponse().getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
throw new ResourceNotFoundException("Unable to find processor with id: " + processorId);
} else if (nodeResponse.getClientResponse().getStatus() != Response.Status.OK.getStatusCode()) {
ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
throw new ClusterRequestException("Method resulted in an unsuccessful HTTP response code: " + nodeResponse.getClientResponse().getStatus());
}
// return processor
ProcessorEntity entity = (ProcessorEntity) nodeResponse.getUpdatedEntity();
if (entity == null) {
entity = nodeResponse.getClientResponse().getEntity(ProcessorEntity.class);
}
processor = entity.getComponent();
} else {
processor = serviceFacade.getProcessor(processorId).getComponent();
}
// return the processor info
final ProcessorConfigDTO processorConfig = processor.getConfig();
return new ProcessorInfo.Builder()
.id(processor.getId())
.name(processor.getName())
.state(processor.getState())
.annotationData(processorConfig.getAnnotationData())
.properties(processorConfig.getProperties())
.validateErrors(processor.getValidationErrors()).build();
}
@Override
// TODO - @PreAuthorize("hasAnyRole('ROLE_DFM')")
public void setProcessorAnnotationData(final NiFiWebContextConfig config, String annotationData)
throws ResourceNotFoundException, InvalidRevisionException, ClusterRequestException {
final Revision revision = config.getRevision();
final String processorId = config.getProcessorId();
if (StringUtils.isBlank(processorId)) {
throw new ResourceNotFoundException(String.format("Context config did not have a processor ID."));
}
if (properties.isClustered()) {
// create the request URL
URI requestUrl;
try {
String path = "/nifi-api/processors/" + URLEncoder.encode(processorId, "UTF-8");
requestUrl = new URI(config.getScheme(), null, "localhost", 0, path, null, null);
} catch (final URISyntaxException | UnsupportedEncodingException use) {
throw new ClusterRequestException(use);
}
// create the revision
RevisionDTO revisionDto = new RevisionDTO();
revisionDto.setClientId(revision.getClientId());
revisionDto.setVersion(revision.getVersion());
// create the processor entity
ProcessorEntity processorEntity = new ProcessorEntity();
processorEntity.setRevision(revisionDto);
// create the processor dto
ProcessorDTO processorDto = new ProcessorDTO();
processorEntity.setComponent(processorDto);
processorDto.setId(processorId);
// create the processor configuration with the given annotation data
ProcessorConfigDTO configDto = new ProcessorConfigDTO();
processorDto.setConfig(configDto);
configDto.setAnnotationData(annotationData);
// set the content type to json
final Map<String, String> headers = getHeaders(config);
headers.put("Content-Type", "application/json");
// replicate request
NodeResponse nodeResponse = null;
// check for issues replicating request
if (nodeResponse.hasThrowable()) {
ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
throw new ClusterRequestException(nodeResponse.getThrowable());
} else if (nodeResponse.getClientResponse().getStatus() == Response.Status.CONFLICT.getStatusCode()) {
ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
throw new InvalidRevisionException(String.format("Invalid revision specified %s", revision));
} else if (nodeResponse.getClientResponse().getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
throw new ResourceNotFoundException("Unable to find processor with id: " + processorId);
} else if (nodeResponse.getClientResponse().getStatus() != Response.Status.OK.getStatusCode()) {
ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
throw new ClusterRequestException("Method resulted in an unsuccessful HTTP response code: " + nodeResponse.getClientResponse().getStatus());
}
} else {
serviceFacade.setProcessorAnnotationData(revision, processorId, annotationData);
}
}
/**
* Gets the headers for the request to replicate to each node while clustered.
*
* @param config config
* @return headers
*/
private Map<String, String> getHeaders(final NiFiWebContextConfig config) {
final Map<String, String> headers = new HashMap<>();
headers.put("Accept", "application/json,application/xml");
if (StringUtils.isNotBlank(config.getProxiedEntitiesChain())) {
headers.put("X-ProxiedEntitiesChain", config.getProxiedEntitiesChain());
}
// add the user's authorities (if any) to the headers
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
final Object userDetailsObj = authentication.getPrincipal();
if (userDetailsObj instanceof NiFiUserDetails) {
// serialize user details object
final String hexEncodedUserDetails = WebUtils.serializeObjectToHex((Serializable) userDetailsObj);
// put serialized user details in header
headers.put("X-ProxiedEntityUserDetails", hexEncodedUserDetails);
}
}
return headers;
}
public void setProperties(NiFiProperties properties) {
this.properties = properties;
}
public void setServiceFacade(NiFiServiceFacade serviceFacade) {
this.serviceFacade = serviceFacade;
}
public void setAuditService(AuditService auditService) {
this.auditService = auditService;
}
public void setControllerServiceLookup(ControllerServiceLookup controllerServiceLookup) {
this.controllerServiceLookup = controllerServiceLookup;
}
}

View File

@ -332,12 +332,12 @@ public class ControllerServiceResource extends ApplicationResource {
/**
* Clears the state for a controller service.
*
* @param revisionEntity The revision is used to verify the client is working with the latest version of the flow.
* @param httpServletRequest servlet request
* @param id The id of the controller service
* @return a componentStateEntity
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}/state/clear-requests")
// TODO - @PreAuthorize("hasAnyRole('ROLE_DFM')")
@ -359,10 +359,6 @@ public class ControllerServiceResource extends ApplicationResource {
)
public Response clearState(
@Context HttpServletRequest httpServletRequest,
@ApiParam(
value = "The revision used to verify the client is working with the latest version of the flow.",
required = true
) final ComponentStateEntity revisionEntity,
@ApiParam(
value = "The controller service id.",
required = true

View File

@ -340,15 +340,14 @@ public class ProcessorResource extends ApplicationResource {
* Clears the state for a processor.
*
* @param httpServletRequest servlet request
* @param revisionEntity The revision is used to verify the client is working with the latest version of the flow.
* @param id The id of the processor
* @return a componentStateEntity
* @throws InterruptedException if interrupted
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Path("/{id}/state/clear-requests")
@Path("{id}/state/clear-requests")
// TODO - @PreAuthorize("hasAnyRole('ROLE_DFM')")
@ApiOperation(
value = "Clears the state for a processor",
@ -368,23 +367,14 @@ public class ProcessorResource extends ApplicationResource {
)
public Response clearState(
@Context final HttpServletRequest httpServletRequest,
@ApiParam(
value = "The revision used to verify the client is working with the latest version of the flow.",
required = true
) final ComponentStateEntity revisionEntity,
@ApiParam(
value = "The processor id.",
required = true
)
@PathParam("id") final String id) throws InterruptedException {
// ensure the revision was specified
if (revisionEntity == null) {
throw new IllegalArgumentException("Revision must be specified.");
}
if (isReplicateRequest()) {
return replicate(HttpMethod.POST, revisionEntity);
return replicate(HttpMethod.POST);
}
// handle expects request (usually from the cluster manager)

View File

@ -16,27 +16,12 @@
*/
package org.apache.nifi.web.api;
import java.net.URI;
import java.util.List;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;
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.commons.lang3.StringUtils;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.RequestAction;
@ -56,12 +41,25 @@ import org.apache.nifi.web.api.entity.ReportingTaskEntity;
import org.apache.nifi.web.api.request.ClientIdParameter;
import org.apache.nifi.web.api.request.LongParameter;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;
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 javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.net.URI;
import java.util.List;
import java.util.Set;
/**
* RESTful endpoint for managing a Reporting Task.
@ -320,12 +318,12 @@ public class ReportingTaskResource extends ApplicationResource {
/**
* Clears the state for a reporting task.
*
* @param revisionEntity The revision is used to verify the client is working with the latest version of the flow.
* @param httpServletRequest servlet request
* @param id The id of the reporting task
* @return a componentStateEntity
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}/state/clear-requests")
// TODO - @PreAuthorize("hasAnyRole('ROLE_DFM')")
@ -347,10 +345,6 @@ public class ReportingTaskResource extends ApplicationResource {
)
public Response clearState(
@Context final HttpServletRequest httpServletRequest,
@ApiParam(
value = "The revision used to verify the client is working with the latest version of the flow.",
required = true
) final ComponentStateEntity revisionEntity,
@ApiParam(
value = "The reporting task id.",
required = true
@ -358,7 +352,7 @@ public class ReportingTaskResource extends ApplicationResource {
@PathParam("id") final String id) {
if (isReplicateRequest()) {
return replicate(HttpMethod.POST, revisionEntity);
return replicate(HttpMethod.POST);
}
// handle expects request (usually from the cluster manager)

View File

@ -140,7 +140,6 @@
<property name="heartbeatMonitor" ref="heartbeatMonitor" />
</bean>
<!-- component ui extension configuration context -->
<bean id="nifiWebConfigurationContext" class="org.apache.nifi.web.StandardNiFiWebConfigurationContext">
<property name="serviceFacade" ref="serviceFacade"/>
@ -150,6 +149,7 @@
<property name="auditService" ref="auditService"/>
<property name="controllerServiceLookup" ref="controllerServiceProvider"/>
<property name="reportingTaskProvider" ref="reportingTaskProvider"/>
<property name="authorizer" ref="authorizer"/>
</bean>
<!-- rest endpoints -->

View File

@ -510,7 +510,7 @@
}
// if this descriptor identifies a controller service, provide a way to create one
if (nf.Common.isDefinedAndNotNull(propertyDescriptor.identifiesControllerService) && nf.Common.isDefinedAndNotNull(configurationOptions.groupId)) {
if (nf.Common.isDefinedAndNotNull(propertyDescriptor.identifiesControllerService)) {
options.push({
text: 'Create new service...',
value: undefined,
@ -887,15 +887,21 @@
// build the controller service entity
var controllerServiceEntity = {
'controllerService': {
'component': {
'type': newControllerServiceType
}
};
// determine the appropriate uri for creating the controller service
var uri = '../nifi-api/controller/controller-services';
if (nf.Common.isDefinedAndNotNull(configurationOptions.groupId)) {
uri = '../nifi-api/process-groups/' + encodeURIComponent(configurationOptions.groupId) + '/controller-services';
}
// add the new controller service
$.ajax({
type: 'POST',
url: '../nifi-api/process-groups/' + encodeURIComponent(configurationOptions.groupId) + '/controller-services/node',
url: uri,
data: JSON.stringify(controllerServiceEntity),
dataType: 'json',
contentType: 'application/json'
@ -913,7 +919,7 @@
// add a row for the new property
var data = grid.getData();
data.updateItem(item.id, $.extend(item, {
value: response.controllerService.id
value: response.component.id
}));
// close the dialog

View File

@ -1348,10 +1348,10 @@ nf.ControllerService = (function () {
* @param {type} propertyName
*/
var getControllerServicePropertyDescriptor = function (propertyName) {
var details = $('#controller-service-configuration').data('controllerServiceDetails');
var controllerServiceEntity = $('#controller-service-configuration').data('controllerServiceDetails');
return $.ajax({
type: 'GET',
url: details.uri + '/descriptors',
url: controllerServiceEntity.component.uri + '/descriptors',
data: {
propertyName: propertyName
},
@ -1633,7 +1633,7 @@ nf.ControllerService = (function () {
// initialize the property table
$('#controller-service-properties').propertytable('destroy').propertytable({
readOnly: false,
groupId: nf.Canvas.getGroupId(),
// groupId: controllerServiceEntity.component.parentGroupId,
dialogContainer: '#new-controller-service-property-container',
descriptorDeferred: getControllerServicePropertyDescriptor,
goToServiceDeferred: function () {
@ -1722,7 +1722,7 @@ nf.ControllerService = (function () {
$('#shell-close-button').click();
// show the custom ui
nf.CustomUi.showCustomUi($('#controller-service-id').text(), controllerService.customUiUrl, true).done(function () {
nf.CustomUi.showCustomUi(controllerServiceEntity, controllerService.customUiUrl, true).done(function () {
// once the custom ui is closed, reload the controller service
reloadControllerServiceAndReferencingComponents(serviceTable, controllerService);
@ -1854,7 +1854,7 @@ nf.ControllerService = (function () {
$('#shell-close-button').click();
// show the custom ui
nf.CustomUi.showCustomUi(controllerService.id, controllerService.customUiUrl, false).done(function () {
nf.CustomUi.showCustomUi(controllerServiceEntity, controllerService.customUiUrl, false).done(function () {
nf.Settings.showSettings();
});
}

View File

@ -21,22 +21,21 @@ nf.CustomUi = {
/**
* Shows the custom ui.
*
* @argument {string} id The component id
* @argument {string} uri The uri for the custom ui
* @argument {object} entity The component id
* @arugment {string} uri The custom ui uri
* @argument {boolean} editable Whether the custom ui should support editing
*/
showCustomUi: function (id, uri, editable) {
showCustomUi: function (entity, uri, editable) {
return $.Deferred(function (deferred) {
nf.Common.getAccessToken('../nifi-api/access/ui-extension-token').done(function (uiExtensionToken) {
// record the processor id
$('#shell-close-button');
var revision = nf.Client.getRevision();
var revision = nf.Client.getRevision(entity);
// build the customer ui params
var customUiParams = {
'id': id,
'processorId': id, // deprecated
'id': entity.id,
'revision': revision.version,
'clientId': revision.clientId,
'editable': editable

View File

@ -590,12 +590,13 @@ nf.ProcessorConfiguration = (function () {
}
// once everything is loaded, show the dialog
$.when.apply(window, requests).done(function (processorResponse, historyResponse) {
// get the updated processor
processor = processorResponse[0].component;
$.when.apply(window, requests).done(function (processorResult, historyResult) {
// get the updated processor'
var processorResponse = processorResult[0];
processor = processorResponse.component;
// get the processor history
var processorHistory = historyResponse[0].componentHistory;
var processorHistory = historyResult[0].componentHistory;
// record the processor details
$('#processor-configuration').data('processorDetails', processor);
@ -747,7 +748,7 @@ nf.ProcessorConfiguration = (function () {
$('#processor-configuration').modal('hide');
// show the custom ui
nf.CustomUi.showCustomUi($('#processor-id').text(), processor.config.customUiUrl, true).done(function () {
nf.CustomUi.showCustomUi(processorResponse, processor.config.customUiUrl, true).done(function () {
// once the custom ui is closed, reload the processor
nf.Processor.reload(processor);

View File

@ -355,7 +355,6 @@ nf.ReportingTask = (function () {
// initialize the property table
$('#reporting-task-properties').propertytable({
readOnly: false,
groupId: nf.Canvas.getGroupId(),
dialogContainer: '#new-reporting-task-property-container',
descriptorDeferred: getReportingTaskPropertyDescriptor,
goToServiceDeferred: goToServiceFromProperty
@ -377,7 +376,6 @@ nf.ReportingTask = (function () {
// initialize the property table
$('#reporting-task-properties').propertytable('destroy').propertytable({
readOnly: false,
groupId: nf.Canvas.getGroupId(),
dialogContainer: '#new-reporting-task-property-container',
descriptorDeferred: getReportingTaskPropertyDescriptor,
goToServiceDeferred: goToServiceFromProperty
@ -503,7 +501,7 @@ nf.ReportingTask = (function () {
$('#shell-close-button').click();
// show the custom ui
nf.CustomUi.showCustomUi($('#reporting-task-id').text(), reportingTask.customUiUrl, true).done(function () {
nf.CustomUi.showCustomUi(reportingTaskEntity, reportingTask.customUiUrl, true).done(function () {
// once the custom ui is closed, reload the reporting task
nf.ReportingTask.reload(reportingTaskEntity.id).done(function (response) {
nf.ControllerService.reloadReferencedServices(getControllerServicesTable(), response.reportingTask);
@ -634,7 +632,7 @@ nf.ReportingTask = (function () {
$('#shell-close-button').click();
// show the custom ui
nf.CustomUi.showCustomUi(reportingTask.id, reportingTask.customUiUrl, false).done(function() {
nf.CustomUi.showCustomUi(reportingTaskEntity, reportingTask.customUiUrl, false).done(function() {
nf.Settings.showSettings();
});
}

View File

@ -209,10 +209,10 @@ nf.ProcessorDetails = (function () {
});
// show the dialog once we have the processor and its history
$.when(getProcessor, getProcessorHistory).done(function (processorResponse, historyResponse) {
var processorResponse = processorResponse[0];
$.when(getProcessor, getProcessorHistory).done(function (processorResult, historyResult) {
var processorResponse = processorResult[0];
var processor = processorResponse.component;
var historyResponse = historyResponse[0];
var historyResponse = historyResult[0];
var history = historyResponse.componentHistory;
// load the properties
@ -238,7 +238,7 @@ nf.ProcessorDetails = (function () {
$('#processor-details').modal('hide');
// show the custom ui
nf.CustomUi.showCustomUi(processor.id, processor.config.customUiUrl, false);
nf.CustomUi.showCustomUi(processorResponse, processor.config.customUiUrl, false);
}
}
});