From 3fb480babaf035352f838a30ad667b359d8e953b Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Mon, 11 Sep 2023 14:27:34 +0800 Subject: [PATCH] refractor: remove additional method declaration Signed-off-by: SuZhou-Joe --- .../permission_control/client.ts | 23 +++- .../saved_objects/service/lib/repository.ts | 124 +----------------- .../service/lib/search_dsl/query_params.ts | 58 ++++++-- .../service/lib/search_dsl/search_dsl.ts | 7 +- .../server/saved_objects/service/lib/utils.ts | 28 +--- .../service/saved_objects_client.ts | 24 +--- src/core/server/saved_objects/types.ts | 11 +- .../workspace_saved_objects_client_wrapper.ts | 75 ++++++++++- 8 files changed, 155 insertions(+), 195 deletions(-) diff --git a/src/core/server/saved_objects/permission_control/client.ts b/src/core/server/saved_objects/permission_control/client.ts index 11e3d6e58dc8..bd47b5cf22ae 100644 --- a/src/core/server/saved_objects/permission_control/client.ts +++ b/src/core/server/saved_objects/permission_control/client.ts @@ -6,9 +6,10 @@ import { i18n } from '@osd/i18n'; import { OpenSearchDashboardsRequest } from '../../http'; import { ensureRawRequest } from '../../http/router'; import { SavedObjectsServiceStart } from '../saved_objects_service'; -import { SavedObjectsBulkGetObject, SavedObjectsRepository, SavedObjectsUtils } from '../service'; +import { SavedObjectsBulkGetObject } from '../service'; import { ACL, Principals, TransformedPermission, PrincipalType } from './acl'; import { Logger } from '../../logging'; +import { WORKSPACE_TYPE } from '../../../utils'; export type SavedObjectsPermissionControlContract = Pick< SavedObjectsPermissionControl, @@ -163,11 +164,19 @@ export class SavedObjectsPermissionControl { permissionModes: SavedObjectsPermissionModes ) { const principals = this.getPrincipalsFromRequest(request); - const repository = this.getInternalRepository() as SavedObjectsRepository; - return await SavedObjectsUtils.getPermittedWorkspaceIds({ - principals, - repository, - permissionModes, - }); + const repository = this.getInternalRepository(); + try { + const result = await repository?.find({ + type: [WORKSPACE_TYPE], + ACLSearchParams: { + workspacePermissionModes: permissionModes, + principals, + }, + perPage: 999, + }); + return result?.saved_objects.map((item) => item.id); + } catch (e) { + return []; + } } } diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts index e3575d575fdc..be7c093b59c9 100644 --- a/src/core/server/saved_objects/service/lib/repository.ts +++ b/src/core/server/saved_objects/service/lib/repository.ts @@ -31,7 +31,6 @@ import { omit, intersection } from 'lodash'; import type { opensearchtypes } from '@opensearch-project/opensearch'; import uuid from 'uuid'; -import { i18n } from '@osd/i18n'; import type { ISavedObjectTypeRegistry } from '../../saved_objects_type_registry'; import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; import { DeleteDocumentResponse, OpenSearchClient } from '../../../opensearch/'; @@ -91,9 +90,7 @@ import { FIND_DEFAULT_PER_PAGE, SavedObjectsUtils, } from './utils'; -import { PUBLIC_WORKSPACE_ID, WorkspacePermissionMode } from '../../../../utils/constants'; -import { ACL, Principals } from '../../permission_control/acl'; -import { WORKSPACE_TYPE } from '../../../../utils'; +import { PUBLIC_WORKSPACE_ID } from '../../../../utils/constants'; // BEWARE: The SavedObjectClient depends on the implementation details of the SavedObjectsRepository // so any breaking changes to this repository are considered breaking changes to the SavedObjectsClient. @@ -902,7 +899,7 @@ export class SavedObjectsRepository { filter, preference, workspaces, - queryDSL, + ACLSearchParams, } = options; if (!type && !typeToNamespacesMap) { @@ -977,7 +974,7 @@ export class SavedObjectsRepository { hasReference, kueryNode, workspaces, - queryDSL, + ACLSearchParams, }), }, }; @@ -1934,111 +1931,6 @@ export class SavedObjectsRepository { }; } - async getPermissionQuery(props: { - permissionTypes: string[]; - principals: Principals; - savedObjectType?: string[]; - }) { - return ACL.generateGetPermittedSavedObjectsQueryDSL( - props.permissionTypes, - props.principals, - props.savedObjectType - ); - } - - async processFindOptions(props: { - options: SavedObjectsFindOptions & { permissionModes?: string[] }; - principals: Principals; - }): Promise { - const { principals } = props; - const options = { ...props.options }; - if (this.isRelatedToWorkspace(options.type)) { - options.queryDSL = await this.getPermissionQuery({ - permissionTypes: options.permissionModes ?? [ - WorkspacePermissionMode.LibraryRead, - WorkspacePermissionMode.LibraryWrite, - WorkspacePermissionMode.Management, - ], - principals, - savedObjectType: [WORKSPACE_TYPE], - }); - } else { - const permittedWorkspaceIds = await SavedObjectsUtils.getPermittedWorkspaceIds({ - permissionModes: [ - WorkspacePermissionMode.LibraryRead, - WorkspacePermissionMode.LibraryWrite, - WorkspacePermissionMode.Management, - ], - principals, - repository: this, - }); - - if (options.workspaces) { - const permittedWorkspaces = options.workspaces.filter((item) => - (permittedWorkspaceIds || []).includes(item) - ); - if (!permittedWorkspaces.length) { - /** - * If user does not have any one workspace access - * deny the request - */ - throw SavedObjectsErrorHelpers.decorateNotAuthorizedError( - new Error( - i18n.translate('workspace.permission.invalidate', { - defaultMessage: 'Invalid workspace permission', - }) - ) - ); - } - - /** - * Overwrite the options.workspaces when user has access on partial workspaces. - */ - options.workspaces = permittedWorkspaces; - } else { - const queryDSL = await this.getPermissionQuery({ - permissionTypes: [WorkspacePermissionMode.Read, WorkspacePermissionMode.Write], - principals, - savedObjectType: Array.isArray(options.type) ? options.type : [options.type], - }); - options.workspaces = undefined; - /** - * Select all the docs that - * 1. ACL matches read or write permission OR - * 2. workspaces matches library_read or library_write or management OR - * 3. Advanced settings - */ - options.queryDSL = { - query: { - bool: { - filter: [ - { - bool: { - should: [ - { - term: { - type: 'config', - }, - }, - queryDSL.query, - { - terms: { - workspaces: permittedWorkspaceIds, - }, - }, - ], - }, - }, - ], - }, - }, - }; - } - } - - return options; - } - /** * Returns index specified by the given type or the default index * @@ -2167,16 +2059,6 @@ export class SavedObjectsRepository { } return body; } - - /** - * check if the type include workspace - * Workspace permission check is totally different from object permission check. - * @param type - * @returns - */ - private isRelatedToWorkspace(type: string | string[]): boolean { - return type === WORKSPACE_TYPE || (Array.isArray(type) && type.includes(WORKSPACE_TYPE)); - } } function getBulkOperationError(error: { type: string; reason?: string }, type: string, id: string) { diff --git a/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts b/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts index d9fbf7199c18..0bda20672e7d 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts @@ -27,13 +27,14 @@ * specific language governing permissions and limitations * under the License. */ -import { mergeWith, isArray } from 'lodash'; // @ts-expect-error no ts import { opensearchKuery } from '../../../opensearch_query'; type KueryNode = any; import { ISavedObjectTypeRegistry } from '../../../saved_objects_type_registry'; import { ALL_NAMESPACES_STRING, DEFAULT_NAMESPACE_STRING } from '../utils'; +import { SavedObjectsFindOptions } from '../../../types'; +import { ACL } from '../../../permission_control/acl'; /** * Gets the types based on the type. Uses mappings to support @@ -166,7 +167,7 @@ interface QueryParams { hasReference?: HasReferenceQueryParams; kueryNode?: KueryNode; workspaces?: string[]; - queryDSL?: Record; + ACLSearchParams?: SavedObjectsFindOptions['ACLSearchParams']; } export function getClauseForReference(reference: HasReferenceQueryParams) { @@ -224,7 +225,7 @@ export function getQueryParams({ hasReference, kueryNode, workspaces, - queryDSL, + ACLSearchParams, }: QueryParams) { const types = getTypes( registry, @@ -283,12 +284,51 @@ export function getQueryParams({ const result = { query: { bool } }; - if (queryDSL) { - return mergeWith({}, result, queryDSL, (objValue, srcValue) => { - if (isArray(objValue)) { - return objValue.concat(srcValue); - } - }); + if (ACLSearchParams) { + const shouldClause: any = []; + if (ACLSearchParams.workspacePermissionModes && ACLSearchParams.principals) { + const workspacePermissionDSL = ACL.generateGetPermittedSavedObjectsQueryDSL( + ACLSearchParams.workspacePermissionModes, + ACLSearchParams.principals + ); + shouldClause.push(workspacePermissionDSL.query); + } + + if (ACLSearchParams.workspaces) { + shouldClause.push({ + terms: { + workspaces: ACLSearchParams.workspaces, + }, + }); + } + + if (ACLSearchParams.objectPermissionModes && ACLSearchParams.principals) { + const objectPermissionDSL = ACL.generateGetPermittedSavedObjectsQueryDSL( + ACLSearchParams.objectPermissionModes, + ACLSearchParams.principals + ); + shouldClause.push(objectPermissionDSL.query); + } + + if (shouldClause.length) { + bool.filter.push({ + bool: { + should: [ + /** + * TODO remove this clause once advanced settings has attached with permission + */ + { + term: { + type: 'config', + }, + }, + ...shouldClause, + ], + }, + }); + } + + return result; } return result; } diff --git a/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts b/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts index d6b8b83ac87e..a93e134a9757 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts @@ -34,6 +34,7 @@ import { IndexMapping } from '../../../mappings'; import { getQueryParams } from './query_params'; import { getSortingParams } from './sorting_params'; import { ISavedObjectTypeRegistry } from '../../../saved_objects_type_registry'; +import { SavedObjectsFindOptions } from '../../../types'; type KueryNode = any; @@ -53,7 +54,7 @@ interface GetSearchDslOptions { }; kueryNode?: KueryNode; workspaces?: string[]; - queryDSL?: Record; + ACLSearchParams?: SavedObjectsFindOptions['ACLSearchParams']; } export function getSearchDsl( @@ -74,7 +75,7 @@ export function getSearchDsl( hasReference, kueryNode, workspaces, - queryDSL, + ACLSearchParams, } = options; if (!type) { @@ -98,7 +99,7 @@ export function getSearchDsl( hasReference, kueryNode, workspaces, - queryDSL, + ACLSearchParams, }), ...getSortingParams(mappings, type, sortField, sortOrder), }; diff --git a/src/core/server/saved_objects/service/lib/utils.ts b/src/core/server/saved_objects/service/lib/utils.ts index c2a156da60d2..490c2b7083d2 100644 --- a/src/core/server/saved_objects/service/lib/utils.ts +++ b/src/core/server/saved_objects/service/lib/utils.ts @@ -29,10 +29,7 @@ */ import { SavedObjectsFindOptions } from '../../types'; -import { SavedObjectsFindResponse, SavedObjectsRepository } from '..'; -import { Principals } from '../../permission_control/acl'; -import { SavedObjectsPermissionModes } from '../../permission_control/client'; -import { WORKSPACE_TYPE } from '../../../../utils'; +import { SavedObjectsFindResponse } from '..'; export const DEFAULT_NAMESPACE_STRING = 'default'; export const ALL_NAMESPACES_STRING = '*'; @@ -90,27 +87,4 @@ export class SavedObjectsUtils { ): string[] { return targetWorkspaces?.filter((item) => !baseWorkspaces?.includes(item)) || []; } - - public static async getPermittedWorkspaceIds(props: { - principals: Principals; - repository: SavedObjectsRepository; - permissionModes: SavedObjectsPermissionModes; - }) { - const { principals, repository, permissionModes } = props; - const queryDSL = await repository.getPermissionQuery({ - permissionTypes: permissionModes, - principals, - savedObjectType: [WORKSPACE_TYPE], - }); - try { - const result = await repository?.find({ - type: [WORKSPACE_TYPE], - queryDSL, - perPage: 999, - }); - return result?.saved_objects.map((item) => item.id); - } catch (e) { - return []; - } - } } diff --git a/src/core/server/saved_objects/service/saved_objects_client.ts b/src/core/server/saved_objects/service/saved_objects_client.ts index 759d6ed8351a..b6f5abea26ef 100644 --- a/src/core/server/saved_objects/service/saved_objects_client.ts +++ b/src/core/server/saved_objects/service/saved_objects_client.ts @@ -28,8 +28,8 @@ * under the License. */ -import { Permissions, Principals } from '../permission_control/acl'; -import { ISavedObjectsRepository, SavedObjectsRepository } from './lib'; +import { Permissions } from '../permission_control/acl'; +import { ISavedObjectsRepository } from './lib'; import { SavedObject, SavedObjectError, @@ -480,26 +480,6 @@ export class SavedObjectsClient { return await this._repository.addToWorkspaces(objects, workspaces, options); }; - /** - * Different DB may have different query DSL for given params - */ - getPermissionQuery = async ( - props: Parameters[0] - ) => { - return await this._repository.getPermissionQuery(props); - }; - - /** - * Different DB may have different query to find granted objects, - * provide a placeholder here for other query implementation - */ - processFindOptions = async (props: { - options: SavedObjectsFindOptions; - principals: Principals; - }): Promise => { - return await this._repository.processFindOptions(props); - }; - /** * delete saved objects by workspace id * @param workspace diff --git a/src/core/server/saved_objects/types.ts b/src/core/server/saved_objects/types.ts index 25ccbff66dd7..f3c54f9ae86e 100644 --- a/src/core/server/saved_objects/types.ts +++ b/src/core/server/saved_objects/types.ts @@ -45,6 +45,7 @@ export { } from './import/types'; import { SavedObject } from '../../types'; +import { Principals } from './permission_control/acl'; type KueryNode = any; @@ -111,7 +112,15 @@ export interface SavedObjectsFindOptions { /** An optional OpenSearch preference value to be used for the query **/ preference?: string; workspaces?: string[]; - queryDSL?: Record; + /** + * The params here will be combined with bool clause and is used for filtering with ACL structure. + */ + ACLSearchParams?: { + workspaces?: string[]; + principals?: Principals; + workspacePermissionModes?: string[]; + objectPermissionModes?: string[]; + }; } /** diff --git a/src/plugins/workspace/server/saved_objects/workspace_saved_objects_client_wrapper.ts b/src/plugins/workspace/server/saved_objects/workspace_saved_objects_client_wrapper.ts index d8071301d73d..4bb352fe6f71 100644 --- a/src/plugins/workspace/server/saved_objects/workspace_saved_objects_client_wrapper.ts +++ b/src/plugins/workspace/server/saved_objects/workspace_saved_objects_client_wrapper.ts @@ -136,6 +136,16 @@ export class WorkspaceSavedObjectsClientWrapper { return matchAny; } + /** + * check if the type include workspace + * Workspace permission check is totally different from object permission check. + * @param type + * @returns + */ + private isRelatedToWorkspace(type: string | string[]): boolean { + return type === WORKSPACE_TYPE || (Array.isArray(type) && type.includes(WORKSPACE_TYPE)); + } + public wrapperFactory: SavedObjectsClientWrapperFactory = (wrapperOptions) => { const deleteWithWorkspacePermissionControl = async ( type: string, @@ -348,11 +358,66 @@ export class WorkspaceSavedObjectsClientWrapper { options: SavedObjectsFindOptions & Pick ) => { const principals = this.permissionControl.getPrincipalsFromRequest(wrapperOptions.request); - const processedOptions = await wrapperOptions.client.processFindOptions({ - options, - principals, - }); - return await wrapperOptions.client.find(processedOptions); + if (!options.ACLSearchParams) { + options.ACLSearchParams = {}; + } + if (this.isRelatedToWorkspace(options.type)) { + options.ACLSearchParams.workspacePermissionModes = [ + WorkspacePermissionMode.LibraryRead, + WorkspacePermissionMode.LibraryWrite, + WorkspacePermissionMode.Management, + ]; + options.ACLSearchParams.principals = principals; + } else { + const permittedWorkspaceIds = await this.permissionControl.getPermittedWorkspaceIds( + wrapperOptions.request, + [ + WorkspacePermissionMode.LibraryRead, + WorkspacePermissionMode.LibraryWrite, + WorkspacePermissionMode.Management, + ] + ); + + if (options.workspaces) { + const permittedWorkspaces = options.workspaces.filter((item) => + (permittedWorkspaceIds || []).includes(item) + ); + if (!permittedWorkspaces.length) { + /** + * If user does not have any one workspace access + * deny the request + */ + throw SavedObjectsErrorHelpers.decorateNotAuthorizedError( + new Error( + i18n.translate('workspace.permission.invalidate', { + defaultMessage: 'Invalid workspace permission', + }) + ) + ); + } + + /** + * Overwrite the options.workspaces when user has access on partial workspaces. + */ + options.workspaces = permittedWorkspaces; + } else { + /** + * Select all the docs that + * 1. ACL matches read or write permission OR + * 2. workspaces matches library_read or library_write or management OR + * 3. Advanced settings + */ + options.workspaces = undefined; + options.ACLSearchParams.workspaces = permittedWorkspaceIds; + options.ACLSearchParams.objectPermissionModes = [ + WorkspacePermissionMode.Read, + WorkspacePermissionMode.Write, + ]; + options.ACLSearchParams.principals = principals; + } + } + + return await wrapperOptions.client.find(options); }; const addToWorkspacesWithPermissionControl = async (