Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Workspace] Only OSD admin can create workspace #6831

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ffb4a0f
Only OSD admin can create workspace
yubonluo May 24, 2024
2ba437e
Changeset file for PR #6831 created/updated
opensearch-changeset-bot[bot] May 24, 2024
ec1f413
Changeset file for PR #6831 created/updated
opensearch-changeset-bot[bot] May 24, 2024
893b039
optimize the code
yubonluo May 27, 2024
5ec5b15
Merge branch 'main' of github.com:opensearch-project/OpenSearch-Dashb…
yubonluo May 27, 2024
832b1e5
Merge branch 'main-create-workspace-OSDAdmin' of github.com:yubonluo/…
yubonluo May 27, 2024
11f6396
delete useless code
yubonluo May 27, 2024
dec5cbe
add integration tests
yubonluo May 27, 2024
d83e340
optimize the code
yubonluo May 27, 2024
fa6809d
delete useless code
yubonluo May 27, 2024
e979d77
optimize the code
yubonluo May 27, 2024
87ebeef
optimize the code
yubonluo May 27, 2024
5b9a786
optimize the code
yubonluo May 27, 2024
0cfa4b0
add overwrite check
yubonluo May 27, 2024
41b17ab
optimize the code
yubonluo May 27, 2024
a179855
optimize the code
yubonluo May 27, 2024
deb4341
solve conflict
yubonluo May 28, 2024
9417e21
Add the logic of whether to update the operation
yubonluo May 30, 2024
8816831
Merge branch 'main' of github.com:opensearch-project/OpenSearch-Dashb…
yubonluo May 30, 2024
a10a23b
Merge branch 'main' into main-create-workspace-OSDAdmin
yubonluo May 31, 2024
8c3675b
optimize the code
yubonluo May 31, 2024
2e2350b
Merge branch 'main-create-workspace-OSDAdmin' of github.com:yubonluo/…
yubonluo May 31, 2024
8a5721e
Optimize the code
yubonluo May 31, 2024
edf33c2
Merge branch 'main' into main-create-workspace-OSDAdmin
yubonluo Jun 3, 2024
35b04bd
Optimize the code
yubonluo Jun 3, 2024
4962ab2
Merge branch 'main-create-workspace-OSDAdmin' of github.com:yubonluo/…
yubonluo Jun 3, 2024
acadee6
Merge branch 'main' into main-create-workspace-OSDAdmin
yubonluo Jun 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/6831.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
feat:
- [Workspace] Only OSD admin can create workspace ([#6831](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6831))
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,20 @@ describe('WorkspaceSavedObjectsClientWrapper', () => {
id: 'workspace-1',
permissions: {
library_read: { users: ['foo'] },
write: { users: ['foo'] },
library_write: { users: ['foo'] },
},
}
);

await repositoryKit.create(
internalSavedObjectsRepository,
'workspace',
{},
{
id: 'workspace-2',
permissions: {
write: { users: ['foo'] },
library_write: { users: ['foo'] },
},
}
Expand Down Expand Up @@ -132,7 +146,9 @@ describe('WorkspaceSavedObjectsClientWrapper', () => {
return { users: ['foo'] };
});

permittedSavedObjectedClient = osd.coreStart.savedObjects.getScopedClient(permittedRequest);
permittedSavedObjectedClient = osd.coreStart.savedObjects.getScopedClient(permittedRequest, {
includedHiddenTypes: ['workspace'],
});
notPermittedSavedObjectedClient = osd.coreStart.savedObjects.getScopedClient(
notPermittedRequest
);
Expand Down Expand Up @@ -316,6 +332,62 @@ describe('WorkspaceSavedObjectsClientWrapper', () => {

expect(createResult.error).toBeUndefined();
});

it('should able to create workspace with override', async () => {
yubonluo marked this conversation as resolved.
Show resolved Hide resolved
const createResult = await permittedSavedObjectedClient.create(
'workspace',
{},
{
id: 'workspace-2',
overwrite: true,
permissions: {
library_write: { users: ['foo'] },
write: { users: ['foo'] },
},
}
);

expect(createResult.error).toBeUndefined();
});

it('should throw forbidden error when user create a workspce and is not OSD admin', async () => {
let error;
try {
await permittedSavedObjectedClient.create('workspace', {}, {});
} catch (e) {
error = e;
}

expect(SavedObjectsErrorHelpers.isForbiddenError(error)).toBe(true);

try {
await permittedSavedObjectedClient.create('workspace', {}, { id: 'workspace-1' });
} catch (e) {
error = e;
}

expect(SavedObjectsErrorHelpers.isForbiddenError(error)).toBe(true);

try {
await permittedSavedObjectedClient.create('workspace', {}, { overwrite: true });
} catch (e) {
error = e;
}

expect(SavedObjectsErrorHelpers.isForbiddenError(error)).toBe(true);

try {
await permittedSavedObjectedClient.create(
'workspace',
{},
{ id: 'no-exist', overwrite: true }
);
} catch (e) {
error = e;
}

expect(SavedObjectsErrorHelpers.isNotFoundError(error)).toBe(true);
});
});

describe('bulkCreate', () => {
Expand Down Expand Up @@ -384,6 +456,54 @@ describe('WorkspaceSavedObjectsClientWrapper', () => {

expect(createResult.saved_objects).toHaveLength(1);
});

it('should able to bulk create workspace with override', async () => {
const createResult = await permittedSavedObjectedClient.bulkCreate(
[
{
id: 'workspace-2',
type: 'workspace',
attributes: {},
},
],
{
overwrite: true,
}
);

expect(createResult.saved_objects).toHaveLength(1);
});

it('should throw forbidden error when user bulk create workspace and is not OSD admin', async () => {
let error;
try {
await permittedSavedObjectedClient.bulkCreate([{ type: 'workspace', attributes: {} }]);
} catch (e) {
error = e;
}

expect(SavedObjectsErrorHelpers.isForbiddenError(error)).toBe(true);

try {
await permittedSavedObjectedClient.bulkCreate([{ type: 'workspace', attributes: {} }], {
overwrite: true,
});
} catch (e) {
error = e;
}

expect(SavedObjectsErrorHelpers.isForbiddenError(error)).toBe(true);

try {
await permittedSavedObjectedClient.bulkCreate([
{ type: 'workspace', id: 'test', attributes: {} },
]);
} catch (e) {
error = e;
}

expect(SavedObjectsErrorHelpers.isForbiddenError(error)).toBe(true);
});
});

describe('update', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,30 @@ describe('WorkspaceSavedObjectsClientWrapper', () => {
workspaces: ['workspace-1'],
});
});
it('should throw permission error when user bulkCreate workspace and is not dashboard admin', async () => {
const { wrapper } = generateWorkspaceSavedObjectsClientWrapper();
let errorCatched;
try {
await wrapper.bulkCreate([{ type: 'workspace', attributes: {} }]);
} catch (e) {
errorCatched = e;
}
expect(errorCatched?.message).toEqual('Invalid permission, please contact OSD admin');

try {
await wrapper.bulkCreate([{ type: 'workspace', id: 'test', attributes: {} }]);
} catch (e) {
errorCatched = e;
}
expect(errorCatched?.message).toEqual('Invalid permission, please contact OSD admin');

try {
await wrapper.bulkCreate([{ type: 'workspace', attributes: {} }], { overwrite: true });
} catch (e) {
errorCatched = e;
}
expect(errorCatched?.message).toEqual('Invalid permission, please contact OSD admin');
});
});

describe('create', () => {
Expand Down Expand Up @@ -426,6 +450,30 @@ describe('WorkspaceSavedObjectsClientWrapper', () => {
}
);
});
it('should throw permission error when user create a workspace and is not dashboard admin', async () => {
const { wrapper } = generateWorkspaceSavedObjectsClientWrapper();
let errorCatched;
try {
await wrapper.create('workspace', { name: 'test' }, { overwrite: true });
} catch (e) {
errorCatched = e;
}
expect(errorCatched?.message).toEqual('Invalid permission, please contact OSD admin');

try {
await wrapper.create('workspace', { name: 'test' }, { id: 'test' });
} catch (e) {
errorCatched = e;
}
expect(errorCatched?.message).toEqual('Invalid permission, please contact OSD admin');

try {
await wrapper.create('workspace', { name: 'test' }, {});
} catch (e) {
errorCatched = e;
}
expect(errorCatched?.message).toEqual('Invalid permission, please contact OSD admin');
});
});
describe('get', () => {
it('should return saved object if no need to validate permission', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ const generateSavedObjectsPermissionError = () =>
)
);

const generateOSDAdminPermissionError = () =>
SavedObjectsErrorHelpers.decorateForbiddenError(
new Error(
i18n.translate('dashboard.admin.permission.invalidate', {
defaultMessage: 'Invalid permission, please contact OSD admin',
})
)
);

const intersection = <T extends string>(...args: T[][]) => {
const occursCountMap: { [key: string]: number } = {};
for (let i = 0; i < args.length; i++) {
Expand Down Expand Up @@ -272,6 +281,13 @@ export class WorkspaceSavedObjectsClientWrapper {
objects: Array<SavedObjectsBulkCreateObject<T>>,
options: SavedObjectsCreateOptions = {}
): Promise<SavedObjectsBulkResponse<T>> => {
// Objects with id in overwrite mode will be regarded as update
const objectsToCreate = options.overwrite ? objects.filter((obj) => !obj.id) : objects;
// Only OSD admin can bulkCreate workspace.
yubonluo marked this conversation as resolved.
Show resolved Hide resolved
if (objectsToCreate.some((obj) => obj.type === WORKSPACE_TYPE)) {
throw generateOSDAdminPermissionError();
}

const hasTargetWorkspaces = options?.workspaces && options.workspaces.length > 0;

if (
Expand Down Expand Up @@ -330,6 +346,13 @@ export class WorkspaceSavedObjectsClientWrapper {
attributes: T,
options?: SavedObjectsCreateOptions
) => {
// If options contains id and overwrite, it is an update action.
const isUpdateMode = options?.id && options?.overwrite;
// Only OSD admin can create workspace.
if (type === WORKSPACE_TYPE && !isUpdateMode) {
throw generateOSDAdminPermissionError();
}

const hasTargetWorkspaces = options?.workspaces && options.workspaces.length > 0;

if (
Expand Down
Loading