Skip to content

Commit

Permalink
integrate dashboard admin with dynamic config
Browse files Browse the repository at this point in the history
Signed-off-by: yubonluo <yubonluo@amazon.com>
  • Loading branch information
yubonluo committed Oct 23, 2024
1 parent 12d072d commit f1e6c5b
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 49 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/8137.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
refactor:
- [Workspace] Integrate dashboard admin with dynamic config ([#8137](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8137))
1 change: 1 addition & 0 deletions src/plugins/workspace/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down
57 changes: 49 additions & 8 deletions src/plugins/workspace/server/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand Down
15 changes: 13 additions & 2 deletions src/plugins/workspace/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';
import { cloneDeep } from 'lodash';
import {
PluginInitializerContext,
CoreSetup,
Expand All @@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -97,8 +99,17 @@ export class WorkspacePlugin implements Plugin<WorkspacePluginSetup, WorkspacePl
} catch (e) {
return toolkit.next();
}
// Get config from dynamic service client.
const dynamicConfigServiceStart = await core.dynamicConfigService.getStartService();
const store = dynamicConfigServiceStart.getAsyncLocalStore();
const client = dynamicConfigServiceStart.getClient();
const config = await client.getConfig(
{ pluginConfigPath: OPENSEARCHDASHBOARDS_CONFIG_PATH },
{ asyncLocalStorageContext: store! }
);
const configUsers: string[] = cloneDeep(config.dashboardAdmin.users);
const configGroups: string[] = cloneDeep(config.dashboardAdmin.groups);

const [configGroups, configUsers] = await getOSDAdminConfigFromYMLConfig(this.globalConfig$);
updateDashboardAdminStateForRequest(request, groups, users, configGroups, configUsers);
return toolkit.next();
});
Expand Down
25 changes: 0 additions & 25 deletions src/plugins/workspace/server/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,19 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { AuthStatus } from '../../../core/server';
import {
httpServerMock,
httpServiceMock,
savedObjectsClientMock,
uiSettingsServiceMock,
} from '../../../core/server/mocks';
import {
generateRandomId,
getOSDAdminConfigFromYMLConfig,
updateDashboardAdminStateForRequest,
transferCurrentUserInPermissions,
getDataSourcesList,
checkAndSetDefaultDataSource,
} from './utils';
import { getWorkspaceState } from '../../../core/server/utils';
import { Observable, of } from 'rxjs';
import { DEFAULT_DATA_SOURCE_UI_SETTINGS_ID } from '../../data_source_management/common';
import { OSD_ADMIN_WILDCARD_MATCH_ALL } from '../common/constants';

Expand Down Expand Up @@ -97,27 +93,6 @@ describe('workspace utils', () => {
expect(getWorkspaceState(mockRequest)?.isDashboardAdmin).toBe(true);
});

it('should get correct admin config when admin config is enabled ', async () => {
const globalConfig$: Observable<any> = 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<any> = 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(
Expand Down
14 changes: 0 additions & 14 deletions src/plugins/workspace/server/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@
*/

import crypto from 'crypto';
import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';
import {
OpenSearchDashboardsRequest,
SharedGlobalConfig,
Permissions,
SavedObjectsClientContract,
IUiSettingsClient,
Expand Down Expand Up @@ -53,17 +50,6 @@ export const updateDashboardAdminStateForRequest = (
});
};

export const getOSDAdminConfigFromYMLConfig = async (
globalConfig$: Observable<SharedGlobalConfig>
) => {
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
Expand Down

0 comments on commit f1e6c5b

Please sign in to comment.