diff --git a/changelogs/fragments/8641.yml b/changelogs/fragments/8641.yml new file mode 100644 index 000000000000..db81be375e22 --- /dev/null +++ b/changelogs/fragments/8641.yml @@ -0,0 +1,2 @@ +feat: +- [Workspace] Response forbidden error for not permitted workspace ([#8641](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8641)) \ No newline at end of file diff --git a/src/plugins/home/server/services/sample_data/routes/install.test.ts b/src/plugins/home/server/services/sample_data/routes/install.test.ts index 9e174b5a53cb..22ec1b2b1096 100644 --- a/src/plugins/home/server/services/sample_data/routes/install.test.ts +++ b/src/plugins/home/server/services/sample_data/routes/install.test.ts @@ -5,6 +5,7 @@ import { CoreSetup, RequestHandlerContext } from 'src/core/server'; import { coreMock, httpServerMock } from '../../../../../../core/server/mocks'; +import { SavedObjectsErrorHelpers } from '../../../../../../core/server'; import { updateWorkspaceState } from '../../../../../../core/server/utils'; import { flightsSpecProvider } from '../data_sets'; import { SampleDatasetSchema } from '../lib/sample_dataset_registry_types'; @@ -222,4 +223,119 @@ describe('sample data install route', () => { }, }); }); + + it('handler response forbidden error when bulkCreate forbidden inside workspace', async () => { + const mockWorkspaceId = 'workspace'; + + const mockClient = jest.fn().mockResolvedValue(true); + + const mockSOClientGetResponse = { + saved_objects: [ + { + type: 'dashboard', + id: '12345', + namespaces: ['default'], + attributes: { title: 'dashboard' }, + }, + ], + }; + const mockSOClient = { + bulkCreate: jest + .fn() + .mockRejectedValue( + SavedObjectsErrorHelpers.decorateForbiddenError(new Error('Invalid workspace permission')) + ), + get: jest.fn().mockResolvedValue(mockSOClientGetResponse), + }; + + const mockContext = { + core: { + opensearch: { + legacy: { + client: { callAsCurrentUser: mockClient }, + }, + }, + savedObjects: { client: mockSOClient }, + }, + }; + const mockBody = { id: 'flights' }; + const mockQuery = {}; + const mockRequest = httpServerMock.createOpenSearchDashboardsRequest({ + params: mockBody, + query: mockQuery, + }); + updateWorkspaceState(mockRequest, { requestWorkspaceId: mockWorkspaceId }); + const mockResponse = httpServerMock.createResponseFactory(); + + createInstallRoute( + mockCoreSetup.http.createRouter(), + sampleDatasets, + mockLogger, + mockUsageTracker + ); + + const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; + const handler = mockRouter.post.mock.calls[0][1]; + + await handler((mockContext as unknown) as RequestHandlerContext, mockRequest, mockResponse); + + expect(mockResponse.forbidden).toBeCalled(); + expect(mockResponse.forbidden.mock.calls[0][0]).toMatchObject({ + body: expect.stringContaining('Invalid workspace permission'), + }); + }); + + it('handler response internal error when bulkCreate throw error', async () => { + const mockClient = jest.fn().mockResolvedValue(true); + + const mockSOClientGetResponse = { + saved_objects: [ + { + type: 'dashboard', + id: '12345', + namespaces: ['default'], + attributes: { title: 'dashboard' }, + }, + ], + }; + const mockSOClient = { + bulkCreate: jest.fn().mockRejectedValue(new Error('Unknown error')), + get: jest.fn().mockResolvedValue(mockSOClientGetResponse), + }; + + const mockContext = { + core: { + opensearch: { + legacy: { + client: { callAsCurrentUser: mockClient }, + }, + }, + savedObjects: { client: mockSOClient }, + }, + }; + const mockBody = { id: 'flights' }; + const mockQuery = {}; + const mockRequest = httpServerMock.createOpenSearchDashboardsRequest({ + params: mockBody, + query: mockQuery, + }); + const mockResponse = httpServerMock.createResponseFactory(); + + createInstallRoute( + mockCoreSetup.http.createRouter(), + sampleDatasets, + mockLogger, + mockUsageTracker + ); + + const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; + const handler = mockRouter.post.mock.calls[0][1]; + + await handler((mockContext as unknown) as RequestHandlerContext, mockRequest, mockResponse); + + expect(mockResponse.internalError).toBeCalled(); + expect(mockResponse.internalError.mock.calls[0][0]).toMatchObject({ + body: expect.stringContaining('Unknown error'), + }); + }); }); diff --git a/src/plugins/home/server/services/sample_data/routes/install.ts b/src/plugins/home/server/services/sample_data/routes/install.ts index 8134f8099352..923a57479c3b 100644 --- a/src/plugins/home/server/services/sample_data/routes/install.ts +++ b/src/plugins/home/server/services/sample_data/routes/install.ts @@ -31,6 +31,7 @@ import { schema } from '@osd/config-schema'; import { IRouter, LegacyCallAPIOptions, Logger } from 'src/core/server'; import { getWorkspaceState } from '../../../../../../core/server/utils'; +import { SavedObjectsErrorHelpers } from '../../../../../../core/server'; import { SampleDatasetSchema } from '../lib/sample_dataset_registry_types'; import { createIndexName } from '../lib/create_index_name'; import { @@ -217,6 +218,9 @@ export function createInstallRoute( } catch (err) { const errMsg = `bulkCreate failed, error: ${err.message}`; logger.warn(errMsg); + if (workspaceId && SavedObjectsErrorHelpers.isForbiddenError(err)) { + return res.forbidden({ body: errMsg }); + } return res.internalError({ body: errMsg }); } const errors = createResults.saved_objects.filter((savedObjectCreateResult) => {