diff --git a/.travis.yml b/.travis.yml
index 811a4c2d0a..db579ce0f5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,4 +14,4 @@ before_install:
- sed -e "s/^\\(127\\.0\\.0\\.1.*\\)/\\1 $(hostname | cut -c1-63)/" /etc/hosts | sudo tee /etc/hosts
- sed -i.bak -e 's|https://nexus.codehaus.org/snapshots/|https://oss.sonatype.org/content/repositories/codehaus-snapshots/|g' ~/.m2/settings.xml
-script: mvn clean install -Pcontrib-check
+script: mvn clean install -T4
diff --git a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
index ea3b9593e8..73a54c59e1 100644
--- a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
+++ b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
@@ -508,7 +508,7 @@ public class NiFiProperties extends Properties {
/**
* @return the user authorizers file
*/
- public File getAuthorizerConfiguraitonFile() {
+ public File getAuthorizerConfigurationFile() {
final String value = getProperty(AUTHORIZER_CONFIGURATION_FILE);
if (StringUtils.isBlank(value)) {
return new File(DEFAULT_AUTHORIZER_CONFIGURATION_FILE);
@@ -520,7 +520,7 @@ public class NiFiProperties extends Properties {
/**
* @return the user authorities file
*/
- public File getLoginIdentityProviderConfiguraitonFile() {
+ public File getLoginIdentityProviderConfigurationFile() {
final String value = getProperty(LOGIN_IDENTITY_PROVIDER_CONFIGURATION_FILE);
if (StringUtils.isBlank(value)) {
return new File(DEFAULT_LOGIN_IDENTITY_PROVIDER_CONFIGURATION_FILE);
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java
index 378c805ee5..e4d7318a87 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java
@@ -126,7 +126,7 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, Autho
}
private Authorizers loadAuthorizersConfiguration() throws Exception {
- final File authorizersConfigurationFile = properties.getAuthorizerConfiguraitonFile();
+ final File authorizersConfigurationFile = properties.getAuthorizerConfigurationFile();
// load the authorizers from the specified file
if (authorizersConfigurationFile.exists()) {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ComponentEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ComponentEntity.java
index 2f7798ee73..231ce5339d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ComponentEntity.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ComponentEntity.java
@@ -31,7 +31,6 @@ public class ComponentEntity extends Entity {
private RevisionDTO revision;
private String id;
- private String uri;
private PositionDTO position;
private AccessPolicyDTO accessPolicy;
@@ -65,22 +64,6 @@ public class ComponentEntity extends Entity {
this.id = id;
}
- /**
- * The uri for linking to this component in this NiFi.
- *
- * @return The uri
- */
- @ApiModelProperty(
- value = "The URI for futures requests to the component."
- )
- public String getUri() {
- return uri;
- }
-
- public void setUri(String uri) {
- this.uri = uri;
- }
-
/**
* The position of this component in the UI if applicable, null otherwise.
*
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml
index 6142066d69..b0d4dda454 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml
@@ -210,6 +210,11 @@
nifi-framework-authorization
provided
+
+ org.apache.nifi
+ nifi-authorizer
+ provided
+
javax.servlet
javax.servlet-api
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java
index 9ea31dc8e0..caa7b36729 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java
@@ -16,8 +16,25 @@
*/
package org.apache.nifi.web.api;
-import java.net.URI;
-import java.util.Set;
+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;
+import org.apache.nifi.authorization.resource.Authorizable;
+import org.apache.nifi.web.NiFiServiceFacade;
+import org.apache.nifi.web.Revision;
+import org.apache.nifi.web.UpdateResult;
+import org.apache.nifi.web.api.dto.ConnectionDTO;
+import org.apache.nifi.web.api.dto.FlowFileSummaryDTO;
+import org.apache.nifi.web.api.dto.ListingRequestDTO;
+import org.apache.nifi.web.api.entity.ConnectionEntity;
+import org.apache.nifi.web.api.request.ClientIdParameter;
+import org.apache.nifi.web.api.request.LongParameter;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
@@ -33,27 +50,8 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.authorization.Authorizer;
-import org.apache.nifi.authorization.RequestAction;
-import org.apache.nifi.authorization.resource.Authorizable;
-import org.apache.nifi.web.NiFiServiceFacade;
-import org.apache.nifi.web.Revision;
-import org.apache.nifi.web.UpdateResult;
-import org.apache.nifi.web.api.dto.ConnectionDTO;
-import org.apache.nifi.web.api.dto.FlowFileSummaryDTO;
-import org.apache.nifi.web.api.dto.ListingRequestDTO;
-import org.apache.nifi.web.api.entity.ConnectionEntity;
-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 java.net.URI;
+import java.util.Set;
/**
* RESTful endpoint for managing a Connection.
@@ -286,7 +284,7 @@ public class ConnectionResource extends ApplicationResource {
// generate the response
if (updateResult.isNew()) {
- return clusterContext(generateCreatedResponse(URI.create(entity.getUri()), entity)).build();
+ return clusterContext(generateCreatedResponse(URI.create(entity.getComponent().getUri()), entity)).build();
} else {
return clusterContext(generateOkResponse(entity)).build();
}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
index f1dc6520da..e05552ff3e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
@@ -16,30 +16,12 @@
*/
package org.apache.nifi.web.api;
-import java.net.URI;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-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;
@@ -66,12 +48,28 @@ import org.apache.nifi.web.api.request.LongParameter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-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.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* RESTful endpoint for managing a Controller Service.
@@ -369,14 +367,17 @@ public class ControllerServiceResource extends ApplicationResource {
return replicate(HttpMethod.POST);
}
- // handle expects request (usually from the cluster manager)
final boolean validationPhase = isValidationPhase(httpServletRequest);
- if (validationPhase) {
+ if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable controllerService = lookup.getControllerService(id);
controllerService.authorize(authorizer, RequestAction.WRITE);
});
+ }
+
+ // handle expects request (usually from the cluster manager)
+ if (validationPhase) {
serviceFacade.verifyCanClearControllerServiceState(id);
return generateContinueResponse().build();
}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FunnelResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FunnelResource.java
index 7ec395c427..acaa2b4baf 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FunnelResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FunnelResource.java
@@ -16,8 +16,23 @@
*/
package org.apache.nifi.web.api;
-import java.net.URI;
-import java.util.Set;
+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;
+import org.apache.nifi.authorization.resource.Authorizable;
+import org.apache.nifi.web.NiFiServiceFacade;
+import org.apache.nifi.web.Revision;
+import org.apache.nifi.web.UpdateResult;
+import org.apache.nifi.web.api.dto.FunnelDTO;
+import org.apache.nifi.web.api.entity.FunnelEntity;
+import org.apache.nifi.web.api.request.ClientIdParameter;
+import org.apache.nifi.web.api.request.LongParameter;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
@@ -33,25 +48,8 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.authorization.Authorizer;
-import org.apache.nifi.authorization.RequestAction;
-import org.apache.nifi.authorization.resource.Authorizable;
-import org.apache.nifi.web.NiFiServiceFacade;
-import org.apache.nifi.web.Revision;
-import org.apache.nifi.web.UpdateResult;
-import org.apache.nifi.web.api.dto.FunnelDTO;
-import org.apache.nifi.web.api.entity.FunnelEntity;
-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 java.net.URI;
+import java.util.Set;
/**
* RESTful endpoint for managing a Funnel.
@@ -316,7 +314,7 @@ public class FunnelResource extends ApplicationResource {
revision,
lookup -> {
final Authorizable funnel = lookup.getFunnel(id);
- funnel.authorize(authorizer, RequestAction.READ);
+ funnel.authorize(authorizer, RequestAction.WRITE);
},
() -> serviceFacade.verifyDeleteFunnel(id),
() -> {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
index 912366e952..dccb1f3163 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
@@ -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;
@@ -57,12 +42,25 @@ import org.apache.nifi.web.api.entity.PropertyDescriptorEntity;
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 Processor.
@@ -377,13 +375,17 @@ public class ProcessorResource extends ApplicationResource {
return replicate(HttpMethod.POST);
}
- // handle expects request (usually from the cluster manager)
- if (isValidationPhase(httpServletRequest)) {
+ final boolean isValidationPhase = isValidationPhase(httpServletRequest);
+ if (isValidationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable processor = lookup.getProcessor(id);
processor.authorize(authorizer, RequestAction.WRITE);
});
+ }
+
+ // handle expects request (usually from the cluster manager)
+ if (isValidationPhase) {
serviceFacade.verifyCanClearProcessorState(id);
return generateContinueResponse().build();
}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
index 66ddfbf1ff..f1fa30454b 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
@@ -355,13 +355,17 @@ public class ReportingTaskResource extends ApplicationResource {
return replicate(HttpMethod.POST);
}
- // handle expects request (usually from the cluster manager)
- if (isValidationPhase(httpServletRequest)) {
+ final boolean isValidationPhase = isValidationPhase(httpServletRequest);
+ if (isValidationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable reportingTask = lookup.getReportingTask(id);
reportingTask.authorize(authorizer, RequestAction.WRITE);
});
+ }
+
+ // handle expects request (usually from the cluster manager)
+ if (isValidationPhase) {
serviceFacade.verifyCanClearReportingTaskState(id);
return generateContinueResponse().build();
}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/NiFiWebApiTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/NiFiWebApiTest.java
index eecc28ac5f..8067491fb6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/NiFiWebApiTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/NiFiWebApiTest.java
@@ -20,18 +20,19 @@ import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.ClientResponse.Status;
import org.apache.nifi.connectable.ConnectableType;
-import org.apache.nifi.integration.accesscontrol.DfmAccessControlTest;
import org.apache.nifi.integration.util.NiFiTestUser;
import org.apache.nifi.integration.util.SourceTestProcessor;
import org.apache.nifi.integration.util.TerminationTestProcessor;
import org.apache.nifi.web.api.dto.ConnectableDTO;
import org.apache.nifi.web.api.dto.ConnectionDTO;
+import org.apache.nifi.web.api.dto.FunnelDTO;
import org.apache.nifi.web.api.dto.LabelDTO;
import org.apache.nifi.web.api.dto.PortDTO;
import org.apache.nifi.web.api.dto.ProcessGroupDTO;
import org.apache.nifi.web.api.dto.ProcessorDTO;
import org.apache.nifi.web.api.dto.RevisionDTO;
import org.apache.nifi.web.api.entity.ConnectionEntity;
+import org.apache.nifi.web.api.entity.FunnelEntity;
import org.apache.nifi.web.api.entity.LabelEntity;
import org.apache.nifi.web.api.entity.PortEntity;
import org.apache.nifi.web.api.entity.ProcessGroupEntity;
@@ -48,11 +49,15 @@ import java.util.Set;
public class NiFiWebApiTest {
public static void populateFlow(Client client, String baseUrl, String clientId) throws Exception {
- NiFiTestUser dfm = new NiFiTestUser(client, DfmAccessControlTest.DFM_USER_DN);
+
+ }
+
+ public static void populateFlow(Client client, String baseUrl, NiFiTestUser user, String clientId) throws Exception {
// -----------------------------------------------
- // Create a local selection processor
+ // Create a source processor
// -----------------------------------------------
+
// create the local selection processor
ProcessorDTO processorDTO = new ProcessorDTO();
processorDTO.setName("Pick up");
@@ -69,7 +74,7 @@ public class NiFiWebApiTest {
processorEntity.setComponent(processorDTO);
// add the processor
- ClientResponse response = dfm.testPost(baseUrl + "/controller/process-groups/root/processors", processorEntity);
+ ClientResponse response = user.testPost(baseUrl + "/process-groups/root/processors", processorEntity);
// ensure a successful response
if (Status.CREATED.getStatusCode() != response.getStatusInfo().getStatusCode()) {
@@ -87,6 +92,7 @@ public class NiFiWebApiTest {
// -----------------------------------------------
// Create a termination processor
// -----------------------------------------------
+
// create the termination processor
processorDTO = new ProcessorDTO();
processorDTO.setName("End");
@@ -98,7 +104,7 @@ public class NiFiWebApiTest {
processorEntity.setComponent(processorDTO);
// add the processor
- response = dfm.testPost(baseUrl + "/controller/process-groups/root/processors", processorEntity);
+ response = user.testPost(baseUrl + "/process-groups/root/processors", processorEntity);
// ensure a successful response
if (Status.CREATED.getStatusCode() != response.getStatusInfo().getStatusCode()) {
@@ -116,6 +122,7 @@ public class NiFiWebApiTest {
// -----------------------------------------------
// Connect the two processors
// -----------------------------------------------
+
ConnectableDTO source = new ConnectableDTO();
source.setId(localSelectionId);
source.setType(ConnectableType.PROCESSOR.name());
@@ -140,7 +147,7 @@ public class NiFiWebApiTest {
connectionEntity.setComponent(connectionDTO);
// add the processor
- response = dfm.testPost(baseUrl + "/controller/process-groups/root/connections", connectionEntity);
+ response = user.testPost(baseUrl + "/process-groups/root/connections", connectionEntity);
// ensure a successful response
if (Status.CREATED.getStatusCode() != response.getStatusInfo().getStatusCode()) {
@@ -153,6 +160,7 @@ public class NiFiWebApiTest {
// -----------------------------------------------
// Create a label
// -----------------------------------------------
+
// create the label
LabelDTO labelDTO = new LabelDTO();
labelDTO.setLabel("Test label");
@@ -163,7 +171,30 @@ public class NiFiWebApiTest {
labelEntity.setComponent(labelDTO);
// add the label
- response = dfm.testPost(baseUrl + "/controller/process-groups/root/labels", labelEntity);
+ response = user.testPost(baseUrl + "/process-groups/root/labels", labelEntity);
+
+ // ensure a successful response
+ if (Status.CREATED.getStatusCode() != response.getStatusInfo().getStatusCode()) {
+ // since it was unable to create the component attempt to extract an
+ // error message from the response body
+ final String responseEntity = response.getEntity(String.class);
+ throw new Exception("Unable to populate initial flow: " + responseEntity);
+ }
+
+ // -----------------------------------------------
+ // Create a funnel
+ // -----------------------------------------------
+
+ // create the funnel
+ FunnelDTO funnelDTO = new FunnelDTO();
+
+ // create the funnel entity
+ FunnelEntity funnelEntity = new FunnelEntity();
+ funnelEntity.setRevision(revision);
+ funnelEntity.setComponent(funnelDTO);
+
+ // add the funnel
+ response = user.testPost(baseUrl + "/process-groups/root/funnels", funnelEntity);
// ensure a successful response
if (Status.CREATED.getStatusCode() != response.getStatusInfo().getStatusCode()) {
@@ -176,6 +207,7 @@ public class NiFiWebApiTest {
// -----------------------------------------------
// Create a process group
// -----------------------------------------------
+
// create the process group
ProcessGroupDTO processGroup = new ProcessGroupDTO();
processGroup.setName("group name");
@@ -186,7 +218,7 @@ public class NiFiWebApiTest {
processGroupEntity.setComponent(processGroup);
// add the process group
- response = dfm.testPost(baseUrl + "/controller/process-groups/root/process-group-references", processGroupEntity);
+ response = user.testPost(baseUrl + "/process-groups/root/process-groups", processGroupEntity);
// ensure a successful response
if (Status.CREATED.getStatusCode() != response.getStatusInfo().getStatusCode()) {
@@ -199,6 +231,7 @@ public class NiFiWebApiTest {
// -----------------------------------------------
// Create an input port
// -----------------------------------------------
+
// create the input port
PortDTO inputPort = new PortDTO();
inputPort.setName("input");
@@ -209,7 +242,7 @@ public class NiFiWebApiTest {
inputPortEntity.setComponent(inputPort);
// add the input port
- response = dfm.testPost(baseUrl + "/controller/process-groups/root/input-ports", inputPortEntity);
+ response = user.testPost(baseUrl + "/process-groups/root/input-ports", inputPortEntity);
// ensure a successful response
if (Status.CREATED.getStatusCode() != response.getStatusInfo().getStatusCode()) {
@@ -222,6 +255,7 @@ public class NiFiWebApiTest {
// -----------------------------------------------
// Create a output ports
// -----------------------------------------------
+
// create the process group
PortDTO outputPort = new PortDTO();
outputPort.setName("output");
@@ -232,7 +266,7 @@ public class NiFiWebApiTest {
outputPortEntity.setComponent(outputPort);
// add the output port
- response = dfm.testPost(baseUrl + "/controller/process-groups/root/output-ports", outputPortEntity);
+ response = user.testPost(baseUrl + "/process-groups/root/output-ports", outputPortEntity);
// ensure a successful response
if (Status.CREATED.getStatusCode() != response.getStatusInfo().getStatusCode()) {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessControlHelper.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessControlHelper.java
new file mode 100644
index 0000000000..2576253735
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessControlHelper.java
@@ -0,0 +1,120 @@
+/*
+ * 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.integration.accesscontrol;
+
+import org.apache.nifi.integration.NiFiWebApiTest;
+import org.apache.nifi.integration.util.NiFiTestAuthorizer;
+import org.apache.nifi.integration.util.NiFiTestServer;
+import org.apache.nifi.integration.util.NiFiTestUser;
+import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.NarClassLoaders;
+import org.apache.nifi.util.NiFiProperties;
+
+import java.io.File;
+
+/**
+ * Access control test for the dfm user.
+ */
+public class AccessControlHelper {
+
+ public static final String NONE_CLIENT_ID = "client-id";
+ public static final String READ_CLIENT_ID = "r-client-id";
+ public static final String WRITE_CLIENT_ID = "w-client-id";
+ public static final String READ_WRITE_CLIENT_ID = "rw-client-id";
+
+ private NiFiTestUser readUser;
+ private NiFiTestUser writeUser;
+ private NiFiTestUser readWriteUser;
+ private NiFiTestUser noneUser;
+
+ private static final String CONTEXT_PATH = "/nifi-api";
+
+ private String flowXmlPath;
+ private NiFiTestServer server;
+ private String baseUrl;
+
+ public AccessControlHelper(final String flowXmlPath) throws Exception {
+ this.flowXmlPath = flowXmlPath;
+
+ // look for the flow.xml and toss it
+ File flow = new File(flowXmlPath);
+ if (flow.exists()) {
+ flow.delete();
+ }
+
+ // configure the location of the nifi properties
+ File nifiPropertiesFile = new File("src/test/resources/access-control/nifi.properties");
+ System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, nifiPropertiesFile.getAbsolutePath());
+
+ // update the flow.xml property
+ NiFiProperties props = NiFiProperties.getInstance();
+ props.setProperty(NiFiProperties.FLOW_CONFIGURATION_FILE, flowXmlPath);
+
+ // load extensions
+ NarClassLoaders.load(props);
+ ExtensionManager.discoverExtensions();
+
+ // start the server
+ server = new NiFiTestServer("src/main/webapp", CONTEXT_PATH);
+ server.startServer();
+ server.loadFlow();
+
+ // get the base url
+ baseUrl = server.getBaseUrl() + CONTEXT_PATH;
+
+ // create the users - user purposefully decoupled from clientId (same user different browsers tabs)
+ readUser = new NiFiTestUser(server.getClient(), NiFiTestAuthorizer.READ_USER_DN);
+ writeUser = new NiFiTestUser(server.getClient(), NiFiTestAuthorizer.WRITE_USER_DN);
+ readWriteUser = new NiFiTestUser(server.getClient(), NiFiTestAuthorizer.READ_WRITE_USER_DN);
+ noneUser = new NiFiTestUser(server.getClient(), NiFiTestAuthorizer.NONE_USER_DN);
+
+ // populate the initial data flow
+ NiFiWebApiTest.populateFlow(server.getClient(), baseUrl, readWriteUser, READ_WRITE_CLIENT_ID);
+ }
+
+ public NiFiTestUser getReadUser() {
+ return readUser;
+ }
+
+ public NiFiTestUser getWriteUser() {
+ return writeUser;
+ }
+
+ public NiFiTestUser getReadWriteUser() {
+ return readWriteUser;
+ }
+
+ public NiFiTestUser getNoneUser() {
+ return noneUser;
+ }
+
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+ public void cleanup() throws Exception {
+ // shutdown the server
+ server.shutdownServer();
+ server = null;
+
+ // look for the flow.xml and toss it
+ File flow = new File(flowXmlPath);
+ if (flow.exists()) {
+ flow.delete();
+ }
+ }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java
index 0302153470..6f30b03539 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java
@@ -37,7 +37,6 @@ import org.apache.nifi.web.util.WebUtils;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
-import org.junit.Ignore;
import org.junit.Test;
import javax.net.ssl.SSLContext;
@@ -48,7 +47,6 @@ import java.util.Map;
/**
* Access token endpoint test.
*/
-@Ignore
public class AccessTokenEndpointTest {
private static final String CLIENT_ID = "token-endpoint-id";
@@ -147,7 +145,7 @@ public class AccessTokenEndpointTest {
}
private ProcessorDTO createProcessor(final String token) throws Exception {
- String url = BASE_URL + "/controller/process-groups/root/processors";
+ String url = BASE_URL + "/process-groups/root/processors";
// authorization header
Map headers = new HashMap<>();
@@ -224,7 +222,6 @@ public class AccessTokenEndpointTest {
public void testRequestAccessUsingToken() throws Exception {
String accessStatusUrl = BASE_URL + "/access";
String accessTokenUrl = BASE_URL + "/access/token";
- String registrationUrl = BASE_URL + "/controller/users";
ClientResponse response = TOKEN_USER.testGet(accessStatusUrl);
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ConnectionAccessControlTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ConnectionAccessControlTest.java
new file mode 100644
index 0000000000..ab01ea87be
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ConnectionAccessControlTest.java
@@ -0,0 +1,427 @@
+/*
+ * 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.integration.accesscontrol;
+
+import com.sun.jersey.api.client.ClientResponse;
+import org.apache.nifi.connectable.ConnectableType;
+import org.apache.nifi.integration.util.NiFiTestAuthorizer;
+import org.apache.nifi.integration.util.NiFiTestUser;
+import org.apache.nifi.web.api.dto.ConnectableDTO;
+import org.apache.nifi.web.api.dto.ConnectionDTO;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.dto.flow.FlowDTO;
+import org.apache.nifi.web.api.entity.ConnectionEntity;
+import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
+import org.apache.nifi.web.api.entity.ProcessorEntity;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.NONE_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.READ_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.READ_WRITE_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.WRITE_CLIENT_ID;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Access control test for connections.
+ */
+public class ConnectionAccessControlTest {
+
+ private static final String FLOW_XML_PATH = "target/test-classes/access-control/flow-connections.xml";
+
+ private static AccessControlHelper helper;
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ helper = new AccessControlHelper(FLOW_XML_PATH);
+ }
+
+ /**
+ * Ensures the READ user can get a connection.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserGetConnection() throws Exception {
+ final ConnectionEntity entity = getRandomConnection(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the READ WRITE user can get a connection.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserGetConnection() throws Exception {
+ final ConnectionEntity entity = getRandomConnection(helper.getReadWriteUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the WRITE user can get a connection.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserGetConnection() throws Exception {
+ final ConnectionEntity entity = getRandomConnection(helper.getWriteUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the NONE user can get a connection.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserGetConnection() throws Exception {
+ final ConnectionEntity entity = getRandomConnection(helper.getNoneUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the READ user cannot put a connection.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserPutConnection() throws Exception {
+ final ConnectionEntity entity = getRandomConnection(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ // attempt update the name
+ entity.getRevision().setClientId(READ_CLIENT_ID);
+ entity.getComponent().setName("Updated Name");
+
+ // perform the request
+ final ClientResponse response = updateConnection(helper.getReadUser(), entity);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ_WRITE user can put a connection.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserPutConnection() throws Exception {
+ final ConnectionEntity entity = getRandomConnection(helper.getReadWriteUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ final String updatedName = "Updated Name";
+
+ // attempt to update the name
+ final long version = entity.getRevision().getVersion();
+ entity.getRevision().setClientId(AccessControlHelper.READ_WRITE_CLIENT_ID);
+ entity.getComponent().setName(updatedName);
+
+ // perform the request
+ final ClientResponse response = updateConnection(helper.getReadWriteUser(), entity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final ConnectionEntity responseEntity = response.getEntity(ConnectionEntity.class);
+
+ // verify
+ assertEquals(READ_WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ assertEquals(updatedName, responseEntity.getComponent().getName());
+ }
+
+ /**
+ * Ensures the READ_WRITE user can put a connection.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserPutConnectionThroughInheritedPolicy() throws Exception {
+ final ConnectionEntity entity = createConnection(NiFiTestAuthorizer.NO_POLICY_COMPONENT_NAME);
+
+ final String updatedName = "Updated name";
+
+ // attempt to update the name
+ final long version = entity.getRevision().getVersion();
+ entity.getRevision().setClientId(READ_WRITE_CLIENT_ID);
+ entity.getComponent().setName(updatedName);
+
+ // perform the request
+ final ClientResponse response = updateConnection(helper.getReadWriteUser(), entity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final ConnectionEntity responseEntity = response.getEntity(ConnectionEntity.class);
+
+ // verify
+ assertEquals(AccessControlHelper.READ_WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ assertEquals(updatedName, responseEntity.getComponent().getName());
+ }
+
+ /**
+ * Ensures the WRITE user can put a connection.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserPutConnection() throws Exception {
+ final ConnectionEntity entity = getRandomConnection(helper.getWriteUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+
+ final String updatedName = "Updated Name";
+
+ // attempt to update the name
+ final ConnectionDTO requestDto = new ConnectionDTO();
+ requestDto.setId(entity.getId());
+ requestDto.setName(updatedName);
+
+ final long version = entity.getRevision().getVersion();
+ final RevisionDTO requestRevision = new RevisionDTO();
+ requestRevision.setVersion(version);
+ requestRevision.setClientId(AccessControlHelper.WRITE_CLIENT_ID);
+
+ final ConnectionEntity requestEntity = new ConnectionEntity();
+ requestEntity.setId(entity.getId());
+ requestEntity.setRevision(requestRevision);
+ requestEntity.setComponent(requestDto);
+
+ // perform the request
+ final ClientResponse response = updateConnection(helper.getWriteUser(), requestEntity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final ConnectionEntity responseEntity = response.getEntity(ConnectionEntity.class);
+
+ // verify
+ assertEquals(WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ }
+
+ /**
+ * Ensures the NONE user cannot put a connection.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserPutConnection() throws Exception {
+ final ConnectionEntity entity = getRandomConnection(helper.getNoneUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+
+ final String updatedName = "Updated Name";
+
+ // attempt to update the name
+ final ConnectionDTO requestDto = new ConnectionDTO();
+ requestDto.setId(entity.getId());
+ requestDto.setName(updatedName);
+
+ final long version = entity.getRevision().getVersion();
+ final RevisionDTO requestRevision = new RevisionDTO();
+ requestRevision.setVersion(version);
+ requestRevision.setClientId(AccessControlHelper.NONE_CLIENT_ID);
+
+ final ConnectionEntity requestEntity = new ConnectionEntity();
+ requestEntity.setId(entity.getId());
+ requestEntity.setRevision(requestRevision);
+ requestEntity.setComponent(requestDto);
+
+ // perform the request
+ final ClientResponse response = updateConnection(helper.getNoneUser(), requestEntity);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ user cannot delete a connection.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserDeleteConnection() throws Exception {
+ verifyDelete(helper.getReadUser(), AccessControlHelper.READ_CLIENT_ID, 403);
+ }
+
+ /**
+ * Ensures the READ WRITE user can delete a connection.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserDeleteConnection() throws Exception {
+ verifyDelete(helper.getReadWriteUser(), AccessControlHelper.READ_WRITE_CLIENT_ID, 200);
+ }
+
+ /**
+ * Ensures the WRITE user can delete a connection.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserDeleteConnection() throws Exception {
+ verifyDelete(helper.getWriteUser(), AccessControlHelper.WRITE_CLIENT_ID, 200);
+ }
+
+ /**
+ * Ensures the NONE user can delete a connection.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserDeleteConnection() throws Exception {
+ verifyDelete(helper.getNoneUser(), NONE_CLIENT_ID, 403);
+ }
+
+ private ConnectionEntity getRandomConnection(final NiFiTestUser user) throws Exception {
+ final String url = helper.getBaseUrl() + "/flow/process-groups/root";
+
+ // get the connections
+ final ClientResponse response = user.testGet(url);
+
+ // ensure the response was successful
+ assertEquals(200, response.getStatus());
+
+ // unmarshal
+ final ProcessGroupFlowEntity flowEntity = response.getEntity(ProcessGroupFlowEntity.class);
+ final FlowDTO flowDto = flowEntity.getProcessGroupFlow().getFlow();
+ final Set connections = flowDto.getConnections();
+
+ // ensure the correct number of connection
+ assertFalse(connections.isEmpty());
+
+ // use the first connection as the target
+ Iterator connectionIter = connections.iterator();
+ assertTrue(connectionIter.hasNext());
+ return connectionIter.next();
+ }
+
+ private ClientResponse updateConnection(final NiFiTestUser user, final ConnectionEntity entity) throws Exception {
+ final String url = helper.getBaseUrl() + "/connections/" + entity.getId();
+
+ // perform the request
+ return user.testPut(url, entity);
+ }
+
+ private ConnectionEntity createConnection(final String name) throws Exception {
+ String url = helper.getBaseUrl() + "/process-groups/root/connections";
+
+ // get two processors
+ final ProcessorEntity one = ProcessorAccessControlTest.createProcessor(helper, "one");
+ final ProcessorEntity two = ProcessorAccessControlTest.createProcessor(helper, "two");
+
+ // create the source connectable
+ ConnectableDTO source = new ConnectableDTO();
+ source.setId(one.getId());
+ source.setType(ConnectableType.PROCESSOR.name());
+
+ // create the target connectable
+ ConnectableDTO target = new ConnectableDTO();
+ target.setId(two.getId());
+ target.setType(ConnectableType.PROCESSOR.name());
+
+ // create the relationships
+ Set relationships = new HashSet<>();
+ relationships.add("success");
+
+ // create the connection
+ ConnectionDTO connection = new ConnectionDTO();
+ connection.setName(name);
+ connection.setSource(source);
+ connection.setDestination(target);
+ connection.setSelectedRelationships(relationships);
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(READ_WRITE_CLIENT_ID);
+ revision.setVersion(0L);
+
+ // create the entity body
+ ConnectionEntity entity = new ConnectionEntity();
+ entity.setRevision(revision);
+ entity.setComponent(connection);
+
+ // perform the request
+ ClientResponse response = helper.getReadWriteUser().testPost(url, entity);
+
+ // ensure the request is successful
+ assertEquals(201, response.getStatus());
+
+ // get the entity body
+ entity = response.getEntity(ConnectionEntity.class);
+
+ // verify creation
+ connection = entity.getComponent();
+ assertEquals(name, connection.getName());
+
+ // get the connection
+ return entity;
+ }
+
+ private void verifyDelete(final NiFiTestUser user, final String clientId, final int responseCode) throws Exception {
+ final ConnectionEntity entity = createConnection("Copy");
+
+ // create the entity body
+ final Map queryParams = new HashMap<>();
+ queryParams.put("revision", String.valueOf(entity.getRevision().getVersion()));
+ queryParams.put("clientId", clientId);
+
+ // perform the request
+ ClientResponse response = user.testDelete(entity.getComponent().getUri(), queryParams);
+
+ // ensure the request is failed with a forbidden status code
+ assertEquals(responseCode, response.getStatus());
+ }
+
+ @AfterClass
+ public static void cleanup() throws Exception {
+ helper.cleanup();
+ }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/FunnelAccessControlTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/FunnelAccessControlTest.java
new file mode 100644
index 0000000000..26590b9a06
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/FunnelAccessControlTest.java
@@ -0,0 +1,361 @@
+/*
+ * 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.integration.accesscontrol;
+
+import com.sun.jersey.api.client.ClientResponse;
+import org.apache.nifi.integration.util.NiFiTestUser;
+import org.apache.nifi.web.api.dto.FunnelDTO;
+import org.apache.nifi.web.api.dto.PositionDTO;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.dto.flow.FlowDTO;
+import org.apache.nifi.web.api.entity.FunnelEntity;
+import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.NONE_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.READ_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.READ_WRITE_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.WRITE_CLIENT_ID;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Access control test for funnels.
+ */
+public class FunnelAccessControlTest {
+
+ private static final String FLOW_XML_PATH = "target/test-classes/access-control/flow-funnels.xml";
+
+ private static AccessControlHelper helper;
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ helper = new AccessControlHelper(FLOW_XML_PATH);
+ }
+
+ /**
+ * Ensures the READ user can get a funnel.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserGetFunnel() throws Exception {
+ final FunnelEntity entity = getRandomFunnel(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the READ WRITE user can get a funnel.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserGetFunnel() throws Exception {
+ final FunnelEntity entity = getRandomFunnel(helper.getReadWriteUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the WRITE user can get a funnel.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserGetFunnel() throws Exception {
+ final FunnelEntity entity = getRandomFunnel(helper.getWriteUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the NONE user can get a funnel.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserGetFunnel() throws Exception {
+ final FunnelEntity entity = getRandomFunnel(helper.getNoneUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the READ user cannot put a funnel.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserPutFunnel() throws Exception {
+ final FunnelEntity entity = getRandomFunnel(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ // attempt update the position
+ entity.getRevision().setClientId(READ_CLIENT_ID);
+ entity.getComponent().setPosition(new PositionDTO(0.0, 10.0));
+
+ // perform the request
+ final ClientResponse response = updateFunnel(helper.getReadUser(), entity);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ_WRITE user can put a funnel.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserPutFunnel() throws Exception {
+ final FunnelEntity entity = getRandomFunnel(helper.getReadWriteUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ final double y = 15.0;
+
+ // attempt to update the position
+ final long version = entity.getRevision().getVersion();
+ entity.getRevision().setClientId(AccessControlHelper.READ_WRITE_CLIENT_ID);
+ entity.getComponent().setPosition(new PositionDTO(0.0, y));
+
+ // perform the request
+ final ClientResponse response = updateFunnel(helper.getReadWriteUser(), entity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final FunnelEntity responseEntity = response.getEntity(FunnelEntity.class);
+
+ // verify
+ assertEquals(READ_WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ assertEquals(y, responseEntity.getComponent().getPosition().getY().doubleValue(), 0);
+ }
+
+ /**
+ * Ensures the WRITE user can put a funnel.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserPutFunnel() throws Exception {
+ final FunnelEntity entity = getRandomFunnel(helper.getWriteUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+
+ final double y = 15.0;
+
+ // attempt to update the position
+ final FunnelDTO requestDto = new FunnelDTO();
+ requestDto.setId(entity.getId());
+ requestDto.setPosition(new PositionDTO(0.0, y));
+
+ final long version = entity.getRevision().getVersion();
+ final RevisionDTO requestRevision = new RevisionDTO();
+ requestRevision.setVersion(version);
+ requestRevision.setClientId(AccessControlHelper.WRITE_CLIENT_ID);
+
+ final FunnelEntity requestEntity = new FunnelEntity();
+ requestEntity.setId(entity.getId());
+ requestEntity.setRevision(requestRevision);
+ requestEntity.setComponent(requestDto);
+
+ // perform the request
+ final ClientResponse response = updateFunnel(helper.getWriteUser(), requestEntity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final FunnelEntity responseEntity = response.getEntity(FunnelEntity.class);
+
+ // verify
+ assertEquals(WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ }
+
+ /**
+ * Ensures the NONE user cannot put a funnel.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserPutFunnel() throws Exception {
+ final FunnelEntity entity = getRandomFunnel(helper.getNoneUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+
+ // attempt to update the position
+ final FunnelDTO requestDto = new FunnelDTO();
+ requestDto.setId(entity.getId());
+ requestDto.setPosition(new PositionDTO(0.0, 15.0));
+
+ final long version = entity.getRevision().getVersion();
+ final RevisionDTO requestRevision = new RevisionDTO();
+ requestRevision.setVersion(version);
+ requestRevision.setClientId(AccessControlHelper.NONE_CLIENT_ID);
+
+ final FunnelEntity requestEntity = new FunnelEntity();
+ requestEntity.setId(entity.getId());
+ requestEntity.setRevision(requestRevision);
+ requestEntity.setComponent(requestDto);
+
+ // perform the request
+ final ClientResponse response = updateFunnel(helper.getNoneUser(), requestEntity);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ user cannot delete a funnel.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserDeleteFunnel() throws Exception {
+ verifyDelete(helper.getReadUser(), AccessControlHelper.READ_CLIENT_ID, 403);
+ }
+
+ /**
+ * Ensures the READ WRITE user can delete a funnel.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserDeleteFunnel() throws Exception {
+ verifyDelete(helper.getReadWriteUser(), AccessControlHelper.READ_WRITE_CLIENT_ID, 200);
+ }
+
+ /**
+ * Ensures the WRITE user can delete a funnel.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserDeleteFunnel() throws Exception {
+ verifyDelete(helper.getWriteUser(), AccessControlHelper.WRITE_CLIENT_ID, 200);
+ }
+
+ /**
+ * Ensures the NONE user can delete a funnel.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserDeleteFunnel() throws Exception {
+ verifyDelete(helper.getNoneUser(), NONE_CLIENT_ID, 403);
+ }
+
+ private FunnelEntity getRandomFunnel(final NiFiTestUser user) throws Exception {
+ final String url = helper.getBaseUrl() + "/flow/process-groups/root";
+
+ // get the flow
+ final ClientResponse response = user.testGet(url);
+
+ // ensure the response was successful
+ assertEquals(200, response.getStatus());
+
+ // unmarshal
+ final ProcessGroupFlowEntity flowEntity = response.getEntity(ProcessGroupFlowEntity.class);
+ final FlowDTO flowDto = flowEntity.getProcessGroupFlow().getFlow();
+ final Set funnels = flowDto.getFunnels();
+
+ // ensure the correct number of funnels
+ assertFalse(funnels.isEmpty());
+
+ // use the first funnel as the target
+ Iterator funnelIter = funnels.iterator();
+ assertTrue(funnelIter.hasNext());
+ return funnelIter.next();
+ }
+
+ private ClientResponse updateFunnel(final NiFiTestUser user, final FunnelEntity entity) throws Exception {
+ final String url = helper.getBaseUrl() + "/funnels/" + entity.getId();
+
+ // perform the request
+ return user.testPut(url, entity);
+ }
+
+ private FunnelEntity createFunnel() throws Exception {
+ String url = helper.getBaseUrl() + "/process-groups/root/funnels";
+
+ // create the funnel
+ FunnelDTO funnel = new FunnelDTO();
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(READ_WRITE_CLIENT_ID);
+ revision.setVersion(0L);
+
+ // create the entity body
+ FunnelEntity entity = new FunnelEntity();
+ entity.setRevision(revision);
+ entity.setComponent(funnel);
+
+ // perform the request
+ ClientResponse response = helper.getReadWriteUser().testPost(url, entity);
+
+ // ensure the request is successful
+ assertEquals(201, response.getStatus());
+
+ // get the entity body
+ return response.getEntity(FunnelEntity.class);
+ }
+
+ private void verifyDelete(final NiFiTestUser user, final String clientId, final int responseCode) throws Exception {
+ final FunnelEntity entity = createFunnel();
+
+ // create the entity body
+ final Map queryParams = new HashMap<>();
+ queryParams.put("revision", String.valueOf(entity.getRevision().getVersion()));
+ queryParams.put("clientId", clientId);
+
+ // perform the request
+ ClientResponse response = user.testDelete(entity.getComponent().getUri(), queryParams);
+
+ // ensure the request is failed with a forbidden status code
+ assertEquals(responseCode, response.getStatus());
+ }
+
+ @AfterClass
+ public static void cleanup() throws Exception {
+ helper.cleanup();
+ }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/InputPortAccessControlTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/InputPortAccessControlTest.java
new file mode 100644
index 0000000000..9f9a966d0c
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/InputPortAccessControlTest.java
@@ -0,0 +1,405 @@
+/*
+ * 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.integration.accesscontrol;
+
+import com.sun.jersey.api.client.ClientResponse;
+import org.apache.nifi.integration.util.NiFiTestAuthorizer;
+import org.apache.nifi.integration.util.NiFiTestUser;
+import org.apache.nifi.web.api.dto.PortDTO;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.dto.flow.FlowDTO;
+import org.apache.nifi.web.api.entity.PortEntity;
+import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.NONE_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.READ_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.READ_WRITE_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.WRITE_CLIENT_ID;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Access control test for input ports.
+ */
+public class InputPortAccessControlTest {
+
+ private static final String FLOW_XML_PATH = "target/test-classes/access-control/flow-input-ports.xml";
+
+ private static AccessControlHelper helper;
+ private static int count = 0;
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ helper = new AccessControlHelper(FLOW_XML_PATH);
+ }
+
+ /**
+ * Ensures the READ user can get an input port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserGetInputPort() throws Exception {
+ final PortEntity entity = getRandomInputPort(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the READ WRITE user can get an input port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserGetInputPort() throws Exception {
+ final PortEntity entity = getRandomInputPort(helper.getReadWriteUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the WRITE user can get an input port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserGetInputPort() throws Exception {
+ final PortEntity entity = getRandomInputPort(helper.getWriteUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the NONE user can get an input port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserGetInputPort() throws Exception {
+ final PortEntity entity = getRandomInputPort(helper.getNoneUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the READ user cannot put an input port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserPutInputPort() throws Exception {
+ final PortEntity entity = getRandomInputPort(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ // attempt update the name
+ entity.getRevision().setClientId(READ_CLIENT_ID);
+ entity.getComponent().setName("Updated Name" + count++);
+
+ // perform the request
+ final ClientResponse response = updateInputPort(helper.getReadUser(), entity);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ_WRITE user can put an input port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserPutInputPort() throws Exception {
+ final PortEntity entity = getRandomInputPort(helper.getReadWriteUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ final String updatedName = "Updated Name" + count++;
+
+ // attempt to update the name
+ final long version = entity.getRevision().getVersion();
+ entity.getRevision().setClientId(AccessControlHelper.READ_WRITE_CLIENT_ID);
+ entity.getComponent().setName(updatedName);
+
+ // perform the request
+ final ClientResponse response = updateInputPort(helper.getReadWriteUser(), entity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final PortEntity responseEntity = response.getEntity(PortEntity.class);
+
+ // verify
+ assertEquals(READ_WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ assertEquals(updatedName, responseEntity.getComponent().getName());
+ }
+
+ /**
+ * Ensures the READ_WRITE user can put an input port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserPutInputPortThroughInheritedPolicy() throws Exception {
+ final PortEntity entity = createInputPort(NiFiTestAuthorizer.NO_POLICY_COMPONENT_NAME);
+
+ final String updatedName = "Updated name" + count++;
+
+ // attempt to update the name
+ final long version = entity.getRevision().getVersion();
+ entity.getRevision().setClientId(READ_WRITE_CLIENT_ID);
+ entity.getComponent().setName(updatedName);
+
+ // perform the request
+ final ClientResponse response = updateInputPort(helper.getReadWriteUser(), entity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final PortEntity responseEntity = response.getEntity(PortEntity.class);
+
+ // verify
+ assertEquals(AccessControlHelper.READ_WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ assertEquals(updatedName, responseEntity.getComponent().getName());
+ }
+
+ /**
+ * Ensures the WRITE user can put an input port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserPutInputPort() throws Exception {
+ final PortEntity entity = getRandomInputPort(helper.getWriteUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+
+ final String updatedName = "Updated Name" + count++;
+
+ // attempt to update the name
+ final PortDTO requestDto = new PortDTO();
+ requestDto.setId(entity.getId());
+ requestDto.setName(updatedName);
+
+ final long version = entity.getRevision().getVersion();
+ final RevisionDTO requestRevision = new RevisionDTO();
+ requestRevision.setVersion(version);
+ requestRevision.setClientId(AccessControlHelper.WRITE_CLIENT_ID);
+
+ final PortEntity requestEntity = new PortEntity();
+ requestEntity.setId(entity.getId());
+ requestEntity.setRevision(requestRevision);
+ requestEntity.setComponent(requestDto);
+
+ // perform the request
+ final ClientResponse response = updateInputPort(helper.getWriteUser(), requestEntity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final PortEntity responseEntity = response.getEntity(PortEntity.class);
+
+ // verify
+ assertEquals(WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ }
+
+ /**
+ * Ensures the NONE user cannot put an input port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserPutInputPort() throws Exception {
+ final PortEntity entity = getRandomInputPort(helper.getNoneUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+
+ final String updatedName = "Updated Name" + count++;
+
+ // attempt to update the name
+ final PortDTO requestDto = new PortDTO();
+ requestDto.setId(entity.getId());
+ requestDto.setName(updatedName);
+
+ final long version = entity.getRevision().getVersion();
+ final RevisionDTO requestRevision = new RevisionDTO();
+ requestRevision.setVersion(version);
+ requestRevision.setClientId(AccessControlHelper.NONE_CLIENT_ID);
+
+ final PortEntity requestEntity = new PortEntity();
+ requestEntity.setId(entity.getId());
+ requestEntity.setRevision(requestRevision);
+ requestEntity.setComponent(requestDto);
+
+ // perform the request
+ final ClientResponse response = updateInputPort(helper.getNoneUser(), requestEntity);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ user cannot delete an input port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserDeleteInputPort() throws Exception {
+ verifyDelete(helper.getReadUser(), AccessControlHelper.READ_CLIENT_ID, 403);
+ }
+
+ /**
+ * Ensures the READ WRITE user can delete an input port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserDeleteInputPort() throws Exception {
+ verifyDelete(helper.getReadWriteUser(), AccessControlHelper.READ_WRITE_CLIENT_ID, 200);
+ }
+
+ /**
+ * Ensures the WRITE user can delete an input port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserDeleteInputPort() throws Exception {
+ verifyDelete(helper.getWriteUser(), AccessControlHelper.WRITE_CLIENT_ID, 200);
+ }
+
+ /**
+ * Ensures the NONE user can delete an input port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserDeleteInputPort() throws Exception {
+ verifyDelete(helper.getNoneUser(), NONE_CLIENT_ID, 403);
+ }
+
+ private PortEntity getRandomInputPort(final NiFiTestUser user) throws Exception {
+ final String url = helper.getBaseUrl() + "/flow/process-groups/root";
+
+ // get the input ports
+ final ClientResponse response = user.testGet(url);
+
+ // ensure the response was successful
+ assertEquals(200, response.getStatus());
+
+ // unmarshal
+ final ProcessGroupFlowEntity flowEntity = response.getEntity(ProcessGroupFlowEntity.class);
+ final FlowDTO flowDto = flowEntity.getProcessGroupFlow().getFlow();
+ final Set inputPorts = flowDto.getInputPorts();
+
+ // ensure the correct number of input ports
+ assertFalse(inputPorts.isEmpty());
+
+ // use the first input port as the target
+ Iterator inputPortIter = inputPorts.iterator();
+ assertTrue(inputPortIter.hasNext());
+ return inputPortIter.next();
+ }
+
+ private ClientResponse updateInputPort(final NiFiTestUser user, final PortEntity entity) throws Exception {
+ final String url = helper.getBaseUrl() + "/input-ports/" + entity.getId();
+
+ // perform the request
+ return user.testPut(url, entity);
+ }
+
+ private PortEntity createInputPort(final String name) throws Exception {
+ String url = helper.getBaseUrl() + "/process-groups/root/input-ports";
+
+ final String updatedName = name + count++;
+
+ // create the input port
+ PortDTO inputPort = new PortDTO();
+ inputPort.setName(updatedName);
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(READ_WRITE_CLIENT_ID);
+ revision.setVersion(0L);
+
+ // create the entity body
+ PortEntity entity = new PortEntity();
+ entity.setRevision(revision);
+ entity.setComponent(inputPort);
+
+ // perform the request
+ ClientResponse response = helper.getReadWriteUser().testPost(url, entity);
+
+ // ensure the request is successful
+ assertEquals(201, response.getStatus());
+
+ // get the entity body
+ entity = response.getEntity(PortEntity.class);
+
+ // verify creation
+ inputPort = entity.getComponent();
+ assertEquals(updatedName, inputPort.getName());
+
+ // get the input port
+ return entity;
+ }
+
+ private void verifyDelete(final NiFiTestUser user, final String clientId, final int responseCode) throws Exception {
+ final PortEntity entity = createInputPort("Copy");
+
+ // create the entity body
+ final Map queryParams = new HashMap<>();
+ queryParams.put("revision", String.valueOf(entity.getRevision().getVersion()));
+ queryParams.put("clientId", clientId);
+
+ // perform the request
+ ClientResponse response = user.testDelete(entity.getComponent().getUri(), queryParams);
+
+ // ensure the request is failed with a forbidden status code
+ assertEquals(responseCode, response.getStatus());
+ }
+
+ @AfterClass
+ public static void cleanup() throws Exception {
+ helper.cleanup();
+ }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/LabelAccessControlTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/LabelAccessControlTest.java
new file mode 100644
index 0000000000..ebfc3c2a59
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/LabelAccessControlTest.java
@@ -0,0 +1,402 @@
+/*
+ * 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.integration.accesscontrol;
+
+import com.sun.jersey.api.client.ClientResponse;
+import org.apache.nifi.integration.util.NiFiTestAuthorizer;
+import org.apache.nifi.integration.util.NiFiTestUser;
+import org.apache.nifi.web.api.dto.LabelDTO;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.dto.flow.FlowDTO;
+import org.apache.nifi.web.api.entity.LabelEntity;
+import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.NONE_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.READ_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.READ_WRITE_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.WRITE_CLIENT_ID;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Access control test for labels.
+ */
+public class LabelAccessControlTest {
+
+ private static final String FLOW_XML_PATH = "target/test-classes/access-control/flow-labels.xml";
+
+ private static AccessControlHelper helper;
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ helper = new AccessControlHelper(FLOW_XML_PATH);
+ }
+
+ /**
+ * Ensures the READ user can get a label.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserGetLabel() throws Exception {
+ final LabelEntity entity = getRandomLabel(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the READ WRITE user can get a label.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserGetLabel() throws Exception {
+ final LabelEntity entity = getRandomLabel(helper.getReadWriteUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the WRITE user can get a label.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserGetLabel() throws Exception {
+ final LabelEntity entity = getRandomLabel(helper.getWriteUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the NONE user can get a label.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserGetLabel() throws Exception {
+ final LabelEntity entity = getRandomLabel(helper.getNoneUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the READ user cannot put a label.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserPutLabel() throws Exception {
+ final LabelEntity entity = getRandomLabel(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ // attempt update the name
+ entity.getRevision().setClientId(READ_CLIENT_ID);
+ entity.getComponent().setLabel("Updated Label");
+
+ // perform the request
+ final ClientResponse response = updateLabel(helper.getReadUser(), entity);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ_WRITE user can put a label.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserPutLabel() throws Exception {
+ final LabelEntity entity = getRandomLabel(helper.getReadWriteUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ final String updatedLabel = "Updated Name";
+
+ // attempt to update the name
+ final long version = entity.getRevision().getVersion();
+ entity.getRevision().setClientId(AccessControlHelper.READ_WRITE_CLIENT_ID);
+ entity.getComponent().setLabel(updatedLabel);
+
+ // perform the request
+ final ClientResponse response = updateLabel(helper.getReadWriteUser(), entity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final LabelEntity responseEntity = response.getEntity(LabelEntity.class);
+
+ // verify
+ assertEquals(READ_WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ assertEquals(updatedLabel, responseEntity.getComponent().getLabel());
+ }
+
+ /**
+ * Ensures the READ_WRITE user can put a label.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserPutLabelThroughInheritedPolicy() throws Exception {
+ final LabelEntity entity = createLabel(NiFiTestAuthorizer.NO_POLICY_COMPONENT_NAME);
+
+ final String updatedLabel = "Updated name";
+
+ // attempt to update the name
+ final long version = entity.getRevision().getVersion();
+ entity.getRevision().setClientId(READ_WRITE_CLIENT_ID);
+ entity.getComponent().setLabel(updatedLabel);
+
+ // perform the request
+ final ClientResponse response = updateLabel(helper.getReadWriteUser(), entity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final LabelEntity responseEntity = response.getEntity(LabelEntity.class);
+
+ // verify
+ assertEquals(AccessControlHelper.READ_WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ assertEquals(updatedLabel, responseEntity.getComponent().getLabel());
+ }
+
+ /**
+ * Ensures the WRITE user can put a label.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserPutLabel() throws Exception {
+ final LabelEntity entity = getRandomLabel(helper.getWriteUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+
+ final String updatedLabel = "Updated Name";
+
+ // attempt to update the label
+ final LabelDTO requestDto = new LabelDTO();
+ requestDto.setId(entity.getId());
+ requestDto.setLabel(updatedLabel);
+
+ final long version = entity.getRevision().getVersion();
+ final RevisionDTO requestRevision = new RevisionDTO();
+ requestRevision.setVersion(version);
+ requestRevision.setClientId(AccessControlHelper.WRITE_CLIENT_ID);
+
+ final LabelEntity requestEntity = new LabelEntity();
+ requestEntity.setId(entity.getId());
+ requestEntity.setRevision(requestRevision);
+ requestEntity.setComponent(requestDto);
+
+ // perform the request
+ final ClientResponse response = updateLabel(helper.getWriteUser(), requestEntity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final LabelEntity responseEntity = response.getEntity(LabelEntity.class);
+
+ // verify
+ assertEquals(WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ }
+
+ /**
+ * Ensures the NONE user cannot put a label.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserPutLabel() throws Exception {
+ final LabelEntity entity = getRandomLabel(helper.getNoneUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+
+ final String updatedName = "Updated Name";
+
+ // attempt to update the name
+ final LabelDTO requestDto = new LabelDTO();
+ requestDto.setId(entity.getId());
+ requestDto.setLabel(updatedName);
+
+ final long version = entity.getRevision().getVersion();
+ final RevisionDTO requestRevision = new RevisionDTO();
+ requestRevision.setVersion(version);
+ requestRevision.setClientId(AccessControlHelper.NONE_CLIENT_ID);
+
+ final LabelEntity requestEntity = new LabelEntity();
+ requestEntity.setId(entity.getId());
+ requestEntity.setRevision(requestRevision);
+ requestEntity.setComponent(requestDto);
+
+ // perform the request
+ final ClientResponse response = updateLabel(helper.getNoneUser(), requestEntity);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ user cannot delete a label.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserDeleteLabel() throws Exception {
+ verifyDelete(helper.getReadUser(), AccessControlHelper.READ_CLIENT_ID, 403);
+ }
+
+ /**
+ * Ensures the READ WRITE user can delete a label.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserDeleteLabel() throws Exception {
+ verifyDelete(helper.getReadWriteUser(), AccessControlHelper.READ_WRITE_CLIENT_ID, 200);
+ }
+
+ /**
+ * Ensures the WRITE user can delete a label.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserDeleteLabel() throws Exception {
+ verifyDelete(helper.getWriteUser(), AccessControlHelper.WRITE_CLIENT_ID, 200);
+ }
+
+ /**
+ * Ensures the NONE user can delete a label.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserDeleteLabel() throws Exception {
+ verifyDelete(helper.getNoneUser(), NONE_CLIENT_ID, 403);
+ }
+
+ private LabelEntity getRandomLabel(final NiFiTestUser user) throws Exception {
+ final String url = helper.getBaseUrl() + "/flow/process-groups/root";
+
+ // get the labels
+ final ClientResponse response = user.testGet(url);
+
+ // ensure the response was successful
+ assertEquals(200, response.getStatus());
+
+ // unmarshal
+ final ProcessGroupFlowEntity flowEntity = response.getEntity(ProcessGroupFlowEntity.class);
+ final FlowDTO flowDto = flowEntity.getProcessGroupFlow().getFlow();
+ final Set labels = flowDto.getLabels();
+
+ // ensure the correct number of labels
+ assertFalse(labels.isEmpty());
+
+ // use the first label as the target
+ Iterator labelIter = labels.iterator();
+ assertTrue(labelIter.hasNext());
+ return labelIter.next();
+ }
+
+ private ClientResponse updateLabel(final NiFiTestUser user, final LabelEntity entity) throws Exception {
+ final String url = helper.getBaseUrl() + "/labels/" + entity.getId();
+
+ // perform the request
+ return user.testPut(url, entity);
+ }
+
+ private LabelEntity createLabel(final String name) throws Exception {
+ String url = helper.getBaseUrl() + "/process-groups/root/labels";
+
+ // create the label
+ LabelDTO label = new LabelDTO();
+ label.setLabel(name);
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(READ_WRITE_CLIENT_ID);
+ revision.setVersion(0L);
+
+ // create the entity body
+ LabelEntity entity = new LabelEntity();
+ entity.setRevision(revision);
+ entity.setComponent(label);
+
+ // perform the request
+ ClientResponse response = helper.getReadWriteUser().testPost(url, entity);
+
+ // ensure the request is successful
+ assertEquals(201, response.getStatus());
+
+ // get the entity body
+ entity = response.getEntity(LabelEntity.class);
+
+ // verify creation
+ label = entity.getComponent();
+ assertEquals(name, label.getLabel());
+
+ // get the label id
+ return entity;
+ }
+
+ private void verifyDelete(final NiFiTestUser user, final String clientId, final int responseCode) throws Exception {
+ final LabelEntity entity = createLabel("Copy");
+
+ // create the entity body
+ final Map queryParams = new HashMap<>();
+ queryParams.put("revision", String.valueOf(entity.getRevision().getVersion()));
+ queryParams.put("clientId", clientId);
+
+ // perform the request
+ ClientResponse response = user.testDelete(entity.getComponent().getUri(), queryParams);
+
+ // ensure the request is failed with a forbidden status code
+ assertEquals(responseCode, response.getStatus());
+ }
+
+ @AfterClass
+ public static void cleanup() throws Exception {
+ helper.cleanup();
+ }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/OutputPortAccessControlTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/OutputPortAccessControlTest.java
new file mode 100644
index 0000000000..900b11ff92
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/OutputPortAccessControlTest.java
@@ -0,0 +1,405 @@
+/*
+ * 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.integration.accesscontrol;
+
+import com.sun.jersey.api.client.ClientResponse;
+import org.apache.nifi.integration.util.NiFiTestAuthorizer;
+import org.apache.nifi.integration.util.NiFiTestUser;
+import org.apache.nifi.web.api.dto.PortDTO;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.dto.flow.FlowDTO;
+import org.apache.nifi.web.api.entity.PortEntity;
+import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.NONE_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.READ_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.READ_WRITE_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.WRITE_CLIENT_ID;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Access control test for output ports.
+ */
+public class OutputPortAccessControlTest {
+
+ private static final String FLOW_XML_PATH = "target/test-classes/access-control/flow-output-ports.xml";
+
+ private static AccessControlHelper helper;
+ private static int count = 0;
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ helper = new AccessControlHelper(FLOW_XML_PATH);
+ }
+
+ /**
+ * Ensures the READ user can get an output port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserGetOutputPort() throws Exception {
+ final PortEntity entity = getRandomOutputPort(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the READ WRITE user can get an output port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserGetOutputPort() throws Exception {
+ final PortEntity entity = getRandomOutputPort(helper.getReadWriteUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the WRITE user can get an output port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserGetOutputPort() throws Exception {
+ final PortEntity entity = getRandomOutputPort(helper.getWriteUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the NONE user can get an output port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserGetOutputPort() throws Exception {
+ final PortEntity entity = getRandomOutputPort(helper.getNoneUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the READ user cannot put an output port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserPutOutputPort() throws Exception {
+ final PortEntity entity = getRandomOutputPort(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ // attempt update the name
+ entity.getRevision().setClientId(READ_CLIENT_ID);
+ entity.getComponent().setName("Updated Name" + count++);
+
+ // perform the request
+ final ClientResponse response = updateOutputPort(helper.getReadUser(), entity);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ_WRITE user can put an output port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserPutOutputPort() throws Exception {
+ final PortEntity entity = getRandomOutputPort(helper.getReadWriteUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ final String updatedName = "Updated Name" + count++;
+
+ // attempt to update the name
+ final long version = entity.getRevision().getVersion();
+ entity.getRevision().setClientId(AccessControlHelper.READ_WRITE_CLIENT_ID);
+ entity.getComponent().setName(updatedName);
+
+ // perform the request
+ final ClientResponse response = updateOutputPort(helper.getReadWriteUser(), entity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final PortEntity responseEntity = response.getEntity(PortEntity.class);
+
+ // verify
+ assertEquals(READ_WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ assertEquals(updatedName, responseEntity.getComponent().getName());
+ }
+
+ /**
+ * Ensures the READ_WRITE user can put an output port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserPutOutputPortThroughInheritedPolicy() throws Exception {
+ final PortEntity entity = createOutputPort(NiFiTestAuthorizer.NO_POLICY_COMPONENT_NAME);
+
+ final String updatedName = "Updated name" + count++;
+
+ // attempt to update the name
+ final long version = entity.getRevision().getVersion();
+ entity.getRevision().setClientId(READ_WRITE_CLIENT_ID);
+ entity.getComponent().setName(updatedName);
+
+ // perform the request
+ final ClientResponse response = updateOutputPort(helper.getReadWriteUser(), entity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final PortEntity responseEntity = response.getEntity(PortEntity.class);
+
+ // verify
+ assertEquals(AccessControlHelper.READ_WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ assertEquals(updatedName, responseEntity.getComponent().getName());
+ }
+
+ /**
+ * Ensures the WRITE user can put an output port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserPutOutputPort() throws Exception {
+ final PortEntity entity = getRandomOutputPort(helper.getWriteUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+
+ final String updatedName = "Updated Name" + count++;
+
+ // attempt to update the name
+ final PortDTO requestDto = new PortDTO();
+ requestDto.setId(entity.getId());
+ requestDto.setName(updatedName);
+
+ final long version = entity.getRevision().getVersion();
+ final RevisionDTO requestRevision = new RevisionDTO();
+ requestRevision.setVersion(version);
+ requestRevision.setClientId(AccessControlHelper.WRITE_CLIENT_ID);
+
+ final PortEntity requestEntity = new PortEntity();
+ requestEntity.setId(entity.getId());
+ requestEntity.setRevision(requestRevision);
+ requestEntity.setComponent(requestDto);
+
+ // perform the request
+ final ClientResponse response = updateOutputPort(helper.getWriteUser(), requestEntity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final PortEntity responseEntity = response.getEntity(PortEntity.class);
+
+ // verify
+ assertEquals(WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ }
+
+ /**
+ * Ensures the NONE user cannot put an output port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserPutOutputPort() throws Exception {
+ final PortEntity entity = getRandomOutputPort(helper.getNoneUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+
+ final String updatedName = "Updated Name" + count++;
+
+ // attempt to update the name
+ final PortDTO requestDto = new PortDTO();
+ requestDto.setId(entity.getId());
+ requestDto.setName(updatedName);
+
+ final long version = entity.getRevision().getVersion();
+ final RevisionDTO requestRevision = new RevisionDTO();
+ requestRevision.setVersion(version);
+ requestRevision.setClientId(AccessControlHelper.NONE_CLIENT_ID);
+
+ final PortEntity requestEntity = new PortEntity();
+ requestEntity.setId(entity.getId());
+ requestEntity.setRevision(requestRevision);
+ requestEntity.setComponent(requestDto);
+
+ // perform the request
+ final ClientResponse response = updateOutputPort(helper.getNoneUser(), requestEntity);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ user cannot delete an output port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserDeleteOutputPort() throws Exception {
+ verifyDelete(helper.getReadUser(), AccessControlHelper.READ_CLIENT_ID, 403);
+ }
+
+ /**
+ * Ensures the READ WRITE user can delete an output port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserDeleteOutputPort() throws Exception {
+ verifyDelete(helper.getReadWriteUser(), AccessControlHelper.READ_WRITE_CLIENT_ID, 200);
+ }
+
+ /**
+ * Ensures the WRITE user can delete an Output port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserDeleteOutputPort() throws Exception {
+ verifyDelete(helper.getWriteUser(), AccessControlHelper.WRITE_CLIENT_ID, 200);
+ }
+
+ /**
+ * Ensures the NONE user can delete an Output port.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserDeleteOutputPort() throws Exception {
+ verifyDelete(helper.getNoneUser(), NONE_CLIENT_ID, 403);
+ }
+
+ private PortEntity getRandomOutputPort(final NiFiTestUser user) throws Exception {
+ final String url = helper.getBaseUrl() + "/flow/process-groups/root";
+
+ // get the output ports
+ final ClientResponse response = user.testGet(url);
+
+ // ensure the response was successful
+ assertEquals(200, response.getStatus());
+
+ // unmarshal
+ final ProcessGroupFlowEntity flowEntity = response.getEntity(ProcessGroupFlowEntity.class);
+ final FlowDTO flowDto = flowEntity.getProcessGroupFlow().getFlow();
+ final Set outputPorts = flowDto.getOutputPorts();
+
+ // ensure the correct number of output ports
+ assertFalse(outputPorts.isEmpty());
+
+ // use the first output port as the target
+ Iterator outputPortIter = outputPorts.iterator();
+ assertTrue(outputPortIter.hasNext());
+ return outputPortIter.next();
+ }
+
+ private ClientResponse updateOutputPort(final NiFiTestUser user, final PortEntity entity) throws Exception {
+ final String url = helper.getBaseUrl() + "/output-ports/" + entity.getId();
+
+ // perform the request
+ return user.testPut(url, entity);
+ }
+
+ private PortEntity createOutputPort(final String name) throws Exception {
+ String url = helper.getBaseUrl() + "/process-groups/root/output-ports";
+
+ final String updatedName = name + count++;
+
+ // create the output port
+ PortDTO outputPort = new PortDTO();
+ outputPort.setName(updatedName);
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(READ_WRITE_CLIENT_ID);
+ revision.setVersion(0L);
+
+ // create the entity body
+ PortEntity entity = new PortEntity();
+ entity.setRevision(revision);
+ entity.setComponent(outputPort);
+
+ // perform the request
+ ClientResponse response = helper.getReadWriteUser().testPost(url, entity);
+
+ // ensure the request is successful
+ assertEquals(201, response.getStatus());
+
+ // get the entity body
+ entity = response.getEntity(PortEntity.class);
+
+ // verify creation
+ outputPort = entity.getComponent();
+ assertEquals(updatedName, outputPort.getName());
+
+ // get the output port
+ return entity;
+ }
+
+ private void verifyDelete(final NiFiTestUser user, final String clientId, final int responseCode) throws Exception {
+ final PortEntity entity = createOutputPort("Copy");
+
+ // create the entity body
+ final Map queryParams = new HashMap<>();
+ queryParams.put("revision", String.valueOf(entity.getRevision().getVersion()));
+ queryParams.put("clientId", clientId);
+
+ // perform the request
+ ClientResponse response = user.testDelete(entity.getComponent().getUri(), queryParams);
+
+ // ensure the request is failed with a forbidden status code
+ assertEquals(responseCode, response.getStatus());
+ }
+
+ @AfterClass
+ public static void cleanup() throws Exception {
+ helper.cleanup();
+ }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ProcessGroupAccessControlTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ProcessGroupAccessControlTest.java
new file mode 100644
index 0000000000..b3179b1884
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ProcessGroupAccessControlTest.java
@@ -0,0 +1,405 @@
+/*
+ * 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.integration.accesscontrol;
+
+import com.sun.jersey.api.client.ClientResponse;
+import org.apache.nifi.integration.util.NiFiTestAuthorizer;
+import org.apache.nifi.integration.util.NiFiTestUser;
+import org.apache.nifi.web.api.dto.ProcessGroupDTO;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.dto.flow.FlowDTO;
+import org.apache.nifi.web.api.entity.ProcessGroupEntity;
+import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.NONE_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.READ_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.READ_WRITE_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.WRITE_CLIENT_ID;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Access control test for process groups.
+ */
+public class ProcessGroupAccessControlTest {
+
+ private static final String FLOW_XML_PATH = "target/test-classes/access-control/flow-processors.xml";
+
+ private static AccessControlHelper helper;
+ private static int count = 0;
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ helper = new AccessControlHelper(FLOW_XML_PATH);
+ }
+
+ /**
+ * Ensures the READ user can get a process group.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserGetProcessGroup() throws Exception {
+ final ProcessGroupEntity entity = getRandomProcessGroup(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the READ WRITE user can get a process group.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserGetProcessGroup() throws Exception {
+ final ProcessGroupEntity entity = getRandomProcessGroup(helper.getReadWriteUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the WRITE user can get a process group.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserGetProcessGroup() throws Exception {
+ final ProcessGroupEntity entity = getRandomProcessGroup(helper.getWriteUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the NONE user can get a process group.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserGetProcessGroup() throws Exception {
+ final ProcessGroupEntity entity = getRandomProcessGroup(helper.getNoneUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the READ user cannot put a processor.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserPutProcessGroup() throws Exception {
+ final ProcessGroupEntity entity = getRandomProcessGroup(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ // attempt update the name
+ entity.getRevision().setClientId(READ_CLIENT_ID);
+ entity.getComponent().setName("Updated Name" + count++);
+
+ // perform the request
+ final ClientResponse response = updateProcessGroup(helper.getReadUser(), entity);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ_WRITE user can put a process group.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserPutProcessGroup() throws Exception {
+ final ProcessGroupEntity entity = getRandomProcessGroup(helper.getReadWriteUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ final String updatedName = "Updated Name" + count++;
+
+ // attempt to update the name
+ final long version = entity.getRevision().getVersion();
+ entity.getRevision().setClientId(AccessControlHelper.READ_WRITE_CLIENT_ID);
+ entity.getComponent().setName(updatedName);
+
+ // perform the request
+ final ClientResponse response = updateProcessGroup(helper.getReadWriteUser(), entity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final ProcessGroupEntity responseEntity = response.getEntity(ProcessGroupEntity.class);
+
+ // verify
+ assertEquals(READ_WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ assertEquals(updatedName, responseEntity.getComponent().getName());
+ }
+
+ /**
+ * Ensures the READ_WRITE user can put a process grup.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserPutProcessGroupThroughInheritedPolicy() throws Exception {
+ final ProcessGroupEntity entity = createProcessGroup(NiFiTestAuthorizer.NO_POLICY_COMPONENT_NAME);
+
+ final String updatedName = "Updated name" + count++;
+
+ // attempt to update the name
+ final long version = entity.getRevision().getVersion();
+ entity.getRevision().setClientId(READ_WRITE_CLIENT_ID);
+ entity.getComponent().setName(updatedName);
+
+ // perform the request
+ final ClientResponse response = updateProcessGroup(helper.getReadWriteUser(), entity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final ProcessGroupEntity responseEntity = response.getEntity(ProcessGroupEntity.class);
+
+ // verify
+ assertEquals(AccessControlHelper.READ_WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ assertEquals(updatedName, responseEntity.getComponent().getName());
+ }
+
+ /**
+ * Ensures the WRITE user can put a process group.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserPutProcessGroup() throws Exception {
+ final ProcessGroupEntity entity = getRandomProcessGroup(helper.getWriteUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+
+ final String updatedName = "Updated Name" + count++;
+
+ // attempt to update the name
+ final ProcessGroupDTO requestDto = new ProcessGroupDTO();
+ requestDto.setId(entity.getId());
+ requestDto.setName(updatedName);
+
+ final long version = entity.getRevision().getVersion();
+ final RevisionDTO requestRevision = new RevisionDTO();
+ requestRevision.setVersion(version);
+ requestRevision.setClientId(AccessControlHelper.WRITE_CLIENT_ID);
+
+ final ProcessGroupEntity requestEntity = new ProcessGroupEntity();
+ requestEntity.setId(entity.getId());
+ requestEntity.setRevision(requestRevision);
+ requestEntity.setComponent(requestDto);
+
+ // perform the request
+ final ClientResponse response = updateProcessGroup(helper.getWriteUser(), requestEntity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final ProcessGroupEntity responseEntity = response.getEntity(ProcessGroupEntity.class);
+
+ // verify
+ assertEquals(WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ }
+
+ /**
+ * Ensures the NONE user cannot put a process group.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserPutProcessGroup() throws Exception {
+ final ProcessGroupEntity entity = getRandomProcessGroup(helper.getNoneUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+
+ final String updatedName = "Updated Name" + count++;
+
+ // attempt to update the name
+ final ProcessGroupDTO requestDto = new ProcessGroupDTO();
+ requestDto.setId(entity.getId());
+ requestDto.setName(updatedName);
+
+ final long version = entity.getRevision().getVersion();
+ final RevisionDTO requestRevision = new RevisionDTO();
+ requestRevision.setVersion(version);
+ requestRevision.setClientId(AccessControlHelper.NONE_CLIENT_ID);
+
+ final ProcessGroupEntity requestEntity = new ProcessGroupEntity();
+ requestEntity.setId(entity.getId());
+ requestEntity.setRevision(requestRevision);
+ requestEntity.setComponent(requestDto);
+
+ // perform the request
+ final ClientResponse response = updateProcessGroup(helper.getNoneUser(), requestEntity);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ user cannot delete a process group.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserDeleteProcessGroup() throws Exception {
+ verifyDelete(helper.getReadUser(), AccessControlHelper.READ_CLIENT_ID, 403);
+ }
+
+ /**
+ * Ensures the READ WRITE user can delete a process group.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserDeleteProcessGroup() throws Exception {
+ verifyDelete(helper.getReadWriteUser(), AccessControlHelper.READ_WRITE_CLIENT_ID, 200);
+ }
+
+ /**
+ * Ensures the WRITE user can delete a process group.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserDeleteProcessGroup() throws Exception {
+ verifyDelete(helper.getWriteUser(), AccessControlHelper.WRITE_CLIENT_ID, 200);
+ }
+
+ /**
+ * Ensures the NONE user can delete a process group.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserDeleteProcessGroup() throws Exception {
+ verifyDelete(helper.getNoneUser(), NONE_CLIENT_ID, 403);
+ }
+
+ private ProcessGroupEntity getRandomProcessGroup(final NiFiTestUser user) throws Exception {
+ final String url = helper.getBaseUrl() + "/flow/process-groups/root";
+
+ // get the process groups
+ final ClientResponse response = user.testGet(url);
+
+ // ensure the response was successful
+ assertEquals(200, response.getStatus());
+
+ // unmarshal
+ final ProcessGroupFlowEntity flowEntity = response.getEntity(ProcessGroupFlowEntity.class);
+ final FlowDTO flowDto = flowEntity.getProcessGroupFlow().getFlow();
+ final Set processGroups = flowDto.getProcessGroups();
+
+ // ensure the correct number of process groups
+ assertFalse(processGroups.isEmpty());
+
+ // use the first process group as the target
+ Iterator processGroupIter = processGroups.iterator();
+ assertTrue(processGroupIter.hasNext());
+ return processGroupIter.next();
+ }
+
+ private ClientResponse updateProcessGroup(final NiFiTestUser user, final ProcessGroupEntity entity) throws Exception {
+ final String url = helper.getBaseUrl() + "/process-groups/" + entity.getId();
+
+ // perform the request
+ return user.testPut(url, entity);
+ }
+
+ private ProcessGroupEntity createProcessGroup(final String name) throws Exception {
+ String url = helper.getBaseUrl() + "/process-groups/root/process-groups";
+
+ final String updatedName = name + count++;
+
+ // create the process group
+ ProcessGroupDTO processor = new ProcessGroupDTO();
+ processor.setName(updatedName);
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(READ_WRITE_CLIENT_ID);
+ revision.setVersion(0L);
+
+ // create the entity body
+ ProcessGroupEntity entity = new ProcessGroupEntity();
+ entity.setRevision(revision);
+ entity.setComponent(processor);
+
+ // perform the request
+ ClientResponse response = helper.getReadWriteUser().testPost(url, entity);
+
+ // ensure the request is successful
+ assertEquals(201, response.getStatus());
+
+ // get the entity body
+ entity = response.getEntity(ProcessGroupEntity.class);
+
+ // verify creation
+ processor = entity.getComponent();
+ assertEquals(updatedName, processor.getName());
+
+ // get the processor
+ return entity;
+ }
+
+ private void verifyDelete(final NiFiTestUser user, final String clientId, final int responseCode) throws Exception {
+ final ProcessGroupEntity entity = createProcessGroup("Copy");
+
+ // create the entity body
+ final Map queryParams = new HashMap<>();
+ queryParams.put("revision", String.valueOf(entity.getRevision().getVersion()));
+ queryParams.put("clientId", clientId);
+
+ // perform the request
+ ClientResponse response = user.testDelete(entity.getComponent().getUri(), queryParams);
+
+ // ensure the request is failed with a forbidden status code
+ assertEquals(responseCode, response.getStatus());
+ }
+
+ @AfterClass
+ public static void cleanup() throws Exception {
+ helper.cleanup();
+ }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ProcessorAccessControlTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ProcessorAccessControlTest.java
new file mode 100644
index 0000000000..80b6b764f5
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ProcessorAccessControlTest.java
@@ -0,0 +1,489 @@
+/*
+ * 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.integration.accesscontrol;
+
+import com.sun.jersey.api.client.ClientResponse;
+import org.apache.nifi.integration.util.NiFiTestAuthorizer;
+import org.apache.nifi.integration.util.NiFiTestUser;
+import org.apache.nifi.integration.util.SourceTestProcessor;
+import org.apache.nifi.web.api.dto.ProcessorDTO;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.dto.flow.FlowDTO;
+import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
+import org.apache.nifi.web.api.entity.ProcessorEntity;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.NONE_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.READ_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.READ_WRITE_CLIENT_ID;
+import static org.apache.nifi.integration.accesscontrol.AccessControlHelper.WRITE_CLIENT_ID;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Access control test for processors.
+ */
+public class ProcessorAccessControlTest {
+
+ private static final String FLOW_XML_PATH = "target/test-classes/access-control/flow-processors.xml";
+
+ private static AccessControlHelper helper;
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ helper = new AccessControlHelper(FLOW_XML_PATH);
+ }
+
+ /**
+ * Ensures the READ user can get a processor.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserGetProcessor() throws Exception {
+ final ProcessorEntity entity = getRandomProcessor(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the READ WRITE user can get a processor.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserGetProcessor() throws Exception {
+ final ProcessorEntity entity = getRandomProcessor(helper.getReadWriteUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the WRITE user can get a processor.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserGetProcessor() throws Exception {
+ final ProcessorEntity entity = getRandomProcessor(helper.getWriteUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the NONE user can get a processor.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserGetProcessor() throws Exception {
+ final ProcessorEntity entity = getRandomProcessor(helper.getNoneUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+ }
+
+ /**
+ * Ensures the READ user cannot put a processor.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserPutProcessor() throws Exception {
+ final ProcessorEntity entity = getRandomProcessor(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ // attempt update the name
+ entity.getRevision().setClientId(READ_CLIENT_ID);
+ entity.getComponent().setName("Updated Name");
+
+ // perform the request
+ final ClientResponse response = updateProcessor(helper.getReadUser(), entity);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ_WRITE user can put a processor.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserPutProcessor() throws Exception {
+ final ProcessorEntity entity = getRandomProcessor(helper.getReadWriteUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ final String updatedName = "Updated Name";
+
+ // attempt to update the name
+ final long version = entity.getRevision().getVersion();
+ entity.getRevision().setClientId(AccessControlHelper.READ_WRITE_CLIENT_ID);
+ entity.getComponent().setName(updatedName);
+
+ // perform the request
+ final ClientResponse response = updateProcessor(helper.getReadWriteUser(), entity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final ProcessorEntity responseEntity = response.getEntity(ProcessorEntity.class);
+
+ // verify
+ assertEquals(READ_WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ assertEquals(updatedName, responseEntity.getComponent().getName());
+ }
+
+ /**
+ * Ensures the READ_WRITE user can put a processor.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserPutProcessorThroughInheritedPolicy() throws Exception {
+ final ProcessorEntity entity = createProcessor(helper, NiFiTestAuthorizer.NO_POLICY_COMPONENT_NAME);
+
+ final String updatedName = "Updated name";
+
+ // attempt to update the name
+ final long version = entity.getRevision().getVersion();
+ entity.getRevision().setClientId(READ_WRITE_CLIENT_ID);
+ entity.getComponent().setName(updatedName);
+
+ // perform the request
+ final ClientResponse response = updateProcessor(helper.getReadWriteUser(), entity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final ProcessorEntity responseEntity = response.getEntity(ProcessorEntity.class);
+
+ // verify
+ assertEquals(AccessControlHelper.READ_WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ assertEquals(updatedName, responseEntity.getComponent().getName());
+ }
+
+ /**
+ * Ensures the WRITE user can put a processor.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserPutProcessor() throws Exception {
+ final ProcessorEntity entity = getRandomProcessor(helper.getWriteUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertTrue(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+
+ final String updatedName = "Updated Name";
+
+ // attempt to update the name
+ final ProcessorDTO requestDto = new ProcessorDTO();
+ requestDto.setId(entity.getId());
+ requestDto.setName(updatedName);
+
+ final long version = entity.getRevision().getVersion();
+ final RevisionDTO requestRevision = new RevisionDTO();
+ requestRevision.setVersion(version);
+ requestRevision.setClientId(AccessControlHelper.WRITE_CLIENT_ID);
+
+ final ProcessorEntity requestEntity = new ProcessorEntity();
+ requestEntity.setId(entity.getId());
+ requestEntity.setRevision(requestRevision);
+ requestEntity.setComponent(requestDto);
+
+ // perform the request
+ final ClientResponse response = updateProcessor(helper.getWriteUser(), requestEntity);
+
+ // ensure successful response
+ assertEquals(200, response.getStatus());
+
+ // get the response
+ final ProcessorEntity responseEntity = response.getEntity(ProcessorEntity.class);
+
+ // verify
+ assertEquals(WRITE_CLIENT_ID, responseEntity.getRevision().getClientId());
+ assertEquals(version + 1, responseEntity.getRevision().getVersion().longValue());
+ }
+
+ /**
+ * Ensures the NONE user cannot put a processor.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserPutProcessor() throws Exception {
+ final ProcessorEntity entity = getRandomProcessor(helper.getNoneUser());
+ assertFalse(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNull(entity.getComponent());
+
+ final String updatedName = "Updated Name";
+
+ // attempt to update the name
+ final ProcessorDTO requestDto = new ProcessorDTO();
+ requestDto.setId(entity.getId());
+ requestDto.setName(updatedName);
+
+ final long version = entity.getRevision().getVersion();
+ final RevisionDTO requestRevision = new RevisionDTO();
+ requestRevision.setVersion(version);
+ requestRevision.setClientId(AccessControlHelper.NONE_CLIENT_ID);
+
+ final ProcessorEntity requestEntity = new ProcessorEntity();
+ requestEntity.setId(entity.getId());
+ requestEntity.setRevision(requestRevision);
+ requestEntity.setComponent(requestDto);
+
+ // perform the request
+ final ClientResponse response = updateProcessor(helper.getNoneUser(), requestEntity);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ user cannot clear state.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserClearState() throws Exception {
+ final ProcessorEntity entity = getRandomProcessor(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ final String url = helper.getBaseUrl() + "/processors/" + entity.getId() + "/state/clear-requests";
+
+ // perform the request
+ final ClientResponse response = helper.getReadUser().testPost(url);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the NONE user cannot clear state.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserClearState() throws Exception {
+ final ProcessorEntity entity = getRandomProcessor(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ final String url = helper.getBaseUrl() + "/processors/" + entity.getId() + "/state/clear-requests";
+
+ // perform the request
+ final ClientResponse response = helper.getNoneUser().testPost(url);
+
+ // ensure forbidden response
+ assertEquals(403, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ WRITE user can clear state.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserClearState() throws Exception {
+ final ProcessorEntity entity = getRandomProcessor(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ final String url = helper.getBaseUrl() + "/processors/" + entity.getId() + "/state/clear-requests";
+
+ // perform the request
+ final ClientResponse response = helper.getReadWriteUser().testPost(url);
+
+ // ensure ok response
+ assertEquals(200, response.getStatus());
+ }
+
+ /**
+ * Ensures the WRITE user can clear state.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserClearState() throws Exception {
+ final ProcessorEntity entity = getRandomProcessor(helper.getReadUser());
+ assertTrue(entity.getAccessPolicy().getCanRead());
+ assertFalse(entity.getAccessPolicy().getCanWrite());
+ assertNotNull(entity.getComponent());
+
+ final String url = helper.getBaseUrl() + "/processors/" + entity.getId() + "/state/clear-requests";
+
+ // perform the request
+ final ClientResponse response = helper.getWriteUser().testPost(url);
+
+ // ensure ok response
+ assertEquals(200, response.getStatus());
+ }
+
+ /**
+ * Ensures the READ user cannot delete a processor.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadUserDeleteProcessor() throws Exception {
+ verifyDelete(helper.getReadUser(), AccessControlHelper.READ_CLIENT_ID, 403);
+ }
+
+ /**
+ * Ensures the READ WRITE user can delete a processor.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testReadWriteUserDeleteProcessor() throws Exception {
+ verifyDelete(helper.getReadWriteUser(), AccessControlHelper.READ_WRITE_CLIENT_ID, 200);
+ }
+
+ /**
+ * Ensures the WRITE user can delete a processor.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testWriteUserDeleteProcessor() throws Exception {
+ verifyDelete(helper.getWriteUser(), AccessControlHelper.WRITE_CLIENT_ID, 200);
+ }
+
+ /**
+ * Ensures the NONE user can delete a processor.
+ *
+ * @throws Exception ex
+ */
+ @Test
+ public void testNoneUserDeleteProcessor() throws Exception {
+ verifyDelete(helper.getNoneUser(), NONE_CLIENT_ID, 403);
+ }
+
+ private ProcessorEntity getRandomProcessor(final NiFiTestUser user) throws Exception {
+ final String url = helper.getBaseUrl() + "/flow/process-groups/root";
+
+ // get the processors
+ final ClientResponse response = user.testGet(url);
+
+ // ensure the response was successful
+ assertEquals(200, response.getStatus());
+
+ // unmarshal
+ final ProcessGroupFlowEntity flowEntity = response.getEntity(ProcessGroupFlowEntity.class);
+ final FlowDTO flowDto = flowEntity.getProcessGroupFlow().getFlow();
+ final Set processors = flowDto.getProcessors();
+
+ // ensure the correct number of processors
+ assertFalse(processors.isEmpty());
+
+ // use the first processor as the target
+ Iterator processorIter = processors.iterator();
+ assertTrue(processorIter.hasNext());
+ return processorIter.next();
+ }
+
+ private ClientResponse updateProcessor(final NiFiTestUser user, final ProcessorEntity entity) throws Exception {
+ final String url = helper.getBaseUrl() + "/processors/" + entity.getId();
+
+ // perform the request
+ return user.testPut(url, entity);
+ }
+
+ public static ProcessorEntity createProcessor(final AccessControlHelper ach, final String name) throws Exception {
+ String url = ach.getBaseUrl() + "/process-groups/root/processors";
+
+ // create the processor
+ ProcessorDTO processor = new ProcessorDTO();
+ processor.setName(name);
+ processor.setType(SourceTestProcessor.class.getName());
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(READ_WRITE_CLIENT_ID);
+ revision.setVersion(0L);
+
+ // create the entity body
+ ProcessorEntity entity = new ProcessorEntity();
+ entity.setRevision(revision);
+ entity.setComponent(processor);
+
+ // perform the request
+ ClientResponse response = ach.getReadWriteUser().testPost(url, entity);
+
+ // ensure the request is successful
+ assertEquals(201, response.getStatus());
+
+ // get the entity body
+ entity = response.getEntity(ProcessorEntity.class);
+
+ // verify creation
+ processor = entity.getComponent();
+ assertEquals(name, processor.getName());
+ assertEquals("org.apache.nifi.integration.util.SourceTestProcessor", processor.getType());
+
+ // get the processor
+ return entity;
+ }
+
+ private void verifyDelete(final NiFiTestUser user, final String clientId, final int responseCode) throws Exception {
+ final ProcessorEntity entity = createProcessor(helper, "Copy");
+
+ // create the entity body
+ final Map queryParams = new HashMap<>();
+ queryParams.put("revision", String.valueOf(entity.getRevision().getVersion()));
+ queryParams.put("clientId", clientId);
+
+ // perform the request
+ ClientResponse response = user.testDelete(entity.getComponent().getUri(), queryParams);
+
+ // ensure the request is failed with a forbidden status code
+ assertEquals(responseCode, response.getStatus());
+ }
+
+ @AfterClass
+ public static void cleanup() throws Exception {
+ helper.cleanup();
+ }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizer.java
index 5795b6915a..419235f2f7 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizer.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizer.java
@@ -21,14 +21,26 @@ import org.apache.nifi.authorization.AuthorizationResult;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.AuthorizerConfigurationContext;
import org.apache.nifi.authorization.AuthorizerInitializationContext;
+import org.apache.nifi.authorization.RequestAction;
import org.apache.nifi.authorization.exception.AuthorizationAccessException;
import org.apache.nifi.authorization.exception.AuthorizerCreationException;
+import org.apache.nifi.authorization.resource.ResourceFactory;
/**
*
*/
public class NiFiTestAuthorizer implements Authorizer {
+ public static final String NO_POLICY_COMPONENT_NAME = "No policies";
+
+ public static final String PROXY_DN = "CN=localhost, OU=Apache NiFi, O=Apache, L=Santa Monica, ST=CA, C=US";
+
+ public static final String NONE_USER_DN = "none@nifi";
+ public static final String READ_USER_DN = "read@nifi";
+ public static final String WRITE_USER_DN = "write@nifi";
+ public static final String READ_WRITE_USER_DN = "readwrite@nifi";
+
+ public static final String TOKEN_USER = "user@nifi";
/**
* Creates a new FileAuthorizationProvider.
@@ -46,7 +58,41 @@ public class NiFiTestAuthorizer implements Authorizer {
@Override
public AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException {
- return AuthorizationResult.approved();
+ // allow proxy
+ if (ResourceFactory.getProxyResource().getIdentifier().equals(request.getResource().getIdentifier()) && PROXY_DN.equals(request.getIdentity())) {
+ return AuthorizationResult.approved();
+ }
+
+ // allow flow
+ if (ResourceFactory.getFlowResource().getIdentifier().equals(request.getResource().getIdentifier())) {
+ return AuthorizationResult.approved();
+ }
+
+ // no policy to test inheritance
+ if (NO_POLICY_COMPONENT_NAME.equals(request.getResource().getName())) {
+ return AuthorizationResult.resourceNotFound();
+ }
+
+ // allow the token user
+ if (TOKEN_USER.equals(request.getIdentity())) {
+ return AuthorizationResult.approved();
+ }
+
+ // read access
+ if (READ_USER_DN.equals(request.getIdentity()) || READ_WRITE_USER_DN.equals(request.getIdentity())) {
+ if (RequestAction.READ.equals(request.getAction())) {
+ return AuthorizationResult.approved();
+ }
+ }
+
+ // write access
+ if (WRITE_USER_DN.equals(request.getIdentity()) || READ_WRITE_USER_DN.equals(request.getIdentity())) {
+ if (RequestAction.WRITE.equals(request.getAction())) {
+ return AuthorizationResult.approved();
+ }
+ }
+
+ return AuthorizationResult.denied();
}
@Override
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/SourceTestProcessor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/SourceTestProcessor.java
index 0309ebb559..2fe6b46044 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/SourceTestProcessor.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/SourceTestProcessor.java
@@ -16,9 +16,8 @@
*/
package org.apache.nifi.integration.util;
-import java.util.HashSet;
-import java.util.Set;
-
+import org.apache.nifi.annotation.behavior.Stateful;
+import org.apache.nifi.components.state.Scope;
import org.apache.nifi.processor.AbstractSessionFactoryProcessor;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSessionFactory;
@@ -26,6 +25,10 @@ import org.apache.nifi.processor.ProcessorInitializationContext;
import org.apache.nifi.processor.Relationship;
import org.apache.nifi.processor.exception.ProcessException;
+import java.util.HashSet;
+import java.util.Set;
+
+@Stateful(scopes = Scope.LOCAL, description = "")
public class SourceTestProcessor extends AbstractSessionFactoryProcessor {
public SourceTestProcessor() {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/authority-providers.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/authorizers.xml
similarity index 93%
rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/authority-providers.xml
rename to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/authorizers.xml
index a3fb0888fc..18161d6118 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/authority-providers.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/authorizers.xml
@@ -16,9 +16,9 @@
-
-
+
+
test-provider
org.apache.nifi.integration.util.NiFiTestAuthorizer
-
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties
index a41e3b1e62..4df1e3d9db 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties
@@ -21,7 +21,7 @@ nifi.flowcontroller.autoResumeState=true
nifi.flowcontroller.graceful.shutdown.period=10 sec
nifi.flowservice.writedelay.interval=2 sec
-nifi.authority.provider.configuration.file=target/test-classes/access-control/authority-providers.xml
+nifi.authorizer.configuration.file=target/test-classes/access-control/authorizers.xml
nifi.login.identity.provider.configuration.file=target/test-classes/access-control/login-identity-providers.xml
nifi.templates.directory=target/test-classes/access-control/templates
nifi.ui.banner.text=TEST BANNER
@@ -82,7 +82,7 @@ nifi.web.war.directory=target/test-classes/lib
nifi.web.http.host=
nifi.web.http.port=
nifi.web.https.host=
-nifi.web.https.port=
+nifi.web.https.port=8443
nifi.web.jetty.working.directory=target/test-classes/access-control/jetty
# security properties #
@@ -99,7 +99,7 @@ nifi.security.truststoreType=JKS
nifi.security.truststorePasswd=localtest
nifi.security.needClientAuth=true
nifi.security.user.login.identity.provider=test-provider
-nifi.security.user.authorizer=
+nifi.security.user.authorizer=test-provider
# cluster common properties (cluster manager and nodes must have same values) #
nifi.cluster.protocol.heartbeat.interval=5 sec
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
index 2ee187adf4..26fae82d39 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
@@ -119,7 +119,7 @@ public class LoginIdentityProviderFactoryBean implements FactoryBean, Disposable
}
private LoginIdentityProviders loadLoginIdentityProvidersConfiguration() throws Exception {
- final File loginIdentityProvidersConfigurationFile = properties.getLoginIdentityProviderConfiguraitonFile();
+ final File loginIdentityProvidersConfigurationFile = properties.getLoginIdentityProviderConfigurationFile();
// load the users from the specified file
if (loginIdentityProvidersConfigurationFile.exists()) {