Skip to content

Commit 27cce8e

Browse files
opensearch-trigger-bot[bot]github-actions[bot]opensearch-changeset-bot[bot]
authored
[Workspace]Deny get or bulkGet for global data source (#8043) (#8051)
* Deny get or bulkGet for global data source * Changeset file for PR #8043 created/updated --------- (cherry picked from commit d22b68b) Signed-off-by: Lin Wang <wonglam@amazon.com> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com>
1 parent 59da1e3 commit 27cce8e

File tree

3 files changed

+83
-1
lines changed

3 files changed

+83
-1
lines changed

changelogs/fragments/8043.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
feat:
2+
- [Workspace]Deny get or bulkGet for global data source ([#8043](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8043))

src/plugins/workspace/server/saved_objects/workspace_saved_objects_client_wrapper.test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ const generateWorkspaceSavedObjectsClientWrapper = (role = NO_DASHBOARD_ADMIN) =
5353
id: 'global-data-source',
5454
attributes: { title: 'Global data source' },
5555
},
56+
{
57+
type: DATA_SOURCE_SAVED_OBJECT_TYPE,
58+
id: 'global-data-source-empty-workspaces',
59+
attributes: { title: 'Global data source empty workspaces' },
60+
workspaces: [],
61+
},
5662
{
5763
type: DATA_SOURCE_SAVED_OBJECT_TYPE,
5864
id: 'workspace-1-data-source',
@@ -620,6 +626,33 @@ describe('WorkspaceSavedObjectsClientWrapper', () => {
620626
workspaces: ['workspace-1'],
621627
});
622628
});
629+
630+
it('should throw permission error when tried to access a global data source', async () => {
631+
const { wrapper } = generateWorkspaceSavedObjectsClientWrapper();
632+
let errorCatched;
633+
try {
634+
await wrapper.get('data-source', 'global-data-source');
635+
} catch (e) {
636+
errorCatched = e;
637+
}
638+
expect(errorCatched?.message).toEqual(
639+
'Invalid data source permission, please associate it to current workspace'
640+
);
641+
});
642+
643+
it('should throw permission error when tried to access a empty workspaces global data source', async () => {
644+
const { wrapper, requestMock } = generateWorkspaceSavedObjectsClientWrapper();
645+
updateWorkspaceState(requestMock, { requestWorkspaceId: undefined });
646+
let errorCatched;
647+
try {
648+
await wrapper.get('data-source', 'global-data-source-empty-workspaces');
649+
} catch (e) {
650+
errorCatched = e;
651+
}
652+
expect(errorCatched?.message).toEqual(
653+
'Invalid data source permission, please associate it to current workspace'
654+
);
655+
});
623656
});
624657
describe('bulk get', () => {
625658
it("should call permission validate with object's workspace and throw permission error", async () => {
@@ -744,6 +777,36 @@ describe('WorkspaceSavedObjectsClientWrapper', () => {
744777
],
745778
});
746779
});
780+
781+
it('should throw permission error when tried to bulk get global data source', async () => {
782+
const { wrapper, requestMock } = generateWorkspaceSavedObjectsClientWrapper();
783+
updateWorkspaceState(requestMock, { requestWorkspaceId: undefined });
784+
let errorCatched;
785+
try {
786+
await wrapper.bulkGet([{ type: 'data-source', id: 'global-data-source' }]);
787+
} catch (e) {
788+
errorCatched = e;
789+
}
790+
expect(errorCatched?.message).toEqual(
791+
'Invalid data source permission, please associate it to current workspace'
792+
);
793+
});
794+
795+
it('should throw permission error when tried to bulk get a empty workspace global data source', async () => {
796+
const { wrapper, requestMock } = generateWorkspaceSavedObjectsClientWrapper();
797+
updateWorkspaceState(requestMock, { requestWorkspaceId: undefined });
798+
let errorCatched;
799+
try {
800+
await wrapper.bulkGet([
801+
{ type: 'data-source', id: 'global-data-source-empty-workspaces' },
802+
]);
803+
} catch (e) {
804+
errorCatched = e;
805+
}
806+
expect(errorCatched?.message).toEqual(
807+
'Invalid data source permission, please associate it to current workspace'
808+
);
809+
});
747810
});
748811
describe('find', () => {
749812
it('should call client.find with consistent params when ACLSearchParams and workspaceOperator not provided', async () => {

src/plugins/workspace/server/saved_objects/workspace_saved_objects_client_wrapper.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,24 @@ export class WorkspaceSavedObjectsClientWrapper {
204204
request: OpenSearchDashboardsRequest
205205
) => {
206206
const requestWorkspaceId = getWorkspaceState(request).requestWorkspaceId;
207-
return !requestWorkspaceId || !!object.workspaces?.includes(requestWorkspaceId);
207+
// Deny access if the object is a global data source (no workspaces assigned)
208+
if (!object.workspaces || object.workspaces.length === 0) {
209+
return false;
210+
}
211+
/**
212+
* Allow access if no specific workspace is requested.
213+
* This typically occurs when retrieving data sources or performing operations
214+
* that don't require a specific workspace, such as pages within the
215+
* Data Administration navigation group that include a data source picker.
216+
*/
217+
if (!requestWorkspaceId) {
218+
return true;
219+
}
220+
/*
221+
* Allow access if the requested workspace matches one of the object's assigned workspaces
222+
* This ensures that the user can only access data sources within their current workspace
223+
*/
224+
return object.workspaces.includes(requestWorkspaceId);
208225
};
209226

210227
private getWorkspaceTypeEnabledClient(request: OpenSearchDashboardsRequest) {

0 commit comments

Comments
 (0)