From 3047f2553bfb4b7088171da739c973288cdac0aa Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Mon, 18 Nov 2024 17:35:07 +0800 Subject: [PATCH] [Workspace] Add ACL related test cases (#1644) * feat: add acl related test cases Signed-off-by: SuZhou-Joe * feat: update Signed-off-by: SuZhou-Joe * feat: remove useless resources Signed-off-by: SuZhou-Joe * feat: update Signed-off-by: SuZhou-Joe * feat: use admin user to clean up all the resources Signed-off-by: SuZhou-Joe --------- Signed-off-by: SuZhou-Joe --- .../mds_workspace_acl.spec.js | 232 ++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 cypress/integration/core-opensearch-dashboards/opensearch-dashboards/workspace-plugin/mds_workspace_acl.spec.js diff --git a/cypress/integration/core-opensearch-dashboards/opensearch-dashboards/workspace-plugin/mds_workspace_acl.spec.js b/cypress/integration/core-opensearch-dashboards/opensearch-dashboards/workspace-plugin/mds_workspace_acl.spec.js new file mode 100644 index 000000000..86c03c4e6 --- /dev/null +++ b/cypress/integration/core-opensearch-dashboards/opensearch-dashboards/workspace-plugin/mds_workspace_acl.spec.js @@ -0,0 +1,232 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { BASE_PATH } from '../../../../utils/base_constants'; +import { ADMIN_AUTH } from '../../../../utils/commands'; +import workspaceTestUser from '../../../../fixtures/dashboard/opensearch_dashboards/workspace/workspaceTestUser.json'; +import workspaceTestRole from '../../../../fixtures/dashboard/opensearch_dashboards/workspace/workspaceTestRole.json'; +import { WORKSPACE_API_PREFIX } from '../../../../utils/dashboards/workspace-plugin/constants'; + +let noPermissionWorkspaceName = 'acl_no_permission_workspace'; +let readOnlyWorkspaceName = 'acl_readonly_workspace'; +let libraryWriteWorkspaceName = 'acl_library_write_workspace'; +let ownerWorkspaceName = 'acl_owner_workspace'; + +let noPermissionWorkspaceId = ''; +let readOnlyWorkspaceId = ''; +let libraryWriteWorkspaceId = ''; +let ownerWorkspaceId = ''; +let datasourceId = ''; + +const getPolicy = (permission, userName) => ({ + [permission]: { + users: [userName], + }, +}); + +const NON_DASHBOARDS_ADMIN_USERNAME = 'workspace-acl-test'; +const WORKSPACE_TEST_ROLE_NAME = 'workspace-acl-test-role'; + +const ACLPolicyMap = { + [noPermissionWorkspaceName]: {}, + [readOnlyWorkspaceName]: { + ...getPolicy('read', NON_DASHBOARDS_ADMIN_USERNAME), + ...getPolicy('library_read', NON_DASHBOARDS_ADMIN_USERNAME), + }, + [libraryWriteWorkspaceName]: { + ...getPolicy('read', NON_DASHBOARDS_ADMIN_USERNAME), + ...getPolicy('library_write', NON_DASHBOARDS_ADMIN_USERNAME), + }, + [ownerWorkspaceName]: { + ...getPolicy('write', NON_DASHBOARDS_ADMIN_USERNAME), + ...getPolicy('library_write', NON_DASHBOARDS_ADMIN_USERNAME), + }, +}; + +const setupWorkspace = (workspaceName, datasourceId) => { + return cy + .createWorkspace({ + name: workspaceName, + settings: { + ...(datasourceId ? { dataSources: [datasourceId] } : {}), + permissions: ACLPolicyMap[workspaceName], + }, + }) + .then((value) => { + // load sample data + cy.loadSampleDataForWorkspace('ecommerce', value, datasourceId); + cy.wrap(value); + }); +}; + +const setupAllTheWorkspaces = () => { + setupWorkspace(noPermissionWorkspaceName, datasourceId).then( + (value) => (noPermissionWorkspaceId = value) + ); + setupWorkspace(readOnlyWorkspaceName, datasourceId).then( + (value) => (readOnlyWorkspaceId = value) + ); + setupWorkspace(libraryWriteWorkspaceName, datasourceId).then( + (value) => (libraryWriteWorkspaceId = value) + ); + setupWorkspace(ownerWorkspaceName, datasourceId).then( + (value) => (ownerWorkspaceId = value) + ); +}; + +if ( + Cypress.env('WORKSPACE_ENABLED') && + Cypress.env('SAVED_OBJECTS_PERMISSION_ENABLED') && + Cypress.env('SECURITY_ENABLED') +) { + describe('Workspace ACL', () => { + const originalUser = ADMIN_AUTH.username; + const originalPassword = ADMIN_AUTH.password; + before(() => { + cy.deleteWorkspaceByName(noPermissionWorkspaceName); + cy.deleteWorkspaceByName(readOnlyWorkspaceName); + cy.deleteWorkspaceByName(libraryWriteWorkspaceName); + cy.deleteWorkspaceByName(ownerWorkspaceName); + + cy.createInternalUser(NON_DASHBOARDS_ADMIN_USERNAME, workspaceTestUser); + cy.createRole(WORKSPACE_TEST_ROLE_NAME, workspaceTestRole); + cy.createRoleMapping(WORKSPACE_TEST_ROLE_NAME, { + users: [NON_DASHBOARDS_ADMIN_USERNAME], + }); + + if (Cypress.env('DATASOURCE_MANAGEMENT_ENABLED')) { + cy.createDataSourceNoAuth().then((result) => { + datasourceId = result[0]; + expect(datasourceId).to.be.a('string').that.is.not.empty; + setupAllTheWorkspaces(datasourceId); + }); + } else { + setupAllTheWorkspaces(datasourceId); + } + }); + + after(() => { + ADMIN_AUTH.newUser = originalUser; + ADMIN_AUTH.newPassword = originalPassword; + cy.removeSampleDataForWorkspace( + 'ecommerce', + ownerWorkspaceId, + datasourceId + ); + cy.deleteWorkspaceByName(noPermissionWorkspaceName); + cy.deleteWorkspaceByName(readOnlyWorkspaceName); + cy.deleteWorkspaceByName(libraryWriteWorkspaceName); + cy.deleteWorkspaceByName(ownerWorkspaceName); + if (Cypress.env('DATASOURCE_MANAGEMENT_ENABLED')) { + cy.deleteDataSource(datasourceId); + } + readOnlyWorkspaceId = ''; + libraryWriteWorkspaceId = ''; + + cy.deleteRoleMapping(WORKSPACE_TEST_ROLE_NAME); + cy.deleteInternalUser(NON_DASHBOARDS_ADMIN_USERNAME); + cy.deleteRole(WORKSPACE_TEST_ROLE_NAME); + }); + + describe('Normal user', () => { + beforeEach(() => { + ADMIN_AUTH.newUser = NON_DASHBOARDS_ADMIN_USERNAME; + ADMIN_AUTH.newPassword = workspaceTestUser.password; + }); + + it('Normal user should not be able to create workspace', () => { + cy.request({ + method: 'POST', + url: `${BASE_PATH}${WORKSPACE_API_PREFIX}`, + headers: { + 'osd-xsrf': true, + }, + body: { + attributes: { + name: 'test_workspace', + features: ['use-case-observability'], + description: 'test_description', + }, + }, + failOnStatusCode: false, + }).then((resp) => + cy + .wrap(resp.body.error) + .should('equal', 'Invalid permission, please contact OSD admin') + ); + }); + + it('Normal users should only see the workspaces they have permission with', () => { + cy.visit(`${BASE_PATH}/app/home`); + cy.contains(readOnlyWorkspaceName); + cy.contains(noPermissionWorkspaceName).should('not.exist'); + }); + + it('Readonly users should not be allowed to update dashboards/visualizations within the workspace', () => { + cy.visit(`${BASE_PATH}/w/${readOnlyWorkspaceId}/app/visualize`); + cy.contains(/\[eCommerce\] Markdown/).click(); + cy.getElementByTestId('visualizeSaveButton').click(); + cy.getElementByTestId('confirmSaveSavedObjectButton').click(); + cy.contains('Forbidden'); + }); + + it('Normal users should not be allowed to visit workspace he/she has no permission', () => { + cy.visit(`${BASE_PATH}/w/${noPermissionWorkspaceId}/app/objects`); + cy.contains('Invalid saved objects permission'); + }); + + it('Normal users should only see the workspaces he has library_write permission in the target workspaces list of duplicate modal', () => { + cy.visit(`${BASE_PATH}/w/${readOnlyWorkspaceId}/app/objects`); + cy.getElementByTestId('savedObjectsTableRowTitle').should('exist'); + cy.getElementByTestId('duplicateObjects') + .click() + .getElementByTestId('savedObjectsDuplicateModal') + .find('[data-test-subj="comboBoxInput"]') + .click(); + + cy.contains(libraryWriteWorkspaceName); + cy.contains(ownerWorkspaceName); + cy.contains(noPermissionWorkspaceName).should('not.exist'); + }); + + it('Users should not be able to update default index pattern / default data source if he/she is not the workspace owner', () => { + cy.visit(`${BASE_PATH}/w/${libraryWriteWorkspaceId}/app/indexPatterns`); + cy.contains('opensearch_dashboards_sample_data_ecommerce').click(); + cy.getElementByTestId('setDefaultIndexPatternButton').click(); + cy.contains('Unable to update UI setting'); + }); + + it('Normal users should not be able to find objects from other workspaces when inside a workspace', () => { + cy.visit(`${BASE_PATH}/w/${readOnlyWorkspaceId}/app/objects`); + cy.getElementByTestId('savedObjectsTableRowTitle').should('exist'); + cy.getElementByTestId( + 'savedObjectsTableColumn-workspace_column' + ).should('not.exist'); + cy.contains('opensearch_dashboards_sample_data_ecommerce'); + // Electron old version may not support search event, so we manually trigger a search event + cy.getElementByTestId('savedObjectSearchBar') + .type('opensearch_dashboards_sample_data_ecommerce{enter}') + .trigger('search'); + cy.getElementByTestId('savedObjectsTableRowTitle').should( + 'have.length', + 1 + ); + }); + + if (Cypress.env('DATASOURCE_MANAGEMENT_ENABLED')) { + it('Normal users should not be able to associate / dissociate data sources from workspace.', () => { + cy.visit(`${BASE_PATH}/w/${ownerWorkspaceId}/app/dataSources`); + cy.contains('Data sources'); + cy.getElementByTestId('workspaceAssociateDataSourceButton').should( + 'not.exist' + ); + cy.getElementByTestId( + 'dataSourcesManagement-dataSourceTable-dissociateButton' + ).should('not.exist'); + }); + } + }); + }); +}