diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts index 26a693349496d..abfb1f12a2771 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts @@ -9,12 +9,7 @@ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { SPACES } from '../lib/spaces'; -import { - createRequest, - expectResponses, - getUrlPrefix, - getTestTitle, -} from '../lib/saved_object_test_utils'; +import { expectResponses, getUrlPrefix, getTestTitle } from '../lib/saved_object_test_utils'; import { ExpectResponseBody, TestCase, TestDefinition, TestSuite } from '../lib/types'; export interface BulkGetTestDefinition extends TestDefinition { @@ -22,6 +17,7 @@ export interface BulkGetTestDefinition extends TestDefinition { } export type BulkGetTestSuite = TestSuite; export interface BulkGetTestCase extends TestCase { + namespaces?: string[]; // used to define individual "object namespaces" string arrays, e.g., bulkGet across multiple namespaces failure?: 400 | 404; // only used for permitted response case } @@ -31,6 +27,12 @@ export const TEST_CASES: Record = Object.freeze({ DOES_NOT_EXIST, }); +const createRequest = ({ type, id, namespaces }: BulkGetTestCase) => ({ + type, + id, + ...(namespaces && { namespaces }), // individual "object namespaces" string array +}); + export function bulkGetTestSuiteFactory(esArchiver: any, supertest: SuperTest) { const expectSavedObjectForbidden = expectResponses.forbiddenTypes('bulk_get'); const expectResponseBody = ( @@ -49,6 +51,7 @@ export function bulkGetTestSuiteFactory(esArchiver: any, supertest: SuperTest { CASES.NAMESPACE_AGNOSTIC, { ...CASES.DOES_NOT_EXIST, ...fail404() }, ]; + const crossNamespace = [ + { + ...CASES.SINGLE_NAMESPACE_SPACE_2, + namespaces: ['x', 'y'], + ...fail400(), // cannot be searched for in multiple spaces + }, + { ...CASES.SINGLE_NAMESPACE_SPACE_2, namespaces: [SPACE_2_ID] }, // second try searches for it in a single other space, which is valid + { + ...CASES.MULTI_NAMESPACE_ISOLATED_ONLY_SPACE_1, + namespaces: [ALL_SPACES_ID], + ...fail400(), // cannot be searched for in multiple spaces + }, + { ...CASES.MULTI_NAMESPACE_ISOLATED_ONLY_SPACE_1, namespaces: [SPACE_1_ID] }, // second try searches for it in a single other space, which is valid + { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, namespaces: [SPACE_2_ID], ...fail404() }, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, namespaces: [SPACE_2_ID, 'x'] }, // unknown space is allowed / ignored + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, namespaces: [ALL_SPACES_ID] }, // this is different than the same test case in the spaces_only and security_only suites, since MULTI_NAMESPACE_ONLY_SPACE_1 *may* return a 404 error to a partially authorized user + ]; const hiddenType = [{ ...CASES.HIDDEN, ...fail400() }]; - const allTypes = normalTypes.concat(hiddenType); - return { normalTypes, hiddenType, allTypes }; + const allTypes = [...normalTypes, ...crossNamespace, ...hiddenType]; + return { normalTypes, crossNamespace, hiddenType, allTypes }; }; export default function ({ getService }: FtrProviderContext) { @@ -58,13 +76,39 @@ export default function ({ getService }: FtrProviderContext) { supertest ); const createTests = (spaceId: string) => { - const { normalTypes, hiddenType, allTypes } = createTestCases(spaceId); + const { normalTypes, crossNamespace, hiddenType, allTypes } = createTestCases(spaceId); // use singleRequest to reduce execution time and/or test combined cases + const authorizedCommon = [ + createTestDefinitions(normalTypes, false, { singleRequest: true }), + createTestDefinitions(hiddenType, true), + ].flat(); + return { unauthorized: createTestDefinitions(allTypes, true), - authorized: [ - createTestDefinitions(normalTypes, false, { singleRequest: true }), - createTestDefinitions(hiddenType, true), + authorizedAtSpace: [ + authorizedCommon, + ...(() => { + const [authorized, unauthorized] = crossNamespace.reduce< + [BulkGetTestCase[], BulkGetTestCase[]] + >( + ([left, right], cur) => { + if (cur.namespaces.some((x) => ![ALL_SPACES_ID, spaceId].includes(x))) { + return [left, [...right, cur]]; + } + return [[...left, cur], right]; + }, + [[], []] + ); + return [ + ...createTestDefinitions(authorized, false, { singleRequest: true }), + ...createTestDefinitions(unauthorized, true), + ]; + })(), + createTestDefinitions(allTypes, true, { singleRequest: true }), + ].flat(), + authorizedEverywhere: [ + authorizedCommon, + createTestDefinitions(crossNamespace, false, { singleRequest: true }), createTestDefinitions(allTypes, true, { singleRequest: true, responseBodyOverride: expectSavedObjectForbidden(['hiddentype']), @@ -77,7 +121,9 @@ export default function ({ getService }: FtrProviderContext) { describe('_bulk_get', () => { getTestScenarios().securityAndSpaces.forEach(({ spaceId, users }) => { const suffix = ` within the ${spaceId} space`; - const { unauthorized, authorized, superuser } = createTests(spaceId); + const { unauthorized, authorizedAtSpace, authorizedEverywhere, superuser } = createTests( + spaceId + ); const _addTests = (user: TestUser, tests: BulkGetTestDefinition[]) => { addTests(`${user.description}${suffix}`, { user, spaceId, tests }); }; @@ -85,16 +131,15 @@ export default function ({ getService }: FtrProviderContext) { [users.noAccess, users.legacyAll, users.allAtOtherSpace].forEach((user) => { _addTests(user, unauthorized); }); - [ - users.dualAll, - users.dualRead, - users.allGlobally, - users.readGlobally, - users.allAtSpace, - users.readAtSpace, - ].forEach((user) => { - _addTests(user, authorized); + + [users.allAtSpace, users.readAtSpace].forEach((user) => { + _addTests(user, authorizedAtSpace); }); + + [users.dualAll, users.dualRead, users.allGlobally, users.readGlobally].forEach((user) => { + _addTests(user, authorizedEverywhere); + }); + _addTests(users.superuser, superuser); }); }); diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/bulk_get.ts b/x-pack/test/saved_object_api_integration/security_only/apis/bulk_get.ts index 18edb7502c65a..4aa722bfc6b07 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/bulk_get.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/bulk_get.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { SPACES, ALL_SPACES_ID } from '../../common/lib/spaces'; import { testCaseFailures, getTestScenarios } from '../../common/lib/saved_object_test_utils'; import { TestUser } from '../../common/lib/types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; @@ -14,6 +15,10 @@ import { BulkGetTestDefinition, } from '../../common/suites/bulk_get'; +const { + SPACE_1: { spaceId: SPACE_1_ID }, + SPACE_2: { spaceId: SPACE_2_ID }, +} = SPACES; const { fail400, fail404 } = testCaseFailures; const createTestCases = () => { @@ -31,6 +36,21 @@ const createTestCases = () => { { ...CASES.MULTI_NAMESPACE_ISOLATED_ONLY_SPACE_1, ...fail404() }, CASES.NAMESPACE_AGNOSTIC, { ...CASES.DOES_NOT_EXIST, ...fail404() }, + { + ...CASES.SINGLE_NAMESPACE_SPACE_2, + namespaces: ['x', 'y'], + ...fail400(), // cannot be searched for in multiple spaces + }, + { ...CASES.SINGLE_NAMESPACE_SPACE_2, namespaces: [SPACE_2_ID] }, // second try searches for it in a single other space, which is valid + { + ...CASES.MULTI_NAMESPACE_ISOLATED_ONLY_SPACE_1, + namespaces: [ALL_SPACES_ID], + ...fail400(), // cannot be searched for in multiple spaces + }, + { ...CASES.MULTI_NAMESPACE_ISOLATED_ONLY_SPACE_1, namespaces: [SPACE_1_ID] }, // second try searches for it in a single other space, which is valid + { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, namespaces: [SPACE_2_ID], ...fail404() }, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, namespaces: [SPACE_2_ID, 'x'] }, // unknown space is allowed / ignored + { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, namespaces: [ALL_SPACES_ID] }, ]; const hiddenType = [{ ...CASES.HIDDEN, ...fail400() }]; const allTypes = normalTypes.concat(hiddenType); diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_get.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_get.ts index e1d0243377b8e..41fa4749cc48e 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_get.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_get.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SPACES } from '../../common/lib/spaces'; +import { SPACES, ALL_SPACES_ID } from '../../common/lib/spaces'; import { testCaseFailures, getTestScenarios } from '../../common/lib/saved_object_test_utils'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { bulkGetTestSuiteFactory, TEST_CASES as CASES } from '../../common/suites/bulk_get'; @@ -38,6 +38,21 @@ const createTestCases = (spaceId: string) => [ CASES.NAMESPACE_AGNOSTIC, { ...CASES.HIDDEN, ...fail400() }, { ...CASES.DOES_NOT_EXIST, ...fail404() }, + { + ...CASES.SINGLE_NAMESPACE_SPACE_2, + namespaces: ['x', 'y'], + ...fail400(), // cannot be searched for in multiple spaces + }, + { ...CASES.SINGLE_NAMESPACE_SPACE_2, namespaces: [SPACE_2_ID] }, // second try searches for it in a single other space, which is valid + { + ...CASES.MULTI_NAMESPACE_ISOLATED_ONLY_SPACE_1, + namespaces: [ALL_SPACES_ID], + ...fail400(), // cannot be searched for in multiple spaces + }, + { ...CASES.MULTI_NAMESPACE_ISOLATED_ONLY_SPACE_1, namespaces: [SPACE_1_ID] }, // second try searches for it in a single other space, which is valid + { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, namespaces: [SPACE_2_ID], ...fail404() }, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, namespaces: [SPACE_2_ID, 'x'] }, // unknown space is allowed / ignored + { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, namespaces: [ALL_SPACES_ID] }, ]; export default function ({ getService }: FtrProviderContext) {