NIFI-1901: - Building component based access control tests for Connections, Funnels, Labels, Input Ports, Output Ports, Processors, and Process Groups. - Restoring Access Token Endpoint tests.
This closes #518 Signed-off-by: Bryan Bende <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/nifi/repo Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/fbd299e8 Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/fbd299e8 Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/fbd299e8 Branch: refs/heads/master Commit: fbd299e885499170b550d17662cc482f550e0b3e Parents: c2b26eb Author: Matt Gilman <[email protected]> Authored: Mon Jun 13 15:39:44 2016 -0400 Committer: Bryan Bende <[email protected]> Committed: Fri Jun 17 10:16:09 2016 -0400 ---------------------------------------------------------------------- .travis.yml | 2 +- .../org/apache/nifi/util/NiFiProperties.java | 4 +- .../authorization/AuthorizerFactoryBean.java | 2 +- .../nifi/web/api/entity/ComponentEntity.java | 17 - .../nifi-web/nifi-web-api/pom.xml | 5 + .../apache/nifi/web/api/ConnectionResource.java | 46 +- .../nifi/web/api/ControllerServiceResource.java | 65 +-- .../org/apache/nifi/web/api/FunnelResource.java | 42 +- .../apache/nifi/web/api/ProcessorResource.java | 60 +-- .../nifi/web/api/ReportingTaskResource.java | 8 +- .../apache/nifi/integration/NiFiWebApiTest.java | 54 +- .../accesscontrol/AccessControlHelper.java | 120 +++++ .../accesscontrol/AccessTokenEndpointTest.java | 5 +- .../ConnectionAccessControlTest.java | 427 ++++++++++++++++ .../accesscontrol/FunnelAccessControlTest.java | 361 ++++++++++++++ .../InputPortAccessControlTest.java | 405 +++++++++++++++ .../accesscontrol/LabelAccessControlTest.java | 402 +++++++++++++++ .../OutputPortAccessControlTest.java | 405 +++++++++++++++ .../ProcessGroupAccessControlTest.java | 405 +++++++++++++++ .../ProcessorAccessControlTest.java | 489 +++++++++++++++++++ .../integration/util/NiFiTestAuthorizer.java | 48 +- .../integration/util/SourceTestProcessor.java | 9 +- .../access-control/authority-providers.xml | 24 - .../resources/access-control/authorizers.xml | 24 + .../resources/access-control/nifi.properties | 6 +- .../LoginIdentityProviderFactoryBean.java | 2 +- 26 files changed, 3261 insertions(+), 176 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/.travis.yml ---------------------------------------------------------------------- diff --git a/.travis.yml b/.travis.yml index 811a4c2..db579ce 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 http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java ---------------------------------------------------------------------- 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 ea3b959..73a54c5 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); http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java ---------------------------------------------------------------------- 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 378c805..e4d7318 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()) { http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ComponentEntity.java ---------------------------------------------------------------------- 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 2f7798e..231ce53 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; @@ -66,22 +65,6 @@ public class ComponentEntity extends Entity { } /** - * 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. * * @return The position http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml ---------------------------------------------------------------------- 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 6142066..b0d4dda 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 @@ -211,6 +211,11 @@ <scope>provided</scope> </dependency> <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-authorizer</artifactId> + <scope>provided</scope> + </dependency> + <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <scope>provided</scope> http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java ---------------------------------------------------------------------- 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 9ea31dc..caa7b36 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(); } http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java ---------------------------------------------------------------------- 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 f1dc652..e05552f 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(); } http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FunnelResource.java ---------------------------------------------------------------------- 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 7ec395c..acaa2b4 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), () -> { http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java ---------------------------------------------------------------------- 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 912366e..dccb1f3 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(); } http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java ---------------------------------------------------------------------- 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 66ddfbf..f1fa304 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(); } http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/NiFiWebApiTest.java ---------------------------------------------------------------------- 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 eecc28a..8067491 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()) { http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessControlHelper.java ---------------------------------------------------------------------- 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 0000000..2576253 --- /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(); + } + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java ---------------------------------------------------------------------- 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 0302153..6f30b03 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<String, String> 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); http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ConnectionAccessControlTest.java ---------------------------------------------------------------------- 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 0000000..ab01ea8 --- /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<ConnectionEntity> connections = flowDto.getConnections(); + + // ensure the correct number of connection + assertFalse(connections.isEmpty()); + + // use the first connection as the target + Iterator<ConnectionEntity> 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<String> 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<String, String> 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(); + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/FunnelAccessControlTest.java ---------------------------------------------------------------------- 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 0000000..26590b9 --- /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<FunnelEntity> funnels = flowDto.getFunnels(); + + // ensure the correct number of funnels + assertFalse(funnels.isEmpty()); + + // use the first funnel as the target + Iterator<FunnelEntity> 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<String, String> 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(); + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/fbd299e8/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/InputPortAccessControlTest.java ---------------------------------------------------------------------- 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 0000000..9f9a966 --- /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<PortEntity> inputPorts = flowDto.getInputPorts(); + + // ensure the correct number of input ports + assertFalse(inputPorts.isEmpty()); + + // use the first input port as the target + Iterator<PortEntity> 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<String, String> 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(); + } +}
