diff --git a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap
index d61cbb909bae..fa127b7462c7 100644
--- a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap
+++ b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap
@@ -3777,24 +3777,16 @@ exports[`Header handles visibility and lock changes 1`] = `
className="euiBreadcrumbs euiHeaderBreadcrumbs euiBreadcrumbs--truncate"
data-test-subj="breadcrumbs"
>
-
+ test
+
+
@@ -9475,24 +9467,16 @@ exports[`Header renders condensed header 1`] = `
className="euiBreadcrumbs euiHeaderBreadcrumbs euiBreadcrumbs--truncate"
data-test-subj="breadcrumbs"
>
-
+ test
+
+
diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts
index 2f389b851de2..f1f3b87c6dc7 100644
--- a/src/core/public/saved_objects/saved_objects_client.ts
+++ b/src/core/public/saved_objects/saved_objects_client.ts
@@ -373,7 +373,6 @@ export class SavedObjectsClient {
namespaces: 'namespaces',
preference: 'preference',
workspaces: 'workspaces',
- queryDSL: 'queryDSL',
};
const currentWorkspaceId = this._getCurrentWorkspace();
@@ -384,14 +383,17 @@ export class SavedObjectsClient {
finalWorkspaces = Array.from(new Set([currentWorkspaceId]));
}
- const renamedQuery = renameKeys(renameMap, {
- ...options,
- ...(finalWorkspaces
- ? {
- workspaces: finalWorkspaces,
- }
- : {}),
- });
+ const renamedQuery = renameKeys, any>(
+ renameMap,
+ {
+ ...options,
+ ...(finalWorkspaces
+ ? {
+ workspaces: finalWorkspaces,
+ }
+ : {}),
+ }
+ );
const query = pick.apply(null, [renamedQuery, ...Object.values(renameMap)]) as Partial<
Record
>;
diff --git a/src/core/server/saved_objects/permission_control/client.ts b/src/core/server/saved_objects/permission_control/client.ts
index 11e3d6e58dc8..f7c0c800ec4c 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: {
+ 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.mock.ts b/src/core/server/saved_objects/service/lib/repository.mock.ts
index 4c535b18b5f2..e02d4dd0b638 100644
--- a/src/core/server/saved_objects/service/lib/repository.mock.ts
+++ b/src/core/server/saved_objects/service/lib/repository.mock.ts
@@ -45,8 +45,6 @@ const create = (): jest.Mocked => ({
deleteByNamespace: jest.fn(),
incrementCounter: jest.fn(),
addToWorkspaces: jest.fn(),
- getPermissionQuery: jest.fn(),
- processFindOptions: jest.fn(),
deleteByWorkspace: jest.fn(),
deleteFromWorkspaces: jest.fn(),
});
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..96da76df5a68 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,43 @@ 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.permissionModes && ACLSearchParams.principals) {
+ const permissionDSL = ACL.generateGetPermittedSavedObjectsQueryDSL(
+ ACLSearchParams.permissionModes,
+ ACLSearchParams.principals
+ );
+ shouldClause.push(permissionDSL.query);
+ }
+
+ if (ACLSearchParams.workspaces) {
+ shouldClause.push({
+ terms: {
+ workspaces: ACLSearchParams.workspaces,
+ },
+ });
+ }
+
+ 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..aa2d1baf85de 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,14 @@ 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;
+ permissionModes?: 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..7da3e7977c8a 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.permissionModes = [
+ 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.permissionModes = [
+ WorkspacePermissionMode.Read,
+ WorkspacePermissionMode.Write,
+ ];
+ options.ACLSearchParams.principals = principals;
+ }
+ }
+
+ return await wrapperOptions.client.find(options);
};
const addToWorkspacesWithPermissionControl = async (