Skip to content

Commit

Permalink
Refractor: move permission control service to workspace directory and…
Browse files Browse the repository at this point in the history
… keep saved objects as clean as possible (opensearch-project#166) (opensearch-project#172)

* refractor: move permission control service to workspace directory and keep saved objects as clean as possible

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

* feat: make lint pass

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

---------

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>
(cherry picked from commit c2a9bd2)
  • Loading branch information
SuZhou-Joe authored Sep 15, 2023
1 parent 3352c1f commit d980ee4
Show file tree
Hide file tree
Showing 13 changed files with 57 additions and 51 deletions.
4 changes: 3 additions & 1 deletion src/core/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,9 @@ export {
SavedObjectsDeleteByWorkspaceOptions,
Permissions,
ACL,
SavedObjectsPermissionControlContract,
Principals,
TransformedPermission,
PrincipalType,
} from './saved_objects';

export {
Expand Down
1 change: 0 additions & 1 deletion src/core/server/legacy/legacy_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,6 @@ export class LegacyService implements CoreService {
registerType: setupDeps.core.savedObjects.registerType,
getImportExportObjectLimit: setupDeps.core.savedObjects.getImportExportObjectLimit,
setRepositoryFactoryProvider: setupDeps.core.savedObjects.setRepositoryFactoryProvider,
permissionControl: setupDeps.core.savedObjects.permissionControl,
},
status: {
isStatusPageAnonymous: setupDeps.core.status.isStatusPageAnonymous,
Expand Down
1 change: 0 additions & 1 deletion src/core/server/plugins/plugin_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ export function createPluginSetupContext<TPlugin, TPluginDependencies>(
registerType: deps.savedObjects.registerType,
getImportExportObjectLimit: deps.savedObjects.getImportExportObjectLimit,
setRepositoryFactoryProvider: deps.savedObjects.setRepositoryFactoryProvider,
permissionControl: deps.savedObjects.permissionControl,
},
status: {
core$: deps.status.core$,
Expand Down
9 changes: 7 additions & 2 deletions src/core/server/saved_objects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,10 @@ export {
export { savedObjectsConfig, savedObjectsMigrationConfig } from './saved_objects_config';
export { SavedObjectTypeRegistry, ISavedObjectTypeRegistry } from './saved_objects_type_registry';

export { Permissions, ACL } from './permission_control/acl';
export { SavedObjectsPermissionControlContract } from './permission_control/client';
export {
Permissions,
ACL,
Principals,
TransformedPermission,
PrincipalType,
} from './permission_control/acl';
2 changes: 0 additions & 2 deletions src/core/server/saved_objects/saved_objects_service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import { typeRegistryMock } from './saved_objects_type_registry.mock';
import { migrationMocks } from './migrations/mocks';
import { ServiceStatusLevels } from '../status';
import { ISavedObjectTypeRegistry } from './saved_objects_type_registry';
import { savedObjectsPermissionControlMock } from './permission_control/client.mock';

type SavedObjectsServiceContract = PublicMethodsOf<SavedObjectsService>;

Expand Down Expand Up @@ -81,7 +80,6 @@ const createSetupContractMock = () => {
registerType: jest.fn(),
getImportExportObjectLimit: jest.fn(),
setRepositoryFactoryProvider: jest.fn(),
permissionControl: savedObjectsPermissionControlMock,
};

setupContract.getImportExportObjectLimit.mockReturnValue(100);
Expand Down
17 changes: 0 additions & 17 deletions src/core/server/saved_objects/saved_objects_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,6 @@ import { registerRoutes } from './routes';
import { ServiceStatus } from '../status';
import { calculateStatus$ } from './status';
import { createMigrationOpenSearchClient } from './migrations/core/';
import {
SavedObjectsPermissionControl,
SavedObjectsPermissionControlContract,
} from './permission_control/client';
import { registerPermissionCheckRoutes } from './permission_control/routes';
/**
* Saved Objects is OpenSearchDashboards's data persistence mechanism allowing plugins to
* use OpenSearch for storing and querying state. The SavedObjectsServiceSetup API exposes methods
Expand Down Expand Up @@ -180,8 +175,6 @@ export interface SavedObjectsServiceSetup {
setRepositoryFactoryProvider: (
respositoryFactoryProvider: SavedObjectRepositoryFactoryProvider
) => void;

permissionControl: SavedObjectsPermissionControlContract;
}

/**
Expand Down Expand Up @@ -308,7 +301,6 @@ export class SavedObjectsService
private started = false;

private respositoryFactoryProvider?: SavedObjectRepositoryFactoryProvider;
private permissionControl?: SavedObjectsPermissionControlContract;

constructor(private readonly coreContext: CoreContext) {
this.logger = coreContext.logger.get('savedobjects-service');
Expand Down Expand Up @@ -336,13 +328,6 @@ export class SavedObjectsService
migratorPromise: this.migrator$.pipe(first()).toPromise(),
});

this.permissionControl = new SavedObjectsPermissionControl(this.logger);

registerPermissionCheckRoutes({
http: setupDeps.http,
permissionControl: this.permissionControl,
});

return {
status$: calculateStatus$(
this.migrator$.pipe(switchMap((migrator) => migrator.getStatus$())),
Expand Down Expand Up @@ -383,7 +368,6 @@ export class SavedObjectsService
}
this.respositoryFactoryProvider = repositoryProvider;
},
permissionControl: this.permissionControl,
};
}

Expand Down Expand Up @@ -500,7 +484,6 @@ export class SavedObjectsService
this.started = true;

const getScopedClient = clientProvider.getClient.bind(clientProvider);
this.permissionControl?.setup(repositoryFactory.createInternalRepository);

return {
getScopedClient,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import { SavedObjectsPermissionControlContract } from './client';

export const savedObjectsPermissionControlMock: SavedObjectsPermissionControlContract = {
setup: jest.fn(),
validate: jest.fn(),
batchValidate: jest.fn(),
getPrincipalsOfObjects: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { i18n } from '@osd/i18n';
import { OpenSearchDashboardsRequest } from '../../http';
import { ensureRawRequest } from '../../http/router';
import { SavedObjectsServiceStart } from '../saved_objects_service';
import { SavedObjectsBulkGetObject } from '../service';
import { ACL, Principals, TransformedPermission, PrincipalType } from './acl';
import { WORKSPACE_TYPE } from '../../../utils';
import { Logger } from '../../logging';
import { ensureRawRequest, OpenSearchDashboardsRequest } from '../../../../core/server';
import {
ACL,
Principals,
TransformedPermission,
PrincipalType,
SavedObjectsBulkGetObject,
SavedObjectsServiceStart,
Logger,
WORKSPACE_TYPE,
} from '../../../../core/server';
import { WORKSPACE_SAVED_OBJECTS_CLIENT_WRAPPER_ID } from '../../common/constants';

export type SavedObjectsPermissionControlContract = Pick<
SavedObjectsPermissionControl,
Expand All @@ -25,9 +30,11 @@ export interface AuthInfo {

export class SavedObjectsPermissionControl {
private readonly logger: Logger;
private createInternalRepository?: SavedObjectsServiceStart['createInternalRepository'];
private getInternalRepository() {
return this.createInternalRepository?.();
private _getScopedClient?: SavedObjectsServiceStart['getScopedClient'];
private getScopedClient(request: OpenSearchDashboardsRequest) {
return this._getScopedClient?.(request, {
excludedWrappers: [WORKSPACE_SAVED_OBJECTS_CLIENT_WRAPPER_ID],
});
}

constructor(logger: Logger) {
Expand Down Expand Up @@ -65,12 +72,10 @@ export class SavedObjectsPermissionControl {
request: OpenSearchDashboardsRequest,
savedObjects: SavedObjectsBulkGetObject[]
) {
return (await this.getInternalRepository()?.bulkGet(savedObjects))?.saved_objects || [];
return (await this.getScopedClient?.(request)?.bulkGet(savedObjects))?.saved_objects || [];
}
public async setup(
createInternalRepository: SavedObjectsServiceStart['createInternalRepository']
) {
this.createInternalRepository = createInternalRepository;
public async setup(getScopedClient: SavedObjectsServiceStart['getScopedClient']) {
this._getScopedClient = getScopedClient;
}
public async validate(
request: OpenSearchDashboardsRequest,
Expand Down Expand Up @@ -164,9 +169,9 @@ export class SavedObjectsPermissionControl {
permissionModes: SavedObjectsPermissionModes
) {
const principals = this.getPrincipalsFromRequest(request);
const repository = this.getInternalRepository();
const savedObjectClient = this.getScopedClient?.(request);
try {
const result = await repository?.find({
const result = await savedObjectClient?.find({
type: [WORKSPACE_TYPE],
ACLSearchParams: {
permissionModes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { InternalHttpServiceSetup } from '../../../http';
import { HttpServiceSetup } from '../../../../../core/server';
import { SavedObjectsPermissionControlContract } from '../client';
import { registerListRoute } from './principals';
import { registerValidateRoute } from './validate';
Expand All @@ -12,10 +12,10 @@ export function registerPermissionCheckRoutes({
http,
permissionControl,
}: {
http: InternalHttpServiceSetup;
http: HttpServiceSetup;
permissionControl: SavedObjectsPermissionControlContract;
}) {
const router = http.createRouter('/api/saved_objects_permission_control/');
const router = http.createRouter();

registerValidateRoute(router, permissionControl);
registerListRoute(router, permissionControl);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@
*/

import { schema } from '@osd/config-schema';
import { IRouter } from '../../../http';
import { IRouter } from '../../../../../core/server';
import { SavedObjectsPermissionControlContract } from '../client';
import { WORKSPACES_API_BASE_URL } from '../../routes';

export const registerListRoute = (
router: IRouter,
permissionControl: SavedObjectsPermissionControlContract
) => {
router.post(
{
path: '/principals',
path: `${WORKSPACES_API_BASE_URL}/principals`,
validate: {
body: schema.object({
objects: schema.arrayOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@
*/

import { schema } from '@osd/config-schema';
import { IRouter } from '../../../http';
import { IRouter } from '../../../../../core/server';
import { SavedObjectsPermissionControlContract } from '../client';
import { WORKSPACES_API_BASE_URL } from '../../routes';

export const registerValidateRoute = (
router: IRouter,
permissionControl: SavedObjectsPermissionControlContract
) => {
router.post(
{
path: '/validate/{type}/{id}',
path: `${WORKSPACES_API_BASE_URL}/validate/{type}/{id}`,
validate: {
params: schema.object({
type: schema.string(),
Expand Down
16 changes: 15 additions & 1 deletion src/plugins/workspace/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,17 @@ import {
WORKSPACE_SAVED_OBJECTS_CLIENT_WRAPPER_ID,
} from '../common/constants';
import { ConfigSchema } from '../config';
import {
SavedObjectsPermissionControl,
SavedObjectsPermissionControlContract,
} from './permission_control/client';
import { registerPermissionCheckRoutes } from './permission_control/routes';

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

private proxyWorkspaceTrafficToRealHandler(setupDeps: CoreSetup) {
/**
Expand Down Expand Up @@ -65,8 +71,15 @@ export class WorkspacePlugin implements Plugin<{}, {}> {
this.client = new WorkspaceClientWithSavedObject(core);

await this.client.setup(core);
this.permissionControl = new SavedObjectsPermissionControl(this.logger);

registerPermissionCheckRoutes({
http: core.http,
permissionControl: this.permissionControl,
});

const workspaceSavedObjectsClientWrapper = new WorkspaceSavedObjectsClientWrapper(
core.savedObjects.permissionControl,
this.permissionControl,
{
config$: this.config$,
}
Expand Down Expand Up @@ -171,6 +184,7 @@ export class WorkspacePlugin implements Plugin<{}, {}> {

public start(core: CoreStart) {
this.logger.debug('Starting SavedObjects service');
this.permissionControl?.setup(core.savedObjects.getScopedClient);
this.client?.setSavedObjectes(core.savedObjects);
this.setupWorkspaces(core);

Expand Down
2 changes: 1 addition & 1 deletion src/plugins/workspace/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from '../../../../core/server';
import { IWorkspaceDBImpl, WorkspaceRoutePermissionItem } from '../types';

const WORKSPACES_API_BASE_URL = '/api/workspaces';
export const WORKSPACES_API_BASE_URL = '/api/workspaces';

const workspacePermissionMode = schema.oneOf([
schema.literal(WorkspacePermissionMode.LibraryRead),
Expand Down

0 comments on commit d980ee4

Please sign in to comment.