Skip to content

Commit

Permalink
feat: accomplish dashboard_admin (#95)
Browse files Browse the repository at this point in the history
* feat: accomplish dashboard_admin

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: add yml default config and comment

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: update

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

---------

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>
  • Loading branch information
SuZhou-Joe authored Aug 22, 2023
1 parent e2778ad commit 866e122
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 10 deletions.
3 changes: 3 additions & 0 deletions config/opensearch_dashboards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,6 @@
# Set the value of this setting to true to enable plugin augmentation on Dashboard
# vis_augmenter.pluginAugmentationEnabled: true

# Set the backend roles, whoever has the backend roles defined in this config will be regard as dashboard admin.
# Dashboard admin will have the access to all the workspaces and objects inside OpenSearch Dashboards.
# workspace.dashboardAdmin.backendRoles: ["dashboard_admin"]
24 changes: 24 additions & 0 deletions src/plugins/workspace/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { schema, TypeOf } from '@osd/config-schema';

export const configSchema = schema.object({
enabled: schema.boolean({ defaultValue: false }),
dashboardAdmin: schema.object(
{
backendRoles: schema.arrayOf(schema.string(), {
defaultValue: ['dashboard_admin'],
}),
},
{
defaultValue: {
backendRoles: ['dashboard_admin'],
},
}
),
});

export type ConfigSchema = TypeOf<typeof configSchema>;
10 changes: 2 additions & 8 deletions src/plugins/workspace/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { schema } from '@osd/config-schema';

import { PluginConfigDescriptor, PluginInitializerContext } from '../../../core/server';
import { WorkspacePlugin } from './plugin';
import { configSchema } from '../config';

// This exports static code and TypeScript types,
// as well as, OpenSearch Dashboards Platform `plugin()` initializer.
Expand All @@ -15,10 +13,6 @@ export function plugin(initializerContext: PluginInitializerContext) {
return new WorkspacePlugin(initializerContext);
}

export { MlCommonsPluginSetup, MlCommonsPluginStart } from './types';

export const config: PluginConfigDescriptor = {
schema: schema.object({
enabled: schema.boolean({ defaultValue: false }),
}),
schema: configSchema,
};
9 changes: 8 additions & 1 deletion src/plugins/workspace/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { i18n } from '@osd/i18n';
import { Observable } from 'rxjs';

import {
PluginInitializerContext,
Expand All @@ -23,10 +24,12 @@ import { IWorkspaceDBImpl, WorkspaceAttribute } from './types';
import { WorkspaceClientWithSavedObject } from './workspace_client';
import { WorkspaceSavedObjectsClientWrapper } from './saved_objects';
import { registerRoutes } from './routes';
import { ConfigSchema } from '../config';

export class WorkspacePlugin implements Plugin<{}, {}> {
private readonly logger: Logger;
private client?: IWorkspaceDBImpl;
private config$: Observable<ConfigSchema>;

private proxyWorkspaceTrafficToRealHandler(setupDeps: CoreSetup) {
/**
Expand All @@ -47,6 +50,7 @@ export class WorkspacePlugin implements Plugin<{}, {}> {

constructor(initializerContext: PluginInitializerContext) {
this.logger = initializerContext.logger.get('plugins', 'workspace');
this.config$ = initializerContext.config.create<ConfigSchema>();
}

public async setup(core: CoreSetup) {
Expand All @@ -56,7 +60,10 @@ export class WorkspacePlugin implements Plugin<{}, {}> {

await this.client.setup(core);
const workspaceSavedObjectsClientWrapper = new WorkspaceSavedObjectsClientWrapper(
core.savedObjects.permissionControl
core.savedObjects.permissionControl,
{
config$: this.config$,
}
);

core.savedObjects.addClientWrapper(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import { i18n } from '@osd/i18n';
import Boom from '@hapi/boom';
import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';

import {
OpenSearchDashboardsRequest,
Expand All @@ -29,6 +31,7 @@ import {
ACL,
WorkspacePermissionMode,
} from '../../../../core/server';
import { ConfigSchema } from '../../config';

// Can't throw unauthorized for now, the page will be refreshed if unauthorized
const generateWorkspacePermissionError = () =>
Expand Down Expand Up @@ -56,6 +59,7 @@ const isWorkspacesLikeAttributes = (attributes: unknown): attributes is Attribut
Array.isArray((attributes as { workspaces: unknown }).workspaces);

export class WorkspaceSavedObjectsClientWrapper {
private config?: ConfigSchema;
private formatWorkspacePermissionModeToStringArray(
permission: WorkspacePermissionMode | WorkspacePermissionMode[]
): string[] {
Expand Down Expand Up @@ -137,6 +141,14 @@ export class WorkspaceSavedObjectsClientWrapper {
}
}

private isDashboardAdmin(request: OpenSearchDashboardsRequest): boolean {
const config = this.config || ({} as ConfigSchema);
const principals = this.permissionControl.getPrincipalsFromRequest(request);
const adminBackendRoles = config?.dashboardAdmin?.backendRoles || [];
const matchAny = principals?.groups?.some((item) => adminBackendRoles.includes(item)) || false;
return matchAny;
}

/**
* check if the type include workspace
* Workspace permission check is totally different from object permission check.
Expand Down Expand Up @@ -374,6 +386,12 @@ export class WorkspaceSavedObjectsClientWrapper {
return await wrapperOptions.client.addToWorkspaces(objects, targetWorkspaces, options);
};

const isDashboardAdmin = this.isDashboardAdmin(wrapperOptions.request);

if (isDashboardAdmin) {
return wrapperOptions.client;
}

return {
...wrapperOptions.client,
get: getWithWorkspacePermissionControl,
Expand All @@ -392,5 +410,20 @@ export class WorkspaceSavedObjectsClientWrapper {
};
};

constructor(private readonly permissionControl: SavedObjectsPermissionControlContract) {}
constructor(
private readonly permissionControl: SavedObjectsPermissionControlContract,
private readonly options: {
config$: Observable<ConfigSchema>;
}
) {
this.options.config$.subscribe((config) => {
this.config = config;
});
this.options.config$
.pipe(first())
.toPromise()
.then((config) => {
this.config = config;
});
}
}

0 comments on commit 866e122

Please sign in to comment.