From e0c96794fae85776b8b0fbb3cb236217eb591960 Mon Sep 17 00:00:00 2001 From: Matt Gilman Date: Tue, 12 Jul 2016 15:45:13 -0400 Subject: [PATCH] NIFI-2095: - Adding a page for managing users and groups. - Adding a page for managing access policies. - Renaming accessPolicy in entity to permissions to avoid confusion with the accessPolicy model. - Adding an Authorizable for access policies. - Refactoring access policies endpoints. NIFI-2022: - Implementing site to site authorizations. --- .../nifi/authorization/RequestAction.java | 25 +- .../remote/util/SiteToSiteRestApiClient.java | 2 +- .../remote/client/http/TestHttpClient.java | 36 +- .../http/TestHttpClientTransaction.java | 18 +- .../nifi/web/api/dto/AccessPolicyDTO.java | 47 +- .../web/api/dto/AccessPolicySummaryDTO.java | 59 + .../nifi/web/api/dto/PermissionsDTO.java | 61 + .../org/apache/nifi/web/api/dto/UserDTO.java | 22 +- .../apache/nifi/web/api/dto/UserGroupDTO.java | 17 + .../web/api/entity/AccessPolicyEntity.java | 15 + .../api/entity/AccessPolicySummaryEntity.java | 45 + .../nifi/web/api/entity/ComponentEntity.java | 18 +- .../entity/ControllerConfigurationEntity.java | 18 +- .../web/api/entity/CurrentUserEntity.java | 46 +- .../web/api/entity/FlowBreadcrumbEntity.java | 18 +- .../web/api/entity/ProcessGroupEntity.java | 6 +- .../api/entity/ProcessGroupFlowEntity.java | 16 +- .../api/entity/TenantCollectionEntity.java} | 33 +- .../nifi/web/api/entity/TenantsEntity.java | 57 + .../nifi/web/api/entity/UsersEntity.java | 17 + .../resource/AccessPolicyAuthorizable.java | 43 +- ...ble.java => DataTransferAuthorizable.java} | 12 +- .../resource/ResourceFactory.java | 74 +- .../authorization/resource/ResourceType.java | 21 +- .../nifi/remote/StandardRootGroupPort.java | 18 +- .../apache/nifi/web/AuthorizableLookup.java | 31 +- .../apache/nifi/web/NiFiServiceFacade.java | 25 +- .../nifi/web/StandardAuthorizableLookup.java | 253 +++- .../nifi/web/StandardNiFiServiceFacade.java | 372 +++--- .../nifi/web/api/AccessPolicyResource.java | 144 ++- .../nifi/web/api/ApplicationResource.java | 218 +++- .../nifi/web/api/DataTransferResource.java | 837 ++++++++++++++ .../org/apache/nifi/web/api/FlowResource.java | 130 +-- .../nifi/web/api/SiteToSiteResource.java | 948 +-------------- .../apache/nifi/web/api/TenantsResource.java | 144 ++- .../apache/nifi/web/api/dto/DtoFactory.java | 83 +- .../nifi/web/api/dto/EntityFactory.java | 124 +- .../nifi/web/controller/ControllerFacade.java | 10 + .../apache/nifi/web/dao/AccessPolicyDAO.java | 13 +- .../org/apache/nifi/web/dao/UserGroupDAO.java | 9 + .../StandardPolicyBasedAuthorizerDAO.java | 61 +- .../main/resources/nifi-web-api-context.xml | 6 + .../web/StandardNiFiServiceFacadeSpec.groovy | 24 +- ...tandardPolicyBasedAuthorizerDAOSpec.groovy | 13 +- .../ITConnectionAccessControl.java | 32 +- .../accesscontrol/ITFunnelAccessControl.java | 32 +- .../ITInputPortAccessControl.java | 32 +- .../accesscontrol/ITLabelAccessControl.java | 32 +- .../ITOutputPortAccessControl.java | 32 +- .../ITProcessGroupAccessControl.java | 32 +- .../ITProcessorAccessControl.java | 48 +- .../web/api/TestDataTransferResource.java | 355 ++++++ .../nifi/web/api/TestSiteToSiteResource.java | 389 +------ .../nifi-web/nifi-web-ui/pom.xml | 45 +- .../filters/bulletin-board.properties | 3 +- .../main/resources/filters/canvas.properties | 5 +- .../main/resources/filters/cluster.properties | 3 +- .../resources/filters/counters.properties | 3 +- .../main/resources/filters/history.properties | 3 +- .../resources/filters/provenance.properties | 3 +- .../main/resources/filters/summary.properties | 3 +- .../resources/filters/templates.properties | 3 +- .../resources/filters/users-min.properties | 18 + .../main/resources/filters/users.properties | 29 + .../src/main/webapp/WEB-INF/pages/canvas.jsp | 2 + .../src/main/webapp/WEB-INF/pages/users.jsp | 70 ++ .../WEB-INF/partials/canvas/canvas-header.jsp | 11 +- .../WEB-INF/partials/canvas/navigation.jsp | 6 + .../partials/canvas/policy-management.jsp | 92 ++ .../partials/canvas/search-users-dialog.jsp | 23 + .../partials/users/user-delete-dialog.jsp | 21 + .../WEB-INF/partials/users/user-dialog.jsp | 48 + .../WEB-INF/partials/users/users-content.jsp | 41 + .../src/main/webapp/css/canvas.css | 2 +- .../src/main/webapp/css/header.css | 9 - .../nifi-web-ui/src/main/webapp/css/main.css | 3 +- .../src/main/webapp/css/policy-management.css | 205 ++++ .../nifi-web-ui/src/main/webapp/css/users.css | 128 ++ .../webapp/js/jquery/combo/jquery.combo.js | 59 +- .../nf-ng-breadcrumbs-controller.js | 2 +- .../nf-ng-canvas-flow-status-controller.js | 7 - .../nf-ng-canvas-global-menu-controller.js | 23 +- .../nf-ng-canvas-graph-controls-controller.js | 2 +- .../nf-ng-canvas-operate-controller.js | 2 +- .../components/nf-ng-template-component.js | 2 +- .../main/webapp/js/nf/canvas/nf-actions.js | 17 +- .../main/webapp/js/nf/canvas/nf-birdseye.js | 4 +- .../webapp/js/nf/canvas/nf-canvas-utils.js | 12 +- .../src/main/webapp/js/nf/canvas/nf-canvas.js | 18 +- .../nf/canvas/nf-connection-configuration.js | 4 +- .../main/webapp/js/nf/canvas/nf-connection.js | 22 +- .../js/nf/canvas/nf-controller-service.js | 8 +- .../js/nf/canvas/nf-controller-services.js | 37 +- .../src/main/webapp/js/nf/canvas/nf-funnel.js | 4 +- .../src/main/webapp/js/nf/canvas/nf-label.js | 10 +- .../js/nf/canvas/nf-policy-management.js | 1027 +++++++++++++++++ .../src/main/webapp/js/nf/canvas/nf-port.js | 12 +- .../canvas/nf-process-group-configuration.js | 6 +- .../js/nf/canvas/nf-process-group-details.js | 68 -- .../webapp/js/nf/canvas/nf-process-group.js | 12 +- .../nf/canvas/nf-processor-configuration.js | 2 +- .../main/webapp/js/nf/canvas/nf-processor.js | 12 +- .../js/nf/canvas/nf-remote-process-group.js | 18 +- .../main/webapp/js/nf/canvas/nf-settings.js | 33 +- .../src/main/webapp/js/nf/nf-common.js | 28 +- .../main/webapp/js/nf/nf-processor-details.js | 2 +- .../js/nf/templates/nf-templates-table.js | 41 +- .../main/webapp/js/nf/users/nf-users-table.js | 907 +++++++++++++++ .../src/main/webapp/js/nf/users/nf-users.js | 147 +++ 109 files changed, 6176 insertions(+), 2324 deletions(-) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessPolicySummaryDTO.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PermissionsDTO.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessPolicySummaryEntity.java rename nifi-nar-bundles/nifi-framework-bundle/nifi-framework/{nifi-web/nifi-web-ui/src/main/webapp/css/process-group-details.css => nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TenantCollectionEntity.java} (55%) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TenantsEntity.java rename nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/{AccessPoliciesAuthorizable.java => DataTransferAuthorizable.java} (74%) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/DataTransferResource.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestDataTransferResource.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users-min.properties create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users.properties create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/policy-management.jsp create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/search-users-dialog.jsp create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/user-delete-dialog.jsp create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/user-dialog.jsp create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/users-content.jsp create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/policy-management.css create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/users.css create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-policy-management.js delete mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-details.js create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users-table.js create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users.js diff --git a/nifi-api/src/main/java/org/apache/nifi/authorization/RequestAction.java b/nifi-api/src/main/java/org/apache/nifi/authorization/RequestAction.java index 182988f88c4d..3e28a677150c 100644 --- a/nifi-api/src/main/java/org/apache/nifi/authorization/RequestAction.java +++ b/nifi-api/src/main/java/org/apache/nifi/authorization/RequestAction.java @@ -20,6 +20,27 @@ * Actions a user/entity can take on a resource. */ public enum RequestAction { - READ, - WRITE; + READ("read"), + WRITE("write"); + + private String value; + + RequestAction(String value) { + this.value = value; + } + + @Override + public String toString() { + return value.toLowerCase(); + } + + public static RequestAction valueOfValue(final String action) { + if (RequestAction.READ.toString().equals(action)) { + return RequestAction.READ; + } else if (RequestAction.WRITE.toString().equals(action)) { + return RequestAction.WRITE; + } else { + throw new IllegalArgumentException("Action must be one of [" + READ.toString() + ", " + WRITE.toString() + "]"); + } + } } diff --git a/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/util/SiteToSiteRestApiClient.java b/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/util/SiteToSiteRestApiClient.java index 4195ae917bab..8910598443aa 100644 --- a/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/util/SiteToSiteRestApiClient.java +++ b/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/util/SiteToSiteRestApiClient.java @@ -326,7 +326,7 @@ public String initiateTransaction(TransferDirection direction, String portId) th private String initiateTransaction(String portType, String portId) throws IOException { logger.debug("initiateTransaction handshaking portType={}, portId={}", portType, portId); - HttpPost post = createPost("/site-to-site/" + portType + "/" + portId + "/transactions"); + HttpPost post = createPost("/data-transfer/" + portType + "/" + portId + "/transactions"); post.setHeader("Accept", "application/json"); diff --git a/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/http/TestHttpClient.java b/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/http/TestHttpClient.java index 7240c7ae979a..3f6dd89208c5 100644 --- a/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/http/TestHttpClient.java +++ b/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/http/TestHttpClient.java @@ -386,29 +386,29 @@ public static void setup() throws Exception { servletHandler.addServletWithMapping(SiteInfoServlet.class, "/site-to-site"); servletHandler.addServletWithMapping(PeersServlet.class, "/site-to-site/peers"); - servletHandler.addServletWithMapping(PortTransactionsServlet.class, "/site-to-site/input-ports/input-running-id/transactions"); - servletHandler.addServletWithMapping(InputPortTransactionServlet.class, "/site-to-site/input-ports/input-running-id/transactions/transaction-id"); - servletHandler.addServletWithMapping(FlowFilesServlet.class, "/site-to-site/input-ports/input-running-id/transactions/transaction-id/flow-files"); + servletHandler.addServletWithMapping(PortTransactionsServlet.class, "/data-transfer/input-ports/input-running-id/transactions"); + servletHandler.addServletWithMapping(InputPortTransactionServlet.class, "/data-transfer/input-ports/input-running-id/transactions/transaction-id"); + servletHandler.addServletWithMapping(FlowFilesServlet.class, "/data-transfer/input-ports/input-running-id/transactions/transaction-id/flow-files"); - servletHandler.addServletWithMapping(PortTransactionsServlet.class, "/site-to-site/input-ports/input-timeout-id/transactions"); - servletHandler.addServletWithMapping(InputPortTransactionServlet.class, "/site-to-site/input-ports/input-timeout-id/transactions/transaction-id"); - servletHandler.addServletWithMapping(FlowFilesTimeoutServlet.class, "/site-to-site/input-ports/input-timeout-id/transactions/transaction-id/flow-files"); + servletHandler.addServletWithMapping(PortTransactionsServlet.class, "/data-transfer/input-ports/input-timeout-id/transactions"); + servletHandler.addServletWithMapping(InputPortTransactionServlet.class, "/data-transfer/input-ports/input-timeout-id/transactions/transaction-id"); + servletHandler.addServletWithMapping(FlowFilesTimeoutServlet.class, "/data-transfer/input-ports/input-timeout-id/transactions/transaction-id/flow-files"); - servletHandler.addServletWithMapping(PortTransactionsServlet.class, "/site-to-site/input-ports/input-timeout-data-ex-id/transactions"); - servletHandler.addServletWithMapping(InputPortTransactionServlet.class, "/site-to-site/input-ports/input-timeout-data-ex-id/transactions/transaction-id"); - servletHandler.addServletWithMapping(FlowFilesTimeoutAfterDataExchangeServlet.class, "/site-to-site/input-ports/input-timeout-data-ex-id/transactions/transaction-id/flow-files"); + servletHandler.addServletWithMapping(PortTransactionsServlet.class, "/data-transfer/input-ports/input-timeout-data-ex-id/transactions"); + servletHandler.addServletWithMapping(InputPortTransactionServlet.class, "/data-transfer/input-ports/input-timeout-data-ex-id/transactions/transaction-id"); + servletHandler.addServletWithMapping(FlowFilesTimeoutAfterDataExchangeServlet.class, "/data-transfer/input-ports/input-timeout-data-ex-id/transactions/transaction-id/flow-files"); - servletHandler.addServletWithMapping(PortTransactionsServlet.class, "/site-to-site/output-ports/output-running-id/transactions"); - servletHandler.addServletWithMapping(OutputPortTransactionServlet.class, "/site-to-site/output-ports/output-running-id/transactions/transaction-id"); - servletHandler.addServletWithMapping(FlowFilesServlet.class, "/site-to-site/output-ports/output-running-id/transactions/transaction-id/flow-files"); + servletHandler.addServletWithMapping(PortTransactionsServlet.class, "/data-transfer/output-ports/output-running-id/transactions"); + servletHandler.addServletWithMapping(OutputPortTransactionServlet.class, "/data-transfer/output-ports/output-running-id/transactions/transaction-id"); + servletHandler.addServletWithMapping(FlowFilesServlet.class, "/data-transfer/output-ports/output-running-id/transactions/transaction-id/flow-files"); - servletHandler.addServletWithMapping(PortTransactionsServlet.class, "/site-to-site/output-ports/output-timeout-id/transactions"); - servletHandler.addServletWithMapping(OutputPortTransactionServlet.class, "/site-to-site/output-ports/output-timeout-id/transactions/transaction-id"); - servletHandler.addServletWithMapping(FlowFilesTimeoutServlet.class, "/site-to-site/output-ports/output-timeout-id/transactions/transaction-id/flow-files"); + servletHandler.addServletWithMapping(PortTransactionsServlet.class, "/data-transfer/output-ports/output-timeout-id/transactions"); + servletHandler.addServletWithMapping(OutputPortTransactionServlet.class, "/data-transfer/output-ports/output-timeout-id/transactions/transaction-id"); + servletHandler.addServletWithMapping(FlowFilesTimeoutServlet.class, "/data-transfer/output-ports/output-timeout-id/transactions/transaction-id/flow-files"); - servletHandler.addServletWithMapping(PortTransactionsServlet.class, "/site-to-site/output-ports/output-timeout-data-ex-id/transactions"); - servletHandler.addServletWithMapping(OutputPortTransactionServlet.class, "/site-to-site/output-ports/output-timeout-data-ex-id/transactions/transaction-id"); - servletHandler.addServletWithMapping(FlowFilesTimeoutAfterDataExchangeServlet.class, "/site-to-site/output-ports/output-timeout-data-ex-id/transactions/transaction-id/flow-files"); + servletHandler.addServletWithMapping(PortTransactionsServlet.class, "/data-transfer/output-ports/output-timeout-data-ex-id/transactions"); + servletHandler.addServletWithMapping(OutputPortTransactionServlet.class, "/data-transfer/output-ports/output-timeout-data-ex-id/transactions/transaction-id"); + servletHandler.addServletWithMapping(FlowFilesTimeoutAfterDataExchangeServlet.class, "/data-transfer/output-ports/output-timeout-data-ex-id/transactions/transaction-id/flow-files"); server.start(); diff --git a/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/protocol/http/TestHttpClientTransaction.java b/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/protocol/http/TestHttpClientTransaction.java index bed5a46c5514..7f3ee5c476db 100644 --- a/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/protocol/http/TestHttpClientTransaction.java +++ b/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/protocol/http/TestHttpClientTransaction.java @@ -100,7 +100,7 @@ public void reportEvent(Severity severity, String category, String message) { public void testReceiveZeroFlowFile() throws IOException { SiteToSiteRestApiClient apiClient = mock(SiteToSiteRestApiClient.class); - final String transactionUrl = "http://www.example.com/site-to-site/input-ports/portId/transactions/transactionId"; + final String transactionUrl = "http://www.example.com/data-transfer/input-ports/portId/transactions/transactionId"; doReturn(false).when(apiClient).openConnectionForReceive(eq(transactionUrl), any(CommunicationsSession.class)); ByteArrayInputStream serverResponse = new ByteArrayInputStream(new byte[0]); @@ -116,7 +116,7 @@ public void testReceiveZeroFlowFile() throws IOException { public void testReceiveOneFlowFile() throws IOException { SiteToSiteRestApiClient apiClient = mock(SiteToSiteRestApiClient.class); - final String transactionUrl = "http://www.example.com/site-to-site/input-ports/portId/transactions/transactionId"; + final String transactionUrl = "http://www.example.com/data-transfer/input-ports/portId/transactions/transactionId"; doReturn(true).when(apiClient).openConnectionForReceive(eq(transactionUrl), any(CommunicationsSession.class)); TransactionResultEntity resultEntity = new TransactionResultEntity(); resultEntity.setResponseCode(CONFIRM_TRANSACTION.getCode()); @@ -138,7 +138,7 @@ public void testReceiveOneFlowFile() throws IOException { public void testReceiveTwoFlowFiles() throws IOException { SiteToSiteRestApiClient apiClient = mock(SiteToSiteRestApiClient.class); - final String transactionUrl = "http://www.example.com/site-to-site/input-ports/portId/transactions/transactionId"; + final String transactionUrl = "http://www.example.com/data-transfer/input-ports/portId/transactions/transactionId"; doReturn(true).when(apiClient).openConnectionForReceive(eq(transactionUrl), any(CommunicationsSession.class)); TransactionResultEntity resultEntity = new TransactionResultEntity(); resultEntity.setResponseCode(CONFIRM_TRANSACTION.getCode()); @@ -161,7 +161,7 @@ public void testReceiveTwoFlowFiles() throws IOException { public void testReceiveWithInvalidChecksum() throws IOException { SiteToSiteRestApiClient apiClient = mock(SiteToSiteRestApiClient.class); - final String transactionUrl = "http://www.example.com/site-to-site/input-ports/portId/transactions/transactionId"; + final String transactionUrl = "http://www.example.com/data-transfer/input-ports/portId/transactions/transactionId"; doReturn(true).when(apiClient).openConnectionForReceive(eq(transactionUrl), any(CommunicationsSession.class)); // The checksum is correct, but here we simulate as if it's wrong, BAD_CHECKSUM. TransactionResultEntity resultEntity = new TransactionResultEntity(); @@ -185,7 +185,7 @@ public void testReceiveWithInvalidChecksum() throws IOException { public void testSendZeroFlowFile() throws IOException { SiteToSiteRestApiClient apiClient = mock(SiteToSiteRestApiClient.class); - final String transactionUrl = "http://www.example.com/site-to-site/input-ports/portId/transactions/transactionId"; + final String transactionUrl = "http://www.example.com/data-transfer/input-ports/portId/transactions/transactionId"; doNothing().when(apiClient).openConnectionForSend(eq(transactionUrl), any(CommunicationsSession.class)); ByteArrayOutputStream serverResponseBos = new ByteArrayOutputStream(); @@ -202,7 +202,7 @@ public void testSendZeroFlowFile() throws IOException { public void testSendOneFlowFile() throws IOException { SiteToSiteRestApiClient apiClient = mock(SiteToSiteRestApiClient.class); - final String transactionUrl = "http://www.example.com/site-to-site/input-ports/portId/transactions/transactionId"; + final String transactionUrl = "http://www.example.com/data-transfer/input-ports/portId/transactions/transactionId"; doNothing().when(apiClient).openConnectionForSend(eq(transactionUrl), any(CommunicationsSession.class)); // Emulate that server returns correct checksum. doAnswer(new Answer() { @@ -236,7 +236,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { public void testSendTwoFlowFiles() throws IOException { SiteToSiteRestApiClient apiClient = mock(SiteToSiteRestApiClient.class); - final String transactionUrl = "http://www.example.com/site-to-site/input-ports/portId/transactions/transactionId"; + final String transactionUrl = "http://www.example.com/data-transfer/input-ports/portId/transactions/transactionId"; doNothing().when(apiClient).openConnectionForSend(eq("portId"), any(CommunicationsSession.class)); // Emulate that server returns correct checksum. doAnswer(new Answer() { @@ -271,7 +271,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { @Test public void testSendWithInvalidChecksum() throws IOException { SiteToSiteRestApiClient apiClient = mock(SiteToSiteRestApiClient.class); - final String transactionUrl = "http://www.example.com/site-to-site/input-ports/portId/transactions/transactionId"; + final String transactionUrl = "http://www.example.com/data-transfer/input-ports/portId/transactions/transactionId"; doNothing().when(apiClient).openConnectionForSend(eq(transactionUrl), any(CommunicationsSession.class)); // Emulate that server returns incorrect checksum. doAnswer(new Answer() { @@ -312,7 +312,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { public void testSendButDestinationFull() throws IOException { SiteToSiteRestApiClient apiClient = mock(SiteToSiteRestApiClient.class); - final String transactionUrl = "http://www.example.com/site-to-site/input-ports/portId/transactions/transactionId"; + final String transactionUrl = "http://www.example.com/data-transfer/input-ports/portId/transactions/transactionId"; doNothing().when(apiClient).openConnectionForSend(eq("portId"), any(CommunicationsSession.class)); // Emulate that server returns correct checksum. doAnswer(new Answer() { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessPolicyDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessPolicyDTO.java index 3f99556a1e81..4f9653f281ca 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessPolicyDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessPolicyDTO.java @@ -26,55 +26,10 @@ * Details for the access configuration. */ @XmlType(name = "accessPolicy") -public class AccessPolicyDTO extends ComponentDTO { +public class AccessPolicyDTO extends AccessPolicySummaryDTO { - private String resource; private Set users; private Set userGroups; - private Boolean canRead; - private Boolean canWrite; - - /** - * @return Indicates whether the user can read a given resource. - */ - @ApiModelProperty( - value = "Indicates whether the user can read a given resource.", - readOnly = true - ) - public Boolean getCanRead() { - return canRead; - } - - public void setCanRead(Boolean canRead) { - this.canRead = canRead; - } - - /** - * @return Indicates whether the user can write a given resource. - */ - @ApiModelProperty( - value = "Indicates whether the user can write a given resource.", - readOnly = true - ) - public Boolean getCanWrite() { - return canWrite; - } - - public void setCanWrite(Boolean canWrite) { - this.canWrite = canWrite; - } - - /** - * @return The resource ID for this access policy. - */ - @ApiModelProperty(value="The resource ID for this access policy.") - public String getResource() { - return resource; - } - - public void setResource(String resource) { - this.resource = resource; - } /** * @return The set of user IDs associated with this access policy. diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessPolicySummaryDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessPolicySummaryDTO.java new file mode 100644 index 000000000000..0fb97c1a4a6e --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessPolicySummaryDTO.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api.dto; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +import javax.xml.bind.annotation.XmlType; + +/** + * Details for the access configuration. + */ +@XmlType(name = "accessPolicy") +public class AccessPolicySummaryDTO extends ComponentDTO { + + private String resource; + private String action; + + /** + * @return The action associated with this access policy. + */ + @ApiModelProperty( + value = "The action associated with this access policy.", + allowableValues = "READ, WRITE" + ) + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + /** + * @return The resource for this access policy. + */ + @ApiModelProperty(value="The resource for this access policy.") + public String getResource() { + return resource; + } + + public void setResource(String resource) { + this.resource = resource; + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PermissionsDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PermissionsDTO.java new file mode 100644 index 000000000000..dc5b7f94cc3b --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PermissionsDTO.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api.dto; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +import javax.xml.bind.annotation.XmlType; + +/** + * Details for the access configuration. + */ +@XmlType(name = "permission") +public class PermissionsDTO { + + private Boolean canRead; + private Boolean canWrite; + + /** + * @return Indicates whether the user can read a given resource. + */ + @ApiModelProperty( + value = "Indicates whether the user can read a given resource.", + readOnly = true + ) + public Boolean getCanRead() { + return canRead; + } + + public void setCanRead(Boolean canRead) { + this.canRead = canRead; + } + + /** + * @return Indicates whether the user can write a given resource. + */ + @ApiModelProperty( + value = "Indicates whether the user can write a given resource.", + readOnly = true + ) + public Boolean getCanWrite() { + return canWrite; + } + + public void setCanWrite(Boolean canWrite) { + this.canWrite = canWrite; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/UserDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/UserDTO.java index 52da60836313..e86630c86e5d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/UserDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/UserDTO.java @@ -17,6 +17,7 @@ package org.apache.nifi.web.api.dto; import com.wordnik.swagger.annotations.ApiModelProperty; +import org.apache.nifi.web.api.entity.AccessPolicySummaryEntity; import org.apache.nifi.web.api.entity.TenantEntity; import javax.xml.bind.annotation.XmlType; @@ -29,11 +30,15 @@ public class UserDTO extends TenantDTO { private Set userGroups; + private Set accessPolicies; /** * @return groups to which the user belongs */ - @ApiModelProperty(value = "The groups to which the user belongs.") + @ApiModelProperty( + value = "The groups to which the user belongs. This field is read only and it provided for convenience.", + readOnly = true + ) public Set getUserGroups() { return userGroups; } @@ -41,4 +46,19 @@ public Set getUserGroups() { public void setUserGroups(Set userGroups) { this.userGroups = userGroups; } + + /** + * @return policies this user is part of + */ + @ApiModelProperty( + value = "The access policies this user belongs to.", + readOnly = true + ) + public Set getAccessPolicies() { + return accessPolicies; + } + + public void setAccessPolicies(Set accessPolicies) { + this.accessPolicies = accessPolicies; + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/UserGroupDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/UserGroupDTO.java index f167f9131a75..af63a14e2af5 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/UserGroupDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/UserGroupDTO.java @@ -17,6 +17,7 @@ package org.apache.nifi.web.api.dto; import com.wordnik.swagger.annotations.ApiModelProperty; +import org.apache.nifi.web.api.entity.AccessPolicyEntity; import org.apache.nifi.web.api.entity.TenantEntity; import javax.xml.bind.annotation.XmlType; @@ -29,6 +30,7 @@ public class UserGroupDTO extends TenantDTO { private Set users; + private Set accessPolicies; /** * @return users in this group @@ -41,4 +43,19 @@ public Set getUsers() { public void setUsers(Set users) { this.users = users; } + + /** + * @return policies this user group is part of + */ + @ApiModelProperty( + value = "The access policies this user group belongs to.", + readOnly = true + ) + public Set getAccessPolicies() { + return accessPolicies; + } + + public void setAccessPolicies(Set accessPolicies) { + this.accessPolicies = accessPolicies; + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessPolicyEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessPolicyEntity.java index 82a977f7ef86..40b6512aae6a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessPolicyEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessPolicyEntity.java @@ -17,8 +17,11 @@ package org.apache.nifi.web.api.entity; import org.apache.nifi.web.api.dto.AccessPolicyDTO; +import org.apache.nifi.web.api.dto.util.TimeAdapter; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.util.Date; /** * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to an {@link AccessPolicyDTO}. @@ -26,6 +29,7 @@ @XmlRootElement(name = "accessPolicyEntity") public class AccessPolicyEntity extends ComponentEntity { + private Date generated; private AccessPolicyDTO component; /** @@ -41,4 +45,15 @@ public void setComponent(AccessPolicyDTO component) { this.component = component; } + /** + * @return When this content was generated + */ + @XmlJavaTypeAdapter(TimeAdapter.class) + public Date getGenerated() { + return generated; + } + + public void setGenerated(Date generated) { + this.generated = generated; + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessPolicySummaryEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessPolicySummaryEntity.java new file mode 100644 index 000000000000..afae44de4117 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AccessPolicySummaryEntity.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api.entity; + +import org.apache.nifi.web.api.dto.AccessPolicySummaryDTO; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. + * This particular entity holds a reference to an {@link AccessPolicySummaryDTO}. + */ +@XmlRootElement(name = "accessPolicySummaryEntity") +public class AccessPolicySummaryEntity extends ComponentEntity { + + private AccessPolicySummaryDTO component; + + /** + * The {@link AccessPolicySummaryDTO} that is being serialized. + * + * @return The {@link AccessPolicySummaryDTO} object + */ + public AccessPolicySummaryDTO getComponent() { + return component; + } + + public void setComponent(AccessPolicySummaryDTO component) { + this.component = component; + } + +} 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 6d6b02246153..f09880baba06 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 @@ -17,8 +17,8 @@ package org.apache.nifi.web.api.entity; import com.wordnik.swagger.annotations.ApiModelProperty; -import org.apache.nifi.web.api.dto.AccessPolicyDTO; import org.apache.nifi.web.api.dto.BulletinDTO; +import org.apache.nifi.web.api.dto.PermissionsDTO; import org.apache.nifi.web.api.dto.PositionDTO; import org.apache.nifi.web.api.dto.RevisionDTO; @@ -35,7 +35,7 @@ public class ComponentEntity extends Entity { private RevisionDTO revision; private String id; private PositionDTO position; - private AccessPolicyDTO accessPolicy; + private PermissionsDTO permissions; private List bulletins; /** @@ -85,19 +85,19 @@ public void setPosition(PositionDTO position) { } /** - * The access policy for this component. + * The permissions for this component. * - * @return The access policy + * @return The permissions */ @ApiModelProperty( - value = "The access policy for this component." + value = "The permissions for this component." ) - public AccessPolicyDTO getAccessPolicy() { - return accessPolicy; + public PermissionsDTO getPermissions() { + return permissions; } - public void setAccessPolicy(AccessPolicyDTO accessPolicy) { - this.accessPolicy = accessPolicy; + public void setPermissions(PermissionsDTO permissions) { + this.permissions = permissions; } /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerConfigurationEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerConfigurationEntity.java index 066950e471ce..c7aa0cd5da9b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerConfigurationEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerConfigurationEntity.java @@ -17,8 +17,8 @@ package org.apache.nifi.web.api.entity; import com.wordnik.swagger.annotations.ApiModelProperty; -import org.apache.nifi.web.api.dto.AccessPolicyDTO; import org.apache.nifi.web.api.dto.ControllerConfigurationDTO; +import org.apache.nifi.web.api.dto.PermissionsDTO; import org.apache.nifi.web.api.dto.RevisionDTO; import org.apache.nifi.web.api.dto.util.TimeAdapter; @@ -35,7 +35,7 @@ public class ControllerConfigurationEntity extends Entity { private Date currentTime; private ControllerConfigurationDTO controllerConfiguration; private RevisionDTO revision; - private AccessPolicyDTO accessPolicy; + private PermissionsDTO permissions; /** * @return revision for this request/response @@ -72,19 +72,19 @@ public void setControllerConfiguration(ControllerConfigurationDTO controllerConf } /** - * The access policy for this component. + * The permissions for this component. * - * @return The access policy + * @return The permissions */ @ApiModelProperty( - value = "The access policy for the controller." + value = "The permissions for this component." ) - public AccessPolicyDTO getAccessPolicy() { - return accessPolicy; + public PermissionsDTO getPermissions() { + return permissions; } - public void setAccessPolicy(AccessPolicyDTO accessPolicy) { - this.accessPolicy = accessPolicy; + public void setPermissions(PermissionsDTO permissions) { + this.permissions = permissions; } /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java index 1a7ff1c69f95..7bb627040d5e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java @@ -16,7 +16,8 @@ */ package org.apache.nifi.web.api.entity; -import org.apache.nifi.web.api.dto.AccessPolicyDTO; +import com.wordnik.swagger.annotations.ApiModelProperty; +import org.apache.nifi.web.api.dto.PermissionsDTO; import javax.xml.bind.annotation.XmlRootElement; @@ -29,14 +30,16 @@ public class CurrentUserEntity extends Entity { private String identity; private boolean anonymous; - private AccessPolicyDTO provenancePermissions; - private AccessPolicyDTO countersPermissions; - private AccessPolicyDTO tenantsPermissions; - private AccessPolicyDTO controllerPermissions; + private PermissionsDTO provenancePermissions; + private PermissionsDTO countersPermissions; + private PermissionsDTO tenantsPermissions; + private PermissionsDTO controllerPermissions; + private PermissionsDTO policiesPermissions; /** * @return the user identity being serialized */ + @ApiModelProperty("The user identity being serialized.") public String getIdentity() { return identity; } @@ -48,6 +51,7 @@ public void setIdentity(String identity) { /** * @return if the user is anonymous */ + @ApiModelProperty("Whether the current user is anonymous.") public boolean isAnonymous() { return anonymous; } @@ -59,44 +63,60 @@ public void setAnonymous(boolean anonymous) { /** * @return if the use can query provenance */ - public AccessPolicyDTO getProvenancePermissions() { + @ApiModelProperty("Permissions for querying provenance.") + public PermissionsDTO getProvenancePermissions() { return provenancePermissions; } - public void setProvenancePermissions(AccessPolicyDTO provenancePermissions) { + public void setProvenancePermissions(PermissionsDTO provenancePermissions) { this.provenancePermissions = provenancePermissions; } /** * @return permissions for accessing counters */ - public AccessPolicyDTO getCountersPermissions() { + @ApiModelProperty("Permissions for accessing counters.") + public PermissionsDTO getCountersPermissions() { return countersPermissions; } - public void setCountersPermissions(AccessPolicyDTO countersPermissions) { + public void setCountersPermissions(PermissionsDTO countersPermissions) { this.countersPermissions = countersPermissions; } /** * @return permissions for accessing users */ - public AccessPolicyDTO getTenantsPermissions() { + @ApiModelProperty("Permissions for accessing tenants.") + public PermissionsDTO getTenantsPermissions() { return tenantsPermissions; } - public void setTenantsPermissions(AccessPolicyDTO tenantsPermissions) { + public void setTenantsPermissions(PermissionsDTO tenantsPermissions) { this.tenantsPermissions = tenantsPermissions; } /** * @return permissions for accessing the controller */ - public AccessPolicyDTO getControllerPermissions() { + @ApiModelProperty("Permissions for accessing the controller.") + public PermissionsDTO getControllerPermissions() { return controllerPermissions; } - public void setControllerPermissions(AccessPolicyDTO controllerPermissions) { + public void setControllerPermissions(PermissionsDTO controllerPermissions) { this.controllerPermissions = controllerPermissions; } + + /** + * @return permissions for accessing the all policies + */ + @ApiModelProperty("Permissions for accessing the policies.") + public PermissionsDTO getPoliciesPermissions() { + return policiesPermissions; + } + + public void setPoliciesPermissions(PermissionsDTO policiesPermissions) { + this.policiesPermissions = policiesPermissions; + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/FlowBreadcrumbEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/FlowBreadcrumbEntity.java index ddd84075e847..4b042f7599d3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/FlowBreadcrumbEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/FlowBreadcrumbEntity.java @@ -17,7 +17,7 @@ package org.apache.nifi.web.api.entity; import com.wordnik.swagger.annotations.ApiModelProperty; -import org.apache.nifi.web.api.dto.AccessPolicyDTO; +import org.apache.nifi.web.api.dto.PermissionsDTO; import org.apache.nifi.web.api.dto.flow.FlowBreadcrumbDTO; import javax.xml.bind.annotation.XmlRootElement; @@ -29,7 +29,7 @@ public class FlowBreadcrumbEntity extends Entity { private String id; - private AccessPolicyDTO accessPolicy; + private PermissionsDTO permissions; private FlowBreadcrumbDTO breadcrumb; private FlowBreadcrumbEntity parentBreadcrumb; @@ -50,19 +50,19 @@ public void setId(String id) { } /** - * The access policy for this ancestor ProcessGroup. + * The permissions for this ancestor ProcessGroup. * - * @return The access policy + * @return The permissions */ @ApiModelProperty( - value = "The access policy for this ancestor ProcessGroup." + value = "The permissions for this ancestor ProcessGroup." ) - public AccessPolicyDTO getAccessPolicy() { - return accessPolicy; + public PermissionsDTO getPermissions() { + return permissions; } - public void setAccessPolicy(AccessPolicyDTO accessPolicy) { - this.accessPolicy = accessPolicy; + public void setPermissions(PermissionsDTO permissions) { + this.permissions = permissions; } /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ProcessGroupEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ProcessGroupEntity.java index 6f649bf35dae..4db154a906d1 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ProcessGroupEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ProcessGroupEntity.java @@ -16,12 +16,12 @@ */ package org.apache.nifi.web.api.entity; -import javax.xml.bind.annotation.XmlRootElement; - import com.wordnik.swagger.annotations.ApiModelProperty; import org.apache.nifi.web.api.dto.ProcessGroupDTO; import org.apache.nifi.web.api.dto.status.ProcessGroupStatusDTO; +import javax.xml.bind.annotation.XmlRootElement; + /** * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a ProcessGroupDTO. */ @@ -44,7 +44,7 @@ public class ProcessGroupEntity extends ComponentEntity { /** * The ProcessGroupDTO that is being serialized. * - * @return The ControllerDTO object + * @return The ProcessGroupDTO object */ public ProcessGroupDTO getComponent() { return component; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ProcessGroupFlowEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ProcessGroupFlowEntity.java index 8dd3b0525dab..ebcb0f857252 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ProcessGroupFlowEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ProcessGroupFlowEntity.java @@ -17,7 +17,7 @@ package org.apache.nifi.web.api.entity; import com.wordnik.swagger.annotations.ApiModelProperty; -import org.apache.nifi.web.api.dto.AccessPolicyDTO; +import org.apache.nifi.web.api.dto.PermissionsDTO; import org.apache.nifi.web.api.dto.flow.ProcessGroupFlowDTO; import javax.xml.bind.annotation.XmlRootElement; @@ -28,23 +28,23 @@ @XmlRootElement(name = "processGroupFlowEntity") public class ProcessGroupFlowEntity extends Entity { - private AccessPolicyDTO accessPolicy; + private PermissionsDTO permissions; private ProcessGroupFlowDTO processGroupFlow; /** - * The access policy for this component. + * The permissions for this component. * - * @return The access policy + * @return The permissions */ @ApiModelProperty( value = "The access policy for this process group." ) - public AccessPolicyDTO getAccessPolicy() { - return accessPolicy; + public PermissionsDTO getPermissions() { + return permissions; } - public void setAccessPolicy(AccessPolicyDTO accessPolicy) { - this.accessPolicy = accessPolicy; + public void setPermissions(PermissionsDTO permissions) { + this.permissions = permissions; } /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/process-group-details.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TenantCollectionEntity.java similarity index 55% rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/process-group-details.css rename to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TenantCollectionEntity.java index a19791a6d562..73c35bd2c47c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/process-group-details.css +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TenantCollectionEntity.java @@ -14,9 +14,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#process-group-details { - z-index: 1301; - display: none; - width: 400px; - height: 290px; -} \ No newline at end of file +package org.apache.nifi.web.api.entity; + +import javax.xml.bind.annotation.XmlRootElement; +import java.util.Set; + +/** + * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. + */ +@XmlRootElement(name = "tenantCollectionEntity") +public class TenantCollectionEntity extends ComponentEntity { + + private Set tenants; + + /** + * The tenants being serialized. + * + * @return The tenants + */ + public Set getTenants() { + return tenants; + } + + public void setTenants(Set tenants) { + this.tenants = tenants; + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TenantsEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TenantsEntity.java new file mode 100644 index 000000000000..49c51c3f2bca --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TenantsEntity.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api.entity; + +import javax.xml.bind.annotation.XmlRootElement; +import java.util.Collection; + +/** + * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a collection of + * TenantEntity objects. + */ +@XmlRootElement(name = "tenantsEntity") +public class TenantsEntity { + + private Collection users; + private Collection userGroups; + + /** + * The collection of users that are being serialized. + * + * @return users + */ + public Collection getUsers() { + return users; + } + + public void setUsers(Collection users) { + this.users = users; + } + + /** + * The collection of user groups that are being serialized. + * + * @return user groups + */ + public Collection getUserGroups() { + return userGroups; + } + + public void setUserGroups(Collection userGroups) { + this.userGroups = userGroups; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/UsersEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/UsersEntity.java index 66243389b7e0..7969f6bd12a6 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/UsersEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/UsersEntity.java @@ -16,8 +16,12 @@ */ package org.apache.nifi.web.api.entity; +import org.apache.nifi.web.api.dto.util.TimeAdapter; + import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import java.util.Collection; +import java.util.Date; /** * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a collection of UserEntity @@ -26,6 +30,7 @@ @XmlRootElement(name = "usersEntity") public class UsersEntity extends Entity { + private Date generated; private Collection users; /** @@ -40,4 +45,16 @@ public Collection getUsers() { public void setUsers(Collection users) { this.users = users; } + + /** + * @return When this content was generated + */ + @XmlJavaTypeAdapter(TimeAdapter.class) + public Date getGenerated() { + return generated; + } + + public void setGenerated(Date generated) { + this.generated = generated; + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/AccessPolicyAuthorizable.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/AccessPolicyAuthorizable.java index 5d5eab7c1a61..08583e19b621 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/AccessPolicyAuthorizable.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/AccessPolicyAuthorizable.java @@ -16,38 +16,39 @@ */ package org.apache.nifi.authorization.resource; -import org.apache.nifi.authorization.AccessPolicy; import org.apache.nifi.authorization.Resource; +/** + * Authorizable for policies of an Authorizable. + */ public class AccessPolicyAuthorizable implements Authorizable { + final Authorizable authorizable; - private final AccessPolicy policy; - - public AccessPolicyAuthorizable(AccessPolicy policy) { - this.policy = policy; + public AccessPolicyAuthorizable(Authorizable authorizable) { + this.authorizable = authorizable; } @Override public Authorizable getParentAuthorizable() { - return new Authorizable() { - @Override - public Authorizable getParentAuthorizable() { - return null; - } - - @Override - public Resource getResource() { - return ResourceFactory.getPoliciesResource(); - } - }; + if (authorizable.getParentAuthorizable() == null) { + return new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return ResourceFactory.getPoliciesResource(); + } + }; + } else { + return new AccessPolicyAuthorizable(authorizable.getParentAuthorizable()); + } } @Override public Resource getResource() { - return ResourceFactory.getPolicyResource(policy.getIdentifier()); - } - - public AccessPolicy getPolicy() { - return policy; + return ResourceFactory.getPolicyResource(authorizable.getResource()); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/AccessPoliciesAuthorizable.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/DataTransferAuthorizable.java similarity index 74% rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/AccessPoliciesAuthorizable.java rename to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/DataTransferAuthorizable.java index ad4ba2993b2b..53aecbcd8bce 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/AccessPoliciesAuthorizable.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/DataTransferAuthorizable.java @@ -18,7 +18,15 @@ import org.apache.nifi.authorization.Resource; -public class AccessPoliciesAuthorizable implements Authorizable { +/** + * Authorizable for policies of an Authorizable. + */ +public class DataTransferAuthorizable implements Authorizable { + final Authorizable authorizable; + + public DataTransferAuthorizable(Authorizable authorizable) { + this.authorizable = authorizable; + } @Override public Authorizable getParentAuthorizable() { @@ -27,6 +35,6 @@ public Authorizable getParentAuthorizable() { @Override public Resource getResource() { - return ResourceFactory.getPoliciesResource(); + return ResourceFactory.getDataTransferResource(authorizable.getResource()); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceFactory.java index 058a1c849891..004429acda44 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceFactory.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceFactory.java @@ -16,7 +16,6 @@ */ package org.apache.nifi.authorization.resource; -import org.apache.nifi.authorization.AccessPolicy; import org.apache.nifi.authorization.Resource; import java.util.Objects; @@ -287,18 +286,6 @@ public String getName() { } }; - private final static Resource TOKEN_RESOURCE = new Resource() { - @Override - public String getIdentifier() { - return ResourceType.Token.getValue(); - } - - @Override - public String getName() { - return "API access token"; - } - }; - private final static Resource POLICIES_RESOURCE = new Resource() { @Override @@ -384,15 +371,6 @@ public static Resource getOutputPortResource() { return OUTPUT_PORT_RESOURCE; } - /** - * Gets the Resource for accessing Policies which allows management of Access Policies. - * - * @return The resource for accessing Policies - */ - public static Resource getPolicyResource() { - return POLICY_RESOURCE; - } - /** * Gets the Resource for accessing Processors. * @@ -494,15 +472,6 @@ public static Resource getTemplateResource() { return TEMPLATE_RESOURCE; } - /** - * Gets the Resource for creating API access tokens. - * - * @return The token request resource - */ - public static Resource getTokenResource() { - return TOKEN_RESOURCE; - } - /** * Gets the Resource for accessing Tenants which includes creating, modifying, and deleting Users and UserGroups. * @@ -513,19 +482,20 @@ public static Resource getTenantResource() { } /** - * Gets a Resource for performing site to site on a port. + * Gets a Resource for performing transferring data to a port. * + * @param isInputPort Whether this port is an input port or an output port * @param identifier The identifier of the component being accessed * @param name The name of the component being accessed * @return The resource */ - public static Resource getSiteToSiteResource(final String identifier, final String name) { + public static Resource getDataTransferResource(final boolean isInputPort, final String identifier, final String name) { Objects.requireNonNull(identifier, "The component identifier must be specified."); return new Resource() { @Override public String getIdentifier() { - return String.format("%s/%s", ResourceType.SiteToSite.getValue(), identifier); + return String.format("%s/%s/%s", ResourceType.DataTransfer.getValue(), isInputPort ? "input-ports" : "output-ports", identifier); } @Override @@ -536,7 +506,29 @@ public String getName() { } /** - * Gets the {@link Resource} for accessing {@link AccessPolicy}s. + * Gets a Resource for performing transferring data to a port. + * + * @param resource The resource to transfer data to + * @return The resource + */ + public static Resource getDataTransferResource(final Resource resource) { + Objects.requireNonNull(resource, "The resource must be specified."); + + return new Resource() { + @Override + public String getIdentifier() { + return String.format("%s%s", ResourceType.DataTransfer.getValue(), resource.getIdentifier()); + } + + @Override + public String getName() { + return "Transfer data to " + resource.getName(); + } + }; + } + + /** + * Gets the {@link Resource} for accessing access policies. * @return The policies resource */ public static Resource getPoliciesResource() { @@ -544,23 +536,23 @@ public static Resource getPoliciesResource() { } /** - * Gets a Resource for accessing an {@link AccessPolicy} configuration. + * Gets a Resource for accessing a resources's policies. * - * @param identifier The identifier of the component being accessed + * @param resource The resource being accessed * @return The resource */ - public static Resource getPolicyResource(final String identifier) { - Objects.requireNonNull(identifier, "The component identifier must be specified."); + public static Resource getPolicyResource(final Resource resource) { + Objects.requireNonNull(resource, "The resource type must be specified."); return new Resource() { @Override public String getIdentifier() { - return String.format("%s/%s", POLICIES_RESOURCE.getIdentifier(), identifier); + return String.format("%s%s", POLICY_RESOURCE.getIdentifier(), resource.getIdentifier()); } @Override public String getName() { - return identifier; + return "Policies for " + resource.getName(); } }; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceType.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceType.java index 1464cabdac0c..177b9a4a2975 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceType.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceType.java @@ -36,10 +36,10 @@ public enum ResourceType { ReportingTask("/reporting-tasks"), Resource("/resources"), SiteToSite("/site-to-site"), + DataTransfer("/data-transfer"), System("/system"), Template("/templates"), - Tenant("/tenants"), - Token("/token"); + Tenant("/tenants"); final String value; @@ -50,4 +50,21 @@ private ResourceType(final String value) { public String getValue() { return value; } + + public static ResourceType valueOfValue(final String rawValue) { + ResourceType type = null; + + for (final ResourceType rt : values()) { + if (rt.getValue().equals(rawValue)) { + type = rt; + break; + } + } + + if (type == null) { + throw new IllegalArgumentException("Unknown resource type value " + rawValue); + } + + return type; + } } \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/StandardRootGroupPort.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/StandardRootGroupPort.java index b94d86fb5f21..64c4b7f5a1cb 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/StandardRootGroupPort.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/StandardRootGroupPort.java @@ -16,12 +16,13 @@ */ package org.apache.nifi.remote; -import org.apache.nifi.authorization.AuthorizationRequest; import org.apache.nifi.authorization.AuthorizationResult; import org.apache.nifi.authorization.AuthorizationResult.Result; import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.RequestAction; -import org.apache.nifi.authorization.resource.ResourceFactory; +import org.apache.nifi.authorization.resource.Authorizable; +import org.apache.nifi.authorization.resource.DataTransferAuthorizable; +import org.apache.nifi.authorization.user.StandardNiFiUser; import org.apache.nifi.components.ValidationResult; import org.apache.nifi.connectable.ConnectableType; import org.apache.nifi.controller.AbstractPort; @@ -355,17 +356,10 @@ public PortAuthorizationResult checkUserAuthorization(final String dn) { return new StandardPortAuthorizationResult(false, "User DN is not known"); } - // build the request - final AuthorizationRequest request = new AuthorizationRequest.Builder() - .identity(dn) - .anonymous(false) - .accessAttempt(true) - .action(RequestAction.WRITE) - .resource(ResourceFactory.getSiteToSiteResource(getIdentifier(), getName())) - .build(); - // perform the authorization - final AuthorizationResult result = authorizer.authorize(request); + final Authorizable dataTransferAuthorizable = new DataTransferAuthorizable(this); + final AuthorizationResult result = dataTransferAuthorizable.checkAuthorization(authorizer, RequestAction.WRITE, new StandardNiFiUser(dn)); + if (!Result.Approved.equals(result.getResult())) { final String message = String.format("%s authorization failed for user %s because %s", this, dn, result.getExplanation()); logger.warn(message); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/AuthorizableLookup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/AuthorizableLookup.java index 01f06c2abad7..7408e30dad98 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/AuthorizableLookup.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/AuthorizableLookup.java @@ -16,7 +16,6 @@ */ package org.apache.nifi.web; -import org.apache.nifi.authorization.AccessPolicy; import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.controller.Snippet; @@ -178,18 +177,36 @@ public interface AuthorizableLookup { * Get the {@link Authorizable} that represents the resource of users and user groups. * @return authorizable */ - Authorizable getTenantAuthorizable(); + Authorizable getTenant(); /** - * Get the {@link Authorizable} the represents the parent resource of {@link AccessPolicy} resources. + * Get the authorizable for access all policies. + * + * @return authorizable + */ + Authorizable getPolicies(); + + /** + * Get the authorizable for the policy of the policy id. + * + * @param id id * @return authorizable */ - Authorizable getAccessPoliciesAuthorizable(); + Authorizable getAccessPolicyById(String id); /** - * Get the {@link Authorizable} the represents the {@link AccessPolicy} with the given ID. - * @param id access policy ID + * Get the authorizable for the policy of the specified resource. + * + * @param resource resource + * @return authorizable + */ + Authorizable getAccessPolicyByResource(String resource); + + /** + * Get the authorizable of the specified resource. + * + * @param resource resource * @return authorizable */ - Authorizable getAccessPolicyAuthorizable(String id); + Authorizable getAuthorizableFromResource(final String resource); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java index b8d9cfdedb5f..6fb3123e8210 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java @@ -16,13 +16,7 @@ */ package org.apache.nifi.web; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; - +import org.apache.nifi.authorization.RequestAction; import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.controller.ScheduledState; import org.apache.nifi.controller.repository.claim.ContentDirection; @@ -98,6 +92,13 @@ import org.apache.nifi.web.api.entity.UserEntity; import org.apache.nifi.web.api.entity.UserGroupEntity; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; + /** * Defines the NiFiServiceFacade interface. */ @@ -152,7 +153,7 @@ public interface NiFiServiceFacade { // ---------------------------------------- // Controller methods // ---------------------------------------- - ControllerDTO getController(); + ControllerDTO getSiteToSiteDetails(); /** * Searches the controller for the specified query string. @@ -1274,6 +1275,14 @@ public interface NiFiServiceFacade { */ AccessPolicyEntity getAccessPolicy(String accessPolicyId); + /** + * Gets the access policy for the specified action, resource type, and component id. + * + * @param resource resource + * @return access policy + */ + AccessPolicyEntity getAccessPolicy(RequestAction requestAction, String resource); + /** * Updates the specified access policy. * @param revision Revision to compare with current base revision diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardAuthorizableLookup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardAuthorizableLookup.java index 413988823afc..7b9da19790a3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardAuthorizableLookup.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardAuthorizableLookup.java @@ -16,11 +16,15 @@ */ package org.apache.nifi.web; +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.authorization.AccessPolicy; import org.apache.nifi.authorization.Resource; -import org.apache.nifi.authorization.resource.AccessPoliciesAuthorizable; import org.apache.nifi.authorization.resource.AccessPolicyAuthorizable; import org.apache.nifi.authorization.resource.Authorizable; +import org.apache.nifi.authorization.resource.DataTransferAuthorizable; +import org.apache.nifi.authorization.resource.ProvenanceEventAuthorizable; import org.apache.nifi.authorization.resource.ResourceFactory; +import org.apache.nifi.authorization.resource.ResourceType; import org.apache.nifi.authorization.resource.TenantAuthorizable; import org.apache.nifi.controller.ConfiguredComponent; import org.apache.nifi.controller.Snippet; @@ -46,7 +50,17 @@ class StandardAuthorizableLookup implements AuthorizableLookup { private static final TenantAuthorizable TENANT_AUTHORIZABLE = new TenantAuthorizable(); - private static final Authorizable ACCESS_POLICIES_AUTHORIZABLE = new AccessPoliciesAuthorizable(); + private static final Authorizable POLICIES_AUTHORIZABLE = new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return ResourceFactory.getPoliciesResource(); + } + }; private static final Authorizable PROVENANCE_AUTHORIZABLE = new Authorizable() { @Override @@ -193,18 +207,243 @@ public Snippet getSnippet(final String id) { } @Override - public Authorizable getTenantAuthorizable() { + public Authorizable getTenant() { return TENANT_AUTHORIZABLE; } @Override - public Authorizable getAccessPoliciesAuthorizable() { - return ACCESS_POLICIES_AUTHORIZABLE; + public Authorizable getPolicies() { + return POLICIES_AUTHORIZABLE; } @Override - public Authorizable getAccessPolicyAuthorizable(String id) { - return new AccessPolicyAuthorizable(accessPolicyDAO.getAccessPolicy(id)); + public Authorizable getAccessPolicyById(final String id) { + final AccessPolicy policy = accessPolicyDAO.getAccessPolicy(id); + return getAccessPolicyByResource(policy.getResource()); + } + + @Override + public Authorizable getAccessPolicyByResource(final String resource) { + try { + return new AccessPolicyAuthorizable(getAuthorizableFromResource(resource)); + } catch (final ResourceNotFoundException e) { + // the underlying component has been removed or resource is invalid... require /policies permissions + return POLICIES_AUTHORIZABLE; + } + } + + @Override + public Authorizable getAuthorizableFromResource(String resource) { + // parse the resource type + ResourceType resourceType = null; + for (ResourceType type : ResourceType.values()) { + if (resource.equals(type.getValue()) || resource.startsWith(type.getValue() + "/")) { + resourceType = type; + } + } + + if (resourceType == null) { + throw new ResourceNotFoundException("Unrecognized resource: " + resource); + } + + // if this is a policy or a provenance event resource, there should be another resource type + if (ResourceType.Policy.equals(resourceType) || ResourceType.ProvenanceEvent.equals(resourceType) || ResourceType.DataTransfer.equals(resourceType)) { + final ResourceType primaryResourceType = resourceType; + + // get the resource type + resource = StringUtils.substringAfter(resource, resourceType.getValue()); + + for (ResourceType type : ResourceType.values()) { + if (resource.equals(type.getValue()) || resource.startsWith(type.getValue() + "/")) { + resourceType = type; + } + } + + if (resourceType == null) { + throw new ResourceNotFoundException("Unrecognized resource: " + resource); + } + + // must either be a policy, event, or data transfer + if (ResourceType.Policy.equals(primaryResourceType)) { + return new AccessPolicyAuthorizable(getAccessPolicy(resourceType, resource)); + } else if (ResourceType.ProvenanceEvent.equals(primaryResourceType)) { + return new ProvenanceEventAuthorizable(getAccessPolicy(resourceType, resource)); + } else { + return new DataTransferAuthorizable(getAccessPolicy(resourceType, resource)); + } + } else { + return getAccessPolicy(resourceType, resource); + } + } + + private Authorizable getAccessPolicy(final ResourceType resourceType, final String resource) { + final String slashComponentId = StringUtils.substringAfter(resource, resourceType.getValue()); + if (slashComponentId.startsWith("/")) { + return getAccessPolicyByResource(resourceType, slashComponentId.substring(1)); + } else { + return getAccessPolicyByResource(resourceType); + } + } + + private Authorizable getAccessPolicyByResource(final ResourceType resourceType, final String componentId) { + Authorizable authorizable = null; + switch (resourceType) { + case Connection: + authorizable = getConnection(componentId); + break; + case ControllerService: + authorizable = getControllerService(componentId); + break; + case Funnel: + authorizable = getFunnel(componentId); + break; + case InputPort: + authorizable = getInputPort(componentId); + break; + case Label: + authorizable = getLabel(componentId); + break; + case OutputPort: + authorizable = getOutputPort(componentId); + break; + case Processor: + authorizable = getProcessor(componentId); + break; + case ProcessGroup: + authorizable = getProcessGroup(componentId); + break; + case RemoteProcessGroup: + authorizable = getRemoteProcessGroup(componentId); + break; + case ReportingTask: + authorizable = getReportingTask(componentId); + break; + case Template: + authorizable = getTemplate(componentId); + break; + case ProvenanceEvent: + authorizable = controllerFacade.getProvenanceEventAuthorizable(componentId); + break; + } + + if (authorizable == null) { + throw new IllegalArgumentException("An unexpected type of resource in this policy " + resourceType.getValue()); + } + + return authorizable; + } + + private Authorizable getAccessPolicyByResource(final ResourceType resourceType) { + Authorizable authorizable = null; + switch (resourceType) { + case Controller: + authorizable = getController(); + break; + case Counters: + authorizable = new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return ResourceFactory.getCountersResource(); + } + }; + break; + case Flow: + authorizable = new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return ResourceFactory.getFlowResource(); + } + }; + break; + case Provenance: + authorizable = new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return ResourceFactory.getProvenanceResource(); + } + }; + break; + case Proxy: + authorizable = new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return ResourceFactory.getProxyResource(); + } + }; + break; + case Policy: + authorizable = POLICIES_AUTHORIZABLE; + break; + case Resource: + authorizable = new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return ResourceFactory.getResourceResource(); + } + }; + break; + case SiteToSite: + // TODO - new site-to-site and port specific site-to-site + authorizable = new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return ResourceFactory.getSiteToSiteResource(); + } + }; + break; + case System: + authorizable = new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return ResourceFactory.getSystemResource(); + } + }; + break; + case Tenant: + authorizable = getTenant(); + break; + } + + if (authorizable == null) { + throw new IllegalArgumentException("An unexpected type of resource in this policy " + resourceType.getValue()); + } + + return authorizable; } @Override diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java index 490a9bb975dc..3875f229aa7b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java @@ -16,29 +16,7 @@ */ package org.apache.nifi.web; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; - +import com.google.common.collect.Sets; import org.apache.nifi.action.Action; import org.apache.nifi.action.Component; import org.apache.nifi.action.FlowChangeAction; @@ -55,6 +33,7 @@ import org.apache.nifi.authorization.Resource; import org.apache.nifi.authorization.User; import org.apache.nifi.authorization.resource.Authorizable; +import org.apache.nifi.authorization.resource.DataTransferAuthorizable; import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.cluster.coordination.ClusterCoordinator; @@ -101,6 +80,7 @@ import org.apache.nifi.reporting.BulletinRepository; import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.web.api.dto.AccessPolicyDTO; +import org.apache.nifi.web.api.dto.AccessPolicySummaryDTO; import org.apache.nifi.web.api.dto.BulletinBoardDTO; import org.apache.nifi.web.api.dto.BulletinDTO; import org.apache.nifi.web.api.dto.BulletinQueryDTO; @@ -127,6 +107,7 @@ import org.apache.nifi.web.api.dto.LabelDTO; import org.apache.nifi.web.api.dto.ListingRequestDTO; import org.apache.nifi.web.api.dto.NodeDTO; +import org.apache.nifi.web.api.dto.PermissionsDTO; import org.apache.nifi.web.api.dto.PortDTO; import org.apache.nifi.web.api.dto.PreviousValueDTO; import org.apache.nifi.web.api.dto.ProcessGroupDTO; @@ -160,6 +141,7 @@ import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO; import org.apache.nifi.web.api.dto.status.StatusHistoryDTO; import org.apache.nifi.web.api.entity.AccessPolicyEntity; +import org.apache.nifi.web.api.entity.AccessPolicySummaryEntity; import org.apache.nifi.web.api.entity.ConnectionEntity; import org.apache.nifi.web.api.entity.ControllerConfigurationEntity; import org.apache.nifi.web.api.entity.ControllerServiceEntity; @@ -210,7 +192,27 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.Sets; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; /** * Implementation of NiFiServiceFacade that performs revision checking. @@ -451,11 +453,12 @@ public void verifyDeleteReportingTask(final String reportingTaskId) { // ----------------------------------------- // Write Operations // ----------------------------------------- + @Override public AccessPolicyEntity updateAccessPolicy(final Revision revision, final AccessPolicyDTO accessPolicyDTO) { - final Authorizable accessPolicyAuthorizable = authorizableLookup.getAccessPolicyAuthorizable(accessPolicyDTO.getId()); + final Authorizable authorizable = authorizableLookup.getAccessPolicyById(accessPolicyDTO.getId()); final RevisionUpdate snapshot = updateComponent(revision, - accessPolicyAuthorizable, + authorizable, () -> accessPolicyDAO.updateAccessPolicy(accessPolicyDTO), accessPolicy -> { final Set users = accessPolicy.getUsers().stream().map(mapUserIdToTenantEntity()).collect(Collectors.toSet()); @@ -463,33 +466,38 @@ public AccessPolicyEntity updateAccessPolicy(final Revision revision, final Acce return dtoFactory.createAccessPolicyDto(accessPolicy, userGroups, users); }); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(accessPolicyAuthorizable); - return entityFactory.createAccessPolicyEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(authorizable); + return entityFactory.createAccessPolicyEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions); } @Override public UserEntity updateUser(final Revision revision, final UserDTO userDTO) { - final Authorizable usersAuthorizable = authorizableLookup.getTenantAuthorizable(); + final Authorizable usersAuthorizable = authorizableLookup.getTenant(); final Set groups = userGroupDAO.getUserGroupsForUser(userDTO.getId()); + final Set policies = userGroupDAO.getAccessPoliciesForUser(userDTO.getId()); final RevisionUpdate snapshot = updateComponent(revision, usersAuthorizable, () -> userDAO.updateUser(userDTO), - user -> dtoFactory.createUserDto(user, groups.stream().map(g -> g.getIdentifier()).map(mapUserGroupIdToTenantEntity()).collect(Collectors.toSet()))); + user -> { + final Set tenantEntities = groups.stream().map(g -> g.getIdentifier()).map(mapUserGroupIdToTenantEntity()).collect(Collectors.toSet()); + final Set policyEntities = policies.stream().map(ap -> createAccessPolicySummaryEntity(ap)).collect(Collectors.toSet()); + return dtoFactory.createUserDto(user, tenantEntities, policyEntities); + }); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(usersAuthorizable); - return entityFactory.createUserEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(usersAuthorizable); + return entityFactory.createUserEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions); } @Override public UserGroupEntity updateUserGroup(final Revision revision, final UserGroupDTO userGroupDTO) { - final Authorizable userGroupsAuthorizable = authorizableLookup.getTenantAuthorizable(); + final Authorizable userGroupsAuthorizable = authorizableLookup.getTenant(); final RevisionUpdate snapshot = updateComponent(revision, userGroupsAuthorizable, () -> userGroupDAO.updateUserGroup(userGroupDTO), userGroup -> dtoFactory.createUserGroupDto(userGroup, userGroup.getUsers().stream().map(mapUserIdToTenantEntity()).collect(Collectors.toSet()))); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(userGroupsAuthorizable); - return entityFactory.createUserGroupEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(userGroupsAuthorizable); + return entityFactory.createUserGroupEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions); } @Override @@ -502,9 +510,9 @@ public ConnectionEntity updateConnection(final Revision revision, final Connecti () -> connectionDAO.updateConnection(connectionDTO), connection -> dtoFactory.createConnectionDto(connection)); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(connectionNode); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(connectionNode); final ConnectionStatusDTO status = dtoFactory.createConnectionStatusDto(controllerFacade.getConnectionStatus(connectionNode.getIdentifier())); - return entityFactory.createConnectionEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy, status); + return entityFactory.createConnectionEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, status); } @Override @@ -516,10 +524,10 @@ public ProcessorEntity updateProcessor(final Revision revision, final ProcessorD () -> processorDAO.updateProcessor(processorDTO), proc -> dtoFactory.createProcessorDto(proc)); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(processorNode); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(processorNode); final ProcessorStatusDTO status = dtoFactory.createProcessorStatusDto(controllerFacade.getProcessorStatus(processorNode.getIdentifier())); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(processorNode.getIdentifier())); - return entityFactory.createProcessorEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy, status, bulletins); + return entityFactory.createProcessorEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, status, bulletins); } @Override @@ -530,8 +538,8 @@ public LabelEntity updateLabel(final Revision revision, final LabelDTO labelDTO) () -> labelDAO.updateLabel(labelDTO), label -> dtoFactory.createLabelDto(label)); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(labelNode); - return entityFactory.createLabelEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(labelNode); + return entityFactory.createLabelEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions); } @Override @@ -542,8 +550,8 @@ public FunnelEntity updateFunnel(final Revision revision, final FunnelDTO funnel () -> funnelDAO.updateFunnel(funnelDTO), funnel -> dtoFactory.createFunnelDto(funnel)); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(funnelNode); - return entityFactory.createFunnelEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(funnelNode); + return entityFactory.createFunnelEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions); } @@ -638,10 +646,10 @@ public PortEntity updateInputPort(final Revision revision, final PortDTO inputPo () -> inputPortDAO.updatePort(inputPortDTO), port -> dtoFactory.createPortDto(port)); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(inputPortNode); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(inputPortNode); final PortStatusDTO status = dtoFactory.createPortStatusDto(controllerFacade.getInputPortStatus(inputPortNode.getIdentifier())); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(inputPortNode.getIdentifier())); - return entityFactory.createPortEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy, status, bulletins); + return entityFactory.createPortEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, status, bulletins); } @Override @@ -652,10 +660,10 @@ public PortEntity updateOutputPort(final Revision revision, final PortDTO output () -> outputPortDAO.updatePort(outputPortDTO), port -> dtoFactory.createPortDto(port)); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(outputPortNode); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(outputPortNode); final PortStatusDTO status = dtoFactory.createPortStatusDto(controllerFacade.getOutputPortStatus(outputPortNode.getIdentifier())); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(outputPortNode.getIdentifier())); - return entityFactory.createPortEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy, status, bulletins); + return entityFactory.createPortEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, status, bulletins); } @Override @@ -667,11 +675,11 @@ public RemoteProcessGroupEntity updateRemoteProcessGroup(final Revision revision () -> remoteProcessGroupDAO.updateRemoteProcessGroup(remoteProcessGroupDTO), remoteProcessGroup -> dtoFactory.createRemoteProcessGroupDto(remoteProcessGroup)); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(remoteProcessGroupNode); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(remoteProcessGroupNode); final RevisionDTO updateRevision = dtoFactory.createRevisionDTO(snapshot.getLastModification()); final RemoteProcessGroupStatusDTO status = dtoFactory.createRemoteProcessGroupStatusDto(controllerFacade.getRemoteProcessGroupStatus(remoteProcessGroupNode.getIdentifier())); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(remoteProcessGroupNode.getIdentifier())); - return entityFactory.createRemoteProcessGroupEntity(snapshot.getComponent(), updateRevision, accessPolicy, status, bulletins); + return entityFactory.createRemoteProcessGroupEntity(snapshot.getComponent(), updateRevision, permissions, status, bulletins); } @Override @@ -685,9 +693,9 @@ public RemoteProcessGroupPortEntity updateRemoteProcessGroupInputPort( () -> remoteProcessGroupDAO.updateRemoteProcessGroupInputPort(remoteProcessGroupId, remoteProcessGroupPortDTO), remoteGroupPort -> dtoFactory.createRemoteProcessGroupPortDto(remoteGroupPort)); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(remoteProcessGroupNode); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(remoteProcessGroupNode); final RevisionDTO updatedRevision = dtoFactory.createRevisionDTO(snapshot.getLastModification()); - return entityFactory.createRemoteProcessGroupPortEntity(snapshot.getComponent(), updatedRevision, accessPolicy); + return entityFactory.createRemoteProcessGroupPortEntity(snapshot.getComponent(), updatedRevision, permissions); } @Override @@ -701,9 +709,9 @@ public RemoteProcessGroupPortEntity updateRemoteProcessGroupOutputPort( () -> remoteProcessGroupDAO.updateRemoteProcessGroupOutputPort(remoteProcessGroupId, remoteProcessGroupPortDTO), remoteGroupPort -> dtoFactory.createRemoteProcessGroupPortDto(remoteGroupPort)); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(remoteProcessGroupNode); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(remoteProcessGroupNode); final RevisionDTO updatedRevision = dtoFactory.createRevisionDTO(snapshot.getLastModification()); - return entityFactory.createRemoteProcessGroupPortEntity(snapshot.getComponent(), updatedRevision, accessPolicy); + return entityFactory.createRemoteProcessGroupPortEntity(snapshot.getComponent(), updatedRevision, permissions); } @Override @@ -714,11 +722,11 @@ public ProcessGroupEntity updateProcessGroup(final Revision revision, final Proc () -> processGroupDAO.updateProcessGroup(processGroupDTO), processGroup -> dtoFactory.createProcessGroupDto(processGroup)); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(processGroupNode); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(processGroupNode); final RevisionDTO updatedRevision = dtoFactory.createRevisionDTO(snapshot.getLastModification()); final ProcessGroupStatusDTO status = dtoFactory.createConciseProcessGroupStatusDto(controllerFacade.getProcessGroupStatus(processGroupNode.getIdentifier())); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(processGroupNode.getIdentifier())); - return entityFactory.createProcessGroupEntity(snapshot.getComponent(), updatedRevision, accessPolicy, status, bulletins); + return entityFactory.createProcessGroupEntity(snapshot.getComponent(), updatedRevision, permissions, status, bulletins); } @Override @@ -766,9 +774,9 @@ public ControllerConfigurationEntity updateControllerConfiguration(final Revisio }, controller -> dtoFactory.createControllerConfigurationDto(controllerFacade)); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(controllerFacade); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(controllerFacade); final RevisionDTO updateRevision = dtoFactory.createRevisionDTO(updatedComponent.getLastModification()); - return entityFactory.createControllerConfigurationEntity(updatedComponent.getComponent(), updateRevision, accessPolicy); + return entityFactory.createControllerConfigurationEntity(updatedComponent.getComponent(), updateRevision, permissions); } @Override @@ -892,11 +900,13 @@ public UserEntity deleteUser(final Revision revision, final String userId) { final User user = userDAO.getUser(userId); final Set userGroups = user != null ? userGroupDAO.getUserGroupsForUser(userId).stream() .map(g -> g.getIdentifier()).map(mapUserGroupIdToTenantEntity()).collect(Collectors.toSet()) : null; + final Set policyEntities = user != null ? userGroupDAO.getAccessPoliciesForUser(userId).stream() + .map(ap -> createAccessPolicySummaryEntity(ap)).collect(Collectors.toSet()) : null; final UserDTO snapshot = deleteComponent( revision, - authorizableLookup.getTenantAuthorizable(), + authorizableLookup.getTenant(), () -> userDAO.deleteUser(userId), - dtoFactory.createUserDto(user, userGroups)); + dtoFactory.createUserDto(user, userGroups, policyEntities)); return entityFactory.createUserEntity(snapshot, null, null); } @@ -909,7 +919,7 @@ public UserGroupEntity deleteUserGroup(final Revision revision, final String use null; final UserGroupDTO snapshot = deleteComponent( revision, - authorizableLookup.getTenantAuthorizable(), + authorizableLookup.getTenant(), () -> userGroupDAO.deleteUserGroup(userGroupId), dtoFactory.createUserGroupDto(userGroup, users)); @@ -923,7 +933,7 @@ public AccessPolicyEntity deleteAccessPolicy(final Revision revision, final Stri final Set users = accessPolicy != null ? accessPolicy.getUsers().stream().map(mapUserIdToTenantEntity()).collect(Collectors.toSet()) : null; final AccessPolicyDTO snapshot = deleteComponent( revision, - authorizableLookup.getAccessPolicyAuthorizable(accessPolicyId), + authorizableLookup.getAccessPolicyById(accessPolicyId), () -> accessPolicyDAO.deleteAccessPolicy(accessPolicyId), dtoFactory.createAccessPolicyDto(accessPolicy, userGroups, users)); @@ -1064,9 +1074,9 @@ public ConnectionEntity createConnection(final Revision revision, final String g connection -> dtoFactory.createConnectionDto(connection)); final Connection connection = connectionDAO.getConnection(connectionDTO.getId()); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(connection); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(connection); final ConnectionStatusDTO status = dtoFactory.createConnectionStatusDto(controllerFacade.getConnectionStatus(connectionDTO.getId())); - return entityFactory.createConnectionEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy, status); + return entityFactory.createConnectionEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, status); } @Override @@ -1099,10 +1109,10 @@ public ProcessorEntity createProcessor(final Revision revision, final String gro processor -> dtoFactory.createProcessorDto(processor)); final ProcessorNode processor = processorDAO.getProcessor(processorDTO.getId()); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(processor); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(processor); final ProcessorStatusDTO status = dtoFactory.createProcessorStatusDto(controllerFacade.getProcessorStatus(processorDTO.getId())); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(processorDTO.getId())); - return entityFactory.createProcessorEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy, status, bulletins); + return entityFactory.createProcessorEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, status, bulletins); } @Override @@ -1114,8 +1124,8 @@ public LabelEntity createLabel(final Revision revision, final String groupId, fi label -> dtoFactory.createLabelDto(label)); final Label label = labelDAO.getLabel(labelDTO.getId()); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(label); - return entityFactory.createLabelEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(label); + return entityFactory.createLabelEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions); } /** @@ -1159,55 +1169,63 @@ public FunnelEntity createFunnel(final Revision revision, final String groupId, funnel -> dtoFactory.createFunnelDto(funnel)); final Funnel funnel = funnelDAO.getFunnel(funnelDTO.getId()); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(funnel); - return entityFactory.createFunnelEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(funnel); + return entityFactory.createFunnelEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions); } @Override public AccessPolicyEntity createAccessPolicy(final Revision revision, final AccessPolicyDTO accessPolicyDTO) { - // TODO read lock on users and groups (and resource+action?) while the policy is being created? - final Authorizable tenantAuthorizable = authorizableLookup.getTenantAuthorizable(); + final Authorizable tenantAuthorizable = authorizableLookup.getTenant(); final String creator = NiFiUserUtils.getNiFiUserIdentity(); + final AccessPolicy newAccessPolicy = accessPolicyDAO.createAccessPolicy(accessPolicyDTO); final AccessPolicyDTO newAccessPolicyDto = dtoFactory.createAccessPolicyDto(newAccessPolicy, newAccessPolicy.getGroups().stream().map(mapUserGroupIdToTenantEntity()).collect(Collectors.toSet()), newAccessPolicy.getUsers().stream().map(userId -> { final RevisionDTO userRevision = dtoFactory.createRevisionDTO(revisionManager.getRevision(userId)); return entityFactory.createTenantEntity(dtoFactory.createTenantDTO(userDAO.getUser(userId)), userRevision, - dtoFactory.createAccessPolicyDto(tenantAuthorizable)); + dtoFactory.createPermissionsDto(tenantAuthorizable)); }).collect(Collectors.toSet())); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(authorizableLookup.getAccessPolicyAuthorizable(newAccessPolicy.getIdentifier())); - return entityFactory.createAccessPolicyEntity(newAccessPolicyDto, dtoFactory.createRevisionDTO(new FlowModification(revision, creator)), accessPolicy); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(authorizableLookup.getAccessPolicyById(accessPolicyDTO.getId())); + return entityFactory.createAccessPolicyEntity(newAccessPolicyDto, dtoFactory.createRevisionDTO(new FlowModification(revision, creator)), permissions); } @Override public UserEntity createUser(final Revision revision, final UserDTO userDTO) { - final Authorizable tenantAuthorizable = authorizableLookup.getTenantAuthorizable(); final String creator = NiFiUserUtils.getNiFiUserIdentity(); final User newUser = userDAO.createUser(userDTO); - final Set groups = userGroupDAO.getUserGroupsForUser(newUser.getIdentifier()); - final UserDTO newUserDto = dtoFactory.createUserDto(newUser, groups.stream() - .map(g -> g.getIdentifier()).map(mapUserGroupIdToTenantEntity()).collect(Collectors.toSet())); + final Set tenantEntities = userGroupDAO.getUserGroupsForUser(newUser.getIdentifier()).stream() + .map(g -> g.getIdentifier()).map(mapUserGroupIdToTenantEntity()).collect(Collectors.toSet()); + final Set policyEntities = userGroupDAO.getAccessPoliciesForUser(newUser.getIdentifier()).stream() + .map(ap -> createAccessPolicySummaryEntity(ap)).collect(Collectors.toSet()); + final UserDTO newUserDto = dtoFactory.createUserDto(newUser, tenantEntities, policyEntities); + + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(authorizableLookup.getTenant()); + return entityFactory.createUserEntity(newUserDto, dtoFactory.createRevisionDTO(new FlowModification(revision, creator)), permissions); + } - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(authorizableLookup.getTenantAuthorizable()); - return entityFactory.createUserEntity(newUserDto, dtoFactory.createRevisionDTO(new FlowModification(revision, creator)), accessPolicy); + private AccessPolicySummaryEntity createAccessPolicySummaryEntity(final AccessPolicy ap) { + final AccessPolicySummaryDTO apSummary = dtoFactory.createAccessPolicySummaryDto(ap); + final PermissionsDTO apPermissions = dtoFactory.createPermissionsDto(authorizableLookup.getAccessPolicyById(ap.getIdentifier())); + final RevisionDTO apRevision = dtoFactory.createRevisionDTO(revisionManager.getRevision(ap.getIdentifier())); + return entityFactory.createAccessPolicySummaryEntity(apSummary, apRevision, apPermissions); } @Override public UserGroupEntity createUserGroup(final Revision revision, final UserGroupDTO userGroupDTO) { - final Authorizable tenantAuthorizable = authorizableLookup.getTenantAuthorizable(); + final Authorizable tenantAuthorizable = authorizableLookup.getTenant(); final String creator = NiFiUserUtils.getNiFiUserIdentity(); final Group newUserGroup = userGroupDAO.createUserGroup(userGroupDTO); final UserGroupDTO newUserGroupDto = dtoFactory.createUserGroupDto(newUserGroup, newUserGroup.getUsers().stream() .map(userId -> { final RevisionDTO userRevision = dtoFactory.createRevisionDTO(revisionManager.getRevision(userId)); return entityFactory.createTenantEntity(dtoFactory.createTenantDTO(userDAO.getUser(userId)), userRevision, - dtoFactory.createAccessPolicyDto(tenantAuthorizable)); + dtoFactory.createPermissionsDto(tenantAuthorizable)); }).collect(Collectors.toSet())); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(authorizableLookup.getTenantAuthorizable()); - return entityFactory.createUserGroupEntity(newUserGroupDto, dtoFactory.createRevisionDTO(new FlowModification(revision, creator)), accessPolicy); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(authorizableLookup.getTenant()); + return entityFactory.createUserGroupEntity(newUserGroupDto, dtoFactory.createRevisionDTO(new FlowModification(revision, creator)), permissions); } private void validateSnippetContents(final FlowSnippetDTO flow) { @@ -1308,10 +1326,10 @@ public PortEntity createInputPort(final Revision revision, final String groupId, port -> dtoFactory.createPortDto(port)); final Port port = inputPortDAO.getPort(inputPortDTO.getId()); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(port); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(port); final PortStatusDTO status = dtoFactory.createPortStatusDto(controllerFacade.getInputPortStatus(port.getIdentifier())); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(port.getIdentifier())); - return entityFactory.createPortEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy, status, bulletins); + return entityFactory.createPortEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, status, bulletins); } @Override @@ -1323,10 +1341,10 @@ public PortEntity createOutputPort(final Revision revision, final String groupId port -> dtoFactory.createPortDto(port)); final Port port = outputPortDAO.getPort(outputPortDTO.getId()); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(port); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(port); final PortStatusDTO status = dtoFactory.createPortStatusDto(controllerFacade.getOutputPortStatus(port.getIdentifier())); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(port.getIdentifier())); - return entityFactory.createPortEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy, status, bulletins); + return entityFactory.createPortEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, status, bulletins); } @Override @@ -1338,10 +1356,10 @@ public ProcessGroupEntity createProcessGroup(final Revision revision, final Stri processGroup -> dtoFactory.createProcessGroupDto(processGroup)); final ProcessGroup processGroup = processGroupDAO.getProcessGroup(processGroupDTO.getId()); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(processGroup); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(processGroup); final ProcessGroupStatusDTO status = dtoFactory.createConciseProcessGroupStatusDto(controllerFacade.getProcessGroupStatus(processGroup.getIdentifier())); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(processGroup.getIdentifier())); - return entityFactory.createProcessGroupEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy, status, bulletins); + return entityFactory.createProcessGroupEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, status, bulletins); } @Override @@ -1353,10 +1371,10 @@ public RemoteProcessGroupEntity createRemoteProcessGroup(final Revision revision remoteProcessGroup -> dtoFactory.createRemoteProcessGroupDto(remoteProcessGroup)); final RemoteProcessGroup remoteProcessGroup = remoteProcessGroupDAO.getRemoteProcessGroup(remoteProcessGroupDTO.getId()); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(remoteProcessGroup); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(remoteProcessGroup); final RemoteProcessGroupStatusDTO status = dtoFactory.createRemoteProcessGroupStatusDto(controllerFacade.getRemoteProcessGroupStatus(remoteProcessGroup.getIdentifier())); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(remoteProcessGroup.getIdentifier())); - return entityFactory.createRemoteProcessGroupEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy, status, bulletins); + return entityFactory.createRemoteProcessGroupEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, status, bulletins); } @Override @@ -1515,9 +1533,9 @@ public ControllerServiceEntity createControllerService(final Revision revision, } final ControllerServiceNode controllerService = controllerServiceDAO.getControllerService(controllerServiceDTO.getId()); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(controllerService); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(controllerService); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(controllerServiceDTO.getId())); - return entityFactory.createControllerServiceEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy, bulletins); + return entityFactory.createControllerServiceEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, bulletins); } @Override @@ -1529,9 +1547,9 @@ public ControllerServiceEntity updateControllerService(final Revision revision, () -> controllerServiceDAO.updateControllerService(controllerServiceDTO), cs -> dtoFactory.createControllerServiceDto(cs)); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(controllerService); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(controllerService); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(controllerServiceDTO.getId())); - return entityFactory.createControllerServiceEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy, bulletins); + return entityFactory.createControllerServiceEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, bulletins); } @Override @@ -1567,18 +1585,16 @@ public RevisionUpdate update() { * Finds the identifiers for all components referencing a ControllerService. * * @param reference ControllerServiceReference - * @param referencingIds Collection of identifiers * @param visited ControllerServices we've already visited */ - private void findControllerServiceReferencingComponentIdentifiers(final ControllerServiceReference reference, final Set referencingIds, final Set visited) { + private void findControllerServiceReferencingComponentIdentifiers(final ControllerServiceReference reference, final Set visited) { for (final ConfiguredComponent component : reference.getReferencingComponents()) { - referencingIds.add(component.getIdentifier()); // if this is a ControllerService consider it's referencing components if (component instanceof ControllerServiceNode) { final ControllerServiceNode node = (ControllerServiceNode) component; if (!visited.contains(node)) { - findControllerServiceReferencingComponentIdentifiers(node.getReferences(), referencingIds, visited); + findControllerServiceReferencingComponentIdentifiers(node.getReferences(), visited); } visited.add(node); } @@ -1592,13 +1608,9 @@ private void findControllerServiceReferencingComponentIdentifiers(final Controll * @return The entity */ private ControllerServiceReferencingComponentsEntity createControllerServiceReferencingComponentsEntity(final ControllerServiceReference reference, final Set lockedIds) { - final Set referencingIds = new HashSet<>(); final Set visited = new HashSet<>(); visited.add(reference.getReferencedComponent()); - findControllerServiceReferencingComponentIdentifiers(reference, referencingIds, visited); - - // TODO remove once we can update a read lock - referencingIds.removeAll(lockedIds); + findControllerServiceReferencingComponentIdentifiers(reference, visited); final Map referencingRevisions = new HashMap<>(); for (final ConfiguredComponent component : reference.getReferencingComponents()) { @@ -1635,9 +1647,9 @@ private ControllerServiceReferencingComponentsEntity createControllerServiceRefe final Set componentEntities = new HashSet<>(); for (final ConfiguredComponent refComponent : referencingComponents) { - AccessPolicyDTO accessPolicy = null; + PermissionsDTO permissions = null; if (refComponent instanceof Authorizable) { - accessPolicy = dtoFactory.createAccessPolicyDto(refComponent); + permissions = dtoFactory.createPermissionsDto(refComponent); } final Revision revision = revisions.get(refComponent.getIdentifier()); @@ -1661,7 +1673,7 @@ private ControllerServiceReferencingComponentsEntity createControllerServiceRefe visited.add(node); } - componentEntities.add(entityFactory.createControllerServiceReferencingComponentEntity(dto, revisionDto, accessPolicy)); + componentEntities.add(entityFactory.createControllerServiceReferencingComponentEntity(dto, revisionDto, permissions)); } final ControllerServiceReferencingComponentsEntity entity = new ControllerServiceReferencingComponentsEntity(); @@ -1703,9 +1715,9 @@ public ReportingTaskEntity createReportingTask(final Revision revision, final Re }); final ReportingTaskNode reportingTask = reportingTaskDAO.getReportingTask(reportingTaskDTO.getId()); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(reportingTask); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(reportingTask); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(reportingTask.getIdentifier())); - return entityFactory.createReportingTaskEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy, bulletins); + return entityFactory.createReportingTaskEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, bulletins); } @Override @@ -1717,9 +1729,9 @@ public ReportingTaskEntity updateReportingTask(final Revision revision, final Re () -> reportingTaskDAO.updateReportingTask(reportingTaskDTO), rt -> dtoFactory.createReportingTaskDto(rt)); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(reportingTask); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(reportingTask); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(reportingTask.getIdentifier())); - return entityFactory.createReportingTaskEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), accessPolicy, bulletins); + return entityFactory.createReportingTaskEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, bulletins); } @Override @@ -1881,9 +1893,9 @@ public CountersDTO getCounters() { private ConnectionEntity createConnectionEntity(final Connection connection) { final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(connection.getIdentifier())); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(connection); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(connection); final ConnectionStatusDTO status = dtoFactory.createConnectionStatusDto(controllerFacade.getConnectionStatus(connection.getIdentifier())); - return entityFactory.createConnectionEntity(dtoFactory.createConnectionDto(connection), revision, accessPolicy, status); + return entityFactory.createConnectionEntity(dtoFactory.createConnectionDto(connection), revision, permissions, status); } @Override @@ -1938,7 +1950,7 @@ public StatusHistoryDTO getConnectionStatusHistory(final String connectionId) { private ProcessorEntity createProcessorEntity(final ProcessorNode processor) { final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(processor.getIdentifier())); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(processor); + final PermissionsDTO accessPolicy = dtoFactory.createPermissionsDto(processor); final ProcessorStatusDTO status = dtoFactory.createProcessorStatusDto(controllerFacade.getProcessorStatus(processor.getIdentifier())); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(processor.getIdentifier())); return entityFactory.createProcessorEntity(dtoFactory.createProcessorDto(processor), revision, accessPolicy, status, bulletins); @@ -1972,11 +1984,11 @@ public Set getTemplates() { return templateDAO.getTemplates().stream() .map(template -> { final TemplateDTO dto = dtoFactory.createTemplateDTO(template); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(template); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(template); final TemplateEntity entity = new TemplateEntity(); entity.setId(dto.getId()); - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setTemplate(dto); return entity; }).collect(Collectors.toSet()); @@ -2087,21 +2099,19 @@ private boolean isUserAuthorized(final NiFiUser user, final RootGroupPort port) return true; } - // TODO - defer to authorizer to see if user is able to retrieve site-to-site details for the specified port - return true; + // authorize this port for data transfer + final Authorizable dataTransferAuthorizable = new DataTransferAuthorizable(port); + final AuthorizationResult result = dataTransferAuthorizable.checkAuthorization(authorizer, RequestAction.WRITE, user); + return Result.Approved.equals(result.getResult()); } @Override - public ControllerDTO getController() { + public ControllerDTO getSiteToSiteDetails() { final NiFiUser user = NiFiUserUtils.getNiFiUser(); if (user == null) { throw new WebApplicationException(new Throwable("Unable to access details for current user.")); } - // TODO - defer to authorizer to see if user is able to retrieve site-to-site details - - // TODO - filter response for access to specific ports - // serialize the input ports this NiFi has access to final Set inputPortDtos = new LinkedHashSet<>(); final Set inputPorts = controllerFacade.getInputPorts(); @@ -2164,9 +2174,9 @@ public ControllerDTO getController() { public ControllerConfigurationEntity getControllerConfiguration() { final Revision rev = revisionManager.getRevision(FlowController.class.getSimpleName()); final ControllerConfigurationDTO dto = dtoFactory.createControllerConfigurationDto(controllerFacade); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(controllerFacade); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(controllerFacade); final RevisionDTO revision = dtoFactory.createRevisionDTO(rev); - return entityFactory.createControllerConfigurationEntity(dto, revision, accessPolicy); + return entityFactory.createControllerConfigurationEntity(dto, revision, permissions); } @Override @@ -2179,14 +2189,53 @@ public FlowConfigurationEntity getFlowConfiguration() { @Override public AccessPolicyEntity getAccessPolicy(final String accessPolicyId) { - final RevisionDTO requestedAccessPolicyRevision = dtoFactory.createRevisionDTO(revisionManager.getRevision(accessPolicyId)); - final AccessPolicy requestedAccessPolicy = accessPolicyDAO.getAccessPolicy(accessPolicyId); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(authorizableLookup.getAccessPolicyAuthorizable(accessPolicyId)); + final AccessPolicy accessPolicy = accessPolicyDAO.getAccessPolicy(accessPolicyId); + return createAccessPolicyEntity(accessPolicy); + } + + @Override + public AccessPolicyEntity getAccessPolicy(final RequestAction requestAction, final String resource) { + Authorizable authorizable; + try { + authorizable = authorizableLookup.getAuthorizableFromResource(resource); + } catch (final ResourceNotFoundException e) { + // unable to find the underlying authorizable... user authorized based on top level /policies... create + // an anonymous authorizable to attempt to locate an existing policy for this resource + authorizable = new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return new Resource() { + @Override + public String getIdentifier() { + return resource; + } + + @Override + public String getName() { + return resource; + } + }; + } + }; + } + + final AccessPolicy accessPolicy = accessPolicyDAO.getAccessPolicy(requestAction, authorizable); + return createAccessPolicyEntity(accessPolicy); + } + + private AccessPolicyEntity createAccessPolicyEntity(final AccessPolicy accessPolicy) { + final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(accessPolicy.getIdentifier())); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(authorizableLookup.getAccessPolicyById(accessPolicy.getIdentifier())); return entityFactory.createAccessPolicyEntity( - dtoFactory.createAccessPolicyDto(requestedAccessPolicy, - requestedAccessPolicy.getGroups().stream().map(mapUserGroupIdToTenantEntity()).collect(Collectors.toSet()), - requestedAccessPolicy.getUsers().stream().map(mapUserIdToTenantEntity()).collect(Collectors.toSet())), - requestedAccessPolicyRevision, accessPolicy); + dtoFactory.createAccessPolicyDto(accessPolicy, + accessPolicy.getGroups().stream().map(mapUserGroupIdToTenantEntity()).collect(Collectors.toSet()), + accessPolicy.getUsers().stream().map(mapUserIdToTenantEntity()).collect(Collectors.toSet())), + revision, permissions); } @Override @@ -2205,17 +2254,19 @@ public Set getUsers() { private UserEntity createUserEntity(final User user) { final RevisionDTO userRevision = dtoFactory.createRevisionDTO(revisionManager.getRevision(user.getIdentifier())); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(authorizableLookup.getTenantAuthorizable()); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(authorizableLookup.getTenant()); final Set userGroups = userGroupDAO.getUserGroupsForUser(user.getIdentifier()).stream() .map(g -> g.getIdentifier()).map(mapUserGroupIdToTenantEntity()).collect(Collectors.toSet()); - return entityFactory.createUserEntity(dtoFactory.createUserDto(user, userGroups), userRevision, accessPolicy); + final Set policyEntities = userGroupDAO.getAccessPoliciesForUser(user.getIdentifier()).stream() + .map(ap -> createAccessPolicySummaryEntity(ap)).collect(Collectors.toSet()); + return entityFactory.createUserEntity(dtoFactory.createUserDto(user, userGroups, policyEntities), userRevision, permissions); } private UserGroupEntity createUserGroupEntity(final Group userGroup) { final RevisionDTO userGroupRevision = dtoFactory.createRevisionDTO(revisionManager.getRevision(userGroup.getIdentifier())); final Set users = userGroup.getUsers().stream().map(mapUserIdToTenantEntity()).collect(Collectors.toSet()); return entityFactory.createUserGroupEntity(dtoFactory.createUserGroupDto(userGroup, users), userGroupRevision, - dtoFactory.createAccessPolicyDto(authorizableLookup.getTenantAuthorizable())); + dtoFactory.createPermissionsDto(authorizableLookup.getTenant())); } @Override @@ -2234,8 +2285,8 @@ public Set getUserGroups() { private LabelEntity createLabelEntity(final Label label) { final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(label.getIdentifier())); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(label); - return entityFactory.createLabelEntity(dtoFactory.createLabelDto(label), revision, accessPolicy); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(label); + return entityFactory.createLabelEntity(dtoFactory.createLabelDto(label), revision, permissions); } @Override @@ -2254,8 +2305,8 @@ public LabelEntity getLabel(final String labelId) { private FunnelEntity createFunnelEntity(final Funnel funnel) { final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(funnel.getIdentifier())); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(funnel); - return entityFactory.createFunnelEntity(dtoFactory.createFunnelDto(funnel), revision, accessPolicy); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(funnel); + return entityFactory.createFunnelEntity(dtoFactory.createFunnelDto(funnel), revision, permissions); } @Override @@ -2274,18 +2325,18 @@ public FunnelEntity getFunnel(final String funnelId) { private PortEntity createInputPortEntity(final Port port) { final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(port.getIdentifier())); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(port); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(port); final PortStatusDTO status = dtoFactory.createPortStatusDto(controllerFacade.getInputPortStatus(port.getIdentifier())); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(port.getIdentifier())); - return entityFactory.createPortEntity(dtoFactory.createPortDto(port), revision, accessPolicy, status, bulletins); + return entityFactory.createPortEntity(dtoFactory.createPortDto(port), revision, permissions, status, bulletins); } private PortEntity createOutputPortEntity(final Port port) { final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(port.getIdentifier())); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(port); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(port); final PortStatusDTO status = dtoFactory.createPortStatusDto(controllerFacade.getOutputPortStatus(port.getIdentifier())); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(port.getIdentifier())); - return entityFactory.createPortEntity(dtoFactory.createPortDto(port), revision, accessPolicy, status, bulletins); + return entityFactory.createPortEntity(dtoFactory.createPortDto(port), revision, permissions, status, bulletins); } @Override @@ -2306,10 +2357,10 @@ public Set getOutputPorts(final String groupId) { private ProcessGroupEntity createProcessGroupEntity(final ProcessGroup group) { final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(group.getIdentifier())); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(group); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(group); final ProcessGroupStatusDTO status = dtoFactory.createConciseProcessGroupStatusDto(controllerFacade.getProcessGroupStatus(group.getIdentifier())); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(group.getIdentifier())); - return entityFactory.createProcessGroupEntity(dtoFactory.createProcessGroupDto(group), revision, accessPolicy, status, bulletins); + return entityFactory.createProcessGroupEntity(dtoFactory.createProcessGroupDto(group), revision, permissions, status, bulletins); } @Override @@ -2322,10 +2373,10 @@ public Set getProcessGroups(final String parentGroupId) { private RemoteProcessGroupEntity createRemoteGroupEntity(final RemoteProcessGroup rpg) { final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(rpg.getIdentifier())); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(rpg); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(rpg); final RemoteProcessGroupStatusDTO status = dtoFactory.createRemoteProcessGroupStatusDto(controllerFacade.getRemoteProcessGroupStatus(rpg.getIdentifier())); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(rpg.getIdentifier())); - return entityFactory.createRemoteProcessGroupEntity(dtoFactory.createRemoteProcessGroupDto(rpg), revision, accessPolicy, status, bulletins); + return entityFactory.createRemoteProcessGroupEntity(dtoFactory.createRemoteProcessGroupDto(rpg), revision, permissions, status, bulletins); } @Override @@ -2380,10 +2431,11 @@ public CurrentUserEntity getCurrentUser() { final CurrentUserEntity entity = new CurrentUserEntity(); entity.setIdentity(user.getIdentity()); entity.setAnonymous(user.isAnonymous()); - entity.setProvenancePermissions(dtoFactory.createAccessPolicyDto(authorizableLookup.getProvenance())); - entity.setCountersPermissions(dtoFactory.createAccessPolicyDto(authorizableLookup.getCounters())); - entity.setTenantsPermissions(dtoFactory.createAccessPolicyDto(authorizableLookup.getTenantAuthorizable())); - entity.setControllerPermissions(dtoFactory.createAccessPolicyDto(authorizableLookup.getController())); + entity.setProvenancePermissions(dtoFactory.createPermissionsDto(authorizableLookup.getProvenance())); + entity.setCountersPermissions(dtoFactory.createPermissionsDto(authorizableLookup.getCounters())); + entity.setTenantsPermissions(dtoFactory.createPermissionsDto(authorizableLookup.getTenant())); + entity.setControllerPermissions(dtoFactory.createPermissionsDto(authorizableLookup.getController())); + entity.setPoliciesPermissions(dtoFactory.createPermissionsDto(authorizableLookup.getPolicies())); return entity; } @@ -2421,8 +2473,8 @@ public ProcessGroupFlowEntity getProcessGroupFlow(final String groupId, final bo // read lock on every component being accessed in the dto conversion final ProcessGroupStatus groupStatus = controllerFacade.getProcessGroupStatus(groupId); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(processGroup); - return entityFactory.createProcessGroupFlowEntity(dtoFactory.createProcessGroupFlowDto(processGroup, groupStatus, revisionManager), accessPolicy); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(processGroup); + return entityFactory.createProcessGroupFlowEntity(dtoFactory.createProcessGroupFlowDto(processGroup, groupStatus, revisionManager), permissions); } @Override @@ -2439,9 +2491,9 @@ private ControllerServiceEntity createControllerServiceEntity(final ControllerSe dto.setReferencingComponents(referencingComponentsEntity.getControllerServiceReferencingComponents()); final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(serviceNode.getIdentifier())); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(serviceNode); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(serviceNode); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(serviceNode.getIdentifier())); - return entityFactory.createControllerServiceEntity(dto, revision, accessPolicy, bulletins); + return entityFactory.createControllerServiceEntity(dto, revision, permissions, bulletins); } @Override @@ -2483,9 +2535,9 @@ public ControllerServiceReferencingComponentsEntity getControllerServiceReferenc private ReportingTaskEntity createReportingTaskEntity(final ReportingTaskNode reportingTask) { final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(reportingTask.getIdentifier())); - final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(reportingTask); + final PermissionsDTO permissions = dtoFactory.createPermissionsDto(reportingTask); final List bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(reportingTask.getIdentifier())); - return entityFactory.createReportingTaskEntity(dtoFactory.createReportingTaskDto(reportingTask), revision, accessPolicy, bulletins); + return entityFactory.createReportingTaskEntity(dtoFactory.createReportingTaskDto(reportingTask), revision, permissions, bulletins); } @Override @@ -2734,7 +2786,7 @@ private Function mapUserGroupIdToTenantEntity() { return userGroupId -> { final RevisionDTO userGroupRevision = dtoFactory.createRevisionDTO(revisionManager.getRevision(userGroupId)); return entityFactory.createTenantEntity(dtoFactory.createTenantDTO(userGroupDAO.getUserGroup(userGroupId)), userGroupRevision, - dtoFactory.createAccessPolicyDto(authorizableLookup.getTenantAuthorizable())); + dtoFactory.createPermissionsDto(authorizableLookup.getTenant())); }; } @@ -2742,7 +2794,7 @@ private Function mapUserIdToTenantEntity() { return userId -> { final RevisionDTO userRevision = dtoFactory.createRevisionDTO(revisionManager.getRevision(userId)); return entityFactory.createTenantEntity(dtoFactory.createTenantDTO(userDAO.getUser(userId)), userRevision, - dtoFactory.createAccessPolicyDto(authorizableLookup.getTenantAuthorizable())); + dtoFactory.createPermissionsDto(authorizableLookup.getTenant())); }; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessPolicyResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessPolicyResource.java index dd537cda5ba2..2344a5ca2f63 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessPolicyResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessPolicyResource.java @@ -98,6 +98,74 @@ public AccessPolicyDTO populateRemainingAccessPolicyContent(AccessPolicyDTO acce return accessPolicy; } + // ----------------- + // get access policy + // ----------------- + + /** + * Retrieves the specified access policy. + * + * @return An accessPolicyEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("{action}/{resource: .+}") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets an access policy", + response = AccessPolicyEntity.class, + authorizations = { + @Authorization(value = "Read Only", type = "ROLE_MONITOR"), + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), + @Authorization(value = "Administrator", type = "ROLE_ADMIN") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") + } + ) + public Response getAccessPolicyForResource( + @ApiParam( + value = "The request action.", + allowableValues = "read, write", + required = true + ) @PathParam("action") final String action, + @ApiParam( + value = "The resource of the policy.", + required = true + ) @PathParam("resource") String rawResource) { + + // parse the action and resource type + final RequestAction requestAction = RequestAction.valueOfValue(action); + final String resource = "/" + rawResource; + + if (isReplicateRequest()) { + return replicate(HttpMethod.GET); + } + + // authorize access + serviceFacade.authorizeAccess(lookup -> { + final Authorizable accessPolicy = lookup.getAccessPolicyByResource(resource); + accessPolicy.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); + }); + + // get the access policy + final AccessPolicyEntity entity = serviceFacade.getAccessPolicy(requestAction, resource); + populateRemainingAccessPolicyEntityContent(entity); + + return clusterContext(generateOkResponse(entity)).build(); + } + + // ----------------------- + // manage an access policy + // ----------------------- + /** * Creates a new access policy. * @@ -111,58 +179,6 @@ public AccessPolicyDTO populateRemainingAccessPolicyContent(AccessPolicyDTO acce // TODO - @PreAuthorize("hasRole('ROLE_DFM')") @ApiOperation( value = "Creates an access policy", - notes = " Available resources:\n" + - " /flow - READ - allows user/entity to load the UI and see the flow structure\n" + - " - WRITE - NA\n" + - " /resource - READ - allows user/entity to retrieve the available resources\n" + - " - WRITE - NA\n" + - " /system - READ - allows user/entity to retrieve system level diagnostics (CPU load, disk utilization, etc)\n" + - " - WRITE - NA\n" + - " /controller - READ - allows user/entity to retrieve configuration details for the controller (controller bulletins, thread pool, reporting tasks, etc)\n" + - " - WRITE - allows user/entity to modify configuration details for the controller\n" + - " /provenance - READ - allows user/entity to perform provenance requests. results will be filtered based on access to provenance data per component\n" + - " - WRITE - NA\n" + - " /token - READ - NA\n" + - " - WRITE - allows user/entity to create a token for access the REST API\n" + - " /site-to-site - READ - allows user/entity to retrieve configuration details for performing site to site data transfers with this NiFi\n" + - " - WRITE - NA\n" + - " /proxy - READ - NA\n" + - " - WRITE - allows user/entity to create a proxy request on behalf of another user\n" + - " /process-groups/{id} - READ - allows user/entity to retrieve configuration details for the process group and all descendant components without explicit " + - "access policies\n" + - " - WRITE - allows user/entity to create/update/delete configuration details for the process group and all descendant components without " + - "explicit access policies\n" + - " /processors/{id} - READ - allows user/entity to retrieve configuration details for the processor overriding any inherited authorizations from an ancestor " + - "process group\n" + - " - WRITE - allows user/entity to update/delete the processor overriding any inherited authorizations from an ancestor process group\n" + - " /input-ports/{id} - READ - allows user/entity to retrieve configuration details for the input port overriding any inherited authorizations from an ancestor " + - "process group\n" + - " - WRITE - allows user/entity to update/delete the input port overriding any inherited authorizations from an ancestor process group\n" + - " /output-ports/{id} - READ - allows user/entity to retrieve configuration details for the output port overriding any inherited authorizations from an ancestor " + - "process group\n" + - " - WRITE - allows user/entity to update/delete the output port overriding any inherited authorizations from an ancestor process group\n" + - " /labels/{id} - READ - allows user/entity to retrieve configuration details for the label overriding any inherited authorizations from an ancestor " + - "process group\n" + - " - WRITE - allows user/entity to update/delete the label overriding any inherited authorizations from an ancestor process group\n" + - " /connections/{id} - READ - allows user/entity to retrieve configuration details for the connection overriding any inherited authorizations from an ancestor " + - "process group\n" + - " - WRITE - allows user/entity to update/delete the label overriding any inherited authorizations from an ancestor process group\n" + - " /remote-process-groups/{id} - READ - allows user/entity to retrieve configuration details for the remote process group overriding any inherited authorizations from an " + - "ancestor process group\n" + - " - WRITE - allows user/entity to update/delete the remote process group overriding any inherited authorizations from an ancestor process " + - "group\n" + - " /templates/{id} - READ - allows user/entity to retrieve configuration details for the template overriding any inherited authorizations from an ancestor " + - "process group\n" + - " - WRITE - allows user/entity to create/update/delete the template overriding any inherited authorizations from an ancestor process group\n" + - " /controller-services/{id} - READ - allows user/entity to retrieve configuration details for the controller service overriding any inherited authorizations from an " + - "ancestor process group\n" + - " - WRITE - allows user/entity to update/delete the controller service overriding any inherited authorizations from an ancestor process " + - "group\n" + - " /reporting-tasks/{id} - READ - allows user/entity to retrieve configuration details for the reporting tasks overriding any inherited authorizations from the " + - "controller\n" + - " - WRITE - allows user/entity to create/update/delete the reporting tasks overriding any inherited authorizations from the controller\n" + - " /{type}/{id}/provenance - READ - allows user/entity to view provenance data from the underlying component\n" + - " - WRITE - NA\n", response = AccessPolicyEntity.class, authorizations = { @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") @@ -192,10 +208,18 @@ public Response createAccessPolicy( throw new IllegalArgumentException("A revision of 0 must be specified when creating a new Policy."); } - if (accessPolicyEntity.getComponent().getId() != null) { + final AccessPolicyDTO requestAccessPolicy = accessPolicyEntity.getComponent(); + if (requestAccessPolicy.getId() != null) { throw new IllegalArgumentException("Access policy ID cannot be specified."); } + if (requestAccessPolicy.getResource() == null) { + throw new IllegalArgumentException("Access policy resource must be specified."); + } + + // ensure this is a valid action + RequestAction.valueOfValue(requestAccessPolicy.getAction()); + if (isReplicateRequest()) { return replicate(HttpMethod.POST, accessPolicyEntity); } @@ -205,7 +229,7 @@ public Response createAccessPolicy( if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable accessPolicies = lookup.getAccessPoliciesAuthorizable(); + final Authorizable accessPolicies = lookup.getAccessPolicyByResource(requestAccessPolicy.getResource()); accessPolicies.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }); } @@ -270,8 +294,8 @@ public Response getAccessPolicy( // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable accessPolicy = lookup.getAccessPolicyAuthorizable(id); - accessPolicy.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); + Authorizable authorizable = lookup.getAccessPolicyById(id); + authorizable.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }); // get the access policy @@ -347,8 +371,8 @@ public Response updateAccessPolicy( serviceFacade, revision, lookup -> { - Authorizable authorizable = lookup.getAccessPolicyAuthorizable(id); - authorizable.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + Authorizable authorizable = lookup.getAccessPolicyById(id); + authorizable.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }, null, () -> { @@ -422,8 +446,8 @@ public Response removeAccessPolicy( serviceFacade, revision, lookup -> { - final Authorizable accessPolicy = lookup.getAccessPolicyAuthorizable(id); - accessPolicy.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); + final Authorizable accessPolicy = lookup.getAccessPolicyById(id); + accessPolicy.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }, () -> { }, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java index 42f836c40ecf..faa1ab47df8c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java @@ -16,31 +16,10 @@ */ package org.apache.nifi.web.api; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.TreeMap; -import java.util.UUID; -import java.util.function.Consumer; -import java.util.function.Supplier; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.ws.rs.core.CacheControl; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.ResponseBuilder; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriBuilderException; -import javax.ws.rs.core.UriInfo; - +import com.sun.jersey.api.core.HttpContext; +import com.sun.jersey.api.representation.Form; +import com.sun.jersey.core.util.MultivaluedMapImpl; +import com.sun.jersey.server.impl.model.method.dispatch.FormDispatchProvider; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.RequestAction; @@ -54,6 +33,13 @@ import org.apache.nifi.cluster.manager.exception.UnknownNodeException; import org.apache.nifi.cluster.protocol.NodeIdentifier; import org.apache.nifi.controller.Snippet; +import org.apache.nifi.remote.HttpRemoteSiteListener; +import org.apache.nifi.remote.VersionNegotiator; +import org.apache.nifi.remote.exception.BadRequestException; +import org.apache.nifi.remote.exception.HandshakeException; +import org.apache.nifi.remote.exception.NotAuthorizedException; +import org.apache.nifi.remote.protocol.ResponseCode; +import org.apache.nifi.remote.protocol.http.HttpHeaders; import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.web.AuthorizableLookup; import org.apache.nifi.web.AuthorizeAccess; @@ -62,14 +48,40 @@ import org.apache.nifi.web.api.dto.RevisionDTO; import org.apache.nifi.web.api.dto.SnippetDTO; import org.apache.nifi.web.api.entity.ComponentEntity; +import org.apache.nifi.web.api.entity.TransactionResultEntity; import org.apache.nifi.web.api.request.ClientIdParameter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.sun.jersey.api.core.HttpContext; -import com.sun.jersey.api.representation.Form; -import com.sun.jersey.core.util.MultivaluedMapImpl; -import com.sun.jersey.server.impl.model.method.dispatch.FormDispatchProvider; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.CacheControl; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriBuilderException; +import javax.ws.rs.core.UriInfo; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import static javax.ws.rs.core.Response.Status.NOT_FOUND; +import static org.apache.commons.lang3.StringUtils.isEmpty; +import static org.apache.nifi.remote.protocol.http.HttpHeaders.LOCATION_URI_INTENT_NAME; +import static org.apache.nifi.remote.protocol.http.HttpHeaders.LOCATION_URI_INTENT_VALUE; /** * Base class for controllers. @@ -712,4 +724,152 @@ protected NiFiProperties getProperties() { public static enum ReplicationTarget { CLUSTER_NODES, CLUSTER_COORDINATOR; } + + // ----------------- + // HTTP site to site + // ----------------- + + protected Integer negotiateTransportProtocolVersion(final HttpServletRequest req, final VersionNegotiator transportProtocolVersionNegotiator) throws BadRequestException { + String protocolVersionStr = req.getHeader(HttpHeaders.PROTOCOL_VERSION); + if (isEmpty(protocolVersionStr)) { + throw new BadRequestException("Protocol version was not specified."); + } + + final Integer requestedProtocolVersion; + try { + requestedProtocolVersion = Integer.valueOf(protocolVersionStr); + } catch (NumberFormatException e) { + throw new BadRequestException("Specified protocol version was not in a valid number format: " + protocolVersionStr); + } + + Integer protocolVersion; + if (transportProtocolVersionNegotiator.isVersionSupported(requestedProtocolVersion)) { + return requestedProtocolVersion; + } else { + protocolVersion = transportProtocolVersionNegotiator.getPreferredVersion(requestedProtocolVersion); + } + + if (protocolVersion == null) { + throw new BadRequestException("Specified protocol version is not supported: " + protocolVersionStr); + } + return protocolVersion; + } + + protected Response.ResponseBuilder setCommonHeaders(final Response.ResponseBuilder builder, final Integer transportProtocolVersion, final HttpRemoteSiteListener transactionManager) { + return builder.header(HttpHeaders.PROTOCOL_VERSION, transportProtocolVersion) + .header(HttpHeaders.SERVER_SIDE_TRANSACTION_TTL, transactionManager.getTransactionTtlSec()); + } + + protected class ResponseCreator { + + public Response nodeTypeErrorResponse(String errMsg) { + return noCache(Response.status(Response.Status.FORBIDDEN)).type(MediaType.TEXT_PLAIN).entity(errMsg).build(); + } + + public Response httpSiteToSiteIsNotEnabledResponse() { + return noCache(Response.status(Response.Status.FORBIDDEN)).type(MediaType.TEXT_PLAIN).entity("HTTP(S) Site-to-Site is not enabled on this host.").build(); + } + + public Response wrongPortTypeResponse(String portType, String portId) { + logger.debug("Port type was wrong. portType={}, portId={}", portType, portId); + TransactionResultEntity entity = new TransactionResultEntity(); + entity.setResponseCode(ResponseCode.ABORT.getCode()); + entity.setMessage("Port was not found."); + entity.setFlowFileSent(0); + return Response.status(NOT_FOUND).entity(entity).type(MediaType.APPLICATION_JSON_TYPE).build(); + } + + public Response transactionNotFoundResponse(String portId, String transactionId) { + logger.debug("Transaction was not found. portId={}, transactionId={}", portId, transactionId); + TransactionResultEntity entity = new TransactionResultEntity(); + entity.setResponseCode(ResponseCode.ABORT.getCode()); + entity.setMessage("Transaction was not found."); + entity.setFlowFileSent(0); + return Response.status(NOT_FOUND).entity(entity).type(MediaType.APPLICATION_JSON_TYPE).build(); + } + + public Response unexpectedErrorResponse(String portId, Exception e) { + logger.error("Unexpected exception occurred. portId={}", portId); + logger.error("Exception detail:", e); + TransactionResultEntity entity = new TransactionResultEntity(); + entity.setResponseCode(ResponseCode.ABORT.getCode()); + entity.setMessage("Server encountered an exception."); + entity.setFlowFileSent(0); + return Response.serverError().entity(entity).type(MediaType.APPLICATION_JSON_TYPE).build(); + } + + public Response unexpectedErrorResponse(String portId, String transactionId, Exception e) { + logger.error("Unexpected exception occurred. portId={}, transactionId={}", portId, transactionId); + logger.error("Exception detail:", e); + TransactionResultEntity entity = new TransactionResultEntity(); + entity.setResponseCode(ResponseCode.ABORT.getCode()); + entity.setMessage("Server encountered an exception."); + entity.setFlowFileSent(0); + return Response.serverError().entity(entity).type(MediaType.APPLICATION_JSON_TYPE).build(); + } + + public Response unauthorizedResponse(NotAuthorizedException e) { + if (logger.isDebugEnabled()) { + logger.debug("Client request was not authorized. {}", e.getMessage()); + } + TransactionResultEntity entity = new TransactionResultEntity(); + entity.setResponseCode(ResponseCode.UNAUTHORIZED.getCode()); + entity.setMessage(e.getMessage()); + entity.setFlowFileSent(0); + return Response.status(Response.Status.UNAUTHORIZED).type(MediaType.APPLICATION_JSON_TYPE).entity(e.getMessage()).build(); + } + + public Response badRequestResponse(Exception e) { + if (logger.isDebugEnabled()) { + logger.debug("Client sent a bad request. {}", e.getMessage()); + } + TransactionResultEntity entity = new TransactionResultEntity(); + entity.setResponseCode(ResponseCode.ABORT.getCode()); + entity.setMessage(e.getMessage()); + entity.setFlowFileSent(0); + return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(entity).build(); + } + + public Response handshakeExceptionResponse(HandshakeException e) { + if(logger.isDebugEnabled()){ + logger.debug("Handshake failed, {}", e.getMessage()); + } + ResponseCode handshakeRes = e.getResponseCode(); + Response.Status statusCd; + TransactionResultEntity entity = new TransactionResultEntity(); + entity.setResponseCode(handshakeRes != null ? handshakeRes.getCode() : ResponseCode.ABORT.getCode()); + entity.setMessage(e.getMessage()); + entity.setFlowFileSent(0); + switch (handshakeRes) { + case PORT_NOT_IN_VALID_STATE: + case PORTS_DESTINATION_FULL: + return Response.status(Response.Status.SERVICE_UNAVAILABLE).type(MediaType.APPLICATION_JSON_TYPE).entity(entity).build(); + case UNAUTHORIZED: + statusCd = Response.Status.UNAUTHORIZED; + break; + case UNKNOWN_PORT: + statusCd = NOT_FOUND; + break; + default: + statusCd = Response.Status.BAD_REQUEST; + } + return Response.status(statusCd).type(MediaType.APPLICATION_JSON_TYPE).entity(entity).build(); + } + + public Response acceptedResponse(final HttpRemoteSiteListener transactionManager, final Object entity, final Integer protocolVersion) { + return noCache(setCommonHeaders(Response.status(Response.Status.ACCEPTED), protocolVersion, transactionManager)) + .entity(entity).build(); + } + + public Response locationResponse(UriInfo uriInfo, String portType, String portId, String transactionId, Object entity, + Integer protocolVersion, final HttpRemoteSiteListener transactionManager) { + + String path = "/data-transfer/" + portType + "/" + portId + "/transactions/" + transactionId; + URI location = uriInfo.getBaseUriBuilder().path(path).build(); + return noCache(setCommonHeaders(Response.created(location), protocolVersion, transactionManager) + .header(LOCATION_URI_INTENT_NAME, LOCATION_URI_INTENT_VALUE)) + .entity(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/DataTransferResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/DataTransferResource.java new file mode 100644 index 000000000000..aad8b4a3ed83 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/DataTransferResource.java @@ -0,0 +1,837 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api; + +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.AccessDeniedException; +import org.apache.nifi.authorization.AuthorizationRequest; +import org.apache.nifi.authorization.AuthorizationResult; +import org.apache.nifi.authorization.AuthorizationResult.Result; +import org.apache.nifi.authorization.Authorizer; +import org.apache.nifi.authorization.RequestAction; +import org.apache.nifi.authorization.Resource; +import org.apache.nifi.authorization.resource.ResourceFactory; +import org.apache.nifi.authorization.resource.ResourceType; +import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.authorization.user.NiFiUserUtils; +import org.apache.nifi.remote.HttpRemoteSiteListener; +import org.apache.nifi.remote.Peer; +import org.apache.nifi.remote.PeerDescription; +import org.apache.nifi.remote.StandardVersionNegotiator; +import org.apache.nifi.remote.VersionNegotiator; +import org.apache.nifi.remote.client.http.TransportProtocolVersionNegotiator; +import org.apache.nifi.remote.exception.BadRequestException; +import org.apache.nifi.remote.exception.HandshakeException; +import org.apache.nifi.remote.exception.NotAuthorizedException; +import org.apache.nifi.remote.exception.RequestExpiredException; +import org.apache.nifi.remote.io.http.HttpOutput; +import org.apache.nifi.remote.io.http.HttpServerCommunicationsSession; +import org.apache.nifi.remote.protocol.HandshakeProperty; +import org.apache.nifi.remote.protocol.ResponseCode; +import org.apache.nifi.remote.protocol.http.HttpFlowFileServerProtocol; +import org.apache.nifi.remote.protocol.http.HttpFlowFileServerProtocolImpl; +import org.apache.nifi.stream.io.ByteArrayOutputStream; +import org.apache.nifi.web.api.entity.TransactionResultEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +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.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.StreamingOutput; +import javax.ws.rs.core.UriInfo; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import static org.apache.commons.lang3.StringUtils.isEmpty; +import static org.apache.nifi.remote.protocol.HandshakeProperty.BATCH_COUNT; +import static org.apache.nifi.remote.protocol.HandshakeProperty.BATCH_DURATION; +import static org.apache.nifi.remote.protocol.HandshakeProperty.BATCH_SIZE; +import static org.apache.nifi.remote.protocol.HandshakeProperty.REQUEST_EXPIRATION_MILLIS; +import static org.apache.nifi.remote.protocol.http.HttpHeaders.HANDSHAKE_PROPERTY_BATCH_COUNT; +import static org.apache.nifi.remote.protocol.http.HttpHeaders.HANDSHAKE_PROPERTY_BATCH_DURATION; +import static org.apache.nifi.remote.protocol.http.HttpHeaders.HANDSHAKE_PROPERTY_BATCH_SIZE; +import static org.apache.nifi.remote.protocol.http.HttpHeaders.HANDSHAKE_PROPERTY_REQUEST_EXPIRATION; +import static org.apache.nifi.remote.protocol.http.HttpHeaders.HANDSHAKE_PROPERTY_USE_COMPRESSION; + +/** + * RESTful endpoint for managing a SiteToSite connection. + */ +@Path("/data-transfer") +@Api( + value = "/data-transfer", + description = "Supports data transfers with this NiFi using HTTP based site to site" +) +public class DataTransferResource extends ApplicationResource { + + private static final Logger logger = LoggerFactory.getLogger(DataTransferResource.class); + + public static final String CHECK_SUM = "checksum"; + public static final String RESPONSE_CODE = "responseCode"; + + + private static final String PORT_TYPE_INPUT = "input-ports"; + private static final String PORT_TYPE_OUTPUT = "output-ports"; + + private Authorizer authorizer; + private final ResponseCreator responseCreator = new ResponseCreator(); + private final VersionNegotiator transportProtocolVersionNegotiator = new TransportProtocolVersionNegotiator(1); + private final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); + + /** + * Authorizes access to data transfers. + * + * Note: Protected for testing purposes + */ + protected void authorizeDataTransfer(final ResourceType resourceType, final String identifier) { + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + + if (!ResourceType.InputPort.equals(resourceType) && !ResourceType.OutputPort.equals(resourceType)) { + throw new IllegalArgumentException("The resource must be an Input or Output Port."); + } + + // TODO - use DataTransferAuthorizable after looking up underlying component for consistentency + final Resource resource = ResourceFactory.getComponentResource(resourceType, identifier, identifier); + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(ResourceFactory.getDataTransferResource(resource)) + .identity(user.getIdentity()) + .anonymous(user.isAnonymous()) + .accessAttempt(true) + .action(RequestAction.WRITE) + .build(); + + final AuthorizationResult result = authorizer.authorize(request); + if (!Result.Approved.equals(result.getResult())) { + final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied"; + throw new AccessDeniedException(message); + } + } + + @POST + @Produces(MediaType.APPLICATION_JSON) + @Path("{portType}/{portId}/transactions") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Create a transaction to the specified output port or input port", + response = TransactionResultEntity.class, + authorizations = { + @Authorization(value = "Read Only", type = "ROLE_MONITOR"), + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), + @Authorization(value = "Administrator", type = "ROLE_ADMIN") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful."), + @ApiResponse(code = 503, message = "NiFi instance is not ready for serving request, or temporarily overloaded. Retrying the same request later may be successful"), + } + ) + public Response createPortTransaction( + @ApiParam( + value = "The port type.", + required = true, + allowableValues = "input-ports, output-ports" + ) + @PathParam("portType") String portType, + @PathParam("portId") String portId, + @Context HttpServletRequest req, + @Context ServletContext context, + @Context UriInfo uriInfo, + InputStream inputStream) { + + + if(!PORT_TYPE_INPUT.equals(portType) && !PORT_TYPE_OUTPUT.equals(portType)){ + return responseCreator.wrongPortTypeResponse(portType, portId); + } + + // authorize access + authorizeDataTransfer(PORT_TYPE_INPUT.equals(portType) ? ResourceType.InputPort : ResourceType.OutputPort, portId); + + final ValidateRequestResult validationResult = validateResult(req, portId); + if (validationResult.errResponse != null) { + return validationResult.errResponse; + } + + logger.debug("createPortTransaction request: clientId={}, portType={}, portId={}", portType, portId); + + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final String transactionId = transactionManager.createTransaction(); + final Peer peer = constructPeer(req, inputStream, out, portId, transactionId); + final int transportProtocolVersion = validationResult.transportProtocolVersion; + + try { + // Execute handshake. + initiateServerProtocol(peer, transportProtocolVersion); + + TransactionResultEntity entity = new TransactionResultEntity(); + entity.setResponseCode(ResponseCode.PROPERTIES_OK.getCode()); + entity.setMessage("Handshake properties are valid, and port is running. A transaction is created:" + transactionId); + + return responseCreator.locationResponse(uriInfo, portType, portId, transactionId, entity, transportProtocolVersion, transactionManager); + + } catch (HandshakeException e) { + transactionManager.cancelTransaction(transactionId); + return responseCreator.handshakeExceptionResponse(e); + + } catch (Exception e) { + transactionManager.cancelTransaction(transactionId); + return responseCreator.unexpectedErrorResponse(portId, e); + } + } + + @POST + @Consumes(MediaType.APPLICATION_OCTET_STREAM) + @Produces(MediaType.TEXT_PLAIN) + @Path("input-ports/{portId}/transactions/{transactionId}/flow-files") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Transfer flow files to the input port", + response = String.class, + authorizations = { + @Authorization(value = "Read Only", type = "ROLE_MONITOR"), + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), + @Authorization(value = "Administrator", type = "ROLE_ADMIN") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful."), + @ApiResponse(code = 503, message = "NiFi instance is not ready for serving request, or temporarily overloaded. Retrying the same request later may be successful"), + } + ) + public Response receiveFlowFiles( + @ApiParam( + value = "The input port id.", + required = true + ) + @PathParam("portId") String portId, + @PathParam("transactionId") String transactionId, + @Context HttpServletRequest req, + @Context ServletContext context, + InputStream inputStream) { + + // authorize access + authorizeDataTransfer(ResourceType.InputPort, portId); + + final ValidateRequestResult validationResult = validateResult(req, portId, transactionId); + if (validationResult.errResponse != null) { + return validationResult.errResponse; + } + + logger.debug("receiveFlowFiles request: portId={}", portId); + + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final Peer peer = constructPeer(req, inputStream, out, portId, transactionId); + final int transportProtocolVersion = validationResult.transportProtocolVersion; + + try { + HttpFlowFileServerProtocol serverProtocol = initiateServerProtocol(peer, transportProtocolVersion); + int numOfFlowFiles = serverProtocol.getPort().receiveFlowFiles(peer, serverProtocol); + logger.debug("finished receiving flow files, numOfFlowFiles={}", numOfFlowFiles); + if (numOfFlowFiles < 1) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("Client should send request when there is data to send. There was no flow file sent.").build(); + } + } catch (HandshakeException e) { + return responseCreator.handshakeExceptionResponse(e); + + } catch (NotAuthorizedException e) { + return responseCreator.unauthorizedResponse(e); + + } catch (BadRequestException | RequestExpiredException e) { + return responseCreator.badRequestResponse(e); + + } catch (Exception e) { + return responseCreator.unexpectedErrorResponse(portId, e); + } + + String serverChecksum = ((HttpServerCommunicationsSession)peer.getCommunicationsSession()).getChecksum(); + return responseCreator.acceptedResponse(transactionManager, serverChecksum, transportProtocolVersion); + } + + private HttpFlowFileServerProtocol initiateServerProtocol(Peer peer, Integer transportProtocolVersion) throws IOException { + // Switch transaction protocol version based on transport protocol version. + TransportProtocolVersionNegotiator negotiatedTransportProtocolVersion = new TransportProtocolVersionNegotiator(transportProtocolVersion); + VersionNegotiator versionNegotiator = new StandardVersionNegotiator(negotiatedTransportProtocolVersion.getTransactionProtocolVersion()); + HttpFlowFileServerProtocol serverProtocol = getHttpFlowFileServerProtocol(versionNegotiator); + HttpRemoteSiteListener.getInstance().setupServerProtocol(serverProtocol); + // TODO: How should I pass cluster information? + // serverProtocol.setNodeInformant(clusterManager); + serverProtocol.handshake(peer); + return serverProtocol; + } + + HttpFlowFileServerProtocol getHttpFlowFileServerProtocol(VersionNegotiator versionNegotiator) { + return new HttpFlowFileServerProtocolImpl(versionNegotiator); + } + + private Peer constructPeer(HttpServletRequest req, InputStream inputStream, OutputStream outputStream, String portId, String transactionId) { + String clientHostName = req.getRemoteHost(); + int clientPort = req.getRemotePort(); + + PeerDescription peerDescription = new PeerDescription(clientHostName, clientPort, req.isSecure()); + + HttpServerCommunicationsSession commSession = new HttpServerCommunicationsSession(inputStream, outputStream, transactionId); + + boolean useCompression = false; + final String useCompressionStr = req.getHeader(HANDSHAKE_PROPERTY_USE_COMPRESSION); + if (!isEmpty(useCompressionStr) && Boolean.valueOf(useCompressionStr)) { + useCompression = true; + } + + final String requestExpiration = req.getHeader(HANDSHAKE_PROPERTY_REQUEST_EXPIRATION); + final String batchCount = req.getHeader(HANDSHAKE_PROPERTY_BATCH_COUNT); + final String batchSize = req.getHeader(HANDSHAKE_PROPERTY_BATCH_SIZE); + final String batchDuration = req.getHeader(HANDSHAKE_PROPERTY_BATCH_DURATION); + + commSession.putHandshakeParam(HandshakeProperty.PORT_IDENTIFIER, portId); + commSession.putHandshakeParam(HandshakeProperty.GZIP, String.valueOf(useCompression)); + + if (!isEmpty(requestExpiration)) commSession.putHandshakeParam(REQUEST_EXPIRATION_MILLIS, requestExpiration); + if (!isEmpty(batchCount)) commSession.putHandshakeParam(BATCH_COUNT, batchCount); + if (!isEmpty(batchSize)) commSession.putHandshakeParam(BATCH_SIZE, batchSize); + if (!isEmpty(batchDuration)) commSession.putHandshakeParam(BATCH_DURATION, batchDuration); + + if(peerDescription.isSecure()){ + NiFiUser nifiUser = NiFiUserUtils.getNiFiUser(); + logger.debug("initiating peer, nifiUser={}", nifiUser); + commSession.setUserDn(nifiUser.getIdentity()); + } + + // TODO: Followed how SocketRemoteSiteListener define peerUrl and clusterUrl, but it can be more meaningful values, especially for clusterUrl. + String peerUrl = "nifi://" + clientHostName + ":" + clientPort; + String clusterUrl = "nifi://localhost:" + req.getLocalPort(); + return new Peer(peerDescription, commSession, peerUrl, clusterUrl); + } + + @DELETE + @Consumes(MediaType.APPLICATION_OCTET_STREAM) + @Produces(MediaType.APPLICATION_JSON) + @Path("output-ports/{portId}/transactions/{transactionId}") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Commit or cancel the specified transaction", + response = TransactionResultEntity.class, + authorizations = { + @Authorization(value = "Read Only", type = "ROLE_MONITOR"), + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), + @Authorization(value = "Administrator", type = "ROLE_ADMIN") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful."), + @ApiResponse(code = 503, message = "NiFi instance is not ready for serving request, or temporarily overloaded. Retrying the same request later may be successful"), + } + ) + public Response commitOutputPortTransaction( + @ApiParam( + value = "The response code. Available values are CONFIRM_TRANSACTION(12) or CANCEL_TRANSACTION(15).", + required = true + ) + @QueryParam(RESPONSE_CODE) Integer responseCode, + @ApiParam( + value = "A checksum calculated at client side using CRC32 to check flow file content integrity. It must match with the value calculated at server side.", + required = true + ) + @QueryParam(CHECK_SUM) @DefaultValue(StringUtils.EMPTY) String checksum, + @ApiParam( + value = "The output port id.", + required = true + ) + @PathParam("portId") String portId, + @ApiParam( + value = "The transaction id.", + required = true + ) + @PathParam("transactionId") String transactionId, + @Context HttpServletRequest req, + @Context ServletContext context, + InputStream inputStream) { + + // authorize access + authorizeDataTransfer(ResourceType.OutputPort, portId); + + final ValidateRequestResult validationResult = validateResult(req, portId, transactionId); + if (validationResult.errResponse != null) { + return validationResult.errResponse; + } + + logger.debug("commitOutputPortTransaction request: portId={}, transactionId={}", portId, transactionId); + + final int transportProtocolVersion = validationResult.transportProtocolVersion; + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final Peer peer = constructPeer(req, inputStream, out, portId, transactionId); + + final TransactionResultEntity entity = new TransactionResultEntity(); + try { + HttpFlowFileServerProtocol serverProtocol = initiateServerProtocol(peer, transportProtocolVersion); + + String inputErrMessage = null; + if (responseCode == null) { + inputErrMessage = "responseCode is required."; + } else if(ResponseCode.CONFIRM_TRANSACTION.getCode() != responseCode + && ResponseCode.CANCEL_TRANSACTION.getCode() != responseCode) { + inputErrMessage = "responseCode " + responseCode + " is invalid. "; + } + + if (inputErrMessage != null){ + entity.setMessage(inputErrMessage); + entity.setResponseCode(ResponseCode.ABORT.getCode()); + return Response.status(Response.Status.BAD_REQUEST).entity(entity).build(); + } + + if (ResponseCode.CANCEL_TRANSACTION.getCode() == responseCode) { + return cancelTransaction(transactionId, entity); + } + + int flowFileSent = serverProtocol.commitTransferTransaction(peer, checksum); + entity.setResponseCode(ResponseCode.CONFIRM_TRANSACTION.getCode()); + entity.setFlowFileSent(flowFileSent); + + } catch (HandshakeException e) { + return responseCreator.handshakeExceptionResponse(e); + + } catch (Exception e) { + HttpServerCommunicationsSession commsSession = (HttpServerCommunicationsSession) peer.getCommunicationsSession(); + logger.error("Failed to process the request", e); + if(ResponseCode.BAD_CHECKSUM.equals(commsSession.getResponseCode())){ + entity.setResponseCode(commsSession.getResponseCode().getCode()); + entity.setMessage(e.getMessage()); + + Response.ResponseBuilder builder = Response.status(Response.Status.BAD_REQUEST).entity(entity); + return clusterContext(noCache(builder)).build(); + } + + return responseCreator.unexpectedErrorResponse(portId, transactionId, e); + } + + return clusterContext(noCache(setCommonHeaders(Response.ok(entity), transportProtocolVersion, transactionManager))).build(); + } + + + @DELETE + @Consumes(MediaType.APPLICATION_OCTET_STREAM) + @Produces(MediaType.APPLICATION_JSON) + @Path("input-ports/{portId}/transactions/{transactionId}") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Commit or cancel the specified transaction", + response = TransactionResultEntity.class, + authorizations = { + @Authorization(value = "Read Only", type = "ROLE_MONITOR"), + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), + @Authorization(value = "Administrator", type = "ROLE_ADMIN") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful."), + @ApiResponse(code = 503, message = "NiFi instance is not ready for serving request, or temporarily overloaded. Retrying the same request later may be successful"), + } + ) + public Response commitInputPortTransaction( + @ApiParam( + value = "The response code. Available values are BAD_CHECKSUM(19), CONFIRM_TRANSACTION(12) or CANCEL_TRANSACTION(15).", + required = true + ) + @QueryParam(RESPONSE_CODE) Integer responseCode, + @ApiParam( + value = "The input port id.", + required = true + ) + @PathParam("portId") String portId, + @ApiParam( + value = "The transaction id.", + required = true + ) + @PathParam("transactionId") String transactionId, + @Context HttpServletRequest req, + @Context ServletContext context, + InputStream inputStream) { + + // authorize access + authorizeDataTransfer(ResourceType.InputPort, portId); + + final ValidateRequestResult validationResult = validateResult(req, portId, transactionId); + if (validationResult.errResponse != null) { + return validationResult.errResponse; + } + + logger.debug("commitInputPortTransaction request: portId={}, transactionId={}, responseCode={}", + portId, transactionId, responseCode); + + final int transportProtocolVersion = validationResult.transportProtocolVersion; + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final Peer peer = constructPeer(req, inputStream, out, portId, transactionId); + + final TransactionResultEntity entity = new TransactionResultEntity(); + try { + HttpFlowFileServerProtocol serverProtocol = initiateServerProtocol(peer, transportProtocolVersion); + HttpServerCommunicationsSession commsSession = (HttpServerCommunicationsSession) peer.getCommunicationsSession(); + // Pass the response code sent from the client. + String inputErrMessage = null; + if (responseCode == null) { + inputErrMessage = "responseCode is required."; + } else if(ResponseCode.BAD_CHECKSUM.getCode() != responseCode + && ResponseCode.CONFIRM_TRANSACTION.getCode() != responseCode + && ResponseCode.CANCEL_TRANSACTION.getCode() != responseCode) { + inputErrMessage = "responseCode " + responseCode + " is invalid. "; + } + + if (inputErrMessage != null){ + entity.setMessage(inputErrMessage); + entity.setResponseCode(ResponseCode.ABORT.getCode()); + return Response.status(Response.Status.BAD_REQUEST).entity(entity).build(); + } + + if (ResponseCode.CANCEL_TRANSACTION.getCode() == responseCode) { + return cancelTransaction(transactionId, entity); + } + + commsSession.setResponseCode(ResponseCode.fromCode(responseCode)); + + try { + int flowFileSent = serverProtocol.commitReceiveTransaction(peer); + entity.setResponseCode(commsSession.getResponseCode().getCode()); + entity.setFlowFileSent(flowFileSent); + + } catch (IOException e){ + if (ResponseCode.BAD_CHECKSUM.getCode() == responseCode && e.getMessage().contains("Received a BadChecksum response")){ + // AbstractFlowFileServerProtocol throws IOException after it canceled transaction. + // This is a known behavior and if we return 500 with this exception, + // it's not clear if there is an issue at server side, or cancel operation has been accomplished. + // Above conditions can guarantee this is the latter case, we return 200 OK here. + entity.setResponseCode(ResponseCode.CANCEL_TRANSACTION.getCode()); + return clusterContext(noCache(Response.ok(entity))).build(); + } else { + return responseCreator.unexpectedErrorResponse(portId, transactionId, e); + } + } + + } catch (HandshakeException e) { + return responseCreator.handshakeExceptionResponse(e); + + } catch (Exception e) { + return responseCreator.unexpectedErrorResponse(portId, transactionId, e); + } + + return clusterContext(noCache(setCommonHeaders(Response.ok(entity), transportProtocolVersion, transactionManager))).build(); + } + + private Response cancelTransaction(String transactionId, TransactionResultEntity entity) { + transactionManager.cancelTransaction(transactionId); + entity.setMessage("Transaction has been canceled."); + entity.setResponseCode(ResponseCode.CANCEL_TRANSACTION.getCode()); + return Response.ok(entity).build(); + } + + + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_OCTET_STREAM) + @Path("output-ports/{portId}/transactions/{transactionId}/flow-files") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Transfer flow files from the output port", + response = StreamingOutput.class, + authorizations = { + @Authorization(value = "Read Only", type = "ROLE_MONITOR"), + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), + @Authorization(value = "Administrator", type = "ROLE_ADMIN") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 200, message = "There is no flow file to return."), + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful."), + @ApiResponse(code = 503, message = "NiFi instance is not ready for serving request, or temporarily overloaded. Retrying the same request later may be successful"), + } + ) + public Response transferFlowFiles( + @ApiParam( + value = "The output port id.", + required = true + ) + @PathParam("portId") String portId, + @PathParam("transactionId") String transactionId, + @Context HttpServletRequest req, + @Context HttpServletResponse res, + @Context ServletContext context, + InputStream inputStream) { + + // authorize access + authorizeDataTransfer(ResourceType.OutputPort, portId); + + final ValidateRequestResult validationResult = validateResult(req, portId, transactionId); + if (validationResult.errResponse != null) { + return validationResult.errResponse; + } + + logger.debug("transferFlowFiles request: portId={}", portId); + + // Before opening the real output stream for HTTP response, + // use this temporary output stream to buffer handshake result. + final ByteArrayOutputStream tempBos = new ByteArrayOutputStream(); + final Peer peer = constructPeer(req, inputStream, tempBos, portId, transactionId); + final int transportProtocolVersion = validationResult.transportProtocolVersion; + try { + final HttpFlowFileServerProtocol serverProtocol = initiateServerProtocol(peer, transportProtocolVersion); + + StreamingOutput flowFileContent = new StreamingOutput() { + @Override + public void write(OutputStream outputStream) throws IOException, WebApplicationException { + + HttpOutput output = (HttpOutput)peer.getCommunicationsSession().getOutput(); + output.setOutputStream(outputStream); + + try { + int numOfFlowFiles = serverProtocol.getPort().transferFlowFiles(peer, serverProtocol); + logger.debug("finished transferring flow files, numOfFlowFiles={}", numOfFlowFiles); + if(numOfFlowFiles < 1){ + // There was no flow file to transfer. Throw this exception to stop responding with SEE OTHER. + throw new WebApplicationException(Response.Status.OK); + } + } catch (NotAuthorizedException | BadRequestException | RequestExpiredException e) { + // Handshake is done outside of write() method, so these exception wouldn't be thrown. + throw new IOException("Failed to process the request.", e); + } + } + + }; + + return responseCreator.acceptedResponse(transactionManager, flowFileContent, transportProtocolVersion); + + } catch (HandshakeException e) { + return responseCreator.handshakeExceptionResponse(e); + + } catch (Exception e) { + return responseCreator.unexpectedErrorResponse(portId, e); + } + } + + @PUT + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("input-ports/{portId}/transactions/{transactionId}") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Extend transaction TTL", + response = TransactionResultEntity.class, + authorizations = { + @Authorization(value = "Read Only", type = "ROLE_MONITOR"), + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), + @Authorization(value = "Administrator", type = "ROLE_ADMIN") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") + } + ) + public Response extendInputPortTransactionTTL( + @PathParam("portId") String portId, + @PathParam("transactionId") String transactionId, + @Context HttpServletRequest req, + @Context HttpServletResponse res, + @Context ServletContext context, + @Context UriInfo uriInfo, + InputStream inputStream) { + + // authorize access + authorizeDataTransfer(ResourceType.InputPort, portId); + + return extendPortTransactionTTL(PORT_TYPE_INPUT, portId, transactionId, req, res, context, uriInfo, inputStream); + } + + @PUT + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("output-ports/{portId}/transactions/{transactionId}") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Extend transaction TTL", + response = TransactionResultEntity.class, + authorizations = { + @Authorization(value = "Read Only", type = "ROLE_MONITOR"), + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), + @Authorization(value = "Administrator", type = "ROLE_ADMIN") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful."), + @ApiResponse(code = 503, message = "NiFi instance is not ready for serving request, or temporarily overloaded. Retrying the same request later may be successful"), + } + ) + public Response extendOutputPortTransactionTTL( + @PathParam("portId") String portId, + @PathParam("transactionId") String transactionId, + @Context HttpServletRequest req, + @Context HttpServletResponse res, + @Context ServletContext context, + @Context UriInfo uriInfo, + InputStream inputStream) { + + // authorize access + authorizeDataTransfer(ResourceType.OutputPort, portId); + + return extendPortTransactionTTL(PORT_TYPE_OUTPUT, portId, transactionId, req, res, context, uriInfo, inputStream); + } + + public Response extendPortTransactionTTL( + String portType, + String portId, + String transactionId, + HttpServletRequest req, + HttpServletResponse res, + ServletContext context, + UriInfo uriInfo, + InputStream inputStream) { + + final ValidateRequestResult validationResult = validateResult(req, portId, transactionId); + if (validationResult.errResponse != null) { + return validationResult.errResponse; + } + + if(!PORT_TYPE_INPUT.equals(portType) && !PORT_TYPE_OUTPUT.equals(portType)){ + return responseCreator.wrongPortTypeResponse(portType, portId); + } + + logger.debug("extendOutputPortTransactionTTL request: portType={}, portId={}, transactionId={}", + portType, portId, transactionId); + + final int transportProtocolVersion = validationResult.transportProtocolVersion; + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final Peer peer = constructPeer(req, inputStream, out, portId, transactionId); + + try { + // Do handshake + initiateServerProtocol(peer, transportProtocolVersion); + transactionManager.extendsTransaction(transactionId); + + final TransactionResultEntity entity = new TransactionResultEntity(); + entity.setResponseCode(ResponseCode.CONTINUE_TRANSACTION.getCode()); + entity.setMessage("Extended TTL."); + return clusterContext(noCache(setCommonHeaders(Response.ok(entity), transportProtocolVersion, transactionManager))).build(); + + } catch (HandshakeException e) { + return responseCreator.handshakeExceptionResponse(e); + + } catch (Exception e) { + return responseCreator.unexpectedErrorResponse(portId, transactionId, e); + } + + } + + private class ValidateRequestResult { + private Integer transportProtocolVersion; + private Response errResponse; + } + + private ValidateRequestResult validateResult(HttpServletRequest req, String portId) { + return validateResult(req, portId, null); + } + + private ValidateRequestResult validateResult(HttpServletRequest req, String portId, String transactionId) { + ValidateRequestResult result = new ValidateRequestResult(); + if(!properties.isSiteToSiteHttpEnabled()) { + result.errResponse = responseCreator.httpSiteToSiteIsNotEnabledResponse(); + return result; + } + + // TODO: NCM no longer exists. + /* + if (properties.isClusterManager()) { + result.errResponse = responseCreator.nodeTypeErrorResponse(req.getPathInfo() + " is not available on a NiFi Cluster Manager."); + return result; + } + */ + + + try { + result.transportProtocolVersion = negotiateTransportProtocolVersion(req, transportProtocolVersionNegotiator); + } catch (BadRequestException e) { + result.errResponse = responseCreator.badRequestResponse(e); + return result; + } + + if(!isEmpty(transactionId) && !transactionManager.isTransactionActive(transactionId)) { + result.errResponse = responseCreator.transactionNotFoundResponse(portId, transactionId); + return result; + } + + return result; + } + + + // setters + + public void setAuthorizer(Authorizer authorizer) { + this.authorizer = authorizer; + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java index 444701402734..b719e2454b6a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java @@ -16,31 +16,13 @@ */ package org.apache.nifi.web.api; -import java.util.ArrayList; -import java.util.Date; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.HttpMethod; -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.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - +import com.sun.jersey.api.core.ResourceContext; +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.AccessDeniedException; import org.apache.nifi.authorization.AuthorizationRequest; @@ -123,13 +105,29 @@ import org.apache.nifi.web.api.request.IntegerParameter; import org.apache.nifi.web.api.request.LongParameter; -import com.sun.jersey.api.core.ResourceContext; -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.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.HttpMethod; +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.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.util.ArrayList; +import java.util.Date; +import java.util.EnumSet; +import java.util.HashMap; +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 Flow. @@ -208,7 +206,7 @@ private FlowDTO populateRemainingFlowStructure(FlowDTO flowStructure) { /** * Authorizes access to the flow. */ - private void authorizeFlow(final RequestAction action) { + private void authorizeFlow() { final NiFiUser user = NiFiUserUtils.getNiFiUser(); final Map userContext; @@ -224,7 +222,7 @@ private void authorizeFlow(final RequestAction action) { .identity(user.getIdentity()) .anonymous(user.isAnonymous()) .accessAttempt(true) - .action(action) + .action(RequestAction.READ) .userContext(userContext) .build(); @@ -287,7 +285,7 @@ private boolean isAuthorized(final RequestAction action, final Resource resource } ) public Response generateClientId() { - authorizeFlow(RequestAction.READ); + authorizeFlow(); return clusterContext(generateOkResponse(generateUuid())).build(); } @@ -321,7 +319,7 @@ public Response generateClientId() { ) public Response getFlowConfig() { - authorizeFlow(RequestAction.READ); + authorizeFlow(); if (isReplicateRequest()) { return replicate(HttpMethod.GET); @@ -350,7 +348,7 @@ public Response getFlowConfig() { ) public Response getCurrentUser() { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // note that the cluster manager will handle this request directly final NiFiUser user = NiFiUserUtils.getNiFiUser(); @@ -413,7 +411,7 @@ public Response getFlow( ) @QueryParam("recursive") @DefaultValue(RECURSIVE) Boolean recursive) throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); if (isReplicateRequest()) { return replicate(HttpMethod.GET); @@ -458,7 +456,7 @@ public Response getFlow( ) public Response getControllerServicesFromController() { - authorizeFlow(RequestAction.READ); + authorizeFlow(); if (isReplicateRequest()) { return replicate(HttpMethod.GET); @@ -511,7 +509,7 @@ public Response getControllerServicesFromGroup( ) @PathParam("id") String groupId) throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // get all the controller services final Set controllerServices = serviceFacade.getControllerServices(groupId); @@ -566,7 +564,7 @@ public Response getReportingTasks( ) @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) { - authorizeFlow(RequestAction.READ); + authorizeFlow(); if (isReplicateRequest()) { return replicate(HttpMethod.GET); @@ -622,7 +620,7 @@ public Response scheduleComponents( @PathParam("id") String id, ScheduleComponentsEntity scheduleComponentsEntity) { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // ensure the same id is being used if (!id.equals(scheduleComponentsEntity.getId())) { @@ -753,7 +751,7 @@ public Response scheduleComponents( } ) public Response searchFlow(@QueryParam("q") @DefaultValue(StringUtils.EMPTY) String value) throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // query the controller final SearchResultsDTO results = serviceFacade.searchController(value); @@ -796,7 +794,7 @@ public Response searchFlow(@QueryParam("q") @DefaultValue(StringUtils.EMPTY) Str ) public Response getControllerStatus() throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); if (isReplicateRequest()) { return replicate(HttpMethod.GET); @@ -841,7 +839,7 @@ public Response getControllerStatus() throws InterruptedException { ) public Response getBanners() { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // get the banner from the properties - will come from the NCM when clustered final String bannerText = getProperties().getBannerText(); @@ -888,7 +886,7 @@ public Response getBanners() { } ) public Response getProcessorTypes() throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // create response entity final ProcessorTypesEntity entity = new ProcessorTypesEntity(); @@ -933,7 +931,7 @@ public Response getControllerServiceTypes( required = false ) @QueryParam("serviceType") String serviceType) throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // create response entity final ControllerServiceTypesEntity entity = new ControllerServiceTypesEntity(); @@ -972,7 +970,7 @@ public Response getControllerServiceTypes( } ) public Response getReportingTaskTypes() throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // create response entity final ReportingTaskTypesEntity entity = new ReportingTaskTypesEntity(); @@ -1011,7 +1009,7 @@ public Response getReportingTaskTypes() throws InterruptedException { } ) public Response getPrioritizers() throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // create response entity final PrioritizerTypesEntity entity = new PrioritizerTypesEntity(); @@ -1049,7 +1047,7 @@ public Response getPrioritizers() throws InterruptedException { } ) public Response getAboutInfo() { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // create the about dto final AboutDTO aboutDTO = new AboutDTO(); @@ -1139,7 +1137,7 @@ public Response getBulletinBoard( ) @QueryParam("limit") IntegerParameter limit) throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // replicate if cluster manager if (isReplicateRequest()) { @@ -1230,7 +1228,7 @@ public Response getProcessorStatus( ) @PathParam("id") String id) throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // ensure a valid request if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { @@ -1312,7 +1310,7 @@ public Response getInputPortStatus( ) @PathParam("id") String id) throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // ensure a valid request if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { @@ -1394,7 +1392,7 @@ public Response getOutputPortStatus( ) @PathParam("id") String id) throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // ensure a valid request if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { @@ -1476,7 +1474,7 @@ public Response getRemoteProcessGroupStatus( ) @PathParam("id") String id) throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // ensure a valid request if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { @@ -1567,7 +1565,7 @@ public Response getProcessGroupStatus( ) @PathParam("id") String groupId) throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // ensure a valid request if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { @@ -1670,7 +1668,7 @@ public Response getConnectionStatus( ) @PathParam("id") String id) throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // ensure a valid request if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { @@ -1746,7 +1744,7 @@ public Response getProcessorStatusHistory( ) @PathParam("id") String id) throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // replicate if cluster manager if (isReplicateRequest()) { @@ -1801,7 +1799,7 @@ public Response getProcessGroupStatusHistory( ) @PathParam("id") String groupId) throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // replicate if cluster manager if (isReplicateRequest()) { @@ -1856,7 +1854,7 @@ public Response getRemoteProcessGroupStatusHistory( ) @PathParam("id") String id) throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // replicate if cluster manager if (isReplicateRequest()) { @@ -1911,7 +1909,7 @@ public Response getConnectionStatusHistory( ) @PathParam("id") String id) throws InterruptedException { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // replicate if cluster manager if (isReplicateRequest()) { @@ -2022,7 +2020,7 @@ public Response queryHistory( ) @QueryParam("sourceId") String sourceId) { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // ensure the page is specified if (offset == null) { @@ -2130,7 +2128,7 @@ public Response getAction( ) @PathParam("id") IntegerParameter id) { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // ensure the id was specified if (id == null) { @@ -2188,7 +2186,7 @@ public Response getComponentHistory( ) @PathParam("componentId") final String componentId) { - authorizeFlow(RequestAction.READ); + authorizeFlow(); if (isReplicateRequest()) { return replicate(HttpMethod.GET); @@ -2240,7 +2238,7 @@ public Response getTemplates() { } // authorize access - authorizeFlow(RequestAction.READ); + authorizeFlow(); // get all the templates final Set templates = serviceFacade.getTemplates(); @@ -2295,7 +2293,7 @@ public Response searchCluster( ) @QueryParam("q") @DefaultValue(StringUtils.EMPTY) String value) { - authorizeFlow(RequestAction.READ); + authorizeFlow(); // ensure connected to the cluster if (!isConnectedToCluster()) { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SiteToSiteResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SiteToSiteResource.java index d689749d66d1..0565f94730c3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SiteToSiteResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SiteToSiteResource.java @@ -16,84 +16,46 @@ */ package org.apache.nifi.web.api; -import com.sun.jersey.api.core.ResourceContext; 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.AccessDeniedException; +import org.apache.nifi.authorization.AuthorizationRequest; +import org.apache.nifi.authorization.AuthorizationResult; +import org.apache.nifi.authorization.AuthorizationResult.Result; import org.apache.nifi.authorization.Authorizer; +import org.apache.nifi.authorization.RequestAction; +import org.apache.nifi.authorization.resource.ResourceFactory; import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.remote.HttpRemoteSiteListener; -import org.apache.nifi.remote.Peer; -import org.apache.nifi.remote.PeerDescription; -import org.apache.nifi.remote.StandardVersionNegotiator; import org.apache.nifi.remote.VersionNegotiator; import org.apache.nifi.remote.client.http.TransportProtocolVersionNegotiator; import org.apache.nifi.remote.exception.BadRequestException; -import org.apache.nifi.remote.exception.HandshakeException; -import org.apache.nifi.remote.exception.NotAuthorizedException; -import org.apache.nifi.remote.exception.RequestExpiredException; -import org.apache.nifi.remote.io.http.HttpOutput; -import org.apache.nifi.remote.io.http.HttpServerCommunicationsSession; -import org.apache.nifi.remote.protocol.HandshakeProperty; -import org.apache.nifi.remote.protocol.ResponseCode; -import org.apache.nifi.remote.protocol.http.HttpFlowFileServerProtocol; -import org.apache.nifi.remote.protocol.http.HttpFlowFileServerProtocolImpl; import org.apache.nifi.remote.protocol.http.HttpHeaders; -import org.apache.nifi.stream.io.ByteArrayOutputStream; import org.apache.nifi.web.NiFiServiceFacade; import org.apache.nifi.web.api.dto.ControllerDTO; import org.apache.nifi.web.api.dto.remote.PeerDTO; import org.apache.nifi.web.api.entity.ControllerEntity; import org.apache.nifi.web.api.entity.PeersEntity; -import org.apache.nifi.web.api.entity.TransactionResultEntity; -import org.apache.nifi.web.api.request.ClientIdParameter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; 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.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import javax.ws.rs.core.StreamingOutput; -import javax.ws.rs.core.UriInfo; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; import java.util.ArrayList; -import static javax.ws.rs.core.Response.Status.NOT_FOUND; import static org.apache.commons.lang3.StringUtils.isEmpty; -import static org.apache.nifi.remote.protocol.HandshakeProperty.BATCH_COUNT; -import static org.apache.nifi.remote.protocol.HandshakeProperty.BATCH_DURATION; -import static org.apache.nifi.remote.protocol.HandshakeProperty.BATCH_SIZE; -import static org.apache.nifi.remote.protocol.HandshakeProperty.REQUEST_EXPIRATION_MILLIS; -import static org.apache.nifi.remote.protocol.http.HttpHeaders.HANDSHAKE_PROPERTY_BATCH_COUNT; -import static org.apache.nifi.remote.protocol.http.HttpHeaders.HANDSHAKE_PROPERTY_BATCH_DURATION; -import static org.apache.nifi.remote.protocol.http.HttpHeaders.HANDSHAKE_PROPERTY_BATCH_SIZE; -import static org.apache.nifi.remote.protocol.http.HttpHeaders.HANDSHAKE_PROPERTY_REQUEST_EXPIRATION; -import static org.apache.nifi.remote.protocol.http.HttpHeaders.HANDSHAKE_PROPERTY_USE_COMPRESSION; -import static org.apache.nifi.remote.protocol.http.HttpHeaders.LOCATION_URI_INTENT_NAME; -import static org.apache.nifi.remote.protocol.http.HttpHeaders.LOCATION_URI_INTENT_VALUE; /** * RESTful endpoint for managing a SiteToSite connection. @@ -107,21 +69,34 @@ public class SiteToSiteResource extends ApplicationResource { private static final Logger logger = LoggerFactory.getLogger(SiteToSiteResource.class); - public static final String CHECK_SUM = "checksum"; - public static final String RESPONSE_CODE = "responseCode"; - - - private static final String PORT_TYPE_INPUT = "input-ports"; - private static final String PORT_TYPE_OUTPUT = "output-ports"; - private NiFiServiceFacade serviceFacade; private Authorizer authorizer; private final ResponseCreator responseCreator = new ResponseCreator(); private final VersionNegotiator transportProtocolVersionNegotiator = new TransportProtocolVersionNegotiator(1); private final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); - @Context - private ResourceContext resourceContext; + /** + * Authorizes access to Site To Site details. + * + * Note: Protected for testing purposes + */ + protected void authorizeSiteToSite() { + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(ResourceFactory.getSiteToSiteResource()) + .identity(user.getIdentity()) + .anonymous(user.isAnonymous()) + .accessAttempt(true) + .action(RequestAction.READ) + .build(); + + final AuthorizationResult result = authorizer.authorize(request); + if (!Result.Approved.equals(result.getResult())) { + final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied"; + throw new AccessDeniedException(message); + } + } /** * Returns the details of this NiFi. @@ -145,15 +120,16 @@ public class SiteToSiteResource extends ApplicationResource { @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") } ) - public Response getSiteToSite( - @Context HttpServletRequest req) { + public Response getSiteToSiteDetails(@Context HttpServletRequest req) { + + authorizeSiteToSite(); if (isReplicateRequest()) { return replicate(HttpMethod.GET); } // get the controller dto - final ControllerDTO controller = serviceFacade.getController(); + final ControllerDTO controller = serviceFacade.getSiteToSiteDetails(); // build the response entity final ControllerEntity entity = new ControllerEntity(); @@ -171,42 +147,9 @@ public Response getSiteToSite( return clusterContext(noCache(Response.ok(entity))).build(); } - private Response.ResponseBuilder setCommonHeaders(Response.ResponseBuilder builder, Integer transportProtocolVersion) { - return builder.header(HttpHeaders.PROTOCOL_VERSION, transportProtocolVersion) - .header(HttpHeaders.SERVER_SIDE_TRANSACTION_TTL, transactionManager.getTransactionTtlSec()); - } - - private Integer negotiateTransportProtocolVersion(@Context HttpServletRequest req) throws BadRequestException { - String protocolVersionStr = req.getHeader(HttpHeaders.PROTOCOL_VERSION); - if (isEmpty(protocolVersionStr)) { - throw new BadRequestException("Protocol version was not specified."); - } - - final Integer requestedProtocolVersion; - try { - requestedProtocolVersion = Integer.valueOf(protocolVersionStr); - } catch (NumberFormatException e) { - throw new BadRequestException("Specified protocol version was not in a valid number format: " + protocolVersionStr); - } - - Integer protocolVersion; - if (transportProtocolVersionNegotiator.isVersionSupported(requestedProtocolVersion)) { - return requestedProtocolVersion; - } else { - protocolVersion = transportProtocolVersionNegotiator.getPreferredVersion(requestedProtocolVersion); - } - - if (protocolVersion == null) { - throw new BadRequestException("Specified protocol version is not supported: " + protocolVersionStr); - } - return protocolVersion; - } - - /** * Returns the available Peers and its status of this NiFi. * - * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response. * @return A peersEntity. */ @GET @@ -227,13 +170,9 @@ private Integer negotiateTransportProtocolVersion(@Context HttpServletRequest re @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") } ) - public Response getPeers( - @ApiParam( - value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.", - required = false - ) - @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @Context HttpServletRequest req) { + public Response getPeers(@Context HttpServletRequest req) { + + authorizeSiteToSite(); if (!properties.isSiteToSiteHttpEnabled()) { return responseCreator.httpSiteToSiteIsNotEnabledResponse(); @@ -241,7 +180,7 @@ public Response getPeers( final Integer transportProtocolVersion; try { - transportProtocolVersion = negotiateTransportProtocolVersion(req); + transportProtocolVersion = negotiateTransportProtocolVersion(req, transportProtocolVersionNegotiator); } catch (BadRequestException e) { return responseCreator.badRequestResponse(e); } @@ -287,712 +226,9 @@ public Response getPeers( PeersEntity entity = new PeersEntity(); entity.setPeers(peers); - return clusterContext(noCache(setCommonHeaders(Response.ok(entity), transportProtocolVersion))).build(); - } - - @POST - @Produces(MediaType.APPLICATION_JSON) - @Path("{portType}/{portId}/transactions") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Create a transaction to the specified output port or input port", - response = TransactionResultEntity.class, - authorizations = { - @Authorization(value = "Read Only", type = "ROLE_MONITOR"), - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), - @Authorization(value = "Administrator", type = "ROLE_ADMIN") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), - @ApiResponse(code = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 404, message = "The specified resource could not be found."), - @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful."), - @ApiResponse(code = 503, message = "NiFi instance is not ready for serving request, or temporarily overloaded. Retrying the same request later may be successful"), - } - ) - public Response createPortTransaction( - @ApiParam( - value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.", - required = false - ) - @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @ApiParam( - value = "The input port id.", - required = true - ) - @PathParam("portType") String portType, - @PathParam("portId") String portId, - @Context HttpServletRequest req, - @Context ServletContext context, - @Context UriInfo uriInfo, - InputStream inputStream) { - - final ValidateRequestResult validationResult = validateResult(req, clientId, portId); - if (validationResult.errResponse != null) { - return validationResult.errResponse; - } - - if(!PORT_TYPE_INPUT.equals(portType) && !PORT_TYPE_OUTPUT.equals(portType)){ - return responseCreator.wrongPortTypeResponse(clientId, portType, portId); - } - - logger.debug("createPortTransaction request: clientId={}, portType={}, portId={}", clientId.getClientId(), portType, portId); - - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - final String transactionId = transactionManager.createTransaction(); - final Peer peer = constructPeer(req, inputStream, out, portId, transactionId); - final int transportProtocolVersion = validationResult.transportProtocolVersion; - - try { - // Execute handshake. - initiateServerProtocol(peer, transportProtocolVersion); - - TransactionResultEntity entity = new TransactionResultEntity(); - entity.setResponseCode(ResponseCode.PROPERTIES_OK.getCode()); - entity.setMessage("Handshake properties are valid, and port is running. A transaction is created:" + transactionId); - - return responseCreator.locationResponse(uriInfo, portType, portId, transactionId, entity, transportProtocolVersion); - - } catch (HandshakeException e) { - transactionManager.cancelTransaction(transactionId); - return responseCreator.handshakeExceptionResponse(e); - - } catch (Exception e) { - transactionManager.cancelTransaction(transactionId); - return responseCreator.unexpectedErrorResponse(clientId, portId, e); - } - } - - @POST - @Consumes(MediaType.APPLICATION_OCTET_STREAM) - @Produces(MediaType.TEXT_PLAIN) - @Path("input-ports/{portId}/transactions/{transactionId}/flow-files") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Transfer flow files to the input port", - response = String.class, - authorizations = { - @Authorization(value = "Read Only", type = "ROLE_MONITOR"), - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), - @Authorization(value = "Administrator", type = "ROLE_ADMIN") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), - @ApiResponse(code = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 404, message = "The specified resource could not be found."), - @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful."), - @ApiResponse(code = 503, message = "NiFi instance is not ready for serving request, or temporarily overloaded. Retrying the same request later may be successful"), - } - ) - public Response receiveFlowFiles( - @ApiParam( - value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.", - required = false - ) - @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @ApiParam( - value = "The input port id.", - required = true - ) - @PathParam("portId") String portId, - @PathParam("transactionId") String transactionId, - @Context HttpServletRequest req, - @Context ServletContext context, - InputStream inputStream) { - - final ValidateRequestResult validationResult = validateResult(req, clientId, portId, transactionId); - if (validationResult.errResponse != null) { - return validationResult.errResponse; - } - - logger.debug("receiveFlowFiles request: clientId={}, portId={}", clientId.getClientId(), portId); - - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - final Peer peer = constructPeer(req, inputStream, out, portId, transactionId); - final int transportProtocolVersion = validationResult.transportProtocolVersion; - - try { - HttpFlowFileServerProtocol serverProtocol = initiateServerProtocol(peer, transportProtocolVersion); - int numOfFlowFiles = serverProtocol.getPort().receiveFlowFiles(peer, serverProtocol); - logger.debug("finished receiving flow files, numOfFlowFiles={}", numOfFlowFiles); - if (numOfFlowFiles < 1) { - return Response.status(Response.Status.BAD_REQUEST) - .entity("Client should send request when there is data to send. There was no flow file sent.").build(); - } - } catch (HandshakeException e) { - return responseCreator.handshakeExceptionResponse(e); - - } catch (NotAuthorizedException e) { - return responseCreator.unauthorizedResponse(e); - - } catch (BadRequestException | RequestExpiredException e) { - return responseCreator.badRequestResponse(e); - - } catch (Exception e) { - return responseCreator.unexpectedErrorResponse(clientId, portId, e); - } - - String serverChecksum = ((HttpServerCommunicationsSession)peer.getCommunicationsSession()).getChecksum(); - return responseCreator.acceptedResponse(serverChecksum, transportProtocolVersion); + return clusterContext(noCache(setCommonHeaders(Response.ok(entity), transportProtocolVersion, transactionManager))).build(); } - private HttpFlowFileServerProtocol initiateServerProtocol(Peer peer, Integer transportProtocolVersion) throws IOException { - // Switch transaction protocol version based on transport protocol version. - TransportProtocolVersionNegotiator negotiatedTransportProtocolVersion = new TransportProtocolVersionNegotiator(transportProtocolVersion); - VersionNegotiator versionNegotiator = new StandardVersionNegotiator(negotiatedTransportProtocolVersion.getTransactionProtocolVersion()); - HttpFlowFileServerProtocol serverProtocol = getHttpFlowFileServerProtocol(versionNegotiator); - HttpRemoteSiteListener.getInstance().setupServerProtocol(serverProtocol); - // TODO: How should I pass cluster information? - // serverProtocol.setNodeInformant(clusterManager); - serverProtocol.handshake(peer); - return serverProtocol; - } - - HttpFlowFileServerProtocol getHttpFlowFileServerProtocol(VersionNegotiator versionNegotiator) { - return new HttpFlowFileServerProtocolImpl(versionNegotiator); - } - - private Peer constructPeer(HttpServletRequest req, InputStream inputStream, OutputStream outputStream, String portId, String transactionId) { - String clientHostName = req.getRemoteHost(); - int clientPort = req.getRemotePort(); - - PeerDescription peerDescription = new PeerDescription(clientHostName, clientPort, req.isSecure()); - - HttpServerCommunicationsSession commSession = new HttpServerCommunicationsSession(inputStream, outputStream, transactionId); - - boolean useCompression = false; - final String useCompressionStr = req.getHeader(HANDSHAKE_PROPERTY_USE_COMPRESSION); - if (!isEmpty(useCompressionStr) && Boolean.valueOf(useCompressionStr)) { - useCompression = true; - } - - final String requestExpiration = req.getHeader(HANDSHAKE_PROPERTY_REQUEST_EXPIRATION); - final String batchCount = req.getHeader(HANDSHAKE_PROPERTY_BATCH_COUNT); - final String batchSize = req.getHeader(HANDSHAKE_PROPERTY_BATCH_SIZE); - final String batchDuration = req.getHeader(HANDSHAKE_PROPERTY_BATCH_DURATION); - - commSession.putHandshakeParam(HandshakeProperty.PORT_IDENTIFIER, portId); - commSession.putHandshakeParam(HandshakeProperty.GZIP, String.valueOf(useCompression)); - - if (!isEmpty(requestExpiration)) commSession.putHandshakeParam(REQUEST_EXPIRATION_MILLIS, requestExpiration); - if (!isEmpty(batchCount)) commSession.putHandshakeParam(BATCH_COUNT, batchCount); - if (!isEmpty(batchSize)) commSession.putHandshakeParam(BATCH_SIZE, batchSize); - if (!isEmpty(batchDuration)) commSession.putHandshakeParam(BATCH_DURATION, batchDuration); - - if(peerDescription.isSecure()){ - NiFiUser nifiUser = NiFiUserUtils.getNiFiUser(); - logger.debug("initiating peer, nifiUser={}", nifiUser); - commSession.setUserDn(nifiUser.getIdentity()); - } - - // TODO: Followed how SocketRemoteSiteListener define peerUrl and clusterUrl, but it can be more meaningful values, especially for clusterUrl. - String peerUrl = "nifi://" + clientHostName + ":" + clientPort; - String clusterUrl = "nifi://localhost:" + req.getLocalPort(); - return new Peer(peerDescription, commSession, peerUrl, clusterUrl); - } - - @DELETE - @Consumes(MediaType.APPLICATION_OCTET_STREAM) - @Produces(MediaType.APPLICATION_JSON) - @Path("output-ports/{portId}/transactions/{transactionId}") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Commit or cancel the specified transaction", - response = TransactionResultEntity.class, - authorizations = { - @Authorization(value = "Read Only", type = "ROLE_MONITOR"), - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), - @Authorization(value = "Administrator", type = "ROLE_ADMIN") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), - @ApiResponse(code = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 404, message = "The specified resource could not be found."), - @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful."), - @ApiResponse(code = 503, message = "NiFi instance is not ready for serving request, or temporarily overloaded. Retrying the same request later may be successful"), - } - ) - public Response commitOutputPortTransaction( - @ApiParam( - value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.", - required = false - ) - @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @ApiParam( - value = "The response code. Available values are CONFIRM_TRANSACTION(12) or CANCEL_TRANSACTION(15).", - required = true - ) - @QueryParam(RESPONSE_CODE) Integer responseCode, - @ApiParam( - value = "A checksum calculated at client side using CRC32 to check flow file content integrity. It must match with the value calculated at server side.", - required = true - ) - @QueryParam(CHECK_SUM) @DefaultValue(StringUtils.EMPTY) String checksum, - @ApiParam( - value = "The input port id.", - required = true - ) - @PathParam("portId") String portId, - @ApiParam( - value = "The transaction id.", - required = true - ) - @PathParam("transactionId") String transactionId, - @Context HttpServletRequest req, - @Context ServletContext context, - InputStream inputStream) { - - final ValidateRequestResult validationResult = validateResult(req, clientId, portId, transactionId); - if (validationResult.errResponse != null) { - return validationResult.errResponse; - } - - - logger.debug("commitOutputPortTransaction request: clientId={}, portId={}, transactionId={}", clientId, portId, transactionId); - - final int transportProtocolVersion = validationResult.transportProtocolVersion; - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - final Peer peer = constructPeer(req, inputStream, out, portId, transactionId); - - final TransactionResultEntity entity = new TransactionResultEntity(); - try { - HttpFlowFileServerProtocol serverProtocol = initiateServerProtocol(peer, transportProtocolVersion); - - String inputErrMessage = null; - if (responseCode == null) { - inputErrMessage = "responseCode is required."; - } else if(ResponseCode.CONFIRM_TRANSACTION.getCode() != responseCode - && ResponseCode.CANCEL_TRANSACTION.getCode() != responseCode) { - inputErrMessage = "responseCode " + responseCode + " is invalid. "; - } - - if (inputErrMessage != null){ - entity.setMessage(inputErrMessage); - entity.setResponseCode(ResponseCode.ABORT.getCode()); - return Response.status(Response.Status.BAD_REQUEST).entity(entity).build(); - } - - if (ResponseCode.CANCEL_TRANSACTION.getCode() == responseCode) { - return cancelTransaction(transactionId, entity); - } - - int flowFileSent = serverProtocol.commitTransferTransaction(peer, checksum); - entity.setResponseCode(ResponseCode.CONFIRM_TRANSACTION.getCode()); - entity.setFlowFileSent(flowFileSent); - - } catch (HandshakeException e) { - return responseCreator.handshakeExceptionResponse(e); - - } catch (Exception e) { - HttpServerCommunicationsSession commsSession = (HttpServerCommunicationsSession) peer.getCommunicationsSession(); - logger.error("Failed to process the request", e); - if(ResponseCode.BAD_CHECKSUM.equals(commsSession.getResponseCode())){ - entity.setResponseCode(commsSession.getResponseCode().getCode()); - entity.setMessage(e.getMessage()); - - Response.ResponseBuilder builder = Response.status(Response.Status.BAD_REQUEST).entity(entity); - return clusterContext(noCache(builder)).build(); - } - - return responseCreator.unexpectedErrorResponse(clientId, portId, transactionId, e); - } - - return clusterContext(noCache(setCommonHeaders(Response.ok(entity), transportProtocolVersion))).build(); - } - - - @DELETE - @Consumes(MediaType.APPLICATION_OCTET_STREAM) - @Produces(MediaType.APPLICATION_JSON) - @Path("input-ports/{portId}/transactions/{transactionId}") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Commit or cancel the specified transaction", - response = TransactionResultEntity.class, - authorizations = { - @Authorization(value = "Read Only", type = "ROLE_MONITOR"), - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), - @Authorization(value = "Administrator", type = "ROLE_ADMIN") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), - @ApiResponse(code = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 404, message = "The specified resource could not be found."), - @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful."), - @ApiResponse(code = 503, message = "NiFi instance is not ready for serving request, or temporarily overloaded. Retrying the same request later may be successful"), - } - ) - public Response commitInputPortTransaction( - @ApiParam( - value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.", - required = false - ) - @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @ApiParam( - value = "The response code. Available values are BAD_CHECKSUM(19), CONFIRM_TRANSACTION(12) or CANCEL_TRANSACTION(15).", - required = true - ) - @QueryParam(RESPONSE_CODE) Integer responseCode, - @ApiParam( - value = "The input port id.", - required = true - ) - @PathParam("portId") String portId, - @ApiParam( - value = "The transaction id.", - required = true - ) - @PathParam("transactionId") String transactionId, - @Context HttpServletRequest req, - @Context ServletContext context, - InputStream inputStream) { - - - final ValidateRequestResult validationResult = validateResult(req, clientId, portId, transactionId); - if (validationResult.errResponse != null) { - return validationResult.errResponse; - } - - logger.debug("commitInputPortTransaction request: clientId={}, portId={}, transactionId={}, responseCode={}", - clientId, portId, transactionId, responseCode); - - final int transportProtocolVersion = validationResult.transportProtocolVersion; - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - final Peer peer = constructPeer(req, inputStream, out, portId, transactionId); - - final TransactionResultEntity entity = new TransactionResultEntity(); - try { - HttpFlowFileServerProtocol serverProtocol = initiateServerProtocol(peer, transportProtocolVersion); - HttpServerCommunicationsSession commsSession = (HttpServerCommunicationsSession) peer.getCommunicationsSession(); - // Pass the response code sent from the client. - String inputErrMessage = null; - if (responseCode == null) { - inputErrMessage = "responseCode is required."; - } else if(ResponseCode.BAD_CHECKSUM.getCode() != responseCode - && ResponseCode.CONFIRM_TRANSACTION.getCode() != responseCode - && ResponseCode.CANCEL_TRANSACTION.getCode() != responseCode) { - inputErrMessage = "responseCode " + responseCode + " is invalid. "; - } - - if (inputErrMessage != null){ - entity.setMessage(inputErrMessage); - entity.setResponseCode(ResponseCode.ABORT.getCode()); - return Response.status(Response.Status.BAD_REQUEST).entity(entity).build(); - } - - if (ResponseCode.CANCEL_TRANSACTION.getCode() == responseCode) { - return cancelTransaction(transactionId, entity); - } - - commsSession.setResponseCode(ResponseCode.fromCode(responseCode)); - - try { - int flowFileSent = serverProtocol.commitReceiveTransaction(peer); - entity.setResponseCode(commsSession.getResponseCode().getCode()); - entity.setFlowFileSent(flowFileSent); - - } catch (IOException e){ - if (ResponseCode.BAD_CHECKSUM.getCode() == responseCode && e.getMessage().contains("Received a BadChecksum response")){ - // AbstractFlowFileServerProtocol throws IOException after it canceled transaction. - // This is a known behavior and if we return 500 with this exception, - // it's not clear if there is an issue at server side, or cancel operation has been accomplished. - // Above conditions can guarantee this is the latter case, we return 200 OK here. - entity.setResponseCode(ResponseCode.CANCEL_TRANSACTION.getCode()); - return clusterContext(noCache(Response.ok(entity))).build(); - } else { - return responseCreator.unexpectedErrorResponse(clientId, portId, transactionId, e); - } - } - - } catch (HandshakeException e) { - return responseCreator.handshakeExceptionResponse(e); - - } catch (Exception e) { - return responseCreator.unexpectedErrorResponse(clientId, portId, transactionId, e); - } - - return clusterContext(noCache(setCommonHeaders(Response.ok(entity), transportProtocolVersion))).build(); - } - - private Response cancelTransaction(String transactionId, TransactionResultEntity entity) { - transactionManager.cancelTransaction(transactionId); - entity.setMessage("Transaction has been canceled."); - entity.setResponseCode(ResponseCode.CANCEL_TRANSACTION.getCode()); - return Response.ok(entity).build(); - } - - - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_OCTET_STREAM) - @Path("output-ports/{portId}/transactions/{transactionId}/flow-files") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Transfer flow files from the output port", - response = StreamingOutput.class, - authorizations = { - @Authorization(value = "Read Only", type = "ROLE_MONITOR"), - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), - @Authorization(value = "Administrator", type = "ROLE_ADMIN") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 200, message = "There is no flow file to return."), - @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), - @ApiResponse(code = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 404, message = "The specified resource could not be found."), - @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful."), - @ApiResponse(code = 503, message = "NiFi instance is not ready for serving request, or temporarily overloaded. Retrying the same request later may be successful"), - } - ) - public Response transferFlowFiles( - @ApiParam( - value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.", - required = false - ) - @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @ApiParam( - value = "The input port id.", - required = true - ) - @PathParam("portId") String portId, - @PathParam("transactionId") String transactionId, - @Context HttpServletRequest req, - @Context HttpServletResponse res, - @Context ServletContext context, - InputStream inputStream) { - - final ValidateRequestResult validationResult = validateResult(req, clientId, portId, transactionId); - if (validationResult.errResponse != null) { - return validationResult.errResponse; - } - - logger.debug("transferFlowFiles request: clientId={}, portId={}", clientId.getClientId(), portId); - - // Before opening the real output stream for HTTP response, - // use this temporary output stream to buffer handshake result. - final ByteArrayOutputStream tempBos = new ByteArrayOutputStream(); - final Peer peer = constructPeer(req, inputStream, tempBos, portId, transactionId); - final int transportProtocolVersion = validationResult.transportProtocolVersion; - try { - final HttpFlowFileServerProtocol serverProtocol = initiateServerProtocol(peer, transportProtocolVersion); - - StreamingOutput flowFileContent = new StreamingOutput() { - @Override - public void write(OutputStream outputStream) throws IOException, WebApplicationException { - - HttpOutput output = (HttpOutput)peer.getCommunicationsSession().getOutput(); - output.setOutputStream(outputStream); - - try { - int numOfFlowFiles = serverProtocol.getPort().transferFlowFiles(peer, serverProtocol); - logger.debug("finished transferring flow files, numOfFlowFiles={}", numOfFlowFiles); - if(numOfFlowFiles < 1){ - // There was no flow file to transfer. Throw this exception to stop responding with SEE OTHER. - throw new WebApplicationException(Response.Status.OK); - } - } catch (NotAuthorizedException | BadRequestException | RequestExpiredException e) { - // Handshake is done outside of write() method, so these exception wouldn't be thrown. - throw new IOException("Failed to process the request.", e); - } - } - - }; - - return responseCreator.acceptedResponse(flowFileContent, transportProtocolVersion); - - } catch (HandshakeException e) { - return responseCreator.handshakeExceptionResponse(e); - - } catch (Exception e) { - return responseCreator.unexpectedErrorResponse(clientId, portId, e); - } - } - - @PUT - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("input-ports/{portId}/transactions/{transactionId}") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Extend transaction TTL", - response = TransactionResultEntity.class, - authorizations = { - @Authorization(value = "Read Only", type = "ROLE_MONITOR"), - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), - @Authorization(value = "Administrator", type = "ROLE_ADMIN") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), - @ApiResponse(code = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 404, message = "The specified resource could not be found."), - @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") - } - ) - public Response extendInputPortTransactionTTL( - @ApiParam( - value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.", - required = false - ) - @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @PathParam("portId") String portId, - @PathParam("transactionId") String transactionId, - @Context HttpServletRequest req, - @Context HttpServletResponse res, - @Context ServletContext context, - @Context UriInfo uriInfo, - InputStream inputStream) { - - return extendPortTransactionTTL(clientId, PORT_TYPE_INPUT, portId, transactionId, req, res, context, uriInfo, inputStream); - - } - - @PUT - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("output-ports/{portId}/transactions/{transactionId}") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Extend transaction TTL", - response = TransactionResultEntity.class, - authorizations = { - @Authorization(value = "Read Only", type = "ROLE_MONITOR"), - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), - @Authorization(value = "Administrator", type = "ROLE_ADMIN") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), - @ApiResponse(code = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 404, message = "The specified resource could not be found."), - @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful."), - @ApiResponse(code = 503, message = "NiFi instance is not ready for serving request, or temporarily overloaded. Retrying the same request later may be successful"), - } - ) - public Response extendOutputPortTransactionTTL( - @ApiParam( - value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.", - required = false - ) - @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @PathParam("portId") String portId, - @PathParam("transactionId") String transactionId, - @Context HttpServletRequest req, - @Context HttpServletResponse res, - @Context ServletContext context, - @Context UriInfo uriInfo, - InputStream inputStream) { - - return extendPortTransactionTTL(clientId, PORT_TYPE_OUTPUT, portId, transactionId, req, res, context, uriInfo, inputStream); - - } - - public Response extendPortTransactionTTL( - ClientIdParameter clientId, - String portType, - String portId, - String transactionId, - HttpServletRequest req, - HttpServletResponse res, - ServletContext context, - UriInfo uriInfo, - InputStream inputStream) { - - final ValidateRequestResult validationResult = validateResult(req, clientId, portId, transactionId); - if (validationResult.errResponse != null) { - return validationResult.errResponse; - } - - if(!PORT_TYPE_INPUT.equals(portType) && !PORT_TYPE_OUTPUT.equals(portType)){ - return responseCreator.wrongPortTypeResponse(clientId, portType, portId); - } - - logger.debug("extendOutputPortTransactionTTL request: clientId={}, portType={}, portId={}, transactionId={}", - clientId.getClientId(), portType, portId, transactionId); - - final int transportProtocolVersion = validationResult.transportProtocolVersion; - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - final Peer peer = constructPeer(req, inputStream, out, portId, transactionId); - - try { - // Do handshake - initiateServerProtocol(peer, transportProtocolVersion); - transactionManager.extendsTransaction(transactionId); - - final TransactionResultEntity entity = new TransactionResultEntity(); - entity.setResponseCode(ResponseCode.CONTINUE_TRANSACTION.getCode()); - entity.setMessage("Extended TTL."); - return clusterContext(noCache(setCommonHeaders(Response.ok(entity), transportProtocolVersion))).build(); - - } catch (HandshakeException e) { - return responseCreator.handshakeExceptionResponse(e); - - } catch (Exception e) { - return responseCreator.unexpectedErrorResponse(clientId, portId, transactionId, e); - } - - } - - private class ValidateRequestResult { - private Integer transportProtocolVersion; - private Response errResponse; - } - - private ValidateRequestResult validateResult(HttpServletRequest req, ClientIdParameter clientId, String portId) { - return validateResult(req, clientId, portId, null); - } - - private ValidateRequestResult validateResult(HttpServletRequest req, ClientIdParameter clientId, String portId, String transactionId) { - ValidateRequestResult result = new ValidateRequestResult(); - if(!properties.isSiteToSiteHttpEnabled()) { - result.errResponse = responseCreator.httpSiteToSiteIsNotEnabledResponse(); - return result; - } - - // TODO: NCM no longer exists. - /* - if (properties.isClusterManager()) { - result.errResponse = responseCreator.nodeTypeErrorResponse(req.getPathInfo() + " is not available on a NiFi Cluster Manager."); - return result; - } - */ - - - try { - result.transportProtocolVersion = negotiateTransportProtocolVersion(req); - } catch (BadRequestException e) { - result.errResponse = responseCreator.badRequestResponse(e); - return result; - } - - if(!isEmpty(transactionId) && !transactionManager.isTransactionActive(transactionId)) { - result.errResponse = responseCreator.transactionNotFoundResponse(clientId, portId, transactionId); - return result; - } - - return result; - } - - // setters public void setServiceFacade(NiFiServiceFacade serviceFacade) { this.serviceFacade = serviceFacade; @@ -1002,114 +238,4 @@ public void setAuthorizer(Authorizer authorizer) { this.authorizer = authorizer; } - private class ResponseCreator { - - private Response nodeTypeErrorResponse(String errMsg) { - return noCache(Response.status(Response.Status.FORBIDDEN)).type(MediaType.TEXT_PLAIN).entity(errMsg).build(); - } - - private Response httpSiteToSiteIsNotEnabledResponse() { - return noCache(Response.status(Response.Status.FORBIDDEN)).type(MediaType.TEXT_PLAIN).entity("HTTP(S) Site-to-Site is not enabled on this host.").build(); - } - - private Response wrongPortTypeResponse(ClientIdParameter clientId, String portType, String portId) { - logger.debug("Port type was wrong. clientId={}, portType={}, portId={}", clientId.getClientId(), portType, portId); - TransactionResultEntity entity = new TransactionResultEntity(); - entity.setResponseCode(ResponseCode.ABORT.getCode()); - entity.setMessage("Port was not found."); - entity.setFlowFileSent(0); - return Response.status(NOT_FOUND).entity(entity).type(MediaType.APPLICATION_JSON_TYPE).build(); - } - - private Response transactionNotFoundResponse(ClientIdParameter clientId, String portId, String transactionId) { - logger.debug("Transaction was not found. clientId={}, portId={}, transactionId={}", clientId.getClientId(), portId, transactionId); - TransactionResultEntity entity = new TransactionResultEntity(); - entity.setResponseCode(ResponseCode.ABORT.getCode()); - entity.setMessage("Transaction was not found."); - entity.setFlowFileSent(0); - return Response.status(NOT_FOUND).entity(entity).type(MediaType.APPLICATION_JSON_TYPE).build(); - } - - private Response unexpectedErrorResponse(ClientIdParameter clientId, String portId, Exception e) { - logger.error("Unexpected exception occurred. clientId={}, portId={}", clientId.getClientId(), portId); - logger.error("Exception detail:", e); - TransactionResultEntity entity = new TransactionResultEntity(); - entity.setResponseCode(ResponseCode.ABORT.getCode()); - entity.setMessage("Server encountered an exception."); - entity.setFlowFileSent(0); - return Response.serverError().entity(entity).type(MediaType.APPLICATION_JSON_TYPE).build(); - } - - private Response unexpectedErrorResponse(ClientIdParameter clientId, String portId, String transactionId, Exception e) { - logger.error("Unexpected exception occurred. clientId={}, portId={}, transactionId={}", clientId.getClientId(), portId, transactionId); - logger.error("Exception detail:", e); - TransactionResultEntity entity = new TransactionResultEntity(); - entity.setResponseCode(ResponseCode.ABORT.getCode()); - entity.setMessage("Server encountered an exception."); - entity.setFlowFileSent(0); - return Response.serverError().entity(entity).type(MediaType.APPLICATION_JSON_TYPE).build(); - } - - private Response unauthorizedResponse(NotAuthorizedException e) { - if (logger.isDebugEnabled()) { - logger.debug("Client request was not authorized. {}", e.getMessage()); - } - TransactionResultEntity entity = new TransactionResultEntity(); - entity.setResponseCode(ResponseCode.UNAUTHORIZED.getCode()); - entity.setMessage(e.getMessage()); - entity.setFlowFileSent(0); - return Response.status(Response.Status.UNAUTHORIZED).type(MediaType.APPLICATION_JSON_TYPE).entity(e.getMessage()).build(); - } - - private Response badRequestResponse(Exception e) { - if (logger.isDebugEnabled()) { - logger.debug("Client sent a bad request. {}", e.getMessage()); - } - TransactionResultEntity entity = new TransactionResultEntity(); - entity.setResponseCode(ResponseCode.ABORT.getCode()); - entity.setMessage(e.getMessage()); - entity.setFlowFileSent(0); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(entity).build(); - } - - private Response handshakeExceptionResponse(HandshakeException e) { - if(logger.isDebugEnabled()){ - logger.debug("Handshake failed, {}", e.getMessage()); - } - ResponseCode handshakeRes = e.getResponseCode(); - Response.Status statusCd; - TransactionResultEntity entity = new TransactionResultEntity(); - entity.setResponseCode(handshakeRes != null ? handshakeRes.getCode() : ResponseCode.ABORT.getCode()); - entity.setMessage(e.getMessage()); - entity.setFlowFileSent(0); - switch (handshakeRes) { - case PORT_NOT_IN_VALID_STATE: - case PORTS_DESTINATION_FULL: - return Response.status(Response.Status.SERVICE_UNAVAILABLE).type(MediaType.APPLICATION_JSON_TYPE).entity(entity).build(); - case UNAUTHORIZED: - statusCd = Response.Status.UNAUTHORIZED; - break; - case UNKNOWN_PORT: - statusCd = NOT_FOUND; - break; - default: - statusCd = Response.Status.BAD_REQUEST; - } - return Response.status(statusCd).type(MediaType.APPLICATION_JSON_TYPE).entity(entity).build(); - } - - private Response acceptedResponse(Object entity, Integer protocolVersion) { - return noCache(setCommonHeaders(Response.status(Response.Status.ACCEPTED), protocolVersion)) - .entity(entity).build(); - } - - private Response locationResponse(UriInfo uriInfo, String portType, String portId, String transactionId, Object entity, Integer protocolVersion) { - String path = "/site-to-site/" + portType + "/" + portId + "/transactions/" + transactionId; - URI location = uriInfo.getBaseUriBuilder().path(path).build(); - return noCache(setCommonHeaders(Response.created(location), protocolVersion) - .header(LOCATION_URI_INTENT_NAME, LOCATION_URI_INTENT_VALUE)) - .entity(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/TenantsResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TenantsResource.java index 4c6dcd3a66a4..353e4841c114 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TenantsResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TenantsResource.java @@ -33,8 +33,12 @@ import org.apache.nifi.web.NiFiServiceFacade; import org.apache.nifi.web.Revision; import org.apache.nifi.web.api.dto.RevisionDTO; +import org.apache.nifi.web.api.dto.TenantDTO; import org.apache.nifi.web.api.dto.UserDTO; import org.apache.nifi.web.api.dto.UserGroupDTO; +import org.apache.nifi.web.api.entity.ClusterSearchResultsEntity; +import org.apache.nifi.web.api.entity.TenantEntity; +import org.apache.nifi.web.api.entity.TenantsEntity; import org.apache.nifi.web.api.entity.UserEntity; import org.apache.nifi.web.api.entity.UserGroupEntity; import org.apache.nifi.web.api.entity.UserGroupsEntity; @@ -58,6 +62,9 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; import java.util.Set; @Path("tenants") @@ -182,8 +189,8 @@ public Response createUser( if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable users = lookup.getTenantAuthorizable(); - users.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + final Authorizable tenants = lookup.getTenant(); + tenants.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }); } if (validationPhase) { @@ -247,8 +254,8 @@ public Response getUser( // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable users = lookup.getTenantAuthorizable(); - users.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); + final Authorizable tenants = lookup.getTenant(); + tenants.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }); // get the user @@ -294,8 +301,8 @@ public Response getUsers() { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable users = lookup.getTenantAuthorizable(); - users.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); + final Authorizable tenants = lookup.getTenant(); + tenants.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }); // get all the users @@ -303,6 +310,7 @@ public Response getUsers() { // create the response entity final UsersEntity entity = new UsersEntity(); + entity.setGenerated(new Date()); entity.setUsers(populateRemainingUserEntitiesContent(users)); // generate the response @@ -375,8 +383,8 @@ public Response updateUser( serviceFacade, revision, lookup -> { - final Authorizable users = lookup.getTenantAuthorizable(); - users.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + final Authorizable tenants = lookup.getTenant(); + tenants.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }, null, () -> { @@ -450,8 +458,8 @@ public Response removeUser( serviceFacade, revision, lookup -> { - final Authorizable users = lookup.getTenantAuthorizable(); - users.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); + final Authorizable tenants = lookup.getTenant(); + tenants.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }, null, () -> { @@ -567,8 +575,8 @@ public Response createUserGroup( if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable userGroups = lookup.getTenantAuthorizable(); - userGroups.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + final Authorizable tenants = lookup.getTenant(); + tenants.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }); } if (validationPhase) { @@ -632,8 +640,8 @@ public Response getUserGroup( // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable userGroups = lookup.getTenantAuthorizable(); - userGroups.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); + final Authorizable tenants = lookup.getTenant(); + tenants.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }); // get the user group @@ -679,8 +687,8 @@ public Response getUserGroups() { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Authorizable userGroups = lookup.getTenantAuthorizable(); - userGroups.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); + final Authorizable tenants = lookup.getTenant(); + tenants.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }); // get all the user groups @@ -760,8 +768,8 @@ public Response updateUserGroup( serviceFacade, revision, lookup -> { - final Authorizable userGroups = lookup.getTenantAuthorizable(); - userGroups.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); + final Authorizable tenants = lookup.getTenant(); + tenants.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); }, null, () -> { @@ -835,8 +843,8 @@ public Response removeUserGroup( serviceFacade, revision, lookup -> { - final Authorizable userGroups = lookup.getTenantAuthorizable(); - userGroups.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); + final Authorizable tenants = lookup.getTenant(); + tenants.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); }, null, () -> { @@ -846,4 +854,100 @@ public Response removeUserGroup( } ); } + + // ------------ + // search users + // ------------ + + /** + * Searches the cluster for a node with a given address. + * + * @param value Search value that will be matched against a node's address + * @return Nodes that match the specified criteria + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("search-results") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Searches the cluster for a node with the specified address", + response = ClusterSearchResultsEntity.class, + authorizations = { + @Authorization(value = "Read Only", type = "ROLE_MONITOR"), + @Authorization(value = "DFM", type = "ROLE_DFM"), + @Authorization(value = "Admin", type = "ROLE_ADMIN") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") + } + ) + public Response searchCluster( + @ApiParam( + value = "Node address to search for.", + required = true + ) + @QueryParam("q") @DefaultValue(StringUtils.EMPTY) String value) { + + if (isReplicateRequest()) { + return replicate(HttpMethod.GET); + } + + // authorize access + serviceFacade.authorizeAccess(lookup -> { + final Authorizable tenants = lookup.getTenant(); + tenants.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); + }); + + final List userMatches = new ArrayList<>(); + final List userGroupMatches = new ArrayList<>(); + + // get the users + for (final UserEntity userEntity : serviceFacade.getUsers()) { + final UserDTO user = userEntity.getComponent(); + if (StringUtils.isBlank(value) || StringUtils.containsIgnoreCase(user.getIdentity(), value)) { + final TenantDTO tenant = new TenantDTO(); + tenant.setId(user.getId()); + tenant.setIdentity(user.getIdentity()); + + final TenantEntity entity = new TenantEntity(); + entity.setPermissions(userEntity.getPermissions()); + entity.setId(userEntity.getId()); + entity.setComponent(tenant); + + userMatches.add(entity); + } + } + + // get the user groups + for (final UserGroupEntity userGroupEntity : serviceFacade.getUserGroups()) { + final UserGroupDTO userGroup = userGroupEntity.getComponent(); + if (StringUtils.isBlank(value) || StringUtils.containsIgnoreCase(userGroup.getIdentity(), value)) { + final TenantDTO tenant = new TenantDTO(); + tenant.setId(userGroup.getId()); + tenant.setIdentity(userGroup.getIdentity()); + + final TenantEntity entity = new TenantEntity(); + entity.setPermissions(userGroupEntity.getPermissions()); + entity.setId(userGroupEntity.getId()); + entity.setComponent(tenant); + + userGroupMatches.add(entity); + } + } + + // build the response + final TenantsEntity results = new TenantsEntity(); + results.setUsers(userMatches); + results.setUserGroups(userGroupMatches); + + // generate an 200 - OK response + return noCache(Response.ok(results)).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/dto/DtoFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java index cf1bca49bdee..7396a3421419 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java @@ -139,6 +139,7 @@ import org.apache.nifi.web.api.dto.status.ProcessorStatusSnapshotDTO; import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO; import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusSnapshotDTO; +import org.apache.nifi.web.api.entity.AccessPolicySummaryEntity; import org.apache.nifi.web.api.entity.FlowBreadcrumbEntity; import org.apache.nifi.web.api.entity.TenantEntity; import org.apache.nifi.web.controller.ControllerFacade; @@ -681,7 +682,7 @@ public LabelDTO createLabelDto(final Label label) { * @param user user * @return dto */ - public UserDTO createUserDto(final User user, final Set groups) { + public UserDTO createUserDto(final User user, final Set groups, final Set accessPolicies) { if (user == null) { return null; } @@ -690,6 +691,7 @@ public UserDTO createUserDto(final User user, final Set groups) { dto.setId(user.getIdentifier()); dto.setUserGroups(groups); dto.setIdentity(user.getIdentity()); + dto.setAccessPolicies(accessPolicies); return dto; } @@ -1516,8 +1518,8 @@ private FlowBreadcrumbEntity createBreadcrumbEntity(final ProcessGroup group) { } final FlowBreadcrumbDTO dto = createBreadcrumbDto(group); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(group); - final FlowBreadcrumbEntity entity = entityFactory.createFlowBreadcrumbEntity(dto, accessPolicy); + final PermissionsDTO permissions = createPermissionsDto(group); + final FlowBreadcrumbEntity entity = entityFactory.createFlowBreadcrumbEntity(dto, permissions); if (group.getParent() != null) { entity.setParentBreadcrumb(createBreadcrumbEntity(group.getParent())); @@ -1544,6 +1546,18 @@ private FlowBreadcrumbDTO createBreadcrumbDto(final ProcessGroup group) { return dto; } + public AccessPolicySummaryDTO createAccessPolicySummaryDto(final AccessPolicy accessPolicy) { + if (accessPolicy == null) { + return null; + } + + final AccessPolicySummaryDTO dto = new AccessPolicySummaryDTO(); + dto.setId(accessPolicy.getIdentifier()); + dto.setResource(accessPolicy.getResource()); + dto.setAction(accessPolicy.getAction().toString()); + return dto; + } + public AccessPolicyDTO createAccessPolicyDto(final AccessPolicy accessPolicy, Set userGroups, Set users) { if (accessPolicy == null) { return null; @@ -1554,23 +1568,18 @@ public AccessPolicyDTO createAccessPolicyDto(final AccessPolicy accessPolicy, Se dto.setUsers(users); dto.setId(accessPolicy.getIdentifier()); dto.setResource(accessPolicy.getResource()); - if (accessPolicy.getAction() == RequestAction.WRITE) { - dto.setCanWrite(Boolean.TRUE); - } else { - dto.setCanRead(Boolean.TRUE); - } - + dto.setAction(accessPolicy.getAction().toString()); return dto; - } + /** - * Creates the AccessPolicyDTO based on the specified Authorizable. + * Creates the PermissionsDTO based on the specified Authorizable. * * @param authorizable authorizable * @return dto */ - public AccessPolicyDTO createAccessPolicyDto(final Authorizable authorizable) { - final AccessPolicyDTO dto = new AccessPolicyDTO(); + public PermissionsDTO createPermissionsDto(final Authorizable authorizable) { + final PermissionsDTO dto = new PermissionsDTO(); dto.setCanRead(authorizable.isAuthorized(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser())); dto.setCanWrite(authorizable.isAuthorized(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser())); return dto; @@ -1614,7 +1623,7 @@ public FlowDTO createFlowDto(final ProcessGroup group, final ProcessGroupStatus // marshal the actual connection as the snippet is pruned final ConnectionDTO dto = createConnectionDto(connection); final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(connection.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(connection); + final PermissionsDTO accessPolicy = createPermissionsDto(connection); final ConnectionStatusDTO status = getComponentStatus( () -> groupStatus.getConnectionStatus().stream().filter(connectionStatus -> connection.getIdentifier().equals(connectionStatus.getId())).findFirst().orElse(null), connectionStatus -> createConnectionStatusDto(connectionStatus) @@ -1628,7 +1637,7 @@ public FlowDTO createFlowDto(final ProcessGroup group, final ProcessGroupStatus // marshal the actual funnel as the snippet is pruned final FunnelDTO dto = createFunnelDto(funnel); final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(funnel.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(funnel); + final PermissionsDTO accessPolicy = createPermissionsDto(funnel); flow.getFunnels().add(entityFactory.createFunnelEntity(dto, revision, accessPolicy)); } @@ -1638,7 +1647,7 @@ public FlowDTO createFlowDto(final ProcessGroup group, final ProcessGroupStatus // marshal the actual port as the snippet is pruned final PortDTO dto = createPortDto(inputPort); final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(inputPort.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(inputPort); + final PermissionsDTO accessPolicy = createPermissionsDto(inputPort); final PortStatusDTO status = getComponentStatus( () -> groupStatus.getInputPortStatus().stream().filter(inputPortStatus -> inputPort.getIdentifier().equals(inputPortStatus.getId())).findFirst().orElse(null), inputPortStatus -> createPortStatusDto(inputPortStatus) @@ -1653,7 +1662,7 @@ public FlowDTO createFlowDto(final ProcessGroup group, final ProcessGroupStatus // marshal the actual port as the snippet is pruned final PortDTO dto = createPortDto(outputPort); final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(outputPort.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(outputPort); + final PermissionsDTO accessPolicy = createPermissionsDto(outputPort); final PortStatusDTO status = getComponentStatus( () -> groupStatus.getOutputPortStatus().stream().filter(outputPortStatus -> outputPort.getIdentifier().equals(outputPortStatus.getId())).findFirst().orElse(null), outputPortStatus -> createPortStatusDto(outputPortStatus) @@ -1668,7 +1677,7 @@ public FlowDTO createFlowDto(final ProcessGroup group, final ProcessGroupStatus // marshal the actual label as the snippet is pruned final LabelDTO dto = createLabelDto(label); final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(label.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(label); + final PermissionsDTO accessPolicy = createPermissionsDto(label); flow.getLabels().add(entityFactory.createLabelEntity(dto, revision, accessPolicy)); } @@ -1678,7 +1687,7 @@ public FlowDTO createFlowDto(final ProcessGroup group, final ProcessGroupStatus // marshal the actual group as the snippet is pruned final ProcessGroupDTO dto = createProcessGroupDto(processGroup); final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(processGroup.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(processGroup); + final PermissionsDTO accessPolicy = createPermissionsDto(processGroup); final ProcessGroupStatusDTO status = getComponentStatus( () -> groupStatus.getProcessGroupStatus().stream().filter(processGroupStatus -> processGroup.getIdentifier().equals(processGroupStatus.getId())).findFirst().orElse(null), processGroupStatus -> createConciseProcessGroupStatusDto(processGroupStatus) @@ -1693,7 +1702,7 @@ public FlowDTO createFlowDto(final ProcessGroup group, final ProcessGroupStatus // marshal the actual processor as the snippet is pruned final ProcessorDTO dto = createProcessorDto(processor); final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(processor.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(processor); + final PermissionsDTO accessPolicy = createPermissionsDto(processor); final ProcessorStatusDTO status = getComponentStatus( () -> groupStatus.getProcessorStatus().stream().filter(processorStatus -> processor.getIdentifier().equals(processorStatus.getId())).findFirst().orElse(null), processorStatus -> createProcessorStatusDto(processorStatus) @@ -1708,7 +1717,7 @@ public FlowDTO createFlowDto(final ProcessGroup group, final ProcessGroupStatus // marshal the actual rpm as the snippet is pruned final RemoteProcessGroupDTO dto = createRemoteProcessGroupDto(remoteProcessGroup); final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(remoteProcessGroup.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(remoteProcessGroup); + final PermissionsDTO accessPolicy = createPermissionsDto(remoteProcessGroup); final RemoteProcessGroupStatusDTO status = getComponentStatus( () -> groupStatus.getRemoteProcessGroupStatus().stream().filter(rpgStatus -> remoteProcessGroup.getIdentifier().equals(rpgStatus.getId())).findFirst().orElse(null), remoteProcessGroupStatus -> createRemoteProcessGroupStatusDto(remoteProcessGroupStatus) @@ -1736,79 +1745,79 @@ public FlowDTO createFlowDto(final ProcessGroup group, final ProcessGroupStatus for (final ProcessorNode procNode : group.getProcessors()) { final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(procNode.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(procNode); + final PermissionsDTO permissions = createPermissionsDto(procNode); final ProcessorStatusDTO status = getComponentStatus( () -> groupStatus.getProcessorStatus().stream().filter(processorStatus -> procNode.getIdentifier().equals(processorStatus.getId())).findFirst().orElse(null), processorStatus -> createProcessorStatusDto(processorStatus) ); final List bulletins = createBulletinDtos(bulletinRepository.findBulletinsForSource(procNode.getIdentifier())); - dto.getProcessors().add(entityFactory.createProcessorEntity(createProcessorDto(procNode), revision, accessPolicy, status, bulletins)); + dto.getProcessors().add(entityFactory.createProcessorEntity(createProcessorDto(procNode), revision, permissions, status, bulletins)); } for (final Connection connNode : group.getConnections()) { final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(connNode.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(connNode); + final PermissionsDTO permissions = createPermissionsDto(connNode); final ConnectionStatusDTO status = getComponentStatus( () -> groupStatus.getConnectionStatus().stream().filter(connectionStatus -> connNode.getIdentifier().equals(connectionStatus.getId())).findFirst().orElse(null), connectionStatus -> createConnectionStatusDto(connectionStatus) ); - dto.getConnections().add(entityFactory.createConnectionEntity(createConnectionDto(connNode), revision, accessPolicy, status)); + dto.getConnections().add(entityFactory.createConnectionEntity(createConnectionDto(connNode), revision, permissions, status)); } for (final Label label : group.getLabels()) { final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(label.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(label); - dto.getLabels().add(entityFactory.createLabelEntity(createLabelDto(label), revision, accessPolicy)); + final PermissionsDTO permissions = createPermissionsDto(label); + dto.getLabels().add(entityFactory.createLabelEntity(createLabelDto(label), revision, permissions)); } for (final Funnel funnel : group.getFunnels()) { final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(funnel.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(funnel); - dto.getFunnels().add(entityFactory.createFunnelEntity(createFunnelDto(funnel), revision, accessPolicy)); + final PermissionsDTO permissions = createPermissionsDto(funnel); + dto.getFunnels().add(entityFactory.createFunnelEntity(createFunnelDto(funnel), revision, permissions)); } for (final ProcessGroup childGroup : group.getProcessGroups()) { final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(childGroup.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(childGroup); + final PermissionsDTO permissions = createPermissionsDto(childGroup); final ProcessGroupStatusDTO status = getComponentStatus( () -> groupStatus.getProcessGroupStatus().stream().filter(processGroupStatus -> childGroup.getIdentifier().equals(processGroupStatus.getId())).findFirst().orElse(null), processGroupStatus -> createConciseProcessGroupStatusDto(processGroupStatus) ); final List bulletins = createBulletinDtos(bulletinRepository.findBulletinsForSource(childGroup.getIdentifier())); - dto.getProcessGroups().add(entityFactory.createProcessGroupEntity(createProcessGroupDto(childGroup), revision, accessPolicy, status, bulletins)); + dto.getProcessGroups().add(entityFactory.createProcessGroupEntity(createProcessGroupDto(childGroup), revision, permissions, status, bulletins)); } for (final RemoteProcessGroup rpg : group.getRemoteProcessGroups()) { final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(rpg.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(rpg); + final PermissionsDTO permissions = createPermissionsDto(rpg); final RemoteProcessGroupStatusDTO status = getComponentStatus( () -> groupStatus.getRemoteProcessGroupStatus().stream().filter(remoteProcessGroupStatus -> rpg.getIdentifier().equals(remoteProcessGroupStatus.getId())).findFirst().orElse(null), remoteProcessGroupStatus -> createRemoteProcessGroupStatusDto(remoteProcessGroupStatus) ); final List bulletins = createBulletinDtos(bulletinRepository.findBulletinsForSource(rpg.getIdentifier())); - dto.getRemoteProcessGroups().add(entityFactory.createRemoteProcessGroupEntity(createRemoteProcessGroupDto(rpg), revision, accessPolicy, status, bulletins)); + dto.getRemoteProcessGroups().add(entityFactory.createRemoteProcessGroupEntity(createRemoteProcessGroupDto(rpg), revision, permissions, status, bulletins)); } for (final Port inputPort : group.getInputPorts()) { final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(inputPort.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(inputPort); + final PermissionsDTO permissions = createPermissionsDto(inputPort); final PortStatusDTO status = getComponentStatus( () -> groupStatus.getInputPortStatus().stream().filter(inputPortStatus -> inputPort.getIdentifier().equals(inputPortStatus.getId())).findFirst().orElse(null), inputPortStatus -> createPortStatusDto(inputPortStatus) ); final List bulletins = createBulletinDtos(bulletinRepository.findBulletinsForSource(inputPort.getIdentifier())); - dto.getInputPorts().add(entityFactory.createPortEntity(createPortDto(inputPort), revision, accessPolicy, status, bulletins)); + dto.getInputPorts().add(entityFactory.createPortEntity(createPortDto(inputPort), revision, permissions, status, bulletins)); } for (final Port outputPort : group.getOutputPorts()) { final RevisionDTO revision = createRevisionDTO(revisionManager.getRevision(outputPort.getIdentifier())); - final AccessPolicyDTO accessPolicy = createAccessPolicyDto(outputPort); + final PermissionsDTO permissions = createPermissionsDto(outputPort); final PortStatusDTO status = getComponentStatus( () -> groupStatus.getOutputPortStatus().stream().filter(outputPortStatus -> outputPort.getIdentifier().equals(outputPortStatus.getId())).findFirst().orElse(null), outputPortStatus -> createPortStatusDto(outputPortStatus) ); final List bulletins = createBulletinDtos(bulletinRepository.findBulletinsForSource(outputPort.getIdentifier())); - dto.getOutputPorts().add(entityFactory.createPortEntity(createPortDto(outputPort), revision, accessPolicy, status, bulletins)); + dto.getOutputPorts().add(entityFactory.createPortEntity(createPortDto(outputPort), revision, permissions, status, bulletins)); } return dto; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java index c530f4165557..f9a68ae8b8dc 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java @@ -24,6 +24,7 @@ import org.apache.nifi.web.api.dto.status.ProcessorStatusDTO; import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO; import org.apache.nifi.web.api.entity.AccessPolicyEntity; +import org.apache.nifi.web.api.entity.AccessPolicySummaryEntity; import org.apache.nifi.web.api.entity.ConnectionEntity; import org.apache.nifi.web.api.entity.ControllerConfigurationEntity; import org.apache.nifi.web.api.entity.ControllerServiceEntity; @@ -48,37 +49,37 @@ public final class EntityFactory { - public ControllerConfigurationEntity createControllerConfigurationEntity(final ControllerConfigurationDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy) { + public ControllerConfigurationEntity createControllerConfigurationEntity(final ControllerConfigurationDTO dto, final RevisionDTO revision, final PermissionsDTO permissions) { final ControllerConfigurationEntity entity = new ControllerConfigurationEntity(); entity.setRevision(revision); entity.setCurrentTime(new Date()); if (dto != null) { - entity.setAccessPolicy(accessPolicy); - if (accessPolicy != null && accessPolicy.getCanRead()) { + entity.setPermissions(permissions); + if (permissions != null && permissions.getCanRead()) { entity.setControllerConfiguration(dto); } } return entity; } - public ProcessGroupFlowEntity createProcessGroupFlowEntity(final ProcessGroupFlowDTO dto, final AccessPolicyDTO accessPolicy) { + public ProcessGroupFlowEntity createProcessGroupFlowEntity(final ProcessGroupFlowDTO dto, final PermissionsDTO permissions) { final ProcessGroupFlowEntity entity = new ProcessGroupFlowEntity(); entity.setProcessGroupFlow(dto); - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); return entity; } - public ProcessorEntity createProcessorEntity(final ProcessorDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy, + public ProcessorEntity createProcessorEntity(final ProcessorDTO dto, final RevisionDTO revision, final PermissionsDTO permissions, final ProcessorStatusDTO status, final List bulletins) { final ProcessorEntity entity = new ProcessorEntity(); entity.setRevision(revision); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setStatus(status); entity.setId(dto.getId()); entity.setPosition(dto.getPosition()); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setComponent(dto); entity.setBulletins(bulletins); } @@ -86,16 +87,16 @@ public ProcessorEntity createProcessorEntity(final ProcessorDTO dto, final Revis return entity; } - public PortEntity createPortEntity(final PortDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy, final PortStatusDTO status, final List bulletins) { + public PortEntity createPortEntity(final PortDTO dto, final RevisionDTO revision, final PermissionsDTO permissions, final PortStatusDTO status, final List bulletins) { final PortEntity entity = new PortEntity(); entity.setRevision(revision); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setStatus(status); entity.setId(dto.getId()); entity.setPosition(dto.getPosition()); entity.setPortType(dto.getType()); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setComponent(dto); entity.setBulletins(bulletins); } @@ -103,13 +104,13 @@ public PortEntity createPortEntity(final PortDTO dto, final RevisionDTO revision return entity; } - public ProcessGroupEntity createProcessGroupEntity(final ProcessGroupDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy, + public ProcessGroupEntity createProcessGroupEntity(final ProcessGroupDTO dto, final RevisionDTO revision, final PermissionsDTO permissions, final ProcessGroupStatusDTO status, final List bulletins) { final ProcessGroupEntity entity = new ProcessGroupEntity(); entity.setRevision(revision); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setStatus(status); entity.setId(dto.getId()); entity.setPosition(dto.getPosition()); @@ -121,7 +122,7 @@ public ProcessGroupEntity createProcessGroupEntity(final ProcessGroupDTO dto, fi entity.setDisabledCount(dto.getDisabledCount()); entity.setActiveRemotePortCount(dto.getActiveRemotePortCount()); entity.setInactiveRemotePortCount(dto.getInactiveRemotePortCount()); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setComponent(dto); entity.setBulletins(bulletins); } @@ -129,11 +130,11 @@ public ProcessGroupEntity createProcessGroupEntity(final ProcessGroupDTO dto, fi return entity; } - public LabelEntity createLabelEntity(final LabelDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy) { + public LabelEntity createLabelEntity(final LabelDTO dto, final RevisionDTO revision, final PermissionsDTO permissions) { final LabelEntity entity = new LabelEntity(); entity.setRevision(revision); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setId(dto.getId()); entity.setPosition(dto.getPosition()); @@ -142,88 +143,103 @@ public LabelEntity createLabelEntity(final LabelDTO dto, final RevisionDTO revis dimensions.setWidth(dto.getWidth()); entity.setDimensions(dimensions); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setComponent(dto); } } return entity; } - public UserEntity createUserEntity(final UserDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy) { + public UserEntity createUserEntity(final UserDTO dto, final RevisionDTO revision, final PermissionsDTO permissions) { final UserEntity entity = new UserEntity(); entity.setRevision(revision); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setId(dto.getId()); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setComponent(dto); } } return entity; } - public TenantEntity createTenantEntity(final TenantDTO dto, final RevisionDTO revsion, final AccessPolicyDTO accessPolicy) { + public TenantEntity createTenantEntity(final TenantDTO dto, final RevisionDTO revision, final PermissionsDTO permissions) { final TenantEntity entity = new TenantEntity(); - entity.setRevision(revsion); + entity.setRevision(revision); + if (dto != null) { + entity.setPermissions(permissions); + entity.setId(dto.getId()); + + if (permissions != null && permissions.getCanRead()) { + entity.setComponent(dto); + } + } + return entity; + } + + public AccessPolicySummaryEntity createAccessPolicySummaryEntity(final AccessPolicySummaryDTO dto, final RevisionDTO revision, final PermissionsDTO permissions) { + final AccessPolicySummaryEntity entity = new AccessPolicySummaryEntity(); + entity.setRevision(revision); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setId(dto.getId()); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setComponent(dto); } } return entity; } - public UserGroupEntity createUserGroupEntity(final UserGroupDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy) { + public UserGroupEntity createUserGroupEntity(final UserGroupDTO dto, final RevisionDTO revision, final PermissionsDTO permissions) { final UserGroupEntity entity = new UserGroupEntity(); entity.setRevision(revision); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setId(dto.getId()); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setComponent(dto); } } return entity; } - public AccessPolicyEntity createAccessPolicyEntity(final AccessPolicyDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy) { + public AccessPolicyEntity createAccessPolicyEntity(final AccessPolicyDTO dto, final RevisionDTO revision, final PermissionsDTO permissions) { final AccessPolicyEntity entity = new AccessPolicyEntity(); entity.setRevision(revision); + entity.setGenerated(new Date()); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setId(dto.getId()); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setComponent(dto); } } return entity; } - public FunnelEntity createFunnelEntity(final FunnelDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy) { + public FunnelEntity createFunnelEntity(final FunnelDTO dto, final RevisionDTO revision, final PermissionsDTO permissions) { final FunnelEntity entity = new FunnelEntity(); entity.setRevision(revision); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setId(dto.getId()); entity.setPosition(dto.getPosition()); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setComponent(dto); } } return entity; } - public ConnectionEntity createConnectionEntity(final ConnectionDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy, final ConnectionStatusDTO status) { + public ConnectionEntity createConnectionEntity(final ConnectionDTO dto, final RevisionDTO revision, final PermissionsDTO permissions, final ConnectionStatusDTO status) { final ConnectionEntity entity = new ConnectionEntity(); entity.setRevision(revision); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setStatus(status); entity.setId(dto.getId()); entity.setPosition(dto.getPosition()); @@ -233,26 +249,26 @@ public ConnectionEntity createConnectionEntity(final ConnectionDTO dto, final Re entity.setSourceGroupId(dto.getSource().getGroupId()); entity.setDestinationId(dto.getDestination().getId()); entity.setDestinationGroupId(dto.getDestination().getGroupId()); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setComponent(dto); } } return entity; } - public RemoteProcessGroupEntity createRemoteProcessGroupEntity(final RemoteProcessGroupDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy, + public RemoteProcessGroupEntity createRemoteProcessGroupEntity(final RemoteProcessGroupDTO dto, final RevisionDTO revision, final PermissionsDTO permissions, final RemoteProcessGroupStatusDTO status, final List bulletins) { final RemoteProcessGroupEntity entity = new RemoteProcessGroupEntity(); entity.setRevision(revision); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setStatus(status); entity.setId(dto.getId()); entity.setPosition(dto.getPosition()); entity.setInputPortCount(dto.getInputPortCount()); entity.setOutputPortCount(dto.getOutputPortCount()); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setComponent(dto); entity.setBulletins(bulletins); } @@ -260,13 +276,13 @@ public RemoteProcessGroupEntity createRemoteProcessGroupEntity(final RemoteProce return entity; } - public RemoteProcessGroupPortEntity createRemoteProcessGroupPortEntity(final RemoteProcessGroupPortDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy) { + public RemoteProcessGroupPortEntity createRemoteProcessGroupPortEntity(final RemoteProcessGroupPortDTO dto, final RevisionDTO revision, final PermissionsDTO permissions) { final RemoteProcessGroupPortEntity entity = new RemoteProcessGroupPortEntity(); entity.setRevision(revision); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setId(dto.getId()); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setRemoteProcessGroupPort(dto); } } @@ -280,13 +296,13 @@ public SnippetEntity createSnippetEntity(final SnippetDTO dto) { return entity; } - public ReportingTaskEntity createReportingTaskEntity(final ReportingTaskDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy, final List bulletins) { + public ReportingTaskEntity createReportingTaskEntity(final ReportingTaskDTO dto, final RevisionDTO revision, final PermissionsDTO permissions, final List bulletins) { final ReportingTaskEntity entity = new ReportingTaskEntity(); entity.setRevision(revision); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setId(dto.getId()); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setComponent(dto); entity.setBulletins(bulletins); } @@ -295,14 +311,14 @@ public ReportingTaskEntity createReportingTaskEntity(final ReportingTaskDTO dto, return entity; } - public ControllerServiceEntity createControllerServiceEntity(final ControllerServiceDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy, final List bulletins) { + public ControllerServiceEntity createControllerServiceEntity(final ControllerServiceDTO dto, final RevisionDTO revision, final PermissionsDTO permissions, final List bulletins) { final ControllerServiceEntity entity = new ControllerServiceEntity(); entity.setRevision(revision); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setId(dto.getId()); entity.setPosition(dto.getPosition()); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setComponent(dto); entity.setBulletins(bulletins); } @@ -311,14 +327,14 @@ public ControllerServiceEntity createControllerServiceEntity(final ControllerSer } public ControllerServiceReferencingComponentEntity createControllerServiceReferencingComponentEntity( - final ControllerServiceReferencingComponentDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy) { + final ControllerServiceReferencingComponentDTO dto, final RevisionDTO revision, final PermissionsDTO permissions) { final ControllerServiceReferencingComponentEntity entity = new ControllerServiceReferencingComponentEntity(); entity.setRevision(revision); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setId(dto.getId()); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setComponent(dto); } } @@ -326,12 +342,12 @@ public ControllerServiceReferencingComponentEntity createControllerServiceRefere return entity; } - public FlowBreadcrumbEntity createFlowBreadcrumbEntity(final FlowBreadcrumbDTO dto, final AccessPolicyDTO accessPolicy) { + public FlowBreadcrumbEntity createFlowBreadcrumbEntity(final FlowBreadcrumbDTO dto, final PermissionsDTO permissions) { final FlowBreadcrumbEntity entity = new FlowBreadcrumbEntity(); if (dto != null) { - entity.setAccessPolicy(accessPolicy); + entity.setPermissions(permissions); entity.setId(dto.getId()); - if (accessPolicy != null && accessPolicy.getCanRead()) { + if (permissions != null && permissions.getCanRead()) { entity.setBreadcrumb(dto); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java index b7472fc05943..a414bc29e9a9 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java @@ -1328,6 +1328,16 @@ public ProvenanceEventDTO getProvenanceEvent(final Long eventId) { } } + /** + * Gets an authorizable for proveance events for a given component id. + * + * @param componentId component id + * @return authorizable + */ + public Authorizable getProvenanceEventAuthorizable(final String componentId) { + return flowController.createProvenanceAuthorizable(componentId); + } + /** * Creates a ProvenanceEventDTO for the specified ProvenanceEventRecord. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/AccessPolicyDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/AccessPolicyDAO.java index 2c0dc8023af1..d3f896541f1f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/AccessPolicyDAO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/AccessPolicyDAO.java @@ -17,6 +17,8 @@ package org.apache.nifi.web.dao; import org.apache.nifi.authorization.AccessPolicy; +import org.apache.nifi.authorization.RequestAction; +import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.web.api.dto.AccessPolicyDTO; public interface AccessPolicyDAO { @@ -36,13 +38,22 @@ public interface AccessPolicyDAO { AccessPolicy createAccessPolicy(AccessPolicyDTO accessPolicyDTO); /** - * Gets the acess policy with the specified ID. + * Gets the access policy with the specified ID. * * @param accessPolicyId The access policy ID * @return The access policy transfer object */ AccessPolicy getAccessPolicy(String accessPolicyId); + /** + * Gets the access policy according to the action and authorizable. + * + * @param requestAction action + * @param authorizable authorizable + * @return access policy + */ + AccessPolicy getAccessPolicy(RequestAction requestAction, Authorizable authorizable); + /** * Updates the specified access policy. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/UserGroupDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/UserGroupDAO.java index b6c299dd56ae..1dc9a6d03ec9 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/UserGroupDAO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/UserGroupDAO.java @@ -16,6 +16,7 @@ */ package org.apache.nifi.web.dao; +import org.apache.nifi.authorization.AccessPolicy; import org.apache.nifi.authorization.Group; import org.apache.nifi.web.api.dto.UserGroupDTO; @@ -53,6 +54,14 @@ public interface UserGroupDAO { */ Set getUserGroupsForUser(String userId); + /** + * Gets the access policies for the user with the specified ID. + * + * @param userId The user ID + * @return The set of access policies + */ + Set getAccessPoliciesForUser(String userId); + /** * Gets all user groups. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardPolicyBasedAuthorizerDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardPolicyBasedAuthorizerDAO.java index 3f12f7398ac3..7488fe10b942 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardPolicyBasedAuthorizerDAO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardPolicyBasedAuthorizerDAO.java @@ -28,6 +28,7 @@ import org.apache.nifi.authorization.exception.AuthorizationAccessException; import org.apache.nifi.authorization.exception.AuthorizerCreationException; import org.apache.nifi.authorization.exception.AuthorizerDestructionException; +import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.web.ResourceNotFoundException; import org.apache.nifi.web.api.dto.AccessPolicyDTO; import org.apache.nifi.web.api.dto.UserDTO; @@ -151,6 +152,13 @@ public void preDestruction() throws AuthorizerDestructionException { } } + private AccessPolicy findAccessPolicy(final RequestAction requestAction, final String resource) { + return authorizer.getAccessPolicies().stream() + .filter(policy -> policy.getAction().equals(requestAction) && policy.getResource().equals(resource)) + .findFirst() + .orElse(null); + } + @Override public boolean hasAccessPolicy(final String accessPolicyId) { return authorizer.getAccessPolicy(accessPolicyId) != null; @@ -158,7 +166,8 @@ public boolean hasAccessPolicy(final String accessPolicyId) { @Override public AccessPolicy createAccessPolicy(final AccessPolicyDTO accessPolicyDTO) { - return authorizer.addAccessPolicy(buildAccessPolicy(accessPolicyDTO.getId(), accessPolicyDTO)); + return authorizer.addAccessPolicy(buildAccessPolicy(accessPolicyDTO.getId(), + accessPolicyDTO.getResource(), RequestAction.valueOfValue(accessPolicyDTO.getAction()), accessPolicyDTO)); } @Override @@ -170,9 +179,28 @@ public AccessPolicy getAccessPolicy(final String accessPolicyId) { return accessPolicy; } + @Override + public AccessPolicy getAccessPolicy(final RequestAction requestAction, final Authorizable authorizable) { + final String resource = authorizable.getResource().getIdentifier(); + + final AccessPolicy accessPolicy = findAccessPolicy(requestAction, authorizable.getResource().getIdentifier()); + if (accessPolicy == null) { + final Authorizable parentAuthorizable = authorizable.getParentAuthorizable(); + if (parentAuthorizable == null) { + throw new ResourceNotFoundException(String.format("Unable to find access policy for %s on %s", requestAction.toString(), resource)); + } else { + return getAccessPolicy(requestAction, parentAuthorizable); + } + } + + return accessPolicy; + } + @Override public AccessPolicy updateAccessPolicy(final AccessPolicyDTO accessPolicyDTO) { - return authorizer.updateAccessPolicy(buildAccessPolicy(getAccessPolicy(accessPolicyDTO.getId()).getIdentifier(), accessPolicyDTO)); + final AccessPolicy currentAccessPolicy = getAccessPolicy(accessPolicyDTO.getId()); + return authorizer.updateAccessPolicy(buildAccessPolicy(currentAccessPolicy.getIdentifier(), + currentAccessPolicy.getResource(), currentAccessPolicy.getAction(), accessPolicyDTO)); } @Override @@ -180,23 +208,19 @@ public AccessPolicy deleteAccessPolicy(final String accessPolicyId) { return authorizer.deleteAccessPolicy(getAccessPolicy(accessPolicyId)); } - private AccessPolicy buildAccessPolicy(final String identifier, final AccessPolicyDTO accessPolicyDTO) { + private AccessPolicy buildAccessPolicy(final String identifier, final String resource, final RequestAction action, final AccessPolicyDTO accessPolicyDTO) { final Set userGroups = accessPolicyDTO.getUserGroups(); final Set users = accessPolicyDTO.getUsers(); final AccessPolicy.Builder builder = new AccessPolicy.Builder() .identifier(identifier) - .resource(accessPolicyDTO.getResource()); + .resource(resource); if (userGroups != null) { builder.addGroups(userGroups.stream().map(ComponentEntity::getId).collect(Collectors.toSet())); } if (users != null) { builder.addUsers(users.stream().map(ComponentEntity::getId).collect(Collectors.toSet())); } - if (Boolean.TRUE == accessPolicyDTO.getCanWrite()) { - builder.action(RequestAction.WRITE); - } else { - builder.action(RequestAction.READ); - } + builder.action(action); return builder.build(); } @@ -226,6 +250,21 @@ public Set getUserGroupsForUser(String userId) { .collect(Collectors.toSet()); } + @Override + public Set getAccessPoliciesForUser(String userId) { + return authorizer.getAccessPolicies().stream() + .filter(p -> { + // policy contains the user + if (p.getUsers().contains(userId)) { + return true; + } + + // policy contains a group with the user + return !p.getGroups().stream().filter(g -> authorizer.getGroup(g).getUsers().contains(userId)).collect(Collectors.toSet()).isEmpty(); + }) + .collect(Collectors.toSet()); + } + @Override public Set getUserGroups() { return authorizer.getGroups(); @@ -281,12 +320,12 @@ public User updateUser(final UserDTO userDTO) { @Override public User deleteUser(final String userId) { - return authorizer.deleteUser(getUser(userId)); + final User user = getUser(userId); + return authorizer.deleteUser(user); } private User buildUser(final String identifier, final UserDTO userDTO) { final User.Builder builder = new User.Builder().identifier(identifier).identity(userDTO.getIdentity()); return builder.build(); } - } \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml index 2e42efb3b0c5..85983caace1a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml @@ -219,6 +219,12 @@ + + + + + + diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy index 87dbf869b52f..677b25d57a59 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy @@ -74,8 +74,8 @@ class StandardNiFiServiceFacadeSpec extends Specification { if (isAuthorized) { assert userEntity?.component?.id == userDto.id assert userEntity?.component?.identity?.equals(userDto.identity) - assert userEntity?.accessPolicy?.canRead - assert userEntity?.accessPolicy?.canWrite + assert userEntity?.permissions?.canRead + assert userEntity?.permissions?.canWrite } else { assert userEntity.component == null } @@ -174,8 +174,8 @@ class StandardNiFiServiceFacadeSpec extends Specification { def userEntity = userEntityUpdateResult?.result if (isAuthorized) { assert userEntity?.component?.id?.equals(userDto.id) - assert userEntity?.accessPolicy?.canRead - assert userEntity?.accessPolicy?.canWrite + assert userEntity?.getPermissions?.canRead + assert userEntity?.getPermissions?.canWrite } else { assert userEntity.component == null } @@ -294,8 +294,8 @@ class StandardNiFiServiceFacadeSpec extends Specification { if (isAuthorized) { assert userGroupEntity?.component?.id == userGroupDto.id assert userGroupEntity?.component?.users?.equals(userGroupDto.users) - assert userGroupEntity?.accessPolicy?.canRead - assert userGroupEntity?.accessPolicy?.canWrite + assert userGroupEntity?.permissions?.canRead + assert userGroupEntity?.permissions?.canWrite } else { assert userGroupEntity?.component == null } @@ -425,8 +425,8 @@ class StandardNiFiServiceFacadeSpec extends Specification { assert userGroupEntity != null if (isAuthorized) { assert userGroupEntity?.component?.id?.equals(userGroupDto.id) - assert userGroupEntity?.accessPolicy?.canRead - assert userGroupEntity?.accessPolicy?.canWrite + assert userGroupEntity?.getPermissions?.canRead + assert userGroupEntity?.getPermissions?.canWrite } else { assert userGroupEntity.component == null } @@ -577,8 +577,8 @@ class StandardNiFiServiceFacadeSpec extends Specification { assert accessPolicyEntity != null if (isAuthorized) { assert accessPolicyEntity?.component?.id?.equals(accessPolicyDto.id) - assert accessPolicyEntity?.accessPolicy?.canRead - assert accessPolicyEntity?.accessPolicy?.canWrite + assert accessPolicyEntity?.permissions?.canRead + assert accessPolicyEntity?.permissions?.canWrite } else { assert accessPolicyEntity.component == null } @@ -722,8 +722,8 @@ class StandardNiFiServiceFacadeSpec extends Specification { assert accessPolicyEntity != null if (isAuthorized) { assert accessPolicyEntity?.component?.id?.equals(accessPolicyDto.id) - assert accessPolicyEntity?.accessPolicy?.canRead - assert accessPolicyEntity?.accessPolicy?.canWrite + assert accessPolicyEntity?.getPermissions?.canRead + assert accessPolicyEntity?.getPermissions?.canWrite } else { assert accessPolicyEntity.component == null } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/dao/impl/StandardPolicyBasedAuthorizerDAOSpec.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/dao/impl/StandardPolicyBasedAuthorizerDAOSpec.groovy index a63036f2f7e2..3a98c70ef6be 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/dao/impl/StandardPolicyBasedAuthorizerDAOSpec.groovy +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/dao/impl/StandardPolicyBasedAuthorizerDAOSpec.groovy @@ -38,7 +38,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification { where: method | daoMethod - 'createAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createAccessPolicy(new AccessPolicyDTO(id: '1', resource: '/1', canRead: true)) } + 'createAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createAccessPolicy(new AccessPolicyDTO(id: '1', resource: '/1', action: "read")) } 'createUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createUser(new UserDTO(id: '1', identity: 'a')) } 'createUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createUserGroup(new UserGroupDTO(id: '1', identity: 'a')) } 'deleteAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).deleteAccessPolicy('1') } @@ -50,7 +50,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification { 'hasAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).hasAccessPolicy('1') } 'hasUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).hasUser('1') } 'hasUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).hasUserGroup('1') } - 'updateAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).updateAccessPolicy(new AccessPolicyDTO(id: '1', resource: '/1', canRead: true)) } + 'updateAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).updateAccessPolicy(new AccessPolicyDTO(id: '1', resource: '/1', action: "read")) } 'updateUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).updateUser(new UserDTO(id: '1', identity: 'a')) } 'updateUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).updateUserGroup(new UserGroupDTO(id: '1', identity: 'a')) } } @@ -81,8 +81,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification { given: def authorizer = Mock AbstractPolicyBasedAuthorizer def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - def requestDTO = new AccessPolicyDTO(id: 'policy-id-1', resource: '/fake/resource', canRead: true, - canWrite: true, + def requestDTO = new AccessPolicyDTO(id: 'policy-id-1', resource: '/fake/resource', action: "read", users: [new TenantEntity(id: 'user-id-1')] as Set, userGroups: [new TenantEntity(id: 'user-group-id-1')] as Set) @@ -144,8 +143,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification { given: def authorizer = Mock AbstractPolicyBasedAuthorizer def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - def requestDTO = new AccessPolicyDTO(id: 'policy-id-1', resource: '/fake/resource', canRead: true, - canWrite: true, + def requestDTO = new AccessPolicyDTO(id: 'policy-id-1', resource: '/fake/resource', action: "read", users: [new TenantEntity(id: 'user-id-1')] as Set, userGroups: [new TenantEntity(id: 'user-group-id-1')] as Set) @@ -169,8 +167,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification { given: def authorizer = Mock AbstractPolicyBasedAuthorizer def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - def requestDTO = new AccessPolicyDTO(id: 'policy-id-1', resource: '/fake/resource', canRead: true, - canWrite: true, + def requestDTO = new AccessPolicyDTO(id: 'policy-id-1', resource: '/fake/resource', action: "read", users: [new TenantEntity(id: 'user-id-1')] as Set, userGroups: [new TenantEntity(id: 'user-group-id-1')] as Set) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITConnectionAccessControl.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITConnectionAccessControl.java index 1d8e597bdd2b..0f5fe92fb228 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITConnectionAccessControl.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITConnectionAccessControl.java @@ -69,8 +69,8 @@ public static void setup() throws Exception { @Test public void testReadUserGetConnection() throws Exception { final ConnectionEntity entity = getRandomConnection(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); } @@ -82,8 +82,8 @@ public void testReadUserGetConnection() throws Exception { @Test public void testReadWriteUserGetConnection() throws Exception { final ConnectionEntity entity = getRandomConnection(helper.getReadWriteUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); } @@ -95,8 +95,8 @@ public void testReadWriteUserGetConnection() throws Exception { @Test public void testWriteUserGetConnection() throws Exception { final ConnectionEntity entity = getRandomConnection(helper.getWriteUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); } @@ -108,8 +108,8 @@ public void testWriteUserGetConnection() throws Exception { @Test public void testNoneUserGetConnection() throws Exception { final ConnectionEntity entity = getRandomConnection(helper.getNoneUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); } @@ -121,8 +121,8 @@ public void testNoneUserGetConnection() throws Exception { @Test public void testReadUserPutConnection() throws Exception { final ConnectionEntity entity = getRandomConnection(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); // attempt update the name @@ -144,8 +144,8 @@ public void testReadUserPutConnection() throws Exception { @Test public void testReadWriteUserPutConnection() throws Exception { final ConnectionEntity entity = getRandomConnection(helper.getReadWriteUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); final String updatedName = "Updated Name"; @@ -209,8 +209,8 @@ public void testReadWriteUserPutConnectionThroughInheritedPolicy() throws Except @Test public void testWriteUserPutConnection() throws Exception { final ConnectionEntity entity = getRandomConnection(helper.getWriteUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); final String updatedName = "Updated Name"; @@ -252,8 +252,8 @@ public void testWriteUserPutConnection() throws Exception { @Test public void testNoneUserPutConnection() throws Exception { final ConnectionEntity entity = getRandomConnection(helper.getNoneUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); final String updatedName = "Updated Name"; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITFunnelAccessControl.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITFunnelAccessControl.java index 5d835fcfaa4b..b66a667d310b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITFunnelAccessControl.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITFunnelAccessControl.java @@ -65,8 +65,8 @@ public static void setup() throws Exception { @Test public void testReadUserGetFunnel() throws Exception { final FunnelEntity entity = getRandomFunnel(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); } @@ -78,8 +78,8 @@ public void testReadUserGetFunnel() throws Exception { @Test public void testReadWriteUserGetFunnel() throws Exception { final FunnelEntity entity = getRandomFunnel(helper.getReadWriteUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); } @@ -91,8 +91,8 @@ public void testReadWriteUserGetFunnel() throws Exception { @Test public void testWriteUserGetFunnel() throws Exception { final FunnelEntity entity = getRandomFunnel(helper.getWriteUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); } @@ -104,8 +104,8 @@ public void testWriteUserGetFunnel() throws Exception { @Test public void testNoneUserGetFunnel() throws Exception { final FunnelEntity entity = getRandomFunnel(helper.getNoneUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); } @@ -117,8 +117,8 @@ public void testNoneUserGetFunnel() throws Exception { @Test public void testReadUserPutFunnel() throws Exception { final FunnelEntity entity = getRandomFunnel(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); // attempt update the position @@ -140,8 +140,8 @@ public void testReadUserPutFunnel() throws Exception { @Test public void testReadWriteUserPutFunnel() throws Exception { final FunnelEntity entity = getRandomFunnel(helper.getReadWriteUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); final double y = 15.0; @@ -174,8 +174,8 @@ public void testReadWriteUserPutFunnel() throws Exception { @Test public void testWriteUserPutFunnel() throws Exception { final FunnelEntity entity = getRandomFunnel(helper.getWriteUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); final double y = 15.0; @@ -217,8 +217,8 @@ public void testWriteUserPutFunnel() throws Exception { @Test public void testNoneUserPutFunnel() throws Exception { final FunnelEntity entity = getRandomFunnel(helper.getNoneUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); // attempt to update the position diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITInputPortAccessControl.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITInputPortAccessControl.java index 3dc8f199d82d..1e67333a06dc 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITInputPortAccessControl.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITInputPortAccessControl.java @@ -66,8 +66,8 @@ public static void setup() throws Exception { @Test public void testReadUserGetInputPort() throws Exception { final PortEntity entity = getRandomInputPort(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); } @@ -79,8 +79,8 @@ public void testReadUserGetInputPort() throws Exception { @Test public void testReadWriteUserGetInputPort() throws Exception { final PortEntity entity = getRandomInputPort(helper.getReadWriteUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); } @@ -92,8 +92,8 @@ public void testReadWriteUserGetInputPort() throws Exception { @Test public void testWriteUserGetInputPort() throws Exception { final PortEntity entity = getRandomInputPort(helper.getWriteUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); } @@ -105,8 +105,8 @@ public void testWriteUserGetInputPort() throws Exception { @Test public void testNoneUserGetInputPort() throws Exception { final PortEntity entity = getRandomInputPort(helper.getNoneUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); } @@ -118,8 +118,8 @@ public void testNoneUserGetInputPort() throws Exception { @Test public void testReadUserPutInputPort() throws Exception { final PortEntity entity = getRandomInputPort(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); // attempt update the name @@ -141,8 +141,8 @@ public void testReadUserPutInputPort() throws Exception { @Test public void testReadWriteUserPutInputPort() throws Exception { final PortEntity entity = getRandomInputPort(helper.getReadWriteUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); final String updatedName = "Updated Name" + count++; @@ -206,8 +206,8 @@ public void testReadWriteUserPutInputPortThroughInheritedPolicy() throws Excepti @Test public void testWriteUserPutInputPort() throws Exception { final PortEntity entity = getRandomInputPort(helper.getWriteUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); final String updatedName = "Updated Name" + count++; @@ -249,8 +249,8 @@ public void testWriteUserPutInputPort() throws Exception { @Test public void testNoneUserPutInputPort() throws Exception { final PortEntity entity = getRandomInputPort(helper.getNoneUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); final String updatedName = "Updated Name" + count++; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITLabelAccessControl.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITLabelAccessControl.java index 3a44d5346a06..d581946aa7b8 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITLabelAccessControl.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITLabelAccessControl.java @@ -65,8 +65,8 @@ public static void setup() throws Exception { @Test public void testReadUserGetLabel() throws Exception { final LabelEntity entity = getRandomLabel(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); } @@ -78,8 +78,8 @@ public void testReadUserGetLabel() throws Exception { @Test public void testReadWriteUserGetLabel() throws Exception { final LabelEntity entity = getRandomLabel(helper.getReadWriteUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); } @@ -91,8 +91,8 @@ public void testReadWriteUserGetLabel() throws Exception { @Test public void testWriteUserGetLabel() throws Exception { final LabelEntity entity = getRandomLabel(helper.getWriteUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); } @@ -104,8 +104,8 @@ public void testWriteUserGetLabel() throws Exception { @Test public void testNoneUserGetLabel() throws Exception { final LabelEntity entity = getRandomLabel(helper.getNoneUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); } @@ -117,8 +117,8 @@ public void testNoneUserGetLabel() throws Exception { @Test public void testReadUserPutLabel() throws Exception { final LabelEntity entity = getRandomLabel(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); // attempt update the name @@ -140,8 +140,8 @@ public void testReadUserPutLabel() throws Exception { @Test public void testReadWriteUserPutLabel() throws Exception { final LabelEntity entity = getRandomLabel(helper.getReadWriteUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); final String updatedLabel = "Updated Name"; @@ -205,8 +205,8 @@ public void testReadWriteUserPutLabelThroughInheritedPolicy() throws Exception { @Test public void testWriteUserPutLabel() throws Exception { final LabelEntity entity = getRandomLabel(helper.getWriteUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); final String updatedLabel = "Updated Name"; @@ -248,8 +248,8 @@ public void testWriteUserPutLabel() throws Exception { @Test public void testNoneUserPutLabel() throws Exception { final LabelEntity entity = getRandomLabel(helper.getNoneUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); final String updatedName = "Updated Name"; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITOutputPortAccessControl.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITOutputPortAccessControl.java index 5de49df1a067..58bc3df37719 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITOutputPortAccessControl.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITOutputPortAccessControl.java @@ -66,8 +66,8 @@ public static void setup() throws Exception { @Test public void testReadUserGetOutputPort() throws Exception { final PortEntity entity = getRandomOutputPort(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); } @@ -79,8 +79,8 @@ public void testReadUserGetOutputPort() throws Exception { @Test public void testReadWriteUserGetOutputPort() throws Exception { final PortEntity entity = getRandomOutputPort(helper.getReadWriteUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); } @@ -92,8 +92,8 @@ public void testReadWriteUserGetOutputPort() throws Exception { @Test public void testWriteUserGetOutputPort() throws Exception { final PortEntity entity = getRandomOutputPort(helper.getWriteUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); } @@ -105,8 +105,8 @@ public void testWriteUserGetOutputPort() throws Exception { @Test public void testNoneUserGetOutputPort() throws Exception { final PortEntity entity = getRandomOutputPort(helper.getNoneUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); } @@ -118,8 +118,8 @@ public void testNoneUserGetOutputPort() throws Exception { @Test public void testReadUserPutOutputPort() throws Exception { final PortEntity entity = getRandomOutputPort(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); // attempt update the name @@ -141,8 +141,8 @@ public void testReadUserPutOutputPort() throws Exception { @Test public void testReadWriteUserPutOutputPort() throws Exception { final PortEntity entity = getRandomOutputPort(helper.getReadWriteUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); final String updatedName = "Updated Name" + count++; @@ -206,8 +206,8 @@ public void testReadWriteUserPutOutputPortThroughInheritedPolicy() throws Except @Test public void testWriteUserPutOutputPort() throws Exception { final PortEntity entity = getRandomOutputPort(helper.getWriteUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); final String updatedName = "Updated Name" + count++; @@ -249,8 +249,8 @@ public void testWriteUserPutOutputPort() throws Exception { @Test public void testNoneUserPutOutputPort() throws Exception { final PortEntity entity = getRandomOutputPort(helper.getNoneUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); final String updatedName = "Updated Name" + count++; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITProcessGroupAccessControl.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITProcessGroupAccessControl.java index 59af06304de9..326a120aab41 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITProcessGroupAccessControl.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITProcessGroupAccessControl.java @@ -66,8 +66,8 @@ public static void setup() throws Exception { @Test public void testReadUserGetProcessGroup() throws Exception { final ProcessGroupEntity entity = getRandomProcessGroup(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); } @@ -79,8 +79,8 @@ public void testReadUserGetProcessGroup() throws Exception { @Test public void testReadWriteUserGetProcessGroup() throws Exception { final ProcessGroupEntity entity = getRandomProcessGroup(helper.getReadWriteUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); } @@ -92,8 +92,8 @@ public void testReadWriteUserGetProcessGroup() throws Exception { @Test public void testWriteUserGetProcessGroup() throws Exception { final ProcessGroupEntity entity = getRandomProcessGroup(helper.getWriteUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); } @@ -105,8 +105,8 @@ public void testWriteUserGetProcessGroup() throws Exception { @Test public void testNoneUserGetProcessGroup() throws Exception { final ProcessGroupEntity entity = getRandomProcessGroup(helper.getNoneUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); } @@ -118,8 +118,8 @@ public void testNoneUserGetProcessGroup() throws Exception { @Test public void testReadUserPutProcessGroup() throws Exception { final ProcessGroupEntity entity = getRandomProcessGroup(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); // attempt update the name @@ -141,8 +141,8 @@ public void testReadUserPutProcessGroup() throws Exception { @Test public void testReadWriteUserPutProcessGroup() throws Exception { final ProcessGroupEntity entity = getRandomProcessGroup(helper.getReadWriteUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); final String updatedName = "Updated Name" + count++; @@ -206,8 +206,8 @@ public void testReadWriteUserPutProcessGroupThroughInheritedPolicy() throws Exce @Test public void testWriteUserPutProcessGroup() throws Exception { final ProcessGroupEntity entity = getRandomProcessGroup(helper.getWriteUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); final String updatedName = "Updated Name" + count++; @@ -249,8 +249,8 @@ public void testWriteUserPutProcessGroup() throws Exception { @Test public void testNoneUserPutProcessGroup() throws Exception { final ProcessGroupEntity entity = getRandomProcessGroup(helper.getNoneUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); final String updatedName = "Updated Name" + count++; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITProcessorAccessControl.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITProcessorAccessControl.java index 143d60e412b8..7c59083ec04e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITProcessorAccessControl.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITProcessorAccessControl.java @@ -66,8 +66,8 @@ public static void setup() throws Exception { @Test public void testReadUserGetProcessor() throws Exception { final ProcessorEntity entity = getRandomProcessor(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); } @@ -79,8 +79,8 @@ public void testReadUserGetProcessor() throws Exception { @Test public void testReadWriteUserGetProcessor() throws Exception { final ProcessorEntity entity = getRandomProcessor(helper.getReadWriteUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); } @@ -92,8 +92,8 @@ public void testReadWriteUserGetProcessor() throws Exception { @Test public void testWriteUserGetProcessor() throws Exception { final ProcessorEntity entity = getRandomProcessor(helper.getWriteUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); } @@ -105,8 +105,8 @@ public void testWriteUserGetProcessor() throws Exception { @Test public void testNoneUserGetProcessor() throws Exception { final ProcessorEntity entity = getRandomProcessor(helper.getNoneUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); } @@ -118,8 +118,8 @@ public void testNoneUserGetProcessor() throws Exception { @Test public void testReadUserPutProcessor() throws Exception { final ProcessorEntity entity = getRandomProcessor(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); // attempt update the name @@ -141,8 +141,8 @@ public void testReadUserPutProcessor() throws Exception { @Test public void testReadWriteUserPutProcessor() throws Exception { final ProcessorEntity entity = getRandomProcessor(helper.getReadWriteUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); final String updatedName = "Updated Name"; @@ -206,8 +206,8 @@ public void testReadWriteUserPutProcessorThroughInheritedPolicy() throws Excepti @Test public void testWriteUserPutProcessor() throws Exception { final ProcessorEntity entity = getRandomProcessor(helper.getWriteUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertTrue(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertTrue(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); final String updatedName = "Updated Name"; @@ -249,8 +249,8 @@ public void testWriteUserPutProcessor() throws Exception { @Test public void testNoneUserPutProcessor() throws Exception { final ProcessorEntity entity = getRandomProcessor(helper.getNoneUser()); - assertFalse(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertFalse(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNull(entity.getComponent()); final String updatedName = "Updated Name"; @@ -285,8 +285,8 @@ public void testNoneUserPutProcessor() throws Exception { @Test public void testReadUserClearState() throws Exception { final ProcessorEntity entity = getRandomProcessor(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); final String url = helper.getBaseUrl() + "/processors/" + entity.getId() + "/state/clear-requests"; @@ -306,8 +306,8 @@ public void testReadUserClearState() throws Exception { @Test public void testNoneUserClearState() throws Exception { final ProcessorEntity entity = getRandomProcessor(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); final String url = helper.getBaseUrl() + "/processors/" + entity.getId() + "/state/clear-requests"; @@ -327,8 +327,8 @@ public void testNoneUserClearState() throws Exception { @Test public void testReadWriteUserClearState() throws Exception { final ProcessorEntity entity = getRandomProcessor(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); final String url = helper.getBaseUrl() + "/processors/" + entity.getId() + "/state/clear-requests"; @@ -348,8 +348,8 @@ public void testReadWriteUserClearState() throws Exception { @Test public void testWriteUserClearState() throws Exception { final ProcessorEntity entity = getRandomProcessor(helper.getReadUser()); - assertTrue(entity.getAccessPolicy().getCanRead()); - assertFalse(entity.getAccessPolicy().getCanWrite()); + assertTrue(entity.getPermissions().getCanRead()); + assertFalse(entity.getPermissions().getCanWrite()); assertNotNull(entity.getComponent()); final String url = helper.getBaseUrl() + "/processors/" + entity.getId() + "/state/clear-requests"; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestDataTransferResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestDataTransferResource.java new file mode 100644 index 000000000000..c23cc7d64eca --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestDataTransferResource.java @@ -0,0 +1,355 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api; + +import org.apache.nifi.authorization.resource.ResourceType; +import org.apache.nifi.remote.HttpRemoteSiteListener; +import org.apache.nifi.remote.Peer; +import org.apache.nifi.remote.RootGroupPort; +import org.apache.nifi.remote.VersionNegotiator; +import org.apache.nifi.remote.exception.HandshakeException; +import org.apache.nifi.remote.io.http.HttpServerCommunicationsSession; +import org.apache.nifi.remote.protocol.ResponseCode; +import org.apache.nifi.remote.protocol.http.HttpFlowFileServerProtocol; +import org.apache.nifi.remote.protocol.http.HttpHeaders; +import org.apache.nifi.util.NiFiProperties; +import org.apache.nifi.web.api.entity.TransactionResultEntity; +import org.junit.BeforeClass; +import org.junit.Test; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.StreamingOutput; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; + +public class TestDataTransferResource { + + @BeforeClass + public static void setup() throws Exception { + final URL resource = TestDataTransferResource.class.getResource("/site-to-site/nifi.properties"); + final String propertiesFile = resource.toURI().getPath(); + System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, propertiesFile); + } + + private HttpServletRequest createCommonHttpServletRequest() { + final HttpServletRequest req = mock(HttpServletRequest.class); + doReturn("1").when(req).getHeader(eq(HttpHeaders.PROTOCOL_VERSION)); + return req; + } + + @Test + public void testCreateTransactionPortNotFound() throws Exception { + final HttpServletRequest req = createCommonHttpServletRequest(); + + final DataTransferResource resource = getDataTransferResource(); + final HttpFlowFileServerProtocol serverProtocol = resource.getHttpFlowFileServerProtocol(null); + + doThrow(new HandshakeException(ResponseCode.UNKNOWN_PORT, "Not found.")).when(serverProtocol).handshake(any()); + + final ServletContext context = null; + final UriInfo uriInfo = null; + final InputStream inputStream = null; + + final Response response = resource.createPortTransaction("input-ports", "port-id", req, context, uriInfo, inputStream); + + TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); + + assertEquals(404, response.getStatus()); + assertEquals(ResponseCode.UNKNOWN_PORT.getCode(), resultEntity.getResponseCode()); + } + + @Test + public void testCreateTransactionPortNotInValidState() throws Exception { + final HttpServletRequest req = createCommonHttpServletRequest(); + + final DataTransferResource resource = getDataTransferResource(); + final HttpFlowFileServerProtocol serverProtocol = resource.getHttpFlowFileServerProtocol(null); + + doThrow(new HandshakeException(ResponseCode.PORT_NOT_IN_VALID_STATE, "Not in valid state.")).when(serverProtocol).handshake(any()); + + final ServletContext context = null; + final UriInfo uriInfo = null; + final InputStream inputStream = null; + + final Response response = resource.createPortTransaction("input-ports", "port-id", req, context, uriInfo, inputStream); + + TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); + + assertEquals(503, response.getStatus()); + assertEquals(ResponseCode.PORT_NOT_IN_VALID_STATE.getCode(), resultEntity.getResponseCode()); + } + + @Test + public void testCreateTransactionUnauthorized() throws Exception { + final HttpServletRequest req = createCommonHttpServletRequest(); + + final DataTransferResource resource = getDataTransferResource(); + final HttpFlowFileServerProtocol serverProtocol = resource.getHttpFlowFileServerProtocol(null); + + doThrow(new HandshakeException(ResponseCode.UNAUTHORIZED, "Unauthorized.")).when(serverProtocol).handshake(any()); + + final ServletContext context = null; + final UriInfo uriInfo = null; + final InputStream inputStream = null; + + final Response response = resource.createPortTransaction("input-ports", "port-id", req, context, uriInfo, inputStream); + + TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); + + assertEquals(401, response.getStatus()); + assertEquals(ResponseCode.UNAUTHORIZED.getCode(), resultEntity.getResponseCode()); + } + + private UriInfo mockUriInfo(final String locationUriStr) throws URISyntaxException { + final UriInfo uriInfo = mock(UriInfo.class); + final UriBuilder uriBuilder = mock(UriBuilder.class); + + final URI locationUri = new URI(locationUriStr); + doReturn(uriBuilder).when(uriInfo).getBaseUriBuilder(); + doReturn(uriBuilder).when(uriBuilder).path(any(String.class)); + doReturn(locationUri).when(uriBuilder).build(); + return uriInfo; + } + + @Test + public void testCreateTransaction() throws Exception { + final HttpServletRequest req = createCommonHttpServletRequest(); + + final DataTransferResource resource = getDataTransferResource(); + + final String locationUriStr = "http://localhost:8080/nifi-api/data-transfer/input-ports/port-id/transactions/transaction-id"; + + final ServletContext context = null; + final UriInfo uriInfo = mockUriInfo(locationUriStr); + final InputStream inputStream = null; + + final Response response = resource.createPortTransaction("input-ports", "port-id", req, context, uriInfo, inputStream); + + TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); + + assertEquals(201, response.getStatus()); + assertEquals(ResponseCode.PROPERTIES_OK.getCode(), resultEntity.getResponseCode()); + assertEquals(locationUriStr, response.getMetadata().getFirst(HttpHeaders.LOCATION_HEADER_NAME).toString()); + } + + @Test + public void testExtendTransaction() throws Exception { + final HttpServletRequest req = createCommonHttpServletRequest(); + + final DataTransferResource resource = getDataTransferResource(); + + final String locationUriStr = "http://localhost:8080/nifi-api/data-transfer/input-ports/port-id/transactions/transaction-id"; + + final ServletContext context = null; + final HttpServletResponse res = null; + final UriInfo uriInfo = mockUriInfo(locationUriStr); + final InputStream inputStream = null; + + final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); + final String transactionId = transactionManager.createTransaction(); + + final Response response = resource.extendPortTransactionTTL("input-ports", "port-id", transactionId, req, res, context, uriInfo, inputStream); + + transactionManager.cancelTransaction(transactionId); + + TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); + + assertEquals(200, response.getStatus()); + assertEquals(ResponseCode.CONTINUE_TRANSACTION.getCode(), resultEntity.getResponseCode()); + } + + @Test + public void testReceiveFlowFiles() throws Exception { + final HttpServletRequest req = createCommonHttpServletRequest(); + + final DataTransferResource resource = getDataTransferResource(); + final HttpFlowFileServerProtocol serverProtocol = resource.getHttpFlowFileServerProtocol(null); + + final RootGroupPort port = mock(RootGroupPort.class); + doReturn(port).when(serverProtocol).getPort(); + doAnswer(invocation -> { + Peer peer = (Peer) invocation.getArguments()[0]; + ((HttpServerCommunicationsSession)peer.getCommunicationsSession()).setChecksum("server-checksum"); + return 7; + }).when(port).receiveFlowFiles(any(Peer.class), any()); + + final ServletContext context = null; + final InputStream inputStream = null; + + final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); + final String transactionId = transactionManager.createTransaction(); + + final Response response = resource.receiveFlowFiles("port-id", transactionId, req, context, inputStream); + + transactionManager.cancelTransaction(transactionId); + + final Object entity = response.getEntity(); + + assertEquals(202, response.getStatus()); + assertEquals("server-checksum", entity); + } + + @Test + public void testReceiveZeroFlowFiles() throws Exception { + final HttpServletRequest req = createCommonHttpServletRequest(); + + final DataTransferResource resource = getDataTransferResource(); + final HttpFlowFileServerProtocol serverProtocol = resource.getHttpFlowFileServerProtocol(null); + + final RootGroupPort port = mock(RootGroupPort.class); + doReturn(port).when(serverProtocol).getPort(); + doAnswer(invocation -> 0).when(port).receiveFlowFiles(any(Peer.class), any()); + + final ServletContext context = null; + final InputStream inputStream = null; + + final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); + final String transactionId = transactionManager.createTransaction(); + + final Response response = resource.receiveFlowFiles("port-id", transactionId, req, context, inputStream); + + transactionManager.cancelTransaction(transactionId); + + assertEquals(400, response.getStatus()); + } + + @Test + public void testCommitInputPortTransaction() throws Exception { + final HttpServletRequest req = createCommonHttpServletRequest(); + + final DataTransferResource resource = getDataTransferResource(); + + final ServletContext context = null; + final InputStream inputStream = null; + + final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); + final String transactionId = transactionManager.createTransaction(); + + final Response response = resource.commitInputPortTransaction(ResponseCode.CONFIRM_TRANSACTION.getCode(), "port-id", transactionId, req, context, inputStream); + + transactionManager.cancelTransaction(transactionId); + + TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); + + assertEquals(200, response.getStatus()); + assertEquals(ResponseCode.CONFIRM_TRANSACTION.getCode(), resultEntity.getResponseCode()); + } + + @Test + public void testTransferFlowFiles() throws Exception { + final HttpServletRequest req = createCommonHttpServletRequest(); + + final DataTransferResource resource = getDataTransferResource(); + + final ServletContext context = null; + final HttpServletResponse res = null; + final InputStream inputStream = null; + + final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); + final String transactionId = transactionManager.createTransaction(); + + final Response response = resource.transferFlowFiles("port-id", transactionId, req, res, context, inputStream); + + transactionManager.cancelTransaction(transactionId); + + final Object entity = response.getEntity(); + + assertEquals(202, response.getStatus()); + assertTrue(entity instanceof StreamingOutput); + } + + @Test + public void testCommitOutputPortTransaction() throws Exception { + final HttpServletRequest req = createCommonHttpServletRequest(); + + final DataTransferResource resource = getDataTransferResource(); + + final ServletContext context = null; + final InputStream inputStream = null; + + final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); + final String transactionId = transactionManager.createTransaction(); + + final Response response = resource.commitOutputPortTransaction(ResponseCode.CONFIRM_TRANSACTION.getCode(), + "client-checksum", "port-id", transactionId, req, context, inputStream); + + transactionManager.cancelTransaction(transactionId); + + TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); + + assertEquals(200, response.getStatus()); + assertEquals(ResponseCode.CONFIRM_TRANSACTION.getCode(), resultEntity.getResponseCode()); + } + + @Test + public void testCommitOutputPortTransactionBadChecksum() throws Exception { + final HttpServletRequest req = createCommonHttpServletRequest(); + + final DataTransferResource resource = getDataTransferResource(); + final HttpFlowFileServerProtocol serverProtocol = resource.getHttpFlowFileServerProtocol(null); + + doThrow(new HandshakeException(ResponseCode.BAD_CHECKSUM, "Bad checksum.")).when(serverProtocol).commitTransferTransaction(any(), any()); + + final ServletContext context = null; + final InputStream inputStream = null; + + final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); + final String transactionId = transactionManager.createTransaction(); + + final Response response = resource.commitOutputPortTransaction(ResponseCode.CONFIRM_TRANSACTION.getCode(), + "client-checksum", "port-id", transactionId, req, context, inputStream); + + transactionManager.cancelTransaction(transactionId); + + TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); + + assertEquals(400, response.getStatus()); + assertEquals(ResponseCode.BAD_CHECKSUM.getCode(), resultEntity.getResponseCode()); + } + + private DataTransferResource getDataTransferResource() { + final HttpFlowFileServerProtocol serverProtocol = mock(HttpFlowFileServerProtocol.class); + final DataTransferResource resource = new DataTransferResource() { + @Override + protected void authorizeDataTransfer(ResourceType resourceType, String identifier) { + } + + @Override + HttpFlowFileServerProtocol getHttpFlowFileServerProtocol(VersionNegotiator versionNegotiator) { + return serverProtocol; + } + }; + resource.setProperties(NiFiProperties.getInstance()); + return resource; + } +} \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestSiteToSiteResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestSiteToSiteResource.java index 79cb62394266..8b65c3a6aaec 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestSiteToSiteResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestSiteToSiteResource.java @@ -16,14 +16,7 @@ */ package org.apache.nifi.web.api; -import org.apache.nifi.remote.HttpRemoteSiteListener; -import org.apache.nifi.remote.Peer; -import org.apache.nifi.remote.RootGroupPort; -import org.apache.nifi.remote.VersionNegotiator; -import org.apache.nifi.remote.exception.HandshakeException; -import org.apache.nifi.remote.io.http.HttpServerCommunicationsSession; import org.apache.nifi.remote.protocol.ResponseCode; -import org.apache.nifi.remote.protocol.http.HttpFlowFileServerProtocol; import org.apache.nifi.remote.protocol.http.HttpHeaders; import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.web.NiFiServiceFacade; @@ -31,32 +24,18 @@ import org.apache.nifi.web.api.entity.ControllerEntity; import org.apache.nifi.web.api.entity.PeersEntity; import org.apache.nifi.web.api.entity.TransactionResultEntity; -import org.apache.nifi.web.api.request.ClientIdParameter; import org.junit.BeforeClass; import org.junit.Test; -import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.Response; -import javax.ws.rs.core.StreamingOutput; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; -import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; public class TestSiteToSiteResource { @@ -78,12 +57,10 @@ public void testGetControllerForOlderVersion() throws Exception { controller.setRemoteSiteHttpListeningPort(8080); controller.setRemoteSiteListeningPort(9990); - doReturn(controller).when(serviceFacade).getController(); + doReturn(controller).when(serviceFacade).getSiteToSiteDetails(); - final SiteToSiteResource resource = new SiteToSiteResource(); - resource.setProperties(NiFiProperties.getInstance()); - resource.setServiceFacade(serviceFacade); - final Response response = resource.getSiteToSite(req); + final SiteToSiteResource resource = getSiteToSiteResource(serviceFacade); + final Response response = resource.getSiteToSiteDetails(req); ControllerEntity resultEntity = (ControllerEntity)response.getEntity(); @@ -105,12 +82,10 @@ public void testGetController() throws Exception { controller.setRemoteSiteHttpListeningPort(8080); controller.setRemoteSiteListeningPort(9990); - doReturn(controller).when(serviceFacade).getController(); + doReturn(controller).when(serviceFacade).getSiteToSiteDetails(); - final SiteToSiteResource resource = new SiteToSiteResource(); - resource.setProperties(NiFiProperties.getInstance()); - resource.setServiceFacade(serviceFacade); - final Response response = resource.getSiteToSite(req); + final SiteToSiteResource resource = getSiteToSiteResource(serviceFacade); + final Response response = resource.getSiteToSiteDetails(req); ControllerEntity resultEntity = (ControllerEntity)response.getEntity(); @@ -131,11 +106,9 @@ public void testPeers() throws Exception { final NiFiServiceFacade serviceFacade = mock(NiFiServiceFacade.class); - final SiteToSiteResource resource = new SiteToSiteResource(); - resource.setProperties(NiFiProperties.getInstance()); - resource.setServiceFacade(serviceFacade); + final SiteToSiteResource resource = getSiteToSiteResource(serviceFacade); - final Response response = resource.getPeers(null, req); + final Response response = resource.getPeers(req); PeersEntity resultEntity = (PeersEntity) response.getEntity(); @@ -150,11 +123,9 @@ public void testPeersVersionWasNotSpecified() throws Exception { final NiFiServiceFacade serviceFacade = mock(NiFiServiceFacade.class); - final SiteToSiteResource resource = new SiteToSiteResource(); - resource.setProperties(NiFiProperties.getInstance()); - resource.setServiceFacade(serviceFacade); + final SiteToSiteResource resource = getSiteToSiteResource(serviceFacade); - final Response response = resource.getPeers(null, req); + final Response response = resource.getPeers(req); TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); assertEquals(400, response.getStatus()); @@ -168,11 +139,9 @@ public void testPeersVersionNegotiationDowngrade() throws Exception { final NiFiServiceFacade serviceFacade = mock(NiFiServiceFacade.class); - final SiteToSiteResource resource = new SiteToSiteResource(); - resource.setProperties(NiFiProperties.getInstance()); - resource.setServiceFacade(serviceFacade); + final SiteToSiteResource resource = getSiteToSiteResource(serviceFacade); - final Response response = resource.getPeers(null, req); + final Response response = resource.getPeers(req); PeersEntity resultEntity = (PeersEntity) response.getEntity(); @@ -181,336 +150,14 @@ public void testPeersVersionNegotiationDowngrade() throws Exception { assertEquals(new Integer(1), response.getMetadata().getFirst(HttpHeaders.PROTOCOL_VERSION)); } - @Test - public void testCreateTransactionPortNotFound() throws Exception { - final HttpServletRequest req = createCommonHttpServletRequest(); - - final NiFiServiceFacade serviceFacade = mock(NiFiServiceFacade.class); - - final SiteToSiteResource resource = spySiteToSiteResource(serviceFacade); - - final HttpFlowFileServerProtocol serverProtocol = mockHttpFlowFileServerProtocol(resource); - - doThrow(new HandshakeException(ResponseCode.UNKNOWN_PORT, "Not found.")).when(serverProtocol).handshake(any()); - - final ClientIdParameter clientId = new ClientIdParameter("client-id"); - final ServletContext context = null; - final UriInfo uriInfo = null; - final InputStream inputStream = null; - - final Response response = resource.createPortTransaction(clientId, "input-ports", "port-id", req, context, uriInfo, inputStream); - - TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); - - assertEquals(404, response.getStatus()); - assertEquals(ResponseCode.UNKNOWN_PORT.getCode(), resultEntity.getResponseCode()); - } - - @Test - public void testCreateTransactionPortNotInValidState() throws Exception { - final HttpServletRequest req = createCommonHttpServletRequest(); - - final NiFiServiceFacade serviceFacade = mock(NiFiServiceFacade.class); - - final SiteToSiteResource resource = spySiteToSiteResource(serviceFacade); - - final HttpFlowFileServerProtocol serverProtocol = mockHttpFlowFileServerProtocol(resource); - - doThrow(new HandshakeException(ResponseCode.PORT_NOT_IN_VALID_STATE, "Not in valid state.")).when(serverProtocol).handshake(any()); - - final ClientIdParameter clientId = new ClientIdParameter("client-id"); - final ServletContext context = null; - final UriInfo uriInfo = null; - final InputStream inputStream = null; - - final Response response = resource.createPortTransaction(clientId, "input-ports", "port-id", req, context, uriInfo, inputStream); - - TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); - - assertEquals(503, response.getStatus()); - assertEquals(ResponseCode.PORT_NOT_IN_VALID_STATE.getCode(), resultEntity.getResponseCode()); - } - - @Test - public void testCreateTransactionUnauthorized() throws Exception { - final HttpServletRequest req = createCommonHttpServletRequest(); - - final NiFiServiceFacade serviceFacade = mock(NiFiServiceFacade.class); - - final SiteToSiteResource resource = spySiteToSiteResource(serviceFacade); - - final HttpFlowFileServerProtocol serverProtocol = mockHttpFlowFileServerProtocol(resource); - - doThrow(new HandshakeException(ResponseCode.UNAUTHORIZED, "Unauthorized.")).when(serverProtocol).handshake(any()); - - final ClientIdParameter clientId = new ClientIdParameter("client-id"); - final ServletContext context = null; - final UriInfo uriInfo = null; - final InputStream inputStream = null; - - final Response response = resource.createPortTransaction(clientId, "input-ports", "port-id", req, context, uriInfo, inputStream); - - TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); - - assertEquals(401, response.getStatus()); - assertEquals(ResponseCode.UNAUTHORIZED.getCode(), resultEntity.getResponseCode()); - } - - private UriInfo mockUriInfo(final String locationUriStr) throws URISyntaxException { - final UriInfo uriInfo = mock(UriInfo.class); - final UriBuilder uriBuilder = mock(UriBuilder.class); - - final URI locationUri = new URI(locationUriStr); - doReturn(uriBuilder).when(uriInfo).getBaseUriBuilder(); - doReturn(uriBuilder).when(uriBuilder).path(any(String.class)); - doReturn(locationUri).when(uriBuilder).build(); - return uriInfo; - } - - @Test - public void testCreateTransaction() throws Exception { - final HttpServletRequest req = createCommonHttpServletRequest(); - - final NiFiServiceFacade serviceFacade = mock(NiFiServiceFacade.class); - - final SiteToSiteResource resource = spySiteToSiteResource(serviceFacade); - - mockHttpFlowFileServerProtocol(resource); - - final String locationUriStr = "http://localhost:8080/nifi-api/site-to-site/input-ports/port-id/transactions/transaction-id"; - - final ClientIdParameter clientId = new ClientIdParameter("client-id"); - final ServletContext context = null; - final UriInfo uriInfo = mockUriInfo(locationUriStr); - final InputStream inputStream = null; - - final Response response = resource.createPortTransaction(clientId, "input-ports", "port-id", req, context, uriInfo, inputStream); - - TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); - - assertEquals(201, response.getStatus()); - assertEquals(ResponseCode.PROPERTIES_OK.getCode(), resultEntity.getResponseCode()); - assertEquals(locationUriStr, response.getMetadata().getFirst(HttpHeaders.LOCATION_HEADER_NAME).toString()); - } - - @Test - public void testExtendTransaction() throws Exception { - final HttpServletRequest req = createCommonHttpServletRequest(); - - final NiFiServiceFacade serviceFacade = mock(NiFiServiceFacade.class); - - final SiteToSiteResource resource = spySiteToSiteResource(serviceFacade); - - mockHttpFlowFileServerProtocol(resource); - - final String locationUriStr = "http://localhost:8080/nifi-api/site-to-site/input-ports/port-id/transactions/transaction-id"; - - final ClientIdParameter clientId = new ClientIdParameter("client-id"); - final ServletContext context = null; - final HttpServletResponse res = null; - final UriInfo uriInfo = mockUriInfo(locationUriStr); - final InputStream inputStream = null; - - final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); - final String transactionId = transactionManager.createTransaction(); - - final Response response = resource.extendPortTransactionTTL(clientId, "input-ports", "port-id", transactionId, req, res, context, uriInfo, inputStream); - - transactionManager.cancelTransaction(transactionId); - - TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); - - assertEquals(200, response.getStatus()); - assertEquals(ResponseCode.CONTINUE_TRANSACTION.getCode(), resultEntity.getResponseCode()); - } - - @Test - public void testReceiveFlowFiles() throws Exception { - final HttpServletRequest req = createCommonHttpServletRequest(); - - final NiFiServiceFacade serviceFacade = mock(NiFiServiceFacade.class); - - final SiteToSiteResource resource = spySiteToSiteResource(serviceFacade); - - final HttpFlowFileServerProtocol serverProtocol = mockHttpFlowFileServerProtocol(resource); - - final RootGroupPort port = mock(RootGroupPort.class); - doReturn(port).when(serverProtocol).getPort(); - doAnswer(invocation -> { - Peer peer = (Peer) invocation.getArguments()[0]; - ((HttpServerCommunicationsSession)peer.getCommunicationsSession()).setChecksum("server-checksum"); - return 7; - }).when(port).receiveFlowFiles(any(Peer.class), any()); - - final ClientIdParameter clientId = new ClientIdParameter("client-id"); - final ServletContext context = null; - final InputStream inputStream = null; - - final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); - final String transactionId = transactionManager.createTransaction(); - - final Response response = resource.receiveFlowFiles(clientId, "port-id", transactionId, req, context, inputStream); - - transactionManager.cancelTransaction(transactionId); - - final Object entity = response.getEntity(); - - assertEquals(202, response.getStatus()); - assertEquals("server-checksum", entity); - } - - @Test - public void testReceiveZeroFlowFiles() throws Exception { - final HttpServletRequest req = createCommonHttpServletRequest(); - - final NiFiServiceFacade serviceFacade = mock(NiFiServiceFacade.class); - - final SiteToSiteResource resource = spySiteToSiteResource(serviceFacade); - - final HttpFlowFileServerProtocol serverProtocol = mockHttpFlowFileServerProtocol(resource); - - final RootGroupPort port = mock(RootGroupPort.class); - doReturn(port).when(serverProtocol).getPort(); - doAnswer(invocation -> 0).when(port).receiveFlowFiles(any(Peer.class), any()); - - final ClientIdParameter clientId = new ClientIdParameter("client-id"); - final ServletContext context = null; - final InputStream inputStream = null; - - final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); - final String transactionId = transactionManager.createTransaction(); - - final Response response = resource.receiveFlowFiles(clientId, "port-id", transactionId, req, context, inputStream); - - transactionManager.cancelTransaction(transactionId); - - assertEquals(400, response.getStatus()); - } - - @Test - public void testCommitInputPortTransaction() throws Exception { - final HttpServletRequest req = createCommonHttpServletRequest(); - - final NiFiServiceFacade serviceFacade = mock(NiFiServiceFacade.class); - - final SiteToSiteResource resource = spySiteToSiteResource(serviceFacade); - - mockHttpFlowFileServerProtocol(resource); - - final ClientIdParameter clientId = new ClientIdParameter("client-id"); - final ServletContext context = null; - final InputStream inputStream = null; - - final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); - final String transactionId = transactionManager.createTransaction(); - - final Response response = resource.commitInputPortTransaction(clientId, ResponseCode.CONFIRM_TRANSACTION.getCode(), "port-id", transactionId, req, context, inputStream); - - transactionManager.cancelTransaction(transactionId); - - TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); - - assertEquals(200, response.getStatus()); - assertEquals(ResponseCode.CONFIRM_TRANSACTION.getCode(), resultEntity.getResponseCode()); - } - - @Test - public void testTransferFlowFiles() throws Exception { - final HttpServletRequest req = createCommonHttpServletRequest(); - - final NiFiServiceFacade serviceFacade = mock(NiFiServiceFacade.class); - - final SiteToSiteResource resource = spySiteToSiteResource(serviceFacade); - - mockHttpFlowFileServerProtocol(resource); - - final ClientIdParameter clientId = new ClientIdParameter("client-id"); - final ServletContext context = null; - final HttpServletResponse res = null; - final InputStream inputStream = null; - - final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); - final String transactionId = transactionManager.createTransaction(); - - final Response response = resource.transferFlowFiles(clientId, "port-id", transactionId, req, res, context, inputStream); - - transactionManager.cancelTransaction(transactionId); - - final Object entity = response.getEntity(); - - assertEquals(202, response.getStatus()); - assertTrue(entity instanceof StreamingOutput); - } - - @Test - public void testCommitOutputPortTransaction() throws Exception { - final HttpServletRequest req = createCommonHttpServletRequest(); - - final NiFiServiceFacade serviceFacade = mock(NiFiServiceFacade.class); - - final SiteToSiteResource resource = spySiteToSiteResource(serviceFacade); - - mockHttpFlowFileServerProtocol(resource); - - final ClientIdParameter clientId = new ClientIdParameter("client-id"); - final ServletContext context = null; - final InputStream inputStream = null; - - final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); - final String transactionId = transactionManager.createTransaction(); - - final Response response = resource.commitOutputPortTransaction(clientId, ResponseCode.CONFIRM_TRANSACTION.getCode(), - "client-checksum", "port-id", transactionId, req, context, inputStream); - - transactionManager.cancelTransaction(transactionId); - - TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); - - assertEquals(200, response.getStatus()); - assertEquals(ResponseCode.CONFIRM_TRANSACTION.getCode(), resultEntity.getResponseCode()); - } - - @Test - public void testCommitOutputPortTransactionBadChecksum() throws Exception { - final HttpServletRequest req = createCommonHttpServletRequest(); - - final NiFiServiceFacade serviceFacade = mock(NiFiServiceFacade.class); - - final SiteToSiteResource resource = spySiteToSiteResource(serviceFacade); - - final HttpFlowFileServerProtocol serverProtocol = mockHttpFlowFileServerProtocol(resource); - doThrow(new HandshakeException(ResponseCode.BAD_CHECKSUM, "Bad checksum.")).when(serverProtocol).commitTransferTransaction(any(), any()); - - final ClientIdParameter clientId = new ClientIdParameter("client-id"); - final ServletContext context = null; - final InputStream inputStream = null; - - final HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(); - final String transactionId = transactionManager.createTransaction(); - - final Response response = resource.commitOutputPortTransaction(clientId, ResponseCode.CONFIRM_TRANSACTION.getCode(), - "client-checksum", "port-id", transactionId, req, context, inputStream); - - transactionManager.cancelTransaction(transactionId); - - TransactionResultEntity resultEntity = (TransactionResultEntity) response.getEntity(); - - assertEquals(400, response.getStatus()); - assertEquals(ResponseCode.BAD_CHECKSUM.getCode(), resultEntity.getResponseCode()); - } - - private HttpFlowFileServerProtocol mockHttpFlowFileServerProtocol(SiteToSiteResource resource) { - final HttpFlowFileServerProtocol serverProtocol = mock(HttpFlowFileServerProtocol.class); - doReturn(serverProtocol).when(resource).getHttpFlowFileServerProtocol(any(VersionNegotiator.class)); - return serverProtocol; - } - - private SiteToSiteResource spySiteToSiteResource(NiFiServiceFacade serviceFacade) { - final SiteToSiteResource resource = spy(SiteToSiteResource.class); + private SiteToSiteResource getSiteToSiteResource(final NiFiServiceFacade serviceFacade) { + final SiteToSiteResource resource = new SiteToSiteResource() { + @Override + protected void authorizeSiteToSite() { + } + }; resource.setProperties(NiFiProperties.getInstance()); resource.setServiceFacade(serviceFacade); return resource; } - - } \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml index 1b65f0b7b92b..c09bd8a4fa44 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml @@ -32,6 +32,7 @@ counters.properties cluster.properties templates.properties + users.properties bulletin-board.properties login.properties provenance.properties @@ -61,6 +62,7 @@ src/main/resources/filters/${counters.filter} src/main/resources/filters/${cluster.filter} src/main/resources/filters/${templates.filter} + src/main/resources/filters/${users.filter} src/main/resources/filters/${bulletin.board.filter} src/main/resources/filters/${login.filter} src/main/resources/filters/${provenance.filter} @@ -96,6 +98,7 @@ **/counters.jsp, **/cluster.jsp, **/templates.jsp, + **/users.jsp, **/bulletin-board.jsp, **/login.jsp @@ -205,6 +208,14 @@ true + + src/main/webapp/WEB-INF/pages + WEB-INF/pages + + users.jsp + + true + src/main/webapp/WEB-INF/pages WEB-INF/pages @@ -247,7 +258,6 @@ ${frontend.working.dir} - install-node-and-npm @@ -297,6 +307,7 @@ counters-min.properties cluster-min.properties templates-min.properties + users-min.properties bulletin-board-min.properties login-min.properties provenance-min.properties @@ -346,7 +357,7 @@ ${staging.dir}/js/nf/canvas/nf-processor-configuration.js ${staging.dir}/js/nf/nf-processor-details.js ${staging.dir}/js/nf/canvas/nf-process-group-configuration.js - ${staging.dir}/js/nf/canvas/nf-process-group-details.js + ${staging.dir}/js/nf/canvas/nf-policy-management.js ${staging.dir}/js/nf/canvas/nf-remote-process-group-configuration.js ${staging.dir}/js/nf/canvas/nf-remote-process-group-details.js ${staging.dir}/js/nf/canvas/nf-remote-process-group-ports.js @@ -464,6 +475,20 @@ ${staging.dir}/js/nf/counters/nf-counters-table.js + + true + ${project.build.directory}/${project.build.finalName}/js/nf/users/nf-users-all.js + + ${staging.dir}/js/nf/nf-client.js + ${staging.dir}/js/nf/nf-common.js + ${staging.dir}/js/nf/nf-universal-capture.js + ${staging.dir}/js/nf/nf-dialog.js + ${staging.dir}/js/nf/nf-storage.js + ${staging.dir}/js/nf/nf-ajax-setup.js + ${staging.dir}/js/nf/users/nf-users.js + ${staging.dir}/js/nf/users/nf-users-table.js + + true ${project.build.directory}/${project.build.finalName}/js/nf/templates/nf-templates-all.js @@ -531,7 +556,7 @@ ${staging.dir}/css/processor-configuration.css ${staging.dir}/css/processor-details.css ${staging.dir}/css/process-group-configuration.css - ${staging.dir}/css/process-group-details.css + ${staging.dir}/css/policy-management.css ${staging.dir}/css/remote-process-group-configuration.css ${staging.dir}/css/port-configuration.css ${staging.dir}/css/port-details.css @@ -609,6 +634,16 @@ ${staging.dir}/css/cluster.css + + true + ${project.build.directory}/${project.build.finalName}/css/nf-users-all.css + + ${staging.dir}/css/main.css + ${staging.dir}/css/banner.css + ${staging.dir}/css/dialog.css + ${staging.dir}/css/users.css + + true ${project.build.directory}/${project.build.finalName}/css/nf-templates-all.css @@ -673,6 +708,8 @@ css/nf-counters-all.css.gz, css/nf-cluster-all.css, css/nf-cluster-all.css.gz, + css/nf-users-all.css, + css/nf-users-all.css.gz, css/nf-templates-all.css, css/nf-templates-all.css.gz, css/nf-bulletin-board-all.css, @@ -712,6 +749,8 @@ js/nf/counters/nf-counters-all.js.gz, js/nf/cluster/nf-cluster-all.js, js/nf/cluster/nf-cluster-all.js.gz, + js/nf/users/nf-users-all.js, + js/nf/users/nf-users-all.js.gz, js/nf/templates/nf-templates-all.js, js/nf/templates/nf-templates-all.js.gz, js/nf/bulletin-board/nf-bulletin-board-all.js, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/bulletin-board.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/bulletin-board.properties index a4afc922e49c..72f99a5d6a7a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/bulletin-board.properties +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/bulletin-board.properties @@ -13,8 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -nf.bulletin.board.script.tags=\n\ -\n\ +nf.bulletin.board.script.tags=\n\ \n\ \n\ \n\ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties index f3d3941ff556..0f219d4149a4 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties @@ -13,8 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -nf.canvas.script.tags=\n\ -\n\ +nf.canvas.script.tags=\n\ \n\ \n\ \n\ @@ -32,7 +31,7 @@ nf.canvas.script.tags=\n\ \n\ \n\ -\n\ +\n\ \n\ \n\ \n\ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/cluster.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/cluster.properties index 0fb872cd1fd8..9061e028ff43 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/cluster.properties +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/cluster.properties @@ -13,8 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -nf.cluster.script.tags=\n\ -\n\ +nf.cluster.script.tags=\n\ \n\ \n\ \n\ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/counters.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/counters.properties index 4c947b4f4d26..441490912006 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/counters.properties +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/counters.properties @@ -13,8 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -nf.counters.script.tags=\n\ -\n\ +nf.counters.script.tags=\n\ \n\ \n\ \n\ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/history.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/history.properties index 6cee206ef8d2..e532204fee1a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/history.properties +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/history.properties @@ -13,8 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -nf.history.script.tags=\n\ -\n\ +nf.history.script.tags=\n\ \n\ \n\ \n\ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties index a87195ca7db1..8536bf946f08 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties @@ -13,8 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -nf.provenance.script.tags=\n\ -\n\ +nf.provenance.script.tags=\n\ \n\ \n\ \n\ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties index 33e1a5a63057..52dfe4902fec 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties @@ -13,8 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -nf.summary.script.tags=\n\ -\n\ +nf.summary.script.tags=\n\ \n\ \n\ \n\ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/templates.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/templates.properties index c0cee4cff8d2..7ad302c9aa9a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/templates.properties +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/templates.properties @@ -13,8 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -nf.templates.script.tags=\n\ -\n\ +nf.templates.script.tags=\n\ \n\ \n\ \n\ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users-min.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users-min.properties new file mode 100644 index 000000000000..9375334eab75 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users-min.properties @@ -0,0 +1,18 @@ +# 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. + +nf.users.script.tags= +nf.users.style.tags=\n\ + \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users.properties new file mode 100644 index 000000000000..b2dee8ea87c4 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users.properties @@ -0,0 +1,29 @@ +# 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. + +nf.users.script.tags=\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ + +nf.users.style.tags=\n\ +\n\ +\n\ +\n\ +\n\ + \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp index 39fdffd3da15..44f284a318c0 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp @@ -95,6 +95,7 @@ + @@ -123,6 +124,7 @@ + diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp new file mode 100644 index 000000000000..473f538ff240 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp @@ -0,0 +1,70 @@ +<%-- + 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. +--%> +<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %> + + + + NiFi Users + + + + ${nf.users.style.tags} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${nf.users.script.tags} + + + + + + + + + + + + diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp index 7d3bcc03f806..d4d7aedf3460 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp @@ -147,12 +147,19 @@ Flow Configuration History + + ng-class="{disabled: !appCtrl.nf.Common.canModifyTenants()}"> Users - + + + + + Policies diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/navigation.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/navigation.jsp index b2b260662321..a15dee1a33d4 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/navigation.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/navigation.jsp @@ -99,6 +99,12 @@ ng-disabled="false">
+
 
+
+ +
 
+ +
+
+
+
+ +
+ Last updated:  +
+
+
+ \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/search-users-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/search-users-dialog.jsp new file mode 100644 index 000000000000..03c62ca4a3ef --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/search-users-dialog.jsp @@ -0,0 +1,23 @@ +<%-- + 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. +--%> +<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %> + +
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/user-delete-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/user-delete-dialog.jsp new file mode 100644 index 000000000000..ef30bafeb479 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/user-delete-dialog.jsp @@ -0,0 +1,21 @@ +<%-- + 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. +--%> +<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %> + \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/user-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/user-dialog.jsp new file mode 100644 index 000000000000..1d206b4b46f1 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/user-dialog.jsp @@ -0,0 +1,48 @@ +<%-- + 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. +--%> +<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %> + \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/users-content.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/users-content.jsp new file mode 100644 index 000000000000..75872f495e40 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/users/users-content.jsp @@ -0,0 +1,41 @@ +<%-- + 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. +--%> +<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %> +
+
+
NiFi Users
+
+
+
+ Displaying  of  +
+
+ +
+
+ +
+
+
+
+
+ +
+ Last updated:  +
+
+
\ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css index f7ddc84c2a6b..db9f4ce0913d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css @@ -18,7 +18,7 @@ @import url(processor-configuration.css); @import url(processor-details.css); @import url(process-group-configuration.css); -@import url(process-group-details.css); +@import url(policy-management.css); @import url(queue-listing.css); @import url(remote-process-group-configuration.css); @import url(controller-service.css); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css index 681673a098fa..c742c2f9a70b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css @@ -202,13 +202,4 @@ md-toolbar.md-small .md-toolbar-tools { font-size: 12px; color: #262626; padding-right: 5px; -} - -#has-pending-accounts { - background-image: url(../images/starburst.png); - width: 9px; - height: 9px; - margin-top: 3px; - margin-left: 5px; - background-color: transparent; } \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css index 5486357ec510..2a0eadd57dd7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css @@ -297,7 +297,7 @@ input::placeholder { color: #728e9b; /*base-color*/ } -input, input[type=text], input[type=password], textarea { +input[type=text], input[type=password], textarea { background-color: #eaeef0; border: 1px solid #eaeef0; font-family: Roboto, sans-serif; @@ -569,6 +569,7 @@ input.filter { button.fa { color: #004849; font-size: 16px; + cursor: pointer; } button.icon { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/policy-management.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/policy-management.css new file mode 100644 index 000000000000..e414934433c4 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/policy-management.css @@ -0,0 +1,205 @@ +/* + * 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. + */ + +#policy-management { + display: none; + position: absolute; + top: 0px; + bottom: 0px; + left: 20px; + right: 20px; + overflow: auto; +} + +#policy-management-header-text { + font-size: 18px; + font-weight: bold; + color: #728E9B; + font-family: Roboto Slab; + margin-bottom: 24px; +} + +#new-policy-user-button { + margin-right: 5px; +} + +button.policy-button { + float: right; + margin-top: 4px; +} + +button.policy-button:disabled { + color: #CCDADB; + cursor: not-allowed; + border: 1px solid #CCDADB; +} + +/* + policy message +*/ + +#policy-message-container { + height: 18px; + color: #775351; + font-family: Roboto; + font-size: 13px; + font-weight: 500; +} + +#policy-message { + float: left; + margin-right: 4px; +} + +#new-policy-message { + float: left; +} + +#override-policy-message { + float: left; +} + +/* + policy selection +*/ + +div.policy-controls { + float: left; +} + +#policy-type-list { + float: left; + width: 190px; + margin-right: 3px; + border-left: 1px solid transparent; + border-right: 1px solid transparent; +} + +#component-policy-target { + float: left; + width: 200px; + margin-right: 3px; + border-left: 1px solid transparent; + border-right: 1px solid transparent; +} + +#controller-policy-target { + float: left; + width: 160px; + margin-right: 3px; + border-left: 1px solid transparent; + border-right: 1px solid transparent; +} + +div.policy-selected-component-container { + float: left; + margin-right: 20px; +} + +div.policy-selected-component-details-container { + float: left; + padding-left: 10px; +} + +div.policy-selected-component-type-icon { + float: left; +} + +div.policy-selected-component-type-icon i.icon { + font-size: 28px; + font-family: flowfont; + color: #ad9897; +} + +div.policy-selected-component-name { + height: 14px; + font-size: 15px; + font-family: Roboto; + color: #262626; + max-width: 300px; + text-overflow: ellipsis; + overflow-x: hidden; + white-space: nowrap; +} + +div.policy-selected-component-type { + font-size: 12px; + font-family: Roboto; + color: #728e9b; + margin-top: 3px; +} + +/* + policy table +*/ + +#policy-table { + position: absolute; + top: 92px; + left: 0px; + bottom: 0px; + right: 0px; + overflow: hidden; + background-color: #fff; +} + +#policy-refresh-container { + position: absolute; + bottom: 20px; + left: 0px; + height: 32px; +} + +#policy-refresh-button { + float: left; +} + +#policy-loading-container { + float: left; + width: 16px; + height: 16px; + background-color: transparent; + margin-top: 4px; + margin-left: 3px; +} + +#policy-last-refreshed { + font-weight: bold; +} + +/* + search users dialog +*/ + +#search-users-dialog { + width: 450px; + height: 250px; +} + +#search-users-results .ui-autocomplete { + max-height: 300px; + overflow: auto; + border: 1px solid #aaaaaa; + z-index: 1351; + border-radius: 0; +} + +#search-users-results .ui-menu .ui-menu-item a.ui-state-focus { + background: #D4E0E5 !important; + border: 1px solid #999999; + border-radius: 0; +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/users.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/users.css new file mode 100644 index 000000000000..1810e5020446 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/users.css @@ -0,0 +1,128 @@ +/* + * 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. + */ + +/* + Users Styles +*/ + +#users { + position: absolute; + top: 0px; + bottom: 0px; + left: 20px; + right: 20px; + overflow: auto; +} + +#users-header-and-filter { + height: 32px; +} + +#users-header-text { + font-size: 18px; + font-weight: bold; + color: #728E9B; + font-family: Roboto Slab; + margin-bottom: 30px; +} + +#users-refresh-container { + position: absolute; + left: 20px; + bottom: 0px; +} + +#users-loading-container { + float: left; + width: 16px; + height: 16px; + background-color: transparent; + margin-top: 4px; + margin-left: 3px; +} + +#users-last-refreshed { + font-weight: bold; +} + +#users-header { + padding-top: 10px; +} + +#new-user-button { + float: right; + margin-top: 4px; +} + +/* users table */ + +#users-table { + position: absolute; + top: 92px; + left: 0px; + bottom: 0px; + right: 0px; + overflow: hidden; + background-color: #fff; +} + +/* users dialog */ + +#user-dialog { + width: 450px; + height: 500px; +} + +#user-identity { + width: 410px; +} + +/* user delete dialog */ + +#user-delete-dialog { + width: 450px; + height: 165px; +} + +/* users/groups list */ + +ul.usersGroupsList li { + height: 24px; + line-height: 24px; + border-bottom: 1px solid #c7d2d7; + width: 100%; + display: inline-flex; +} + +ul.usersGroupsList li.even { + background-color: #f4f6f7; +} + +ul.usersGroupsList li div.nf-checkbox { + margin: 0 10px; + margin-top: 6px; + min-width: 12px; + max-width: 12px; +} + +div.available-identities { + margin-left: 5px; + margin-right: 10px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/combo/jquery.combo.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/combo/jquery.combo.js index 036f17eda16f..232fe20c8a08 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/combo/jquery.combo.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/combo/jquery.combo.js @@ -118,6 +118,30 @@ }; + var setDisabled = function (combo, optionElement, option, disabled) { + // reset the option element + optionElement.removeClass('unset').off('click'); + + if (disabled === true) { + optionElement.addClass('unset'); + } else { + optionElement.on('click', function () { + //remove active styles + $('.combo').removeClass('combo-open'); + + // select the option + selectOption(combo, option.text, option.value); + + // click the glass pane which will hide the options + $('.combo-glass-pane').click(); + }).hover(function () { + $(this).addClass('pointer').css('background', '#eaeef0'); + }, function () { + $(this).removeClass('pointer').css('background', '#ffffff'); + }); + } + }; + var methods = { /** @@ -146,6 +170,8 @@ }, function () { combo.removeClass('button-over').addClass('combo-button-normal'); }).click(function (event) { + var comboConfigOptions = combo.data('options'); + //add active styles $(this).addClass('combo-open'); @@ -166,8 +192,8 @@ // set the max height if necessary var maxHeight = -1; - if (isDefinedAndNotNull(options.maxHeight)) { - maxHeight = parseInt(options.maxHeight); + if (isDefinedAndNotNull(comboConfigOptions.maxHeight)) { + maxHeight = parseInt(comboConfigOptions.maxHeight); if (maxHeight > 0) { comboOptions.css('max-height', maxHeight + 'px'); } @@ -177,7 +203,7 @@ var optionList = $('
    ').appendTo(comboOptions); // process the options - $.each(options.options, function (i, option) { + $.each(comboConfigOptions.options, function (i, option) { var optionText = $('').attr('title', option.text).text(option.text); var optionValue = $('').text(option.value); @@ -297,14 +323,35 @@ }); }, + /** + * Sets whether the specified option is enabled or disabled. + * + * @param option + * @param enabled + */ + setOptionEnabled: function (option, enabled) { + return this.each(function () { + var combo = $(this); + var comboConfigOptions = combo.data('options'); + + $.each(comboConfigOptions.options, function (i, configOption) { + if (configOption.value === option.value) { + configOption.disabled = !enabled; + } + }); + }); + }, + /** * Destroy's the combo. */ destroy: function () { - $(this).empty().unbind().removeData(); + return this.each(function () { + $(this).empty().unbind().removeData(); - // remove the options if open - $('div.combo-glass-pane').click(); + // remove the options if open + $('div.combo-glass-pane').click(); + }); } }; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-breadcrumbs-controller.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-breadcrumbs-controller.js index 2087034cbf9d..58a73a5e9d99 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-breadcrumbs-controller.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-breadcrumbs-controller.js @@ -42,7 +42,7 @@ nf.ng.BreadcrumbsCtrl = function (serviceProvider, $sanitize) { */ generateBreadcrumbs: function(breadcrumbEntity) { var label = breadcrumbEntity.id; - if (breadcrumbEntity.accessPolicy.canRead) { + if (breadcrumbEntity.permissions.canRead) { label = breadcrumbEntity.breadcrumb.name; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-flow-status-controller.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-flow-status-controller.js index debc1ca829eb..c66938643107 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-flow-status-controller.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-flow-status-controller.js @@ -382,13 +382,6 @@ nf.ng.Canvas.FlowStatusCtrl = function (serviceProvider, $sanitize) { nf.Common.isDefinedAndNotNull(status.connectedNodes) ? $sanitize(status.connectedNodes) : '-'; this.bulletins.update(status); - - // handle any pending user request - if (status.hasPendingAccounts === true) { - $('#has-pending-accounts').show(); - } else { - $('#has-pending-accounts').hide(); - } } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-global-menu-controller.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-global-menu-controller.js index f0865183a837..186b5f9dd912 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-global-menu-controller.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-global-menu-controller.js @@ -193,13 +193,34 @@ nf.ng.Canvas.GlobalMenuCtrl = function (serviceProvider) { * Launch the users shell. */ launch: function () { - if (nf.Common.canAccessTenants()) { + if (nf.Common.canModifyTenants()) { nf.Shell.showPage('users'); } } } }; + /** + * The policies menu item controller. + */ + this.policies = { + + /** + * The policies menu item's shell controller. + */ + shell: { + + /** + * Launch the policies shell. + */ + launch: function () { + if (nf.Common.canModifyPolicies() && nf.Common.canAccessTenants()) { + nf.PolicyManagement.showGlobalPolicies(); + } + } + } + }; + /** * The templates menu item controller. */ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-graph-controls-controller.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-graph-controls-controller.js index 9990eaeadfd5..9ee45cbd49c5 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-graph-controls-controller.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-graph-controls-controller.js @@ -215,7 +215,7 @@ nf.ng.Canvas.GraphControlsCtrl = function (serviceProvider, navigateCtrl, operat } else { if (selection.size() === 1) { var d = selection.datum(); - if (d.accessPolicy.canRead) { + if (d.permissions.canRead) { if (nf.CanvasUtils.isLabel(selection)) { if ($.trim(d.component.label) !== '') { return d.component.label; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-operate-controller.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-operate-controller.js index 0f03386f7c02..5d68462c064b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-operate-controller.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-operate-controller.js @@ -298,7 +298,7 @@ nf.ng.Canvas.OperateCtrl = function () { }).fail(function (xhr, status, error) { if (xhr.status === 400 || xhr.status === 404 || xhr.status === 409) { nf.Dialog.showOkDialog({ - headerText: 'Malformed Request', + headerText: 'Error', dialogContent: nf.Common.escapeHtml(xhr.responseText) }); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-template-component.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-template-component.js index 947d31600802..a029e79b9b84 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-template-component.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-template-component.js @@ -173,7 +173,7 @@ nf.ng.TemplateComponent = function (serviceProvider) { if (nf.Common.isDefinedAndNotNull(templates) && templates.length > 0) { var options = []; $.each(templates, function (_, templateEntity) { - if (templateEntity.accessPolicy.canRead === true) { + if (templateEntity.permissions.canRead === true) { options.push({ text: templateEntity.template.name, value: templateEntity.id, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js index 593ac3f0589f..f0f606bcf149 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js @@ -157,7 +157,7 @@ nf.Actions = (function () { // reload the group's connections var connections = nf.Connection.getComponentConnections(remoteProcessGroup.id); $.each(connections, function (_, connection) { - if (connection.accessPolicy.canRead) { + if (connection.permissions.canRead) { nf.Connection.reload(connection.component); } }); @@ -702,16 +702,27 @@ nf.Actions = (function () { } }, + /** + * Opens the policy management page for the selected component. + * + * @param selection + */ + managePolicies: function(selection) { + if (selection.size() <= 1) { + nf.PolicyManagement.showComponentPolicy(selection); + } + }, + // Defines an action for showing component details (like configuration but read only). showDetails: function (selection) { if (selection.empty()) { - nf.ProcessGroupDetails.showConfiguration(nf.Canvas.getGroupId()); + nf.ProcessGroupConfiguration.showConfiguration(nf.Canvas.getGroupId()); } else if (selection.size() === 1) { var selectionData = selection.datum(); if (nf.CanvasUtils.isProcessor(selection)) { nf.ProcessorDetails.showDetails(nf.Canvas.getGroupId(), selectionData.id); } else if (nf.CanvasUtils.isProcessGroup(selection)) { - nf.ProcessGroupDetails.showConfiguration(selectionData.id); + nf.ProcessGroupConfiguration.showConfiguration(selectionData.id); } else if (nf.CanvasUtils.isRemoteProcessGroup(selection)) { nf.RemoteProcessGroupDetails.showDetails(selection); } else if (nf.CanvasUtils.isInputPort(selection) || nf.CanvasUtils.isOutputPort(selection)) { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-birdseye.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-birdseye.js index 51fb5505d167..80aaa17a213e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-birdseye.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-birdseye.js @@ -151,7 +151,7 @@ nf.Birdseye = (function () { $.each(components.labels, function (_, d) { var color = nf.Label.defaultColor(); - if (d.accessPolicy.canRead) { + if (d.permissions.canRead) { // use the specified color if appropriate if (nf.Common.isDefinedAndNotNull(d.component.style['background-color'])) { color = d.component.style['background-color']; @@ -189,7 +189,7 @@ nf.Birdseye = (function () { $.each(components.processors, function (_, d) { var color = '#dde4eb'; - if (d.accessPolicy.canRead) { + if (d.permissions.canRead) { // use the specified color if appropriate if (nf.Common.isDefinedAndNotNull(d.component.style['background-color'])) { color = d.component.style['background-color']; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js index 31576bafe29b..47ab0f96939d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js @@ -260,7 +260,7 @@ nf.CanvasUtils = (function () { editable: function (selection) { var selectionData = selection.datum(); - if (selectionData.accessPolicy.canWrite && selectionData.accessPolicy.canRead) { + if (selectionData.permissions.canWrite && selectionData.permissions.canRead) { if (!selection.classed('connectable')) { selection.call(nf.Connectable.activate); } @@ -888,7 +888,7 @@ nf.CanvasUtils = (function () { canModify: function (selection) { var selectionSize = selection.size(); var writableSize = selection.filter(function (d) { - return d.accessPolicy.canWrite && d.accessPolicy.canRead; + return d.permissions.canWrite && d.permissions.canRead; }).size(); return selectionSize === writableSize; @@ -903,7 +903,7 @@ nf.CanvasUtils = (function () { canRead: function (selection) { var selectionSize = selection.size(); var readableSize = selection.filter(function (d) { - return d.accessPolicy.canRead; + return d.permissions.canRead; }).size(); return selectionSize === readableSize; @@ -923,7 +923,7 @@ nf.CanvasUtils = (function () { var selectionData = selection.datum(); // check access policies first - if (selectionData.accessPolicy.canRead === false || selectionData.accessPolicy.canWrite === false) { + if (selectionData.permissions.canRead === false || selectionData.permissions.canWrite === false) { return false; } @@ -1101,7 +1101,7 @@ nf.CanvasUtils = (function () { if (source.empty() === false) { var sourceData = source.datum(); - if (sourceData.accessPolicy.canRead) { + if (sourceData.permissions.canRead) { // update the source status if necessary if (nf.CanvasUtils.isProcessor(source)) { nf.Processor.reload(sourceData.component); @@ -1119,7 +1119,7 @@ nf.CanvasUtils = (function () { if (destination.empty() === false) { var destinationData = destination.datum(); - if (destinationData.accessPolicy.canRead) { + if (destinationData.permissions.canRead) { // update the destination component accordingly if (nf.CanvasUtils.isProcessor(destination)) { nf.Processor.reload(destinationData.component); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js index d83c60a652ea..d5bc4ce5a024 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js @@ -114,7 +114,7 @@ nf.Canvas = (function () { var polling = false; var groupId = 'root'; var groupName = null; - var accessPolicy = null; + var permissions = null; var parentGroupId = null; var clustered = false; var svg = null; @@ -631,15 +631,15 @@ nf.Canvas = (function () { // get the current group name from the breadcrumb var breadcrumb = processGroupFlow.breadcrumb; - if (breadcrumb.accessPolicy.canRead) { + if (breadcrumb.permissions.canRead) { nf.Canvas.setGroupName(breadcrumb.breadcrumb.name); } else { nf.Canvas.setGroupName(breadcrumb.id); } // update the access policies - accessPolicy = flowResponse.accessPolicy; - + permissions = flowResponse.permissions; + // update the breadcrumbs nf.ng.Bridge.injector.get('breadcrumbsCtrl').resetBreadcrumbs(); nf.ng.Bridge.injector.get('breadcrumbsCtrl').generateBreadcrumbs(breadcrumb); @@ -832,6 +832,7 @@ nf.Canvas = (function () { nf.ConnectionConfiguration.init(); nf.ControllerService.init(); nf.ReportingTask.init(); + nf.PolicyManagement.init(); nf.ProcessorConfiguration.init(); nf.ProcessGroupConfiguration.init(); nf.RemoteProcessGroupConfiguration.init(); @@ -839,7 +840,6 @@ nf.Canvas = (function () { nf.PortConfiguration.init(); nf.LabelConfiguration.init(); nf.ProcessorDetails.init(); - nf.ProcessGroupDetails.init(); nf.PortDetails.init(); nf.ConnectionDetails.init(); nf.RemoteProcessGroupDetails.init(); @@ -925,10 +925,10 @@ nf.Canvas = (function () { * @returns {boolean} can write */ canRead: function () { - if (accessPolicy === null) { + if (permissions === null) { return false; } else { - return accessPolicy.canRead === true; + return permissions.canRead === true; } }, @@ -938,10 +938,10 @@ nf.Canvas = (function () { * @returns {boolean} can write */ canWrite: function () { - if (accessPolicy === null) { + if (permissions === null) { return false; } else { - return accessPolicy.canWrite === true; + return permissions.canWrite === true; } }, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js index b2ca3f82e8b1..0878a543e88c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js @@ -281,7 +281,7 @@ nf.ConnectionConfiguration = (function () { // show the output port options var options = []; $.each(processGroupContents.outputPorts, function (i, outputPort) { - if (outputPort.accessPolicy.canRead && outputPort.accessPolicy.canWrite) { + if (outputPort.permissions.canRead && outputPort.permissions.canWrite) { var component = outputPort.component; options.push({ text: component.name, @@ -506,7 +506,7 @@ nf.ConnectionConfiguration = (function () { // show the input port options var options = []; $.each(processGroupContents.inputPorts, function (i, inputPort) { - if (inputPort.accessPolicy.canRead && inputPort.accessPolicy.canWrite) { + if (inputPort.permissions.canRead && inputPort.permissions.canWrite) { var component = inputPort.component; options.push({ text: component.name, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js index 41f08eaf724f..f559e9615176 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js @@ -308,7 +308,7 @@ nf.Connection = (function () { .classed('grouped', function (d) { var grouped = false; - if (d.accessPolicy.canRead) { + if (d.permissions.canRead) { // if there are more than one selected relationship, mark this as grouped if (nf.Common.isDefinedAndNotNull(d.component.selectedRelationships) && d.component.selectedRelationships.length > 1) { grouped = true; @@ -320,7 +320,7 @@ nf.Connection = (function () { .classed('ghost', function (d) { var ghost = false; - if (d.accessPolicy.canRead) { + if (d.permissions.canRead) { // if the connection has a relationship that is unavailable, mark it a ghost relationship if (hasUnavailableRelationship(d)) { ghost = true; @@ -333,13 +333,13 @@ nf.Connection = (function () { // update connection path updated.select('path.connection-path') .classed('unauthorized', function (d) { - return d.accessPolicy.canRead === false; + return d.permissions.canRead === false; }); // update connection behavior updated.select('path.connection-path-selectable') .on('dblclick', function (d) { - if (d.accessPolicy.canWrite && d.accessPolicy.canRead) { + if (d.permissions.canWrite && d.permissions.canRead) { var position = d3.mouse(this.parentNode); // find where to put this bend point @@ -466,7 +466,7 @@ nf.Connection = (function () { 'marker-end': function () { var marker = 'normal'; - if (d.accessPolicy.canRead) { + if (d.permissions.canRead) { // if the connection has a relationship that is unavailable, mark it a ghost relationship if (hasUnavailableRelationship(d)) { marker = 'ghost'; @@ -501,7 +501,7 @@ nf.Connection = (function () { var endpoints = connection.selectAll('rect.endpoint'); var midpoints = connection.selectAll('rect.midpoint'); - if (d.accessPolicy.canWrite) { + if (d.permissions.canWrite) { // ------------------ // bends - startpoint @@ -695,7 +695,7 @@ nf.Connection = (function () { var backgrounds = []; var borders = []; - if (d.accessPolicy.canRead) { + if (d.permissions.canRead) { // ----------------------- // connection label - from @@ -1087,14 +1087,14 @@ nf.Connection = (function () { return (rowHeight * labelCount); }) .classed('unauthorized', function () { - return d.accessPolicy.canRead === false; + return d.permissions.canRead === false; }); connectionLabelContainer.select('rect.border') .attr('height', function () { return (rowHeight * labelCount); }) .classed('unauthorized', function () { - return d.accessPolicy.canRead === false; + return d.permissions.canRead === false; }); // update the coloring of the backgrounds @@ -1115,7 +1115,7 @@ nf.Connection = (function () { } }); - if (d.accessPolicy.canRead) { + if (d.permissions.canRead) { // determine whether or not to show the expiration icon connectionLabelContainer.select('g.expiration-icon') .classed('hidden', function () { @@ -1126,7 +1126,7 @@ nf.Connection = (function () { }); } - if (d.accessPolicy.canWrite) { + if (d.permissions.canWrite) { // only support dragging the label when appropriate connectionLabelContainer.call(labelDrag); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js index 67b4ed6de915..5e3899a5a0c2 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js @@ -179,7 +179,7 @@ nf.ControllerService = (function () { // reload all dependent processors if they are currently visible $.each(controllerService.referencingComponents, function (_, referencingComponentEntity) { // ensure we can read the referencing component prior to reloading - if (referencingComponentEntity.accessPolicy.canRead === false) { + if (referencingComponentEntity.permissions.canRead === false) { return; } @@ -401,7 +401,7 @@ nf.ControllerService = (function () { var unauthorized = $('
      '); $.each(referencingComponents, function (_, referencingComponentEntity) { // check the access policy for this referencing component - if (referencingComponentEntity.accessPolicy.canRead === false || referencingComponentEntity.accessPolicy.canWrite === false) { + if (referencingComponentEntity.permissions.canRead === false || referencingComponentEntity.permissions.canWrite === false) { var unauthorizedReferencingComponent = $('
      ').text(referencingComponentEntity.id); unauthorized.append(unauthorizedReferencingComponent); } else { @@ -1029,7 +1029,7 @@ nf.ControllerService = (function () { var hasUnauthorized = false; $.each(controllerService.referencingComponents, function (_, referencingComponent) { - if (referencingComponent.accessPolicy.canRead === false || referencingComponent.accessPolicy.canWrite === false) { + if (referencingComponent.permissions.canRead === false || referencingComponent.permissions.canWrite === false) { hasUnauthorized = true; return false; } @@ -1263,7 +1263,7 @@ nf.ControllerService = (function () { var hasUnauthorized = false; $.each(controllerService.referencingComponents, function (_, referencingComponent) { - if (referencingComponent.accessPolicy.canRead === false || referencingComponent.accessPolicy.canWrite === false) { + if (referencingComponent.permissions.canRead === false || referencingComponent.permissions.canWrite === false) { hasUnauthorized = true; return false; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-services.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-services.js index 9a8a5e58995f..665cb1458d6f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-services.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-services.js @@ -415,7 +415,7 @@ nf.ControllerServices = (function () { * @returns {String} */ var nameFormatter = function (row, cell, value, columnDef, dataContext) { - if (!dataContext.accessPolicy.canRead) { + if (!dataContext.permissions.canRead) { return '' + dataContext.id + ''; } @@ -433,7 +433,7 @@ nf.ControllerServices = (function () { * @returns {String} */ var typeFormatter = function (row, cell, value, columnDef, dataContext) { - if (!dataContext.accessPolicy.canRead) { + if (!dataContext.permissions.canRead) { return ''; } @@ -492,24 +492,24 @@ nf.ControllerServices = (function () { var initControllerServices = function (serviceTable) { // more details formatter var moreControllerServiceDetails = function (row, cell, value, columnDef, dataContext) { - if (!dataContext.accessPolicy.canRead) { + if (!dataContext.permissions.canRead) { return ''; } - var markup = '
      '; + var markup = '
      '; // always include a button to view the usage - markup += '
      '; + markup += '
      '; var hasErrors = !nf.Common.isEmpty(dataContext.component.validationErrors); var hasBulletins = !nf.Common.isEmpty(dataContext.bulletins); if (hasErrors) { - markup += '
      '; + markup += '
      '; } if (hasBulletins) { - markup += '
      '; + markup += '
      '; } if (hasErrors || hasBulletins) { @@ -520,7 +520,7 @@ nf.ControllerServices = (function () { }; var controllerServiceStateFormatter = function (row, cell, value, columnDef, dataContext) { - if (!dataContext.accessPolicy.canRead) { + if (!dataContext.permissions.canRead) { return ''; } @@ -553,25 +553,28 @@ nf.ControllerServices = (function () { var controllerServiceActionFormatter = function (row, cell, value, columnDef, dataContext) { var markup = ''; - if (dataContext.accessPolicy.canRead && dataContext.accessPolicy.canWrite) { + if (dataContext.permissions.canRead && dataContext.permissions.canWrite) { if (dataContext.component.state === 'ENABLED' || dataContext.component.state === 'ENABLING') { - markup += '
      '; + markup += '
      '; } else if (dataContext.component.state === 'DISABLED') { - markup += '
      '; + markup += '
      '; // if there are no validation errors allow enabling if (nf.Common.isEmpty(dataContext.component.validationErrors)) { - markup += '
      '; + markup += '
      '; } - markup += '
      '; + markup += '
      '; } if (dataContext.component.persistsState === true) { - markup += '
      '; + markup += '
      '; } } + // TODO - only if we can adminster policies + markup += '
      '; + return markup; }; @@ -629,6 +632,12 @@ nf.ControllerServices = (function () { nf.ControllerService.remove(serviceTable, controllerServiceEntity); } else if (target.hasClass('view-state-controller-service')) { nf.ComponentState.showState(controllerServiceEntity.component, controllerServiceEntity.state === 'DISABLED'); + } else if (target.hasClass('edit-access-policies')) { + // show the policies for this service + nf.PolicyManagement.showControllerServicePolicy(controllerServiceEntity); + + // close the settings dialog + $('#shell-close-button').click(); } } else if (controllerServicesGrid.getColumns()[args.cell].id === 'moreDetails') { if (target.hasClass('view-controller-service')) { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-funnel.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-funnel.js index 46f8795efbbc..efda58c352f3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-funnel.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-funnel.js @@ -128,13 +128,13 @@ nf.Funnel = (function () { // funnel border authorization updated.select('rect.border') .classed('unauthorized', function (d) { - return d.accessPolicy.canRead === false; + return d.permissions.canRead === false; }); // funnel body authorization updated.select('rect.body') .classed('unauthorized', function (d) { - return d.accessPolicy.canRead === false; + return d.permissions.canRead === false; }); updated.each(function () { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label.js index 94c7b0c0bbc2..dbcb5a0de519 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label.js @@ -129,7 +129,7 @@ nf.Label = (function () { } }) .classed('unauthorized', function (d) { - return d.accessPolicy.canRead === false; + return d.permissions.canRead === false; }); // update the body fill using the configured color @@ -143,7 +143,7 @@ nf.Label = (function () { } }) .style('fill', function (d) { - if (!d.accessPolicy.canRead) { + if (!d.permissions.canRead) { return null; } @@ -157,7 +157,7 @@ nf.Label = (function () { return color; }) .classed('unauthorized', function (d) { - return d.accessPolicy.canRead === false; + return d.permissions.canRead === false; }); // go through each label being updated @@ -170,7 +170,7 @@ nf.Label = (function () { // update the label var labelText = label.select('text.label-value'); var labelPoint = label.selectAll('rect.labelpoint'); - if (d.accessPolicy.canRead) { + if (d.permissions.canRead) { // udpate the font size labelText.attr('font-size', function () { var fontSize = '12px'; @@ -209,7 +209,7 @@ nf.Label = (function () { // labelpoints // ----------- - if (d.accessPolicy.canWrite) { + if (d.permissions.canWrite) { var pointData = [ {x: d.dimensions.width, y: d.dimensions.height} ]; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-policy-management.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-policy-management.js new file mode 100644 index 000000000000..652290aa67ed --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-policy-management.js @@ -0,0 +1,1027 @@ +/* + * 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. + */ + +/* global nf, d3 */ + +nf.PolicyManagement = (function () { + + var config = { + urls: { + api: '../nifi-api', + searchTenants: '../nifi-api/tenants/search-results' + } + }; + + var initialized = false; + + var initAddTenantToPolicyDialog = function () { + $('#new-policy-user-button').on('click', function () { + $('#search-users-dialog').modal('show'); + }); + + $('#delete-policy-button').on('click', function () { + promptToDeletePolicy(); + }); + + $('#search-users-dialog').modal({ + scrollableContentStyle: 'scrollable', + headerText: 'Search users', + buttons: [{ + buttonText: 'Ok', + color: { + base: '#728E9B', + hover: '#004849', + text: '#ffffff' + }, + handler: { + click: function () { + var tenantSearchTerm = $('#search-users-field').val(); + + // create the search request + $.ajax({ + type: 'GET', + data: { + q: tenantSearchTerm + }, + dataType: 'json', + url: config.urls.searchTenants + }).done(function (response) { + var selectedUsers = $.map(response.users, function (user) { + return $.extend({ + type: 'user' + }, user); + }); + var selectedGroups = $.map(response.userGroups, function (userGroup) { + return $.extend({ + type: 'group' + }, userGroup); + }); + + var selectedUser = []; + selectedUser = selectedUser.concat(selectedUsers, selectedGroups); + + // ensure the search found some results + if (!$.isArray(selectedUser) || selectedUser.length === 0) { + nf.Dialog.showOkDialog({ + headerText: 'User Search', + dialogContent: 'No users match \'' + nf.Common.escapeHtml(tenantSearchTerm) + '\'.' + }); + } else if (selectedUser.length > 1) { + nf.Dialog.showOkDialog({ + headerText: 'User Search', + dialogContent: 'More than one user matches \'' + nf.Common.escapeHtml(tenantSearchTerm) + '\'.' + }); + } else if (selectedUser.length === 1) { + var user = selectedUser[0]; + + // add to table and update policy + var policyGrid = $('#policy-table').data('gridInstance'); + var policyData = policyGrid.getData(); + + // begin the update + policyData.beginUpdate(); + + // remove the user + policyData.addItem(user); + + // end the update + policyData.endUpdate(); + + // update the policy + updatePolicy(); + + // close the dialog + $('#search-users-dialog').modal('hide'); + } + }); + } + } + }, + { + buttonText: 'Cancel', + color: { + base: '#E3E8EB', + hover: '#C7D2D7', + text: '#004849' + }, + handler: { + click: function () { + // close the dialog + $('#search-users-dialog').modal('hide'); + } + } + }], + handler: { + close: function () { + // reset the search fields + $('#search-users-field').val(''); + $('#selected-user-id').text(''); + } + } + }); + + // configure the user auto complete + $.widget('nf.userSearchAutocomplete', $.ui.autocomplete, { + _normalize: function (searchResults) { + var items = []; + items.push(searchResults); + return items; + }, + _renderMenu: function (ul, items) { + // results are normalized into a single element array + var searchResults = items[0]; + + var self = this; + $.each(searchResults.userGroups, function (_, tenant) { + self._renderGroup(ul, { + label: tenant.component.identity, + value: tenant.component.identity + }); + }); + $.each(searchResults.users, function (_, tenant) { + self._renderUser(ul, { + label: tenant.component.identity, + value: tenant.component.identity + }); + }); + + // ensure there were some results + if (ul.children().length === 0) { + ul.append('
    • No users matched the search terms
    • '); + } + }, + _resizeMenu: function () { + var ul = this.menu.element; + ul.width($('#search-users-field').width() + 6); + }, + _renderUser: function (ul, match) { + var userContent = $('').text(match.label); + return $('
    • ').data('ui-autocomplete-item', match).append(userContent).appendTo(ul); + }, + _renderGroup: function (ul, match) { + var groupLabel = $('').text(match.label); + var groupContent = $('').append('
      ').append(groupLabel); + return $('
    • ').data('ui-autocomplete-item', match).append(groupContent).appendTo(ul); + } + }); + + // configure the autocomplete field + $('#search-users-field').userSearchAutocomplete({ + minLength: 0, + appendTo: '#search-users-results', + position: { + my: 'left top', + at: 'left bottom', + offset: '0 1' + }, + source: function (request, response) { + // create the search request + $.ajax({ + type: 'GET', + data: { + q: request.term + }, + dataType: 'json', + url: config.urls.searchTenants + }).done(function (searchResponse) { + response(searchResponse); + }); + } + }); + }; + + var initPolicyTable = function () { + // create/override a policy + $('#create-policy-link, #override-policy-link').on('click', function () { + createPolicy(); + }); + + // policy type listing + $('#policy-type-list').combo({ + options: [{ + text: 'access the user interface', + value: 'flow' + }, { + text: 'access the controller', + value: 'controller' + }, { + text: 'query provenance', + value: 'provenance' + }, { + text: 'access all policies', + value: 'policies' + }, { + text: 'retrieve site-to-site details', + value: 'site-to-site' + }, { + text: 'view system diagnostics', + value: 'system' + }, { + text: 'proxy user requests', + value: 'proxy' + }, { + text: 'access counters', + value: 'counters' + }], + select: function (option) { + if (initialized) { + // record the policy type + $('#selected-policy-type').text(option.value); + + // if the option is for a specific component + if (option.value === 'controller' || option.value === 'counters' || option.value === 'policies') { + // update the policy target and let it relaod the policy + $('#controller-policy-target').combo('setSelectedOption', { + 'value': 'read' + }).show(); + } else { + $('#controller-policy-target').hide(); + + // record the action + if (option.value === 'proxy') { + $('#selected-policy-action').text('write'); + } else { + $('#selected-policy-action').text('read'); + } + + // reload the policy + loadPolicy(); + } + } + } + }); + + // controller policy target + $('#controller-policy-target').combo({ + options: [{ + text: 'view', + value: 'read' + }, { + text: 'modify', + value: 'write' + }], + select: function (option) { + if (initialized) { + // record the policy action + $('#selected-policy-action').text(option.value); + + // reload the policy + loadPolicy(); + } + } + }); + + // component policy target + $('#component-policy-target').combo({ + options: [{ + text: 'view the component', + value: 'read-component' + }, { + text: 'modify the component', + value: 'write-component' + }, { + text: 'view the provenance events', + value: 'read-provenance-events' + }, { + text: 'view the policies', + value: 'read-policies' + }, { + text: 'modify the policies', + value: 'write-policies' + }, { + text: 'receive data via site-to-site', + value: 'write-receive-data', + disabled: true + }, { + text: 'send data via site-to-site', + value: 'write-send-data', + disabled: true + }], + select: function (option) { + if (initialized) { + var resource = $('#selected-policy-component-type').text(); + + if (option.value === 'read-component') { + $('#selected-policy-action').text('read'); + } else if (option.value === 'write-component') { + $('#selected-policy-action').text('write'); + } else if (option.value === 'read-provenance-events') { + $('#selected-policy-action').text('read'); + resource = ('provenance-events/' + resource); + } else if (option.value === 'read-policies') { + $('#selected-policy-action').text('read'); + resource = ('policies/' + resource); + } else if (option.value === 'write-policies') { + $('#selected-policy-action').text('write'); + resource = ('policies/' + resource); + } else if (option.value === 'write-receive-data') { + $('#selected-policy-action').text('write'); + resource = 'data-transfer/input-ports'; + } else if (option.value === 'write-send-data') { + $('#selected-policy-action').text('write'); + resource = 'data-transfer/output-ports'; + } + + // set the resource + $('#selected-policy-type').text(resource); + + // reload the policy + loadPolicy(); + } + } + }); + + // function for formatting the user identity + var identityFormatter = function (row, cell, value, columnDef, dataContext) { + var markup = ''; + if (dataContext.type === 'group') { + markup += '
      '; + } + + markup += dataContext.component.identity; + + return markup; + }; + + // function for formatting the actions column + var actionFormatter = function (row, cell, value, columnDef, dataContext) { + var markup = ''; + + markup += '
      '; + + return markup; + }; + + // initialize the templates table + var usersColumns = [ + {id: 'identity', name: 'User', sortable: true, resizable: true, formatter: identityFormatter}, + {id: 'actions', name: ' ', sortable: false, resizable: false, formatter: actionFormatter, width: 100, maxWidth: 100} + ]; + var usersOptions = { + forceFitColumns: true, + enableTextSelectionOnCells: true, + enableCellNavigation: true, + enableColumnReorder: false, + autoEdit: false + }; + + // initialize the dataview + var policyData = new Slick.Data.DataView({ + inlineFilters: false + }); + policyData.setItems([]); + + // initialize the sort + sort({ + columnId: 'identity', + sortAsc: true + }, policyData); + + // initialize the grid + var policyGrid = new Slick.Grid('#policy-table', policyData, usersColumns, usersOptions); + policyGrid.setSelectionModel(new Slick.RowSelectionModel()); + policyGrid.registerPlugin(new Slick.AutoTooltips()); + policyGrid.setSortColumn('identity', true); + policyGrid.onSort.subscribe(function (e, args) { + sort({ + columnId: args.sortCol.id, + sortAsc: args.sortAsc + }, policyData); + }); + + // configure a click listener + policyGrid.onClick.subscribe(function (e, args) { + var target = $(e.target); + + // get the node at this row + var item = policyData.getItem(args.row); + + // determine the desired action + if (policyGrid.getColumns()[args.cell].id === 'actions') { + if (target.hasClass('delete-user')) { + promptToRemoveUserFromPolicy(item); + } + } + }); + + // wire up the dataview to the grid + policyData.onRowCountChanged.subscribe(function (e, args) { + policyGrid.updateRowCount(); + policyGrid.render(); + + // update the total number of displayed policy users + $('#displayed-policy-users').text(args.current); + }); + policyData.onRowsChanged.subscribe(function (e, args) { + policyGrid.invalidateRows(args.rows); + policyGrid.render(); + }); + + // hold onto an instance of the grid + $('#policy-table').data('gridInstance', policyGrid); + + // initialize the number of displayed items + $('#displayed-policy-users').text('0'); + }; + + /** + * Sorts the specified data using the specified sort details. + * + * @param {object} sortDetails + * @param {object} data + */ + var sort = function (sortDetails, data) { + // defines a function for sorting + var comparer = function (a, b) { + var aString = nf.Common.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : ''; + var bString = nf.Common.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : ''; + return aString === bString ? 0 : aString > bString ? 1 : -1; + }; + + // perform the sort + data.sort(comparer, sortDetails.sortAsc); + }; + + /** + * Prompts for the removal of the specified user. + * + * @param item + */ + var promptToRemoveUserFromPolicy = function (item) { + nf.Dialog.showYesNoDialog({ + headerText: 'Update Policy', + dialogContent: 'Remove \'' + nf.Common.escapeHtml(item.component.identity) + '\' from this policy?', + yesHandler: function () { + removeUserFromPolicy(item); + } + }); + }; + + /** + * Removes the specified item from the current policy. + * + * @param item + */ + var removeUserFromPolicy = function (item) { + var policyGrid = $('#policy-table').data('gridInstance'); + var policyData = policyGrid.getData(); + + // begin the update + policyData.beginUpdate(); + + // remove the user + policyData.deleteItem(item.id); + + // end the update + policyData.endUpdate(); + + // save the configuration + updatePolicy(); + }; + + /** + * Prompts for the deletion of the selected policy. + */ + var promptToDeletePolicy = function () { + nf.Dialog.showYesNoDialog({ + headerText: 'Update Policy', + dialogContent: 'Are you sure you want to delete this policy?', + yesHandler: function () { + deletePolicy(); + } + }); + }; + + /** + * Deletes the current policy. + */ + var deletePolicy = function () { + var currentEntity = $('#policy-table').data('policy'); + + if (nf.Common.isDefinedAndNotNull(currentEntity)) { + $.ajax({ + type: 'DELETE', + url: currentEntity.component.uri + '?' + $.param(nf.Client.getRevision(currentEntity)), + dataType: 'json' + }).done(function () { + loadPolicy(); + }).fail(nf.Common.handleAjaxError); + } else { + nf.Dialog.showOkDialog({ + headerText: 'Update Policy', + dialogContent: 'No policy selected' + }); + } + }; + + /** + * Gets the currently selected resource. + */ + var getSelectedResourceAndAction = function () { + var componentId = $('#selected-policy-component-id').text(); + var resource = $('#selected-policy-type').text(); + if (componentId !== '') { + resource += ('/' + componentId); + } + + return { + 'action': $('#selected-policy-action').text(), + 'resource': '/' + resource + }; + }; + + /** + * Populates the table with the specified users and groups. + * + * @param users + * @param userGroups + */ + var populateTable = function (users, userGroups) { + var policyGrid = $('#policy-table').data('gridInstance'); + var policyData = policyGrid.getData(); + + // begin the update + policyData.beginUpdate(); + + var policyUsers = []; + + // add each user + $.each(users, function (_, user) { + policyUsers.push($.extend({ + type: 'user' + }, user)); + }); + + // add each group + $.each(userGroups, function (_, group) { + policyUsers.push($.extend({ + type: 'group' + }, group)); + }); + + // set the rows + policyData.setItems(policyUsers); + + // end the update + policyData.endUpdate(); + + // re-sort and clear selection after updating + policyData.reSort(); + policyGrid.getSelectionModel().setSelectedRows([]); + }; + + /** + * Converts the specified resource into human readable form. + * + * @param resource + */ + var convertToHumanReadableResource = function (resource) { + if (resource === '/policies') { + return 'all policies'; + } else if (resource === '/controller') { + return 'the controller'; + } else { + return 'Process Group ' + nf.Common.substringAfterLast(resource, '/'); + } + }; + + /** + * Populates the specified policy. + * + * @param policyEntity + */ + var populatePolicy = function (policyEntity) { + var policy = policyEntity.component; + + // get the currently selected policy + var resourceAndAction = getSelectedResourceAndAction(); + + // reset of the policy message + resetPolicyMessage(); + + // store the current policy version + $('#policy-table').data('policy', policyEntity); + + // allow removal and modification as the policy is not inherited + $('#new-policy-user-button').prop('disabled', false); + + // update the refresh timestamp + $('#policy-last-refreshed').text(policyEntity.generated); + + // see if the policy is for this resource + if (resourceAndAction.resource === policy.resource) { + // allow remove when policy is not inherited + $('#delete-policy-button').prop('disabled', false); + } else { + $('#policy-message').text('Showing effective policy inherited from ' + convertToHumanReadableResource(policy.resource) + '. '); + $('#new-policy-message').hide(); + $('#override-policy-message').show(); + + // require non inherited policy for removal + $('#delete-policy-button').prop('disabled', true); + } + + // populate the table + populateTable(policy.users, policy.userGroups); + }; + + /** + * Loads the configuration for the specified process group. + */ + var loadPolicy = function () { + var resourceAndAction = getSelectedResourceAndAction(); + + return $.Deferred(function (deferred) { + $.ajax({ + type: 'GET', + url: '../nifi-api/policies/' + resourceAndAction.action + resourceAndAction.resource, + dataType: 'json' + }).done(function (policyEntity) { + var policy = policyEntity.component; + + $('#policy-message').text(policy.resource); + + // populate the policy details + populatePolicy(policyEntity); + + deferred.resolve(); + }).fail(function (xhr, status, error) { + if (xhr.status === 404) { + // show an appropriate messate + $('#policy-message').text('No policy for the specified resource. '); + $('#new-policy-message').show(); + $('#override-policy-message').hide(); + + // reset the current policy + $('#policy-table').removeData('policy'); + + // require non inherited policy for removal and modification + $('#new-policy-user-button').prop('disabled', true); + $('#delete-policy-button').prop('disabled', true); + + // populate the table with no users + populateTable([], []); + + deferred.resolve(); + } else { + deferred.reject(); + nf.Common.handleAjaxError(xhr, status, error); + } + }); + }).promise(); + }; + + /** + * Creates a new policy for the current selection. + */ + var createPolicy = function () { + var resourceAndAction = getSelectedResourceAndAction(); + + var entity = { + 'revision': nf.Client.getRevision({ + 'revision': { + 'version': 0 + } + }), + 'component': { + 'action': resourceAndAction.action, + 'resource': resourceAndAction.resource + } + }; + + $.ajax({ + type: 'POST', + url: '../nifi-api/policies', + data: JSON.stringify(entity), + dataType: 'json', + contentType: 'application/json' + }).done(function (policyEntity) { + populatePolicy(policyEntity); + }).fail(nf.Common.handleAjaxError); + }; + + /** + * Updates the policy for the current selection. + */ + var updatePolicy = function () { + var policyGrid = $('#policy-table').data('gridInstance'); + var policyData = policyGrid.getData(); + + var users = []; + var userGroups = []; + + var items = policyData.getItems(); + $.each(items, function (_, item) { + if (item.type === 'user') { + users.push(item); + } else { + userGroups.push(item); + } + + // remove the type as it was added client side to render differently + delete item.type; + }); + + var currentEntity = $('#policy-table').data('policy'); + if (nf.Common.isDefinedAndNotNull(currentEntity)) { + var entity = { + 'revision': nf.Client.getRevision(currentEntity), + 'component': { + 'id': currentEntity.id, + 'users': users, + 'userGroups': userGroups + } + }; + + $.ajax({ + type: 'PUT', + url: currentEntity.component.uri, + data: JSON.stringify(entity), + dataType: 'json', + contentType: 'application/json' + }).done(function (policyEntity) { + populatePolicy(policyEntity); + }).fail(nf.Common.handleAjaxError); + } else { + nf.Dialog.showOkDialog({ + headerText: 'Update Policy', + dialogContent: 'No policy selected' + }); + } + }; + + /** + * Shows the process group configuration. + */ + var showPolicy = function () { + // show the configuration dialog + nf.Shell.showContent('#policy-management').done(function () { + reset(); + }); + + // adjust the table size + nf.PolicyManagement.resetTableSize(); + }; + + /** + * Reset the policy message. + */ + var resetPolicyMessage = function () { + $('#policy-message').text(''); + $('#new-policy-message').hide(); + $('#override-policy-message').hide(); + }; + + /** + * Resets the policy management dialog. + */ + var reset = function () { + resetPolicyMessage(); + + // clear the selected policy details + $('#selected-policy-type').text(''); + $('#selected-policy-action').text(''); + $('#selected-policy-component-id').text(''); + $('#selected-policy-component-type').text(''); + + // clear the selected component details + $('div.policy-selected-component-container').hide(); + + // reset button state + $('#delete-policy-button').prop('disabled', false); + $('#new-policy-user-button').prop('disabled', false); + }; + + /** + * Populates the initial policy resource. + * + * @param resource + */ + var populateComponentResource = function (resource) { + // record the initial resource type + $('#selected-policy-component-type').text(resource); + + var policyTarget = $('#component-policy-target').combo('getSelectedOption').value; + if (policyTarget === 'read-component') { + $('#selected-policy-action').text('read'); + } else if (policyTarget === 'write-component') { + $('#selected-policy-action').text('write'); + } else if (policyTarget === 'read-provenance-events') { + $('#selected-policy-action').text('read'); + resource = ('provenance-events/' + resource); + } else if (policyTarget === 'read-policies') { + $('#selected-policy-action').text('read'); + resource = ('policies/' + resource); + } else if (policyTarget === 'write-policies') { + $('#selected-policy-action').text('write'); + resource = ('policies/' + resource); + } + + // update the policy type + $('#selected-policy-type').text(resource); + }; + + return { + /** + * Initializes the settings page. + */ + init: function () { + initAddTenantToPolicyDialog(); + initPolicyTable(); + + // mark as initialized + initialized = true; + }, + + /** + * Update the size of the grid based on its container's current size. + */ + resetTableSize: function () { + var policyTable = $('#policy-table'); + if (policyTable.is(':visible')) { + var policyGrid = policyTable.data('gridInstance'); + if (nf.Common.isDefinedAndNotNull(policyGrid)) { + policyGrid.resizeCanvas(); + } + } + }, + + /** + * Shows the controller service policy. + * + * @param d + */ + showControllerServicePolicy: function (d) { + // reset the policy message + resetPolicyMessage(); + + // update the policy controls visibility + $('#component-policy-controls').show(); + $('#global-policy-controls').hide(); + + // update the visibility + if (d.permissions.canRead) { + $('#policy-selected-controller-service-container div.policy-selected-component-name').text(d.component.name); + } else { + $('#policy-selected-controller-service-container div.policy-selected-component-name').text(d.id); + } + $('#policy-selected-controller-service-container').show(); + + // populate the initial resource + $('#selected-policy-component-id').text(d.id); + populateComponentResource('controller-services'); + + return loadPolicy().done(showPolicy); + }, + + /** + * Shows the reporting task policy. + * + * @param d + */ + showReportingTaskPolicy: function (d) { + // reset the policy message + resetPolicyMessage(); + + // update the policy controls visibility + $('#component-policy-controls').show(); + $('#global-policy-controls').hide(); + + // update the visibility + if (d.permissions.canRead) { + $('#policy-selected-reporting-task-container div.policy-selected-component-name').text(d.component.name); + } else { + $('#policy-selected-reporting-task-container div.policy-selected-component-name').text(d.id); + } + $('#policy-selected-reporting-task-container').show(); + + // populate the initial resource + $('#selected-policy-component-id').text(d.id); + populateComponentResource('reporting-tasks'); + + return loadPolicy().done(showPolicy); + }, + + /** + * Shows the template policy. + * + * @param d + */ + showTemplatePolicy: function (d) { + // reset the policy message + resetPolicyMessage(); + + // update the policy controls visibility + $('#component-policy-controls').show(); + $('#global-policy-controls').hide(); + + // update the visibility + if (d.permissions.canRead) { + $('#policy-selected-template-container div.policy-selected-component-name').text(d.template.name); + } else { + $('#policy-selected-template-container div.policy-selected-component-name').text(d.id); + } + $('#policy-selected-template-container').show(); + + // populate the initial resource + $('#selected-policy-component-id').text(d.id); + populateComponentResource('templates'); + + return loadPolicy().done(showPolicy); + }, + + /** + * Shows the component policy dialog. + */ + showComponentPolicy: function (selection) { + // reset the policy message + resetPolicyMessage(); + + // update the policy controls visibility + $('#component-policy-controls').show(); + $('#global-policy-controls').hide(); + + // update the visibility + $('#policy-selected-component-container').show(); + + var resource; + if (selection.empty()) { + $('#selected-policy-component-id').text(nf.Canvas.getGroupId()); + resource = 'process-groups'; + } else { + var d = selection.datum(); + $('#selected-policy-component-id').text(d.id); + + if (nf.CanvasUtils.isProcessor(selection)) { + resource = 'processors'; + } else if (nf.CanvasUtils.isConnection(selection)) { + resource = 'connections'; + } else if (nf.CanvasUtils.isProcessGroup(selection)) { + resource = 'process-groups'; + } else if (nf.CanvasUtils.isInputPort(selection)) { + resource = 'input-ports'; + } else if (nf.CanvasUtils.isOutputPort(selection)) { + resource = 'output-ports'; + } else if (nf.CanvasUtils.isRemoteProcessGroup(selection)) { + resource = 'remote-process-groups'; + } else if (nf.CanvasUtils.isLabel(selection)) { + resource = 'labels'; + } + + // enable site to site option + $('#component-policy-target') + .combo('setOptionEnabled', { + value: 'write-receive-data' + }, nf.CanvasUtils.isInputPort(selection) && nf.Canvas.getParentGroupId() === null) + .combo('setOptionEnabled', { + value: 'write-send-data' + }, nf.CanvasUtils.isOutputPort(selection) && nf.Canvas.getParentGroupId() === null); + } + + // populate the initial resource + populateComponentResource(resource); + + return loadPolicy().done(showPolicy); + }, + + /** + * Shows the global policies dialog. + */ + showGlobalPolicies: function () { + // reset the policy message + resetPolicyMessage(); + + // update the policy controls visibility + $('#component-policy-controls').hide(); + $('#global-policy-controls').show(); + + // reload the current policies + var policyType = $('#policy-type-list').combo('getSelectedOption').value; + $('#selected-policy-type').text(policyType); + + if (policyType === 'controller') { + $('#selected-policy-action').text($('#controller-policy-target').combo('getSelectedOption').value); + } else if (policyType === 'proxy') { + $('#selected-policy-action').text('write'); + } else { + $('#selected-policy-action').text('read'); + } + + return loadPolicy().done(showPolicy); + }, + }; +}()); \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js index 38ccbca015f3..ec08e5059f3a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js @@ -159,7 +159,7 @@ nf.Port = (function () { // only activate dragging and connecting if appropriate port.filter(function (d) { - return d.accessPolicy.canWrite && d.accessPolicy.canRead; + return d.permissions.canWrite && d.permissions.canRead; }).call(nf.Draggable.activate).call(nf.Connectable.activate); }; @@ -176,13 +176,13 @@ nf.Port = (function () { // port border authorization updated.select('rect.border') .classed('unauthorized', function (d) { - return d.accessPolicy.canRead === false; + return d.permissions.canRead === false; }); // port body authorization updated.select('rect.body') .classed('unauthorized', function (d) { - return d.accessPolicy.canRead === false; + return d.permissions.canRead === false; }); updated.each(function (portData) { @@ -262,7 +262,7 @@ nf.Port = (function () { }); } - if (portData.accessPolicy.canRead) { + if (portData.permissions.canRead) { // update the port name port.select('text.port-name') .each(function (d) { @@ -291,7 +291,7 @@ nf.Port = (function () { // populate the stats port.call(updatePortStatus); } else { - if (portData.accessPolicy.canRead) { + if (portData.permissions.canRead) { // update the port name port.select('text.port-name') .text(function (d) { @@ -364,7 +364,7 @@ nf.Port = (function () { } // if there are validation errors generate a tooltip - if (d.accessPolicy.canRead && !nf.Common.isEmpty(d.component.validationErrors)) { + if (d.permissions.canRead && !nf.Common.isEmpty(d.component.validationErrors)) { tip = d3.select('#port-tooltips').append('div') .attr('id', function () { return 'run-status-tip-' + d.id; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-configuration.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-configuration.js index 26d253dc3a81..d27a46af8930 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-configuration.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-configuration.js @@ -74,7 +74,7 @@ nf.ProcessGroupConfiguration = (function () { contentType: 'application/json' }).done(function (response) { // refresh the process group if necessary - if (response.accessPolicy.canRead && response.component.parentGroupId === nf.Canvas.getGroupId()) { + if (response.permissions.canRead && response.component.parentGroupId === nf.Canvas.getGroupId()) { nf.ProcessGroup.set(response); } @@ -122,7 +122,7 @@ nf.ProcessGroupConfiguration = (function () { url: config.urls.api + '/process-groups/' + encodeURIComponent(groupId), dataType: 'json' }).done(function (response) { - if (response.accessPolicy.canWrite) { + if (response.permissions.canWrite) { var processGroup = response.component; // populate the process group settings @@ -137,7 +137,7 @@ nf.ProcessGroupConfiguration = (function () { saveConfiguration(response.revision.version, response.id); }); } else { - if (response.accessPolicy.canRead) { + if (response.permissions.canRead) { // populate the process group settings $('#read-only-process-group-name').removeClass('unset').text(response.component.name); $('#read-only-process-group-comments').removeClass('unset').text(response.component.comments); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-details.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-details.js deleted file mode 100644 index 019d3aa9658f..000000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-details.js +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* global nf */ - -nf.ProcessGroupDetails = (function () { - - return { - init: function () { - // configure the processor details dialog - $('#process-group-details').modal({ - scrollableContentStyle: 'scrollable', - headerText: 'Process Group Details', - buttons: [{ - buttonText: 'Ok', - color: { - base: '#728E9B', - hover: '#004849', - text: '#ffffff' - }, - handler: { - click: function () { - // hide the dialog - $('#process-group-details').modal('hide'); - } - } - }], - handler: { - close: function () { - // clear the processor details - nf.Common.clearField('read-only-process-group-id'); - nf.Common.clearField('read-only-process-group-name'); - nf.Common.clearField('read-only-process-group-comments'); - } - } - }); - }, - - showDetails: function (selection) { - // if the specified selection is a process group - if (nf.CanvasUtils.isProcessGroup(selection)) { - var selectionData = selection.datum(); - - // populate the port settings - nf.Common.populateField('read-only-process-group-id', selectionData.id); - nf.Common.populateField('read-only-process-group-name', selectionData.component.name); - nf.Common.populateField('read-only-process-group-comments', selectionData.component.comments); - - // show the details - $('#process-group-details').modal('show'); - } - } - }; -}()); \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js index d4dbe37654d3..fc96c012b8ef 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js @@ -157,7 +157,7 @@ nf.ProcessGroup = (function () { // only support dragging, connection, and drag and drop if appropriate processGroup.filter(function (d) { - return d.accessPolicy.canWrite && d.accessPolicy.canRead; + return d.permissions.canWrite && d.permissions.canRead; }) .on('mouseover.drop', function (d) { // Using mouseover/out to workaround chrome issue #122746 @@ -210,13 +210,13 @@ nf.ProcessGroup = (function () { // process group border authorization updated.select('rect.border') .classed('unauthorized', function (d) { - return d.accessPolicy.canRead === false; + return d.permissions.canRead === false; }); // process group body authorization updated.select('rect.body') .classed('unauthorized', function (d) { - return d.accessPolicy.canRead === false; + return d.permissions.canRead === false; }); updated.each(function (processGroupData) { @@ -742,7 +742,7 @@ nf.ProcessGroup = (function () { return stoppedX + stoppedCount.node().getComputedTextLength() + CONTENTS_SPACER; }) .classed('has-validation-errors', function (d) { - return d.accessPolicy.canRead && d.component.invalidCount > 0; + return d.permissions.canRead && d.component.invalidCount > 0; }); var invalidCount = details.select('text.process-group-invalid-count') .attr('x', function () { @@ -768,7 +768,7 @@ nf.ProcessGroup = (function () { return d.disabledCount; }); - if (processGroupData.accessPolicy.canRead) { + if (processGroupData.permissions.canRead) { // update the process group comments details.select('text.process-group-comments') .each(function (d) { @@ -812,7 +812,7 @@ nf.ProcessGroup = (function () { // populate the stats processGroup.call(updateProcessGroupStatus); } else { - if (processGroupData.accessPolicy.canRead) { + if (processGroupData.permissions.canRead) { // update the process group name processGroup.select('text.process-group-name') .text(function (d) { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js index a1602df23a4f..86c5228e5d68 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js @@ -374,7 +374,7 @@ nf.ProcessorConfiguration = (function () { var reloadProcessorConnections = function (processor) { var connections = nf.Connection.getComponentConnections(processor.id); $.each(connections, function (_, connection) { - if (connection.accessPolicy.canRead) { + if (connection.permissions.canRead) { if (connection.sourceId === processor.id) { nf.Connection.reload(connection.component); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js index d553b576b688..1f133ce073db 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js @@ -143,13 +143,13 @@ nf.Processor = (function () { // processor border authorization updated.select('rect.border') .classed('unauthorized', function (d) { - return d.accessPolicy.canRead === false; + return d.permissions.canRead === false; }); // processor body authorization updated.select('rect.body') .classed('unauthorized', function (d) { - return d.accessPolicy.canRead === false; + return d.permissions.canRead === false; }); updated.each(function (processorData) { @@ -474,7 +474,7 @@ nf.Processor = (function () { .text('\uf24a'); } - if (processorData.accessPolicy.canRead) { + if (processorData.permissions.canRead) { // update the processor name processor.select('text.processor-name') .each(function (d) { @@ -516,7 +516,7 @@ nf.Processor = (function () { // populate the stats processor.call(updateProcessorStatus); } else { - if (processorData.accessPolicy.canRead) { + if (processorData.permissions.canRead) { // update the processor name processor.select('text.processor-name') .text(function (d) { @@ -553,7 +553,7 @@ nf.Processor = (function () { // get the default color var color = nf.Processor.defaultColor(); - if (!d.accessPolicy.canRead) { + if (!d.permissions.canRead) { return color; } @@ -615,7 +615,7 @@ nf.Processor = (function () { } // if there are validation errors generate a tooltip - if (d.accessPolicy.canRead && !nf.Common.isEmpty(d.component.validationErrors)) { + if (d.permissions.canRead && !nf.Common.isEmpty(d.component.validationErrors)) { tip = d3.select('#processor-tooltips').append('div') .attr('id', function () { return 'run-status-tip-' + d.id; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js index ceaddc277c1b..b0537614f9a6 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js @@ -168,13 +168,13 @@ nf.RemoteProcessGroup = (function () { // remote process group border authorization updated.select('rect.border') .classed('unauthorized', function (d) { - return d.accessPolicy.canRead === false; + return d.permissions.canRead === false; }); // remote process group body authorization updated.select('rect.body') .classed('unauthorized', function (d) { - return d.accessPolicy.canRead === false; + return d.permissions.canRead === false; }); updated.each(function (remoteProcessGroupData) { @@ -493,7 +493,7 @@ nf.RemoteProcessGroup = (function () { .text('\uf24a'); } - if (remoteProcessGroupData.accessPolicy.canRead) { + if (remoteProcessGroupData.permissions.canRead) { // remote process group uri details.select('text.remote-process-group-uri') .each(function (d) { @@ -612,7 +612,7 @@ nf.RemoteProcessGroup = (function () { // populate the stats remoteProcessGroup.call(updateProcessGroupStatus); } else { - if (remoteProcessGroupData.accessPolicy.canRead) { + if (remoteProcessGroupData.permissions.canRead) { // update the process group name remoteProcessGroup.select('text.remote-process-group-name') .text(function (d) { @@ -694,7 +694,7 @@ nf.RemoteProcessGroup = (function () { updated.select('text.remote-process-group-transmission-status') .text(function (d) { var icon = ''; - if (d.accessPolicy.canRead) { + if (d.permissions.canRead) { if (!nf.Common.isEmpty(d.component.authorizationIssues)) { icon = '\uf071'; } else if (d.component.transmitting === true) { @@ -707,7 +707,7 @@ nf.RemoteProcessGroup = (function () { }) .attr('font-family', function (d) { var family = ''; - if (d.accessPolicy.canRead) { + if (d.permissions.canRead) { if (!nf.Common.isEmpty(d.component.authorizationIssues) || d.component.transmitting) { family = 'FontAwesome'; } else { @@ -717,7 +717,7 @@ nf.RemoteProcessGroup = (function () { return family; }) .classed('has-authorization-errors', function (d) { - return d.accessPolicy.canRead && !nf.Common.isEmpty(d.component.authorizationIssues); + return d.permissions.canRead && !nf.Common.isEmpty(d.component.authorizationIssues); }) .each(function (d) { // remove the existing tip if necessary @@ -727,7 +727,7 @@ nf.RemoteProcessGroup = (function () { } // if there are validation errors generate a tooltip - if (d.accessPolicy.canRead && !nf.Common.isEmpty(d.component.authorizationIssues)) { + if (d.permissions.canRead && !nf.Common.isEmpty(d.component.authorizationIssues)) { tip = d3.select('#remote-process-group-tooltips').append('div') .attr('id', function () { return 'authorization-issues-' + d.id; @@ -944,7 +944,7 @@ nf.RemoteProcessGroup = (function () { // reload the group's connections var connections = nf.Connection.getComponentConnections(remoteProcessGroup.id); $.each(connections, function (_, connection) { - if (connection.accessPolicy.canRead) { + if (connection.permissions.canRead) { nf.Connection.reload(connection.component); } }); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js index 5c0b3038c3d0..7ba7213dc68c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js @@ -171,7 +171,7 @@ nf.Settings = (function () { * @returns {String} */ var nameFormatter = function (row, cell, value, columnDef, dataContext) { - if (!dataContext.accessPolicy.canRead) { + if (!dataContext.permissions.canRead) { return '' + dataContext.id + ''; } @@ -189,7 +189,7 @@ nf.Settings = (function () { * @returns {String} */ var typeFormatter = function (row, cell, value, columnDef, dataContext) { - if (!dataContext.accessPolicy.canRead) { + if (!dataContext.permissions.canRead) { return ''; } @@ -589,7 +589,7 @@ nf.Settings = (function () { initNewReportingTaskDialog(); var moreReportingTaskDetails = function (row, cell, value, columnDef, dataContext) { - if (!dataContext.accessPolicy.canRead) { + if (!dataContext.permissions.canRead) { return ''; } @@ -617,7 +617,7 @@ nf.Settings = (function () { }; var reportingTaskRunStatusFormatter = function (row, cell, value, columnDef, dataContext) { - if (!dataContext.accessPolicy.canRead) { + if (!dataContext.permissions.canRead) { return ''; } @@ -649,25 +649,28 @@ nf.Settings = (function () { var reportingTaskActionFormatter = function (row, cell, value, columnDef, dataContext) { var markup = ''; - if (dataContext.accessPolicy.canRead && dataContext.accessPolicy.canWrite) { + if (dataContext.permissions.canRead && dataContext.permissions.canWrite) { if (dataContext.component.state === 'RUNNING') { - markup += '
      '; + markup += '
      '; } else if (dataContext.component.state === 'STOPPED' || dataContext.component.state === 'DISABLED') { - markup += '
      '; + markup += '
      '; // support starting when stopped and no validation errors if (dataContext.component.state === 'STOPPED' && nf.Common.isEmpty(dataContext.component.validationErrors)) { - markup += '
      '; + markup += '
      '; } - markup += '
      '; + markup += '
      '; } if (dataContext.component.persistsState === true) { - markup += '
      '; + markup += '
      '; } } + // TODO - only if we can adminster policies + markup += '
      '; + return markup; }; @@ -726,6 +729,12 @@ nf.Settings = (function () { } else if (target.hasClass('view-state-reporting-task')) { var canClear = reportingTaskEntity.component.state === 'STOPPED' && reportingTaskEntity.component.activeThreadCount === 0; nf.ComponentState.showState(reportingTaskEntity.component, canClear); + } else if (target.hasClass('edit-access-policies')) { + // show the policies for this service + nf.PolicyManagement.showReportingTaskPolicy(reportingTaskEntity); + + // close the settings dialog + $('#shell-close-button').click(); } } else if (reportingTasksGrid.getColumns()[args.cell].id === 'moreDetails') { if (target.hasClass('view-reporting-task')) { @@ -848,7 +857,7 @@ nf.Settings = (function () { // update the current time $('#settings-last-refreshed').text(response.currentTime); - if (response.accessPolicy.canWrite) { + if (response.permissions.canWrite) { // populate the settings $('#maximum-timer-driven-thread-count-field').removeClass('unset').val(response.controllerConfiguration.maxTimerDrivenThreadCount); $('#maximum-event-driven-thread-count-field').removeClass('unset').val(response.controllerConfiguration.maxEventDrivenThreadCount); @@ -860,7 +869,7 @@ nf.Settings = (function () { saveSettings(response.revision.version); }); } else { - if (response.accessPolicy.canRead) { + if (response.permissions.canRead) { // populate the settings $('#read-only-maximum-timer-driven-thread-count-field').removeClass('unset').text(response.controllerConfiguration.maxTimerDrivenThreadCount); $('#read-only-maximum-event-driven-thread-count-field').removeClass('unset').text(response.controllerConfiguration.maxEventDrivenThreadCount); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js index 7c9951e72d34..d3d848a4ec96 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js @@ -285,6 +285,32 @@ nf.Common = (function () { } }, + /** + * Determines whether the current user can access counters. + * + * @returns {boolean} + */ + canAccessPolicies: function () { + if (nf.Common.isDefinedAndNotNull(nf.Common.currentUser)) { + return nf.Common.currentUser.policiesPermissions.canRead === true; + } else { + return false; + } + }, + + /** + * Determines whether the current user can modify counters. + * + * @returns {boolean} + */ + canModifyPolicies: function () { + if (nf.Common.isDefinedAndNotNull(nf.Common.currentUser)) { + return nf.Common.currentUser.policiesPermissions.canRead === true && nf.Common.currentUser.policiesPermissions.canWrite === true; + } else { + return false; + } + }, + /** * Determines whether the current user can access the controller. * @@ -402,7 +428,7 @@ nf.Common = (function () { // status code 400, 403, 404, and 409 are expected response codes for common errors. if (xhr.status === 400 || xhr.status === 403 || xhr.status === 404 || xhr.status === 409) { nf.Dialog.showOkDialog({ - headerText: 'Malformed Request', + headerText: 'Error', dialogContent: nf.Common.escapeHtml(xhr.responseText) }); } else { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js index f76adf6b8e56..8a7f86eb8eeb 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js @@ -261,7 +261,7 @@ nf.ProcessorDetails = (function () { }).fail(function (xhr, status, error) { if (xhr.status === 400 || xhr.status === 404 || xhr.status === 409) { nf.Dialog.showOkDialog({ - headerText: 'Malformed Request', + headerText: 'Error', dialogContent: nf.Common.escapeHtml(xhr.responseText) }); } else { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js index 5477a31e685f..0b588654c253 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js @@ -73,6 +73,22 @@ nf.TemplatesTable = (function () { }); }; + /** + * Opens the access policies for the specified template. + * + * @param templateEntity + */ + var openAccessPolicies = function (templateEntity) { + // only attempt this if we're within a frame + if (top !== window) { + // and our parent has canvas utils and shell defined + if (nf.Common.isDefinedAndNotNull(parent.nf) && nf.Common.isDefinedAndNotNull(parent.nf.PolicyManagement) && nf.Common.isDefinedAndNotNull(parent.nf.Shell)) { + parent.nf.PolicyManagement.showTemplatePolicy(templateEntity); + parent.$('#shell-close-button').click(); + } + } + }; + /** * Deletes the template with the specified id. * @@ -212,7 +228,7 @@ nf.TemplatesTable = (function () { }); var timestampFormatter = function (row, cell, value, columnDef, dataContext) { - if (!dataContext.accessPolicy.canRead) { + if (!dataContext.permissions.canRead) { return ''; } @@ -220,7 +236,7 @@ nf.TemplatesTable = (function () { }; var nameFormatter = function (row, cell, value, columnDef, dataContext) { - if (!dataContext.accessPolicy.canRead) { + if (!dataContext.permissions.canRead) { return '' + dataContext.id + ''; } @@ -228,7 +244,7 @@ nf.TemplatesTable = (function () { }; var descriptionFormatter = function (row, cell, value, columnDef, dataContext) { - if (!dataContext.accessPolicy.canRead) { + if (!dataContext.permissions.canRead) { return ''; } @@ -236,7 +252,7 @@ nf.TemplatesTable = (function () { }; var groupIdFormatter = function (row, cell, value, columnDef, dataContext) { - if (!dataContext.accessPolicy.canRead) { + if (!dataContext.permissions.canRead) { return ''; } @@ -247,14 +263,21 @@ nf.TemplatesTable = (function () { var actionFormatter = function (row, cell, value, columnDef, dataContext) { var markup = ''; - if (dataContext.accessPolicy.canRead === true) { - markup += '
      '; + if (dataContext.permissions.canRead === true) { + markup += '
      '; } // all DFMs to remove templates - if (dataContext.accessPolicy.canWrite === true) { - markup += '
      '; + if (dataContext.permissions.canWrite === true) { + markup += '
      '; } + + // if we in the shell + // TODO - only if we can adminster policies + if (top !== window) { + markup += '
      '; + } + return markup; }; @@ -317,6 +340,8 @@ nf.TemplatesTable = (function () { downloadTemplate(item); } else if (target.hasClass('prompt-to-delete-template')) { promptToDeleteTemplate(item); + } else if (target.hasClass('edit-access-policies')) { + openAccessPolicies(item); } } }); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users-table.js new file mode 100644 index 000000000000..5707f52adcda --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users-table.js @@ -0,0 +1,907 @@ +/* + * 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. + */ + +/* global nf, Slick */ + +nf.UsersTable = (function () { + + /** + * Configuration object used to hold a number of configuration items. + */ + var config = { + filterText: 'Filter', + styles: { + filterList: 'users-filter-list' + }, + urls: { + users: '../nifi-api/tenants/users', + userGroups: '../nifi-api/tenants/user-groups' + } + }; + + var initUserDeleteDialog = function () { + $('#user-delete-dialog').modal({ + headerText: 'Delete User', + buttons: [{ + buttonText: 'Delete', + color: { + base: '#728E9B', + hover: '#004849', + text: '#ffffff' + }, + handler: { + click: function () { + var userId = $('#user-id-delete-dialog').val(); + + // get the user + var usersGrid = $('#users-table').data('gridInstance'); + var usersData = usersGrid.getData(); + var user = usersData.getItemById(userId); + + // update the user + $.ajax({ + type: 'DELETE', + url: user.component.uri + '?' + $.param(nf.Client.getRevision(user)), + dataType: 'json' + }).done(function () { + nf.UsersTable.loadUsersTable(); + }).fail(nf.Common.handleAjaxError); + + // hide the dialog + $('#user-delete-dialog').modal('hide'); + } + } + }, { + buttonText: 'Cancel', + color: { + base: '#E3E8EB', + hover: '#C7D2D7', + text: '#004849' + }, + handler: { + click: function () { + $('#user-delete-dialog').modal('hide'); + } + } + }], + handler: { + close: function () { + // clear the current user + $('#user-id-delete-dialog').val(''); + $('#user-name-delete-dialog').text(''); + } + } + }); + }; + + /** + * Gets the currently selected groups. + * + * @returns {Array} groups + */ + var getSelectedGroups = function () { + var selectedGroups = []; + $('#available-groups div.group-check').filter(function () { + return $(this).hasClass('checkbox-checked'); + }).each(function () { + var id = $(this).next('span.group-id').text(); + selectedGroups.push({ + 'id': id + }); + }); + return selectedGroups; + }; + + /** + * Gets the currently selected users. + * + * @returns {Array} users + */ + var getSelectedUsers = function () { + var selectedUsers = []; + $('#available-users div.user-check').filter(function () { + return $(this).hasClass('checkbox-checked'); + }).each(function () { + var id = $(this).next('span.user-id').text(); + selectedUsers.push({ + 'id': id + }); + }); + return selectedUsers; + }; + + /** + * Adds the specified user to the specified group. + * + * @param groupEntity + * @param userEntity + * @returns xhr + */ + var addUserToGroup = function (groupEntity, userEntity) { + var groupMembers = []; + + // get all the current users + $.each(groupEntity.component.users, function (_, member) { + groupMembers.push({ + 'id': member.id + }); + }); + + // add the new user + groupMembers.push({ + 'id': userEntity.id + }); + + // build the request entity + var updatedGroupEntity = { + 'revision': nf.Client.getRevision(groupEntity), + 'component': $.extend({}, groupEntity.component, { + 'users': groupMembers + }) + }; + + // update the group + return $.ajax({ + type: 'PUT', + url: groupEntity.component.uri, + data: JSON.stringify(updatedGroupEntity), + dataType: 'json', + contentType: 'application/json' + }); + }; + + /** + * Adds the specified user to the specified group. + * + * @param groupEntity + * @param userEntity + * @returns xhr + */ + var removeUserFromGroup = function (groupEntity, userEntity) { + var groupMembers = []; + + // get all the current users + $.each(groupEntity.component.users, function (_, member) { + // do not include the specified user + if (member.id !== userEntity.id) { + groupMembers.push({ + 'id': member.id + }); + } + }); + + // build the request entity + var updatedGroupEntity = { + 'revision': nf.Client.getRevision(groupEntity), + 'component': $.extend({}, groupEntity.component, { + 'users': groupMembers + }) + }; + + // update the group + return $.ajax({ + type: 'PUT', + url: groupEntity.component.uri, + data: JSON.stringify(updatedGroupEntity), + dataType: 'json', + contentType: 'application/json' + }); + }; + + /** + * Creates the specified user. + * + * @param newUserEntity + * @param selectedGroups + */ + var createUser = function (newUserEntity, selectedGroups) { + // get the grid and data + var usersGrid = $('#users-table').data('gridInstance'); + var usersData = usersGrid.getData(); + + // create the user + var userXhr = $.ajax({ + type: 'POST', + url: config.urls.users, + data: JSON.stringify(newUserEntity), + dataType: 'json', + contentType: 'application/json' + }); + + // if the user was successfully created + userXhr.done(function (userEntity) { + var xhrs = []; + $.each(selectedGroups, function (_, selectedGroup) { + var groupEntity = usersData.getItemById(selectedGroup.id) + xhrs.push(addUserToGroup(groupEntity, userEntity)); + }); + + $.when.apply(window, xhrs).always(function () { + nf.UsersTable.loadUsersTable().done(function () { + // select the new user + var row = usersData.getRowById(userEntity.id); + usersGrid.setSelectedRows([row]); + usersGrid.scrollRowIntoView(row); + }); + }).fail(nf.Common.handleAjaxError); + }).fail(nf.Common.handleAjaxError); + }; + + /** + * Updates the specified user. + * + * @param userId + * @param userIdentity + * @param selectedGroups + */ + var updateUser = function (userId, userIdentity, selectedGroups) { + // get the grid and data + var usersGrid = $('#users-table').data('gridInstance'); + var usersData = usersGrid.getData(); + var userEntity = usersData.getItemById(userId); + + var updatedUserEntity = { + 'revision': nf.Client.getRevision(userEntity), + 'component': { + 'id': userId, + 'identity': userIdentity + } + }; + + // update the user + var userXhr = $.ajax({ + type: 'PUT', + url: userEntity.component.uri, + data: JSON.stringify(updatedUserEntity), + dataType: 'json', + contentType: 'application/json' + }); + + userXhr.done(function (updatedUserEntity) { + + // determine what to add/remove + var groupsAdded = []; + var groupsRemoved = []; + $.each(updatedUserEntity.component.userGroups, function(_, currentGroup) { + var isSelected = $.grep(selectedGroups, function (group) { + return group.id === currentGroup.id; + }); + + // if the current group is not selected, mark it for removed + if (isSelected.length === 0) { + groupsRemoved.push(currentGroup); + } + }); + $.each(selectedGroups, function(_, selectedGroup) { + var isSelected = $.grep(updatedUserEntity.component.userGroups, function (group) { + return group.id === selectedGroup.id; + }); + + // if the selected group is not current, mark it for addition + if (isSelected.length === 0) { + groupsAdded.push(selectedGroup); + } + }); + + // update each group + var xhrs = []; + $.each(groupsAdded, function (_, group) { + var groupEntity = usersData.getItemById(group.id); + xhrs.push(addUserToGroup(groupEntity, updatedUserEntity)) + }); + $.each(groupsRemoved, function (_, group) { + var groupEntity = usersData.getItemById(group.id); + xhrs.push(removeUserFromGroup(groupEntity, updatedUserEntity)); + }); + + $.when.apply(window, xhrs).always(function () { + nf.UsersTable.loadUsersTable(); + }).fail(nf.Common.handleAjaxError); + }).fail(nf.Common.handleAjaxError); + }; + + /** + * Creates the specified group. + * + * @param newGroupEntity + */ + var createGroup = function (newGroupEntity) { + // create the group + $.ajax({ + type: 'POST', + url: config.urls.userGroups, + data: JSON.stringify(newGroupEntity), + dataType: 'json', + contentType: 'application/json' + }).done(function (groupEntity) { + nf.UsersTable.loadUsersTable().done(function () { + // add the user + var usersGrid = $('#users-table').data('gridInstance'); + var usersData = usersGrid.getData(); + + // select the new user + var row = usersData.getRowById(groupEntity.id); + usersGrid.setSelectedRows([row]); + usersGrid.scrollRowIntoView(row); + }); + }).fail(nf.Common.handleAjaxError); + }; + + var updateGroup = function (groupId, groupIdentity, selectedUsers) { + // get the grid and data + var usersGrid = $('#users-table').data('gridInstance'); + var usersData = usersGrid.getData(); + var groupEntity = usersData.getItemById(groupId); + + var updatedGroupoEntity = { + 'revision': nf.Client.getRevision(groupEntity), + 'component': { + 'id': groupId, + 'identity': groupIdentity, + 'users': selectedUsers + } + }; + + // update the user + $.ajax({ + type: 'PUT', + url: groupEntity.component.uri, + data: JSON.stringify(updatedGroupoEntity), + dataType: 'json', + contentType: 'application/json' + }).done(function (groupEntity) { + nf.UsersTable.loadUsersTable(); + }); + }; + + /** + * Initializes the user table. + */ + var initUserDialog = function () { + $('#user-dialog').modal({ + headerText: 'User/Group', + buttons: [{ + buttonText: 'Ok', + color: { + base: '#728E9B', + hover: '#004849', + text: '#ffffff' + }, + handler: { + click: function () { + var userId = $('#user-id-edit-dialog').text(); + var userIdentity = $('#user-identity-edit-dialog').val(); + + // see if we should create or update this user + if ($.trim(userId) === '') { + var tenantEntity = { + 'revision': nf.Client.getRevision({ + 'revision': { + 'version': 0 + } + }) + }; + + // handle whether it's a user or a group + if ($('#individual-radio-button').is(':checked')) { + // record the user groups + tenantEntity['component'] = { + 'identity': userIdentity + }; + + createUser(tenantEntity, getSelectedGroups()); + } else { + // record the users + tenantEntity['component'] = { + 'identity': userIdentity, + 'users': getSelectedUsers() + }; + + createGroup(tenantEntity); + } + + // update any selected policies + } else { + // handle whether it's a user or a group + if ($('#individual-radio-button').is(':checked')) { + updateUser(userId, userIdentity, getSelectedGroups()); + + // update any selected policies + } else { + updateGroup(userId, userIdentity, getSelectedUsers()); + + // update any selected policies + } + } + + $('#user-dialog').modal('hide'); + } + } + }, { + buttonText: 'Cancel', + color: { + base: '#E3E8EB', + hover: '#C7D2D7', + text: '#004849' + }, + handler: { + click: function () { + $('#user-dialog').modal('hide'); + } + } + }], + handler: { + close: function () { + // reset the radio button + $('#user-dialog input[name="userOrGroup"]').attr('disabled', false); + $('#individual-radio-button').prop('checked', true); + $('#user-groups').show(); + $('#group-members').hide(); + + // clear the fields + $('#user-id-edit-dialog').text(''); + $('#user-identity-edit-dialog').val(''); + $('#available-users').empty() + $('#available-groups').empty() + } + } + }); + + // listen for changes to the user/group radio + $('#user-dialog input[name="userOrGroup"]').on('change', function () { + if ($(this).val() === 'individual') { + $('#user-groups').show(); + $('#group-members').hide(); + } else { + $('#user-groups').hide(); + $('#group-members').show(); + } + }); + }; + + /** + * Initializes the processor list. + */ + var initUsersTable = function () { + // define the function for filtering the list + $('#users-filter').keyup(function () { + applyFilter(); + }).focus(function () { + if ($(this).hasClass(config.styles.filterList)) { + $(this).removeClass(config.styles.filterList).val(''); + } + }).blur(function () { + if ($(this).val() === '') { + $(this).addClass(config.styles.filterList).val(config.filterText); + } + }).addClass(config.styles.filterList).val(config.filterText); + + // filter type + $('#users-filter-type').combo({ + options: [{ + text: 'by user', + value: 'identity' + }], + select: function (option) { + applyFilter(); + } + }); + + // function for formatting the user identity + var identityFormatter = function (row, cell, value, columnDef, dataContext) { + var markup = ''; + if (dataContext.type === 'group') { + markup += '
      '; + } + + markup += dataContext.component.identity; + + return markup; + }; + + // function for formatting the members/groups + var membersGroupsFormatter = function (row, cell, value, columnDef, dataContext) { + if (dataContext.type === 'group') { + return 'Members: ' + dataContext.component.users.map(function (user) { + return user.component.identity; + }).join(', ') + ''; + } else { + return 'Member of: ' + dataContext.component.userGroups.map(function (group) { + return group.component.identity; + }).join(', ') + ''; + } + }; + + // function for formatting the actions column + var actionFormatter = function (row, cell, value, columnDef, dataContext) { + var markup = ''; + + markup += '
      '; + + markup += '
      '; + + return markup; + }; + + // initialize the templates table + var usersColumns = [ + {id: 'identity', name: 'User', sortable: true, resizable: true, formatter: identityFormatter}, + {id: 'membersGroups', name: ' ', sortable: true, defaultSortAsc: false, resizable: true, formatter: membersGroupsFormatter}, + {id: 'actions', name: ' ', sortable: false, resizable: false, formatter: actionFormatter, width: 100, maxWidth: 100} + ]; + var usersOptions = { + forceFitColumns: true, + enableTextSelectionOnCells: true, + enableCellNavigation: true, + enableColumnReorder: false, + autoEdit: false + }; + + // initialize the dataview + var usersData = new Slick.Data.DataView({ + inlineFilters: false + }); + usersData.setItems([]); + usersData.setFilterArgs({ + searchString: getFilterText(), + property: $('#users-filter-type').combo('getSelectedOption').value + }); + usersData.setFilter(filter); + + // initialize the sort + sort({ + columnId: 'identity', + sortAsc: true + }, usersData); + + // initialize the grid + var usersGrid = new Slick.Grid('#users-table', usersData, usersColumns, usersOptions); + usersGrid.setSelectionModel(new Slick.RowSelectionModel()); + usersGrid.registerPlugin(new Slick.AutoTooltips()); + usersGrid.setSortColumn('identity', true); + usersGrid.onSort.subscribe(function (e, args) { + sort({ + columnId: args.sortCol.id, + sortAsc: args.sortAsc + }, usersData); + }); + + // configure a click listener + usersGrid.onClick.subscribe(function (e, args) { + var target = $(e.target); + + // get the node at this row + var item = usersData.getItem(args.row); + + // determine the desired action + if (usersGrid.getColumns()[args.cell].id === 'actions') { + if (target.hasClass('edit-user')) { + editUser(item); + } else if (target.hasClass('delete-user')) { + deleteUser(item); + } + } + }); + + // wire up the dataview to the grid + usersData.onRowCountChanged.subscribe(function (e, args) { + usersGrid.updateRowCount(); + usersGrid.render(); + + // update the total number of displayed processors + $('#displayed-users').text(args.current); + }); + usersData.onRowsChanged.subscribe(function (e, args) { + usersGrid.invalidateRows(args.rows); + usersGrid.render(); + }); + + // hold onto an instance of the grid + $('#users-table').data('gridInstance', usersGrid); + + // initialize the number of displayed items + $('#displayed-users').text('0'); + }; + + /** + * Sorts the specified data using the specified sort details. + * + * @param {object} sortDetails + * @param {object} data + */ + var sort = function (sortDetails, data) { + // defines a function for sorting + var comparer = function (a, b) { + var aString = nf.Common.isDefinedAndNotNull(a.component[sortDetails.columnId]) ? a.component[sortDetails.columnId] : ''; + var bString = nf.Common.isDefinedAndNotNull(b.component[sortDetails.columnId]) ? b.component[sortDetails.columnId] : ''; + return aString === bString ? 0 : aString > bString ? 1 : -1; + }; + + // perform the sort + data.sort(comparer, sortDetails.sortAsc); + }; + + /** + * Get the text out of the filter field. If the filter field doesn't + * have any text it will contain the text 'filter list' so this method + * accounts for that. + */ + var getFilterText = function () { + var filterText = ''; + var filterField = $('#users-filter'); + if (!filterField.hasClass(config.styles.filterList)) { + filterText = filterField.val(); + } + return filterText; + }; + + /** + * Applies the filter found in the filter expression text field. + */ + var applyFilter = function () { + // get the dataview + var usersGrid = $('#users-table').data('gridInstance'); + + // ensure the grid has been initialized + if (nf.Common.isDefinedAndNotNull(usersGrid)) { + var usersData = usersGrid.getData(); + + // update the search criteria + usersData.setFilterArgs({ + searchString: getFilterText(), + property: $('#users-filter-type').combo('getSelectedOption').value + }); + usersData.refresh(); + } + }; + + /** + * Performs the filtering. + * + * @param {object} item The item subject to filtering + * @param {object} args Filter arguments + * @returns {Boolean} Whether or not to include the item + */ + var filter = function (item, args) { + if (args.searchString === '') { + return true; + } + + try { + // perform the row filtering + var filterExp = new RegExp(args.searchString, 'i'); + } catch (e) { + // invalid regex + return false; + } + + return item.component[args.property].search(filterExp) >= 0; + }; + + /** + * Builds a listing a users. + */ + var buildUsersList = function () { + var usersGrid = $('#users-table').data('gridInstance'); + var usersData = usersGrid.getData(); + var usersList = $('#available-users'); + + // add a row for each user + var count = 0; + $.each(usersData.getItems(), function(_, user) { + if (user.type === 'user') { + // checkbox + var checkbox = $('
      ').addClass('group-user-' + user.id); + + // user id + var userId = $('').text(user.id); + + // identity + var identity = $('
      ').text(user.component.identity); + + // clear + var clear = $('
      '); + + // list item + var li = $('
    • ').append(checkbox).append(userId).append(identity).append(clear).appendTo(usersList); + if (count++ % 2 === 0) { + li.addClass('even'); + } + } + }); + }; + + /** + * Builds a listing a users. + */ + var buildGroupsList = function () { + var usersGrid = $('#users-table').data('gridInstance'); + var usersData = usersGrid.getData(); + var groupsList = $('#available-groups'); + + // add a row for each user + var count = 0; + $.each(usersData.getItems(), function(_, group) { + if (group.type === 'group') { + // checkbox + var checkbox = $('
      ').addClass('user-group-' + group.id); + + // group id + var groupId = $('').text(group.id); + + // icon + var groupIcon = $('
      '); + + // identity + var identity = $('
      ').text(group.component.identity); + + // clear + var clear = $('
      '); + + // list item + var li = $('
    • ').append(checkbox).append(groupId).append(groupIcon).append(identity).append(clear).appendTo(groupsList); + if (count++ % 2 === 0) { + li.addClass('even'); + } + } + }); + }; + + /** + * Edit's the specified user's account. + * + * @argument {object} user The user item + */ + var editUser = function (user) { + // populate the users info + $('#user-id-edit-dialog').text(user.id); + $('#user-identity-edit-dialog').val(user.component.identity); + + if (user.type === 'user') { + $('#individual-radio-button').prop('checked', true); + $('#user-groups').show(); + $('#group-members').hide(); + + // list all the groups currently in the table + buildGroupsList(); + + // select each group this user belongs to + $.each(user.component.userGroups, function (_, userGroup) { + $('div.group-check.user-group-' + userGroup.id).removeClass('checkbox-unchecked').addClass('checkbox-checked'); + }); + } else { + $('#group-radio-button').prop('checked', true); + $('#user-groups').hide(); + $('#group-members').show(); + + // list all the users currently in the table + buildUsersList(); + + // select each user that belongs to this group + $.each(user.component.users, function (_, member) { + $('div.user-check.group-user-' + member.id).removeClass('checkbox-unchecked').addClass('checkbox-checked'); + }); + } + + // disable the type radio + $('#user-dialog input[name="userOrGroup"]').attr('disabled', true); + + // show the dialog + $('#user-dialog').modal('show'); + }; + + /** + * Delete's the specified user's account. + * + * @argument {object} user The user item + */ + var deleteUser = function (user) { + // populate the users info + $('#user-id-delete-dialog').val(user.id); + $('#user-identity-delete-dialog').text(user.component.identity); + + // show the dialog + $('#user-delete-dialog').modal('show'); + }; + + return { + init: function () { + initUserDialog(); + initUserDeleteDialog(); + initUsersTable(); + + $('#new-user-button').on('click', function () { + buildUsersList(); + buildGroupsList(); + $('#user-dialog').modal('show'); + }); + }, + + /** + * Update the size of the grid based on its container's current size. + */ + resetTableSize: function () { + var usersTable = $('#users-table'); + if (usersTable.is(':visible')) { + var grid = usersTable.data('gridInstance'); + if (nf.Common.isDefinedAndNotNull(grid)) { + grid.resizeCanvas(); + } + } + }, + + /** + * Load the processor status table. + */ + loadUsersTable: function () { + var users = $.ajax({ + type: 'GET', + url: config.urls.users, + dataType: 'json' + }); + + var groups = $.ajax({ + type: 'GET', + url: config.urls.userGroups, + dataType: 'json' + }); + + return $.when(users, groups).done(function (usersResults, groupsResults) { + var usersResponse = usersResults[0]; + var groupsResponse = groupsResults[0]; + + // update the refresh timestamp + $('#users-last-refreshed').text(usersResponse.generated); + + var usersGrid = $('#users-table').data('gridInstance'); + var usersData = usersGrid.getData(); + + // begin the update + usersData.beginUpdate(); + + var users = []; + + // add each user + $.each(usersResponse.users, function (_, user) { + users.push($.extend({ + type: 'user' + }, user)); + }); + + // add each group + $.each(groupsResponse.userGroups, function (_, group) { + users.push($.extend({ + type: 'group' + }, group)); + }); + + // set the rows + usersData.setItems(users); + + // end the update + usersData.endUpdate(); + + // re-sort and clear selection after updating + usersData.reSort(); + usersGrid.invalidate(); + usersGrid.getSelectionModel().setSelectedRows([]); + + $('#total-users').text(usersData.getLength()); + }).fail(nf.Common.handleAjaxError); + } + }; +}()); \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users.js new file mode 100644 index 000000000000..dbd7d1c967a8 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users.js @@ -0,0 +1,147 @@ +/* + * 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. + */ +$(document).ready(function () { + // initialize the counters page + nf.Users.init(); + + //alter styles if we're not in the shell + if (top === window) { + $('#users').css('top', 20); + $('#users-refresh-container').css('bottom', 20); + } +}); + +nf.Users = (function () { + + /** + * Configuration object used to hold a number of configuration items. + */ + var config = { + urls: { + banners: '../nifi-api/flow/banners', + controllerAbout: '../nifi-api/flow/about' + } + }; + + /** + * Loads the current users authorities. + */ + var ensureAccess = function () { + return $.Deferred(function(deferred) { + deferred.resolve(); + }).promise(); + }; + + var initializeUsersPage = function () { + // define mouse over event for the refresh button + nf.Common.addHoverEffect('#user-refresh-button', 'button-refresh', 'button-refresh-hover').click(function () { + nf.UsersTable.loadUsersTable(); + }); + + // get the banners if we're not in the shell + return $.Deferred(function (deferred) { + if (top === window) { + $.ajax({ + type: 'GET', + url: config.urls.banners, + dataType: 'json' + }).done(function (bannerResponse) { + // ensure the banners response is specified + if (nf.Common.isDefinedAndNotNull(bannerResponse.banners)) { + if (nf.Common.isDefinedAndNotNull(bannerResponse.banners.headerText) && bannerResponse.banners.headerText !== '') { + // update the header text + var bannerHeader = $('#banner-header').text(bannerResponse.banners.headerText).show(); + + // show the banner + var updateTop = function (elementId) { + var element = $('#' + elementId); + element.css('top', (parseInt(bannerHeader.css('height'), 10) + parseInt(element.css('top'), 10)) + 'px'); + }; + + // update the position of elements affected by top banners + updateTop('users'); + } + + if (nf.Common.isDefinedAndNotNull(bannerResponse.banners.footerText) && bannerResponse.banners.footerText !== '') { + // update the footer text and show it + var bannerFooter = $('#banner-footer').text(bannerResponse.banners.footerText).show(); + + var updateBottom = function (elementId) { + var element = $('#' + elementId); + element.css('bottom', parseInt(bannerFooter.css('height'), 10) + 'px'); + }; + + // update the position of elements affected by bottom banners + updateBottom('users'); + } + } + + deferred.resolve(); + }).fail(function (xhr, status, error) { + nf.Common.handleAjaxError(xhr, status, error); + deferred.reject(); + }); + } else { + deferred.resolve(); + } + }); + }; + + return { + /** + * Initializes the counters page. + */ + init: function () { + nf.Storage.init(); + + // initialize the client + nf.Client.init(); + + // load the users authorities + ensureAccess().done(function () { + // create the counters table + nf.UsersTable.init(); + + // load the users table + nf.UsersTable.loadUsersTable().done(function () { + // finish initializing users page + initializeUsersPage().done(function () { + // listen for browser resize events to update the page size + $(window).resize(nf.UsersTable.resetTableSize); + + // configure the initial grid height + nf.UsersTable.resetTableSize(); + + // get the about details + $.ajax({ + type: 'GET', + url: config.urls.controllerAbout, + dataType: 'json' + }).done(function (response) { + var aboutDetails = response.about; + var countersTitle = aboutDetails.title + ' Users'; + + // set the document title and the about title + document.title = countersTitle; + $('#users-header-text').text(countersTitle); + }).fail(nf.Common.handleAjaxError); + }); + }); + }); + } + }; +}()); \ No newline at end of file