Skip to content

Commit

Permalink
add permissions fields to workspace CRUD APIs
Browse files Browse the repository at this point in the history
Signed-off-by: Lin Wang <[email protected]>
  • Loading branch information
wanglam committed Feb 28, 2024
1 parent 9168d9b commit 8f106ae
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 7 deletions.
3 changes: 2 additions & 1 deletion src/core/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export {
StringValidationRegex,
StringValidationRegexString,
WorkspaceObject,
WorkspaceAttribute,
} from '../types';

export {
Expand Down Expand Up @@ -353,4 +354,4 @@ export { __osdBootstrap__ } from './osd_bootstrap';

export { WorkspacesStart, WorkspacesSetup, WorkspacesService } from './workspace';

export { WORKSPACE_TYPE } from '../utils';
export { WORKSPACE_TYPE, WorkspacePermissionMode } from '../utils';
1 change: 1 addition & 0 deletions src/core/server/http/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export {
RouteValidationResultFactory,
DestructiveRouteMethod,
SafeRouteMethod,
ensureRawRequest,
} from './router';
export { BasePathProxyServer } from './base_path_proxy_server';
export { OnPreRoutingHandler, OnPreRoutingToolkit } from './lifecycle/on_pre_routing';
Expand Down
2 changes: 2 additions & 0 deletions src/core/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ export {
SessionStorageFactory,
DestructiveRouteMethod,
SafeRouteMethod,
ensureRawRequest,
} from './http';

export {
Expand Down Expand Up @@ -355,6 +356,7 @@ export {
MANAGEMENT_WORKSPACE_ID,
WORKSPACE_TYPE,
PERSONAL_WORKSPACE_ID_PREFIX,
WorkspacePermissionMode,
} from '../utils';

export {
Expand Down
7 changes: 7 additions & 0 deletions src/core/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ export const WORKSPACE_TYPE = 'workspace';

export const WORKSPACE_PATH_PREFIX = '/w';

export enum WorkspacePermissionMode {
Read = 'read',
Write = 'write',
LibraryRead = 'library_read',
LibraryWrite = 'library_write',
}

export const PUBLIC_WORKSPACE_ID = 'public';

export const MANAGEMENT_WORKSPACE_ID = 'management';
Expand Down
1 change: 1 addition & 0 deletions src/core/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ export {
MANAGEMENT_WORKSPACE_ID,
WORKSPACE_TYPE,
PERSONAL_WORKSPACE_ID_PREFIX,
WorkspacePermissionMode,
} from './constants';
18 changes: 16 additions & 2 deletions src/plugins/workspace/public/workspace_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
HttpSetup,
WorkspaceAttribute,
WorkspacesSetup,
WorkspacePermissionMode,
} from '../../../core/public';

const WORKSPACES_API_BASE_URL = '/api/workspaces';
Expand All @@ -29,6 +30,15 @@ type IResponse<T> =
error?: string;
};

type WorkspacePermissionItem = {
modes: Array<
| WorkspacePermissionMode.LibraryRead
| WorkspacePermissionMode.LibraryWrite
| WorkspacePermissionMode.Read
| WorkspacePermissionMode.Write
>;
} & ({ type: 'user'; userId: string } | { type: 'group'; group: string });

interface WorkspaceFindOptions {
page?: number;
perPage?: number;
Expand Down Expand Up @@ -151,14 +161,16 @@ export class WorkspaceClient {
* @returns
*/
public async create(
attributes: Omit<WorkspaceAttribute, 'id'>
attributes: Omit<WorkspaceAttribute, 'id'>,
permissions?: WorkspacePermissionItem[]
): Promise<IResponse<WorkspaceAttribute>> {
const path = this.getPath();

const result = await this.safeFetch<WorkspaceAttribute>(path, {
method: 'POST',
body: JSON.stringify({
attributes,
permissions,
}),
});

Expand Down Expand Up @@ -236,11 +248,13 @@ export class WorkspaceClient {
*/
public async update(
id: string,
attributes: Partial<WorkspaceAttribute>
attributes: Partial<WorkspaceAttribute>,
permissions?: WorkspacePermissionItem[]
): Promise<IResponse<boolean>> {
const path = this.getPath(id);
const body = {
attributes,
permissions,
};

const result = await this.safeFetch(path, {
Expand Down
20 changes: 20 additions & 0 deletions src/plugins/workspace/server/integration_tests/routes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,26 @@ describe('workspace service', () => {
expect(result.body.success).toEqual(true);
expect(typeof result.body.result.id).toBe('string');
});
it('create with permissions', async () => {
await osdTestServer.request
.post(root, `/api/workspaces`)
.send({
attributes: omitId(testWorkspace),
permissions: [{ type: 'invalid-type', userId: 'foo', modes: ['library_read', 'read'] }],
})
.expect(400);

const result: any = await osdTestServer.request
.post(root, `/api/workspaces`)
.send({
attributes: omitId(testWorkspace),
permissions: [{ type: 'user', userId: 'foo', modes: ['library_read', 'read'] }],
})
.expect(200);

expect(result.body.success).toEqual(true);
expect(typeof result.body.result.id).toBe('string');
});
it('get', async () => {
const result = await osdTestServer.request
.post(root, `/api/workspaces`)
Expand Down
63 changes: 59 additions & 4 deletions src/plugins/workspace/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,36 @@
*/

import { schema } from '@osd/config-schema';
import { CoreSetup, Logger } from '../../../../core/server';
import { IWorkspaceClientImpl } from '../types';
import {
CoreSetup,
Logger,
WorkspacePermissionMode,
ensureRawRequest,
} from '../../../../core/server';
import { IWorkspaceClientImpl, WorkspacePermissionItem } from '../types';

const WORKSPACES_API_BASE_URL = '/api/workspaces';

const workspacePermissionMode = schema.oneOf([
schema.literal(WorkspacePermissionMode.Read),
schema.literal(WorkspacePermissionMode.Write),
schema.literal(WorkspacePermissionMode.LibraryRead),
schema.literal(WorkspacePermissionMode.LibraryWrite),
]);

const workspacePermission = schema.oneOf([
schema.object({
type: schema.literal('user'),
userId: schema.string(),
modes: schema.arrayOf(workspacePermissionMode),
}),
schema.object({
type: schema.literal('group'),
group: schema.string(),
modes: schema.arrayOf(workspacePermissionMode),
}),
]);

const workspaceAttributesSchema = schema.object({
description: schema.maybe(schema.string()),
name: schema.string(),
Expand Down Expand Up @@ -40,6 +65,7 @@ export function registerRoutes({
page: schema.number({ min: 0, defaultValue: 1 }),
sortField: schema.maybe(schema.string()),
searchFields: schema.maybe(schema.arrayOf(schema.string())),
permissionModes: schema.maybe(schema.arrayOf(workspacePermissionMode)),
}),
},
},
Expand Down Expand Up @@ -94,11 +120,31 @@ export function registerRoutes({
validate: {
body: schema.object({
attributes: workspaceAttributesSchema,
permissions: schema.maybe(
schema.oneOf([workspacePermission, schema.arrayOf(workspacePermission)])
),
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
const { attributes } = req.body;
const { attributes, permissions: permissionsInRequest } = req.body;
const rawRequest = ensureRawRequest(req);
const authInfo = rawRequest?.auth?.credentials?.authInfo as { user_name?: string } | null;
let permissions: WorkspacePermissionItem[] = [];
if (permissionsInRequest) {
permissions = Array.isArray(permissionsInRequest)
? permissionsInRequest
: [permissionsInRequest];
}

// Assign workspace owner to current user
if (!!authInfo?.user_name) {
permissions.push({
type: 'user',
userId: authInfo.user_name,
modes: [WorkspacePermissionMode.LibraryWrite, WorkspacePermissionMode.Write],
});
}

const result = await client.create(
{
Expand All @@ -108,6 +154,7 @@ export function registerRoutes({
},
{
...attributes,
...(permissions.length ? { permissions } : {}),
}
);
return res.ok({ body: result });
Expand All @@ -122,12 +169,19 @@ export function registerRoutes({
}),
body: schema.object({
attributes: workspaceAttributesSchema,
permissions: schema.maybe(
schema.oneOf([workspacePermission, schema.arrayOf(workspacePermission)])
),
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
const { id } = req.params;
const { attributes } = req.body;
const { attributes, permissions } = req.body;
let finalPermissions: WorkspacePermissionItem[] = [];
if (permissions) {
finalPermissions = Array.isArray(permissions) ? permissions : [permissions];
}

const result = await client.update(
{
Expand All @@ -138,6 +192,7 @@ export function registerRoutes({
id,
{
...attributes,
...(finalPermissions.length ? { permissions: finalPermissions } : {}),
}
);
return res.ok({ body: result });
Expand Down
10 changes: 10 additions & 0 deletions src/plugins/workspace/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
RequestHandlerContext,
SavedObjectsFindResponse,
CoreSetup,
WorkspacePermissionMode,
WorkspaceAttribute,
SavedObjectsServiceStart,
} from '../../../core/server';
Expand Down Expand Up @@ -117,3 +118,12 @@ export type IResponse<T> =
success: false;
error?: string;
};

export type WorkspacePermissionItem = {
modes: Array<
| WorkspacePermissionMode.LibraryRead
| WorkspacePermissionMode.LibraryWrite
| WorkspacePermissionMode.Read
| WorkspacePermissionMode.Write
>;
} & ({ type: 'user'; userId: string } | { type: 'group'; group: string });

0 comments on commit 8f106ae

Please sign in to comment.