From f1e6c5b08ac9fd6eda6a0ba7825d2ee18b07eda6 Mon Sep 17 00:00:00 2001 From: yubonluo Date: Wed, 11 Sep 2024 20:14:12 +0800 Subject: [PATCH] integrate dashboard admin with dynamic config Signed-off-by: yubonluo --- changelogs/fragments/8137.yml | 2 + src/plugins/workspace/common/constants.ts | 1 + src/plugins/workspace/server/plugin.test.ts | 57 ++++++++++++++++++--- src/plugins/workspace/server/plugin.ts | 15 +++++- src/plugins/workspace/server/utils.test.ts | 25 --------- src/plugins/workspace/server/utils.ts | 14 ----- 6 files changed, 65 insertions(+), 49 deletions(-) create mode 100644 changelogs/fragments/8137.yml diff --git a/changelogs/fragments/8137.yml b/changelogs/fragments/8137.yml new file mode 100644 index 000000000000..78e5516f34a6 --- /dev/null +++ b/changelogs/fragments/8137.yml @@ -0,0 +1,2 @@ +refactor: +- [Workspace] Integrate dashboard admin with dynamic config ([#8137](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8137)) \ No newline at end of file diff --git a/src/plugins/workspace/common/constants.ts b/src/plugins/workspace/common/constants.ts index 366e5ace85d1..7ea882c03c14 100644 --- a/src/plugins/workspace/common/constants.ts +++ b/src/plugins/workspace/common/constants.ts @@ -168,6 +168,7 @@ export enum AssociationDataSourceModalMode { DirectQueryConnections = 'direction-query-connections', } export const USE_CASE_PREFIX = 'use-case-'; +export const OPENSEARCHDASHBOARDS_CONFIG_PATH = 'opensearchDashboards'; // Workspace will handle both data source and data connection type saved object. export const WORKSPACE_DATA_SOURCE_AND_CONNECTION_OBJECT_TYPES = [ diff --git a/src/plugins/workspace/server/plugin.test.ts b/src/plugins/workspace/server/plugin.test.ts index c3f47042d340..9149a02d6ffa 100644 --- a/src/plugins/workspace/server/plugin.test.ts +++ b/src/plugins/workspace/server/plugin.test.ts @@ -13,7 +13,6 @@ import { updateWorkspaceState, } from '../../../core/server/utils'; import * as serverUtils from '../../../core/server/utils/auth_info'; -import * as utilsExports from './utils'; import { SavedObjectsPermissionControl } from './permission_control/client'; describe('Workspace server plugin', () => { @@ -109,6 +108,21 @@ describe('Workspace server plugin', () => { describe('#setupPermission', () => { const setupMock = coreMock.createSetup(); + const getConfigMock = jest.fn().mockResolvedValue({ + dashboardAdmin: { + users: ['dashboard-admin-user'], + groups: [], + }, + }); + jest.spyOn(setupMock.dynamicConfigService, 'getStartService').mockResolvedValue({ + ...setupMock.dynamicConfigService.getStartService(), + getAsyncLocalStore: jest.fn(), + getClient: () => ({ + getConfig: getConfigMock, + bulkGetConfigs: jest.fn(), + listConfigs: jest.fn(), + }), + }); const initializerContextConfigMock = coreMock.createPluginInitializerContext({ enabled: true, permission: { @@ -137,13 +151,10 @@ describe('Workspace server plugin', () => { expect(toolKitMock.next).toBeCalledTimes(1); }); - it('with configuring user as OSD admin', async () => { + it('with dynamic config and user is dashboard admin', async () => { jest .spyOn(serverUtils, 'getPrincipalsFromRequest') - .mockImplementation(() => ({ users: [`user1`] })); - jest - .spyOn(utilsExports, 'getOSDAdminConfigFromYMLConfig') - .mockResolvedValue([['group1'], ['user1']]); + .mockImplementation(() => ({ users: [`dashboard-admin-user`] })); await workspacePlugin.setup(setupMock); const toolKitMock = httpServerMock.createToolkit(); @@ -160,11 +171,36 @@ describe('Workspace server plugin', () => { expect(toolKitMock.next).toBeCalledTimes(1); }); + it('with dynamic config and user is not dashboard admin', async () => { + jest + .spyOn(serverUtils, 'getPrincipalsFromRequest') + .mockImplementation(() => ({ users: [`none-dashboard-admin-user`] })); + + await workspacePlugin.setup(setupMock); + const toolKitMock = httpServerMock.createToolkit(); + + await registerOnPostAuthFn( + requestWithWorkspaceInUrl, + httpServerMock.createResponseFactory(), + toolKitMock + ); + + expect(getWorkspaceState(requestWithWorkspaceInUrl)).toEqual({ + isDashboardAdmin: false, + }); + expect(toolKitMock.next).toBeCalledTimes(1); + }); + it('with configuring wildcard * and anyone will be OSD admin', async () => { jest .spyOn(serverUtils, 'getPrincipalsFromRequest') .mockImplementation(() => ({ users: [`user1`] })); - jest.spyOn(utilsExports, 'getOSDAdminConfigFromYMLConfig').mockResolvedValue([[], ['*']]); + getConfigMock.mockResolvedValueOnce({ + dashboardAdmin: { + users: ['*'], + groups: [], + }, + }); await workspacePlugin.setup(setupMock); const toolKitMock = httpServerMock.createToolkit(); @@ -185,7 +221,12 @@ describe('Workspace server plugin', () => { jest .spyOn(serverUtils, 'getPrincipalsFromRequest') .mockImplementation(() => ({ users: [`user1`] })); - jest.spyOn(utilsExports, 'getOSDAdminConfigFromYMLConfig').mockResolvedValue([[], []]); + getConfigMock.mockResolvedValueOnce({ + dashboardAdmin: { + users: [], + groups: [], + }, + }); await workspacePlugin.setup(setupMock); const toolKitMock = httpServerMock.createToolkit(); diff --git a/src/plugins/workspace/server/plugin.ts b/src/plugins/workspace/server/plugin.ts index 6c82097fda3d..8aa8de48c8c7 100644 --- a/src/plugins/workspace/server/plugin.ts +++ b/src/plugins/workspace/server/plugin.ts @@ -5,6 +5,7 @@ import { Observable } from 'rxjs'; import { first } from 'rxjs/operators'; +import { cloneDeep } from 'lodash'; import { PluginInitializerContext, CoreSetup, @@ -26,6 +27,7 @@ import { WORKSPACE_NAVIGATION_APP_ID, DEFAULT_WORKSPACE, PRIORITY_FOR_REPOSITORY_WRAPPER, + OPENSEARCHDASHBOARDS_CONFIG_PATH, } from '../common/constants'; import { IWorkspaceClientImpl, WorkspacePluginSetup, WorkspacePluginStart } from './types'; import { WorkspaceClient } from './workspace_client'; @@ -47,7 +49,7 @@ import { SavedObjectsPermissionControl, SavedObjectsPermissionControlContract, } from './permission_control/client'; -import { getOSDAdminConfigFromYMLConfig, updateDashboardAdminStateForRequest } from './utils'; +import { updateDashboardAdminStateForRequest } from './utils'; import { WorkspaceIdConsumerWrapper } from './saved_objects/workspace_id_consumer_wrapper'; import { WorkspaceUiSettingsClientWrapper } from './saved_objects/workspace_ui_settings_client_wrapper'; import { uiSettings } from './ui_settings'; @@ -97,8 +99,17 @@ export class WorkspacePlugin implements Plugin { expect(getWorkspaceState(mockRequest)?.isDashboardAdmin).toBe(true); }); - it('should get correct admin config when admin config is enabled ', async () => { - const globalConfig$: Observable = of({ - opensearchDashboards: { - dashboardAdmin: { - groups: ['group1', 'group2'], - users: ['user1', 'user2'], - }, - }, - }); - const [groups, users] = await getOSDAdminConfigFromYMLConfig(globalConfig$); - expect(groups).toEqual(['group1', 'group2']); - expect(users).toEqual(['user1', 'user2']); - }); - - it('should get [] when admin config is not enabled', async () => { - const globalConfig$: Observable = of({}); - const [groups, users] = await getOSDAdminConfigFromYMLConfig(globalConfig$); - expect(groups).toEqual([]); - expect(users).toEqual([]); - }); - it('should transfer current user placeholder in permissions', () => { expect(transferCurrentUserInPermissions('foo', undefined)).toBeUndefined(); expect( diff --git a/src/plugins/workspace/server/utils.ts b/src/plugins/workspace/server/utils.ts index 0fab0ce56176..98bfe4339a52 100644 --- a/src/plugins/workspace/server/utils.ts +++ b/src/plugins/workspace/server/utils.ts @@ -4,11 +4,8 @@ */ import crypto from 'crypto'; -import { Observable } from 'rxjs'; -import { first } from 'rxjs/operators'; import { OpenSearchDashboardsRequest, - SharedGlobalConfig, Permissions, SavedObjectsClientContract, IUiSettingsClient, @@ -53,17 +50,6 @@ export const updateDashboardAdminStateForRequest = ( }); }; -export const getOSDAdminConfigFromYMLConfig = async ( - globalConfig$: Observable -) => { - const globalConfig = await globalConfig$.pipe(first()).toPromise(); - const groupsResult = (globalConfig.opensearchDashboards?.dashboardAdmin?.groups || - []) as string[]; - const usersResult = (globalConfig.opensearchDashboards?.dashboardAdmin?.users || []) as string[]; - - return [groupsResult, usersResult]; -}; - export const transferCurrentUserInPermissions = ( realUserId: string, permissions: Permissions | undefined