Skip to content

Commit

Permalink
Merge branch 'main' into update-workspace-with-full-flow
Browse files Browse the repository at this point in the history
  • Loading branch information
wanglam authored Nov 20, 2024
2 parents 5d877b9 + 7003895 commit ce0aa13
Show file tree
Hide file tree
Showing 9 changed files with 704 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1 +1 @@
* @tianleh @kavilla @ohltyler @CCongWang @ashwin-pc @peterzhuamazon @ananzh @prudhvigodithi @xluo-aws @Hailong-am @SuZhou-Joe @ruanyl @wanglam @raintygao @zhongnansu @yujin-emma
* @tianleh @kavilla @ohltyler @CCongWang @ashwin-pc @peterzhuamazon @ananzh @prudhvigodithi @xluo-aws @Hailong-am @SuZhou-Joe @ruanyl @wanglam @raintygao @zhongnansu @yujin-emma @yubonluo
1 change: 1 addition & 0 deletions MAINTAINERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This document contains a list of maintainers in this repo. See [opensearch-proje
| Tianyu Gao | [raintygao](https://github.com/raintygao) | Amazon |
| Zhongnan Su | [zhongnansu](https://github.com/zhongnansu) | Amazon |
| Emma Jin | [yujin-emma](https://github.com/yujin-emma) | Amazon |
| Yubo Luo | [yubonluo](https://github.com/yubonluo) | Amazon |

## Emeritus

Expand Down
Original file line number Diff line number Diff line change
@@ -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');
});
}
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,34 +118,38 @@ if (Cypress.env('WORKSPACE_ENABLED')) {
cy.get('.euiLink')
.contains('Quickstart guide')
.should('be.visible')
.and(
'have.attr',
'href',
'https://opensearch.org/docs/latest/dashboards/quickstart/'
);
.and('have.attr', 'href')
.and((href) => {
expect(href).to.match(
/https:\/\/opensearch.org\/docs\/(latest|(\d.)+)\/dashboards\/quickstart\/$/
);
});
cy.get('.euiLink')
.contains('Building data visualizations')
.should('be.visible')
.and(
'have.attr',
'href',
'https://opensearch.org/docs/latest/dashboards/visualize/viz-index/'
);
.and('have.attr', 'href')
.and((href) => {
expect(href).to.match(
/https:\/\/opensearch.org\/docs\/(latest|(\d.)+)\/dashboards\/visualize\/viz-index\/$/
);
});
cy.get('.euiLink')
.contains('Creating dashboards')
.should('be.visible')
.and(
'have.attr',
'href',
'https://opensearch.org/docs/latest/dashboards/dashboard/index/'
);
.and('have.attr', 'href')
.and((href) => {
expect(href).to.match(
/https:\/\/opensearch.org\/docs\/(latest|(\d.)+)\/dashboards\/dashboard\/index\/$/
);
});
cy.contains('Learn more in Documentation')
.should('be.visible')
.and(
'have.attr',
'href',
'https://opensearch.org/docs/latest/dashboards/index/'
);
.and('have.attr', 'href')
.and((href) => {
expect(href).to.match(
/https:\/\/opensearch.org\/docs\/(latest|(\d.)+)\/dashboards\/index\/$/
);
});
});
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { MiscUtils } from '@opensearch-dashboards-test/opensearch-dashboards-test-library';
const miscUtils = new MiscUtils(cy);
const workspaceName = 'test_workspace_collaborators';
let workspaceId;
let dataSourceTitle1 = 'no_auth_data_source_title_1';
let dataSourceTitle2 = 'no_auth_data_source_title_2';
let dataSourceId1;
let dataSourceId2;
if (
Cypress.env('WORKSPACE_ENABLED') &&
Cypress.env('DATASOURCE_MANAGEMENT_ENABLED')
) {
describe('Workspace association data source', () => {
before(() => {
cy.createDataSourceNoAuth({ title: dataSourceTitle1 }).then((result) => {
dataSourceId1 = result[0];
});
cy.createDataSourceNoAuth({ title: dataSourceTitle2 }).then((result) => {
dataSourceId2 = result[0];
});
});
beforeEach(() => {
cy.deleteWorkspaceByName(workspaceName);
//Create a workspace before each test
cy.createWorkspace({
name: workspaceName,
features: ['use-case-observability'],
settings: {
permissions: {
library_write: { users: ['%me%'] },
write: { users: ['%me%'] },
},
},
}).then((value) => {
workspaceId = value;
});
});

after(() => {
cy.deleteDataSource(dataSourceId1);
cy.deleteDataSource(dataSourceId2);
});
afterEach(() => {
cy.deleteWorkspaceById(workspaceId);
});

it('should associate and dissociate data source successfully', () => {
miscUtils.visitPage(`w/${workspaceId}/app/dataSources`);

cy.getElementByTestId('workspaceAssociateDataSourceButton').click();
cy.contains('OpenSearch data sources').click();
cy.contains(dataSourceTitle1, {
withinSubject: parent.document.body,
}).click({ force: true });
cy.contains(dataSourceTitle2, {
withinSubject: parent.document.body,
}).click({ force: true });
cy.getElementByTestId(
'workspace-detail-dataSources-associateModal-save-button'
).click();

// The table is updated after successful association
cy.contains('table', dataSourceTitle1);
cy.contains('table', dataSourceTitle2);

// The table is updated after successful dissociation
cy.getElementByTestId('checkboxSelectAll').check();
cy.getElementByTestId('dissociateSelectedDataSources').click();
cy.getElementByTestId('confirmModalConfirmButton').click();
cy.contains('table', dataSourceTitle1).should('not.exist');
cy.contains('table', dataSourceTitle2).should('not.exist');
});
});
}
Loading

0 comments on commit ce0aa13

Please sign in to comment.