From e8088b82a513f33f7b9a392acca7b7f6777b2fa3 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Wed, 13 Nov 2024 18:11:07 +0100 Subject: [PATCH 01/38] Moved tests to agnostic setup --- .buildkite/ftr_security_stateful_configs.yml | 4 + .../kbn-es/src/stateful_resources/roles.yml | 127 ++++ .../default_configs/stateful.config.base.ts | 10 +- .../spaces_api_integration/common/config.ts | 2 + .../common/lib/authentication.ts | 246 ++++++ .../common/lib/types.ts | 1 + .../common/suites/common.ts | 47 ++ ..._to_space.ts => copy_to_space.agnostic.ts} | 62 +- .../common/suites/create.agnostic.ts | 228 ++++++ .../common/suites/create.ts | 4 +- .../common/suites/delete.agnostic.ts | 240 ++++++ .../disable_legacy_url_aliases.agnostic.ts | 167 +++++ .../common/suites/get.agnostic.ts | 156 ++++ .../common/suites/get_all.agnostic.ts | 217 ++++++ ...esolve_copy_to_space_conflicts.agnostic.ts | 703 ++++++++++++++++++ .../common/suites/update.agnostic.ts | 164 ++++ .../ftr_provider_context.d.ts | 13 + .../apis}/copy_to_space.ts | 23 +- .../security_and_spaces/apis/create.ts | 20 +- .../security_and_spaces/apis/delete.ts | 19 +- .../apis/disable_legacy_url_aliases.ts | 82 ++ .../security_and_spaces/apis/get.ts | 18 +- .../security_and_spaces/apis/get_all.ts | 68 +- .../security_and_spaces/apis/index.ts | 20 + .../apis/resolve_copy_to_space_conflicts.ts | 27 +- .../security_and_spaces/apis/update.ts | 18 +- .../deployment_agnostic/services/index.ts | 16 + .../spaces_only/apis/copy_to_space.ts | 7 +- .../spaces_only/apis/create.ts | 16 +- .../spaces_only/apis/delete.ts | 17 +- .../spaces_only/apis/get.ts | 16 +- .../spaces_only/apis/get_all.ts | 19 +- .../spaces_only/apis/index.ts | 28 + .../apis/resolve_copy_to_space_conflicts.ts | 9 +- .../spaces_only/apis/update.ts | 17 +- .../spaces_only/config.ts} | 8 +- .../deployment_agnostic/stateful.config.ts | 70 ++ .../stateful.config_basic.ts | 10 + .../stateful.config_trial.ts | 10 + .../stateful.copy_to_space.config_trial.ts} | 7 +- .../apis/copy_to_space/index.ts | 23 - .../security_and_spaces/apis/index.ts | 6 - .../security_and_spaces/config_trial.ts | 5 +- .../spaces_only/apis/index.ts | 7 - 44 files changed, 2729 insertions(+), 248 deletions(-) create mode 100644 x-pack/test/spaces_api_integration/common/suites/common.ts rename x-pack/test/spaces_api_integration/common/suites/{copy_to_space.ts => copy_to_space.agnostic.ts} (96%) create mode 100644 x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts create mode 100644 x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts create mode 100644 x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.agnostic.ts create mode 100644 x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts create mode 100644 x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts create mode 100644 x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts create mode 100644 x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/ftr_provider_context.d.ts rename x-pack/test/spaces_api_integration/{security_and_spaces/apis/copy_to_space => deployment_agnostic/security_and_spaces/apis}/copy_to_space.ts (92%) rename x-pack/test/spaces_api_integration/{ => deployment_agnostic}/security_and_spaces/apis/create.ts (91%) rename x-pack/test/spaces_api_integration/{ => deployment_agnostic}/security_and_spaces/apis/delete.ts (89%) create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/disable_legacy_url_aliases.ts rename x-pack/test/spaces_api_integration/{ => deployment_agnostic}/security_and_spaces/apis/get.ts (93%) rename x-pack/test/spaces_api_integration/{ => deployment_agnostic}/security_and_spaces/apis/get_all.ts (92%) create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts rename x-pack/test/spaces_api_integration/{ => deployment_agnostic}/security_and_spaces/apis/resolve_copy_to_space_conflicts.ts (90%) rename x-pack/test/spaces_api_integration/{ => deployment_agnostic}/security_and_spaces/apis/update.ts (90%) create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/services/index.ts rename x-pack/test/spaces_api_integration/{ => deployment_agnostic}/spaces_only/apis/copy_to_space.ts (86%) rename x-pack/test/spaces_api_integration/{ => deployment_agnostic}/spaces_only/apis/create.ts (67%) rename x-pack/test/spaces_api_integration/{ => deployment_agnostic}/spaces_only/apis/delete.ts (61%) rename x-pack/test/spaces_api_integration/{ => deployment_agnostic}/spaces_only/apis/get.ts (75%) rename x-pack/test/spaces_api_integration/{ => deployment_agnostic}/spaces_only/apis/get_all.ts (69%) create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/index.ts rename x-pack/test/spaces_api_integration/{ => deployment_agnostic}/spaces_only/apis/resolve_copy_to_space_conflicts.ts (84%) rename x-pack/test/spaces_api_integration/{ => deployment_agnostic}/spaces_only/apis/update.ts (63%) rename x-pack/test/spaces_api_integration/{security_and_spaces/copy_to_space_config_basic.ts => deployment_agnostic/spaces_only/config.ts} (56%) create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config.ts create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_basic.ts create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts rename x-pack/test/spaces_api_integration/{security_and_spaces/copy_to_space_config_trial.ts => deployment_agnostic/stateful.copy_to_space.config_trial.ts} (56%) delete mode 100644 x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space/index.ts diff --git a/.buildkite/ftr_security_stateful_configs.yml b/.buildkite/ftr_security_stateful_configs.yml index d9953cfe1e727..01827494d95df 100644 --- a/.buildkite/ftr_security_stateful_configs.yml +++ b/.buildkite/ftr_security_stateful_configs.yml @@ -106,3 +106,7 @@ enabled: - x-pack/test/automatic_import_api_integration/apis/config_basic.ts - x-pack/test/automatic_import_api_integration/apis/config_graphs.ts - x-pack/test/security_solution_api_integration/test_suites/asset_inventory/entity_store/trial_license_complete_tier/configs/ess.config.ts + - x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config.ts + - x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts + - x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts + - x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/config.ts diff --git a/packages/kbn-es/src/stateful_resources/roles.yml b/packages/kbn-es/src/stateful_resources/roles.yml index 49ae1fafad958..bc11627a83b09 100644 --- a/packages/kbn-es/src/stateful_resources/roles.yml +++ b/packages/kbn-es/src/stateful_resources/roles.yml @@ -128,3 +128,130 @@ system_indices_superuser: privileges: ['*'] resources: ['*'] run_as: ['*'] + +machine_learning_admin: + cluster: ['manage_ml'] + indices: + - names: ['.ml-anomalies*','.ml-notifications*','.ml-state*','.ml-meta*','.ml-stats-*'] + privileges: ['view_index_metadata', 'read'] + allow_restricted_indices: true + - names: ['.ml-annotations*'] + privileges: ['write', 'read', 'view_index_metadata'] + allow_restricted_indices: true + applications: + - application: 'kibana-*' + privileges: ['reserved_ml_admin'] + resources: ['*'] + run_as: [] + +machine_learning_user: + cluster: ['monitor_ml'] + indices: + - names: ['.ml-anomalies*','.ml-notifications*',] + privileges: ['view_index_metadata', 'read'] + allow_restricted_indices: false + - names: ['.ml-annotations*'] + privileges: ['write', 'read', 'view_index_metadata'] + allow_restricted_indices: false + applications: + - application: 'kibana-*' + privileges: ['reserved_ml_user'] + resources: ['*'] + run_as: [] + +apm_user: + cluster: [] + indices: + - names: + - 'apm-*' + privileges: + - 'read' + - 'view_index_metadata' + allow_restricted_indices: false + - names: + - 'logs-apm.*' + privileges: + - 'read' + - 'view_index_metadata' + allow_restricted_indices: false + - names: + - 'logs-apm-*' + privileges: + - 'read' + - 'view_index_metadata' + allow_restricted_indices: false + - names: + - 'metrics-apm.*' + privileges: + - 'read' + - 'view_index_metadata' + allow_restricted_indices: false + - names: + - 'metrics-apm-*' + privileges: + - 'read' + - 'view_index_metadata' + allow_restricted_indices: false + - names: + - 'traces-apm.*' + privileges: + - 'read' + - 'view_index_metadata' + allow_restricted_indices: false + - names: + - 'traces-apm-*' + privileges: + - 'read' + - 'view_index_metadata' + allow_restricted_indices: false + - names: + - '.ml-anomalies*' + privileges: + - 'read' + - 'view_index_metadata' + allow_restricted_indices: false + - names: + - 'observability-annotations' + privileges: + - 'read' + - 'view_index_metadata' + allow_restricted_indices: false + applications: + - application: 'kibana-*' + privileges: + - 'reserved_ml_apm_user' + resources: + - '*' + run_as: [] + +monitoring_user: + cluster: + - "cluster:monitor/main" + - "cluster:monitor/xpack/info" + - "cluster:monitor/remote/info" + indices: + - names: + - ".monitoring-*" + privileges: + - "read" + - "read_cross_cluster" + allow_restricted_indices: false + - names: + - "/metrics-(beats|elasticsearch|enterprisesearch|kibana|logstash).*/" + privileges: + - "read" + - "read_cross_cluster" + allow_restricted_indices: false + - names: + - "metricbeat-*" + privileges: + - "read" + - "read_cross_cluster" + allow_restricted_indices: false + applications: + - application: "kibana-*" + privileges: + - "reserved_monitoring" + resources: + - "*" + run_as: [] diff --git a/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts b/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts index 1d879c856c7c8..951adbc34a395 100644 --- a/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts +++ b/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts @@ -39,10 +39,10 @@ export function createStatefulTestConfig { if (options.esServerArgs || options.kbnServerArgs) { - throw new Error( - `FTR doesn't provision custom ES/Kibana server arguments into the ESS deployment. - It may lead to unexpected test failures on Cloud. Please contact #appex-qa.` - ); + // throw new Error( + // `FTR doesn't provision custom ES/Kibana server arguments into the ESS deployment. + // It may lead to unexpected test failures on Cloud. Please contact #appex-qa.` + // ); } // if config is executed on CI or locally @@ -118,6 +118,7 @@ export function createStatefulTestConfig {}, + samlAuth: () => {}, }, junit: { reportName: 'X-Pack Spaces API Integration Tests -- ' + name, diff --git a/x-pack/test/spaces_api_integration/common/lib/authentication.ts b/x-pack/test/spaces_api_integration/common/lib/authentication.ts index cbd261008dacb..76bdf582fbd2e 100644 --- a/x-pack/test/spaces_api_integration/common/lib/authentication.ts +++ b/x-pack/test/spaces_api_integration/common/lib/authentication.ts @@ -9,97 +9,343 @@ export const AUTHENTICATION = { NOT_A_KIBANA_USER: { username: 'not_a_kibana_user', password: 'password', + role: 'no_access', }, SUPERUSER: { username: 'elastic', password: 'changeme', + role: 'system_indices_superuser', }, KIBANA_LEGACY_USER: { username: 'a_kibana_legacy_user', password: 'password', + role: 'kibana_legacy_user', }, KIBANA_DUAL_PRIVILEGES_USER: { username: 'a_kibana_dual_privileges_user', password: 'password', + role: 'kibana_dual_privileges_user', }, KIBANA_DUAL_PRIVILEGES_DASHBOARD_ONLY_USER: { username: 'a_kibana_dual_privileges_dashboard_only_user', password: 'password', + role: 'kibana_dual_privileges_dashboard_only_user', }, KIBANA_RBAC_USER: { username: 'a_kibana_rbac_user', password: 'password', + role: 'kibana_rbac_user', }, KIBANA_RBAC_DASHBOARD_ONLY_USER: { username: 'a_kibana_rbac_dashboard_only_user', password: 'password', + role: 'kibana_rbac_dashboard_only_user', }, KIBANA_RBAC_DEFAULT_SPACE_ALL_USER: { username: 'a_kibana_rbac_default_space_all_user', password: 'password', + role: 'kibana_rbac_default_space_all_user', }, KIBANA_RBAC_DEFAULT_SPACE_READ_USER: { username: 'a_kibana_rbac_default_space_read_user', password: 'password', + role: 'kibana_rbac_default_space_read_user', }, KIBANA_RBAC_SPACE_1_ALL_USER: { username: 'a_kibana_rbac_space_1_all_user', password: 'password', + role: 'kibana_rbac_space_1_all_user', }, KIBANA_RBAC_SPACE_1_READ_USER: { username: 'a_kibana_rbac_space_1_read_user', password: 'password', + role: 'kibana_rbac_space_1_read_user', }, KIBANA_RBAC_SPACE_2_ALL_USER: { username: 'a_kibana_rbac_space_2_all_user', password: 'password', + role: 'kibana_rbac_space_2_all_user', }, KIBANA_RBAC_SPACE_2_READ_USER: { username: 'a_kibana_rbac_space_2_read_user', password: 'password', + role: 'kibana_rbac_space_2_read_user', }, KIBANA_RBAC_SPACE_1_2_ALL_USER: { username: 'a_kibana_rbac_space_1_2_all_user', password: 'password', + role: 'kibana_rbac_space_1_2_all_user', }, KIBANA_RBAC_SPACE_1_2_READ_USER: { username: 'a_kibana_rbac_space_1_2_read_user', password: 'password', + role: 'kibana_rbac_space_1_2_read_user', }, KIBANA_RBAC_SPACE_3_ALL_USER: { username: 'a_kibana_rbac_space_3_all_user', password: 'password', + role: 'kibana_rbac_space_3_all_user', }, KIBANA_RBAC_SPACE_3_READ_USER: { username: 'a_kibana_rbac_space_3_read_user', password: 'password', + role: 'kibana_rbac_space_3_read_user', }, KIBANA_RBAC_DEFAULT_SPACE_SAVED_OBJECTS_ALL_USER: { username: 'a_kibana_rbac_default_space_saved_objects_all_user', password: 'password', + role: 'kibana_rbac_default_space_saved_objects_all_user', }, KIBANA_RBAC_DEFAULT_SPACE_SAVED_OBJECTS_READ_USER: { username: 'a_kibana_rbac_default_space_saved_objects_read_user', password: 'password', + role: 'kibana_rbac_default_space_saved_objects_read_user', }, KIBANA_RBAC_SPACE_1_SAVED_OBJECTS_ALL_USER: { username: 'a_kibana_rbac_space_1_saved_objects_all_user', password: 'password', + role: 'kibana_rbac_space_1_saved_objects_all_user', }, KIBANA_RBAC_SPACE_1_SAVED_OBJECTS_READ_USER: { username: 'a_kibana_rbac_space_1_saved_objects_read_user', password: 'password', + role: 'kibana_rbac_space_1_saved_objects_read_user', }, MACHINE_LEARING_ADMIN: { username: 'a_machine_learning_admin', password: 'password', + role: 'machine_learning_admin', }, MACHINE_LEARNING_USER: { username: 'a_machine_learning_user', password: 'password', + role: 'machine_learning_user', }, MONITORING_USER: { username: 'a_monitoring_user', password: 'password', + role: 'monitoring_user', }, }; + +export const ROLES = { + kibana_legacy_user: { + elasticsearch: { + indices: [ + { + names: ['.kibana*'], + privileges: ['manage', 'read', 'index', 'delete'], + }, + ], + }, + kibana: [], + }, + kibana_dual_privileges_user: { + elasticsearch: { + indices: [ + { + names: ['.kibana*'], + privileges: ['manage', 'read', 'index', 'delete'], + }, + ], + }, + kibana: [ + { + base: ['all'], + spaces: ['*'], + }, + ], + }, + kibana_dual_privileges_dashboard_only_user: { + elasticsearch: { + indices: [ + { + names: ['.kibana*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + }, + kibana: [ + { + base: ['read'], + spaces: ['*'], + }, + ], + }, + kibana_rbac_user: { + kibana: [ + { + base: ['all'], + spaces: ['*'], + }, + ], + elasticsearch: {}, + }, + kibana_rbac_dashboard_only_user: { + kibana: [ + { + base: ['read'], + spaces: ['*'], + }, + ], + elasticsearch: {}, + }, + kibana_rbac_default_space_all_user: { + kibana: [ + { + base: ['all'], + spaces: ['default'], + }, + ], + elasticsearch: {}, + }, + kibana_rbac_default_space_read_user: { + kibana: [ + { + base: ['read'], + spaces: ['default'], + }, + ], + elasticsearch: {}, + }, + kibana_rbac_space_1_all_user: { + kibana: [ + { + base: ['all'], + spaces: ['space_1'], + }, + ], + elasticsearch: {}, + }, + kibana_rbac_space_1_read_user: { + kibana: [ + { + base: ['read'], + spaces: ['space_1'], + }, + ], + elasticsearch: {}, + }, + kibana_rbac_space_2_all_user: { + kibana: [ + { + base: ['all'], + spaces: ['space_2'], + }, + ], + elasticsearch: {}, + }, + kibana_rbac_space_2_read_user: { + kibana: [ + { + base: ['read'], + spaces: ['space_2'], + }, + ], + elasticsearch: {}, + }, + kibana_rbac_space_1_2_all_user: { + kibana: [ + { + base: ['all'], + spaces: ['space_1', 'space_2'], + }, + ], + elasticsearch: {}, + }, + kibana_rbac_space_1_2_read_user: { + kibana: [ + { + base: ['read'], + spaces: ['space_1', 'space_2'], + }, + ], + elasticsearch: {}, + }, + kibana_rbac_space_3_all_user: { + kibana: [ + { + base: ['all'], + spaces: ['space_3'], + }, + ], + elasticsearch: {}, + }, + kibana_rbac_space_3_read_user: { + kibana: [ + { + base: ['read'], + spaces: ['space_3'], + }, + ], + elasticsearch: {}, + }, + kibana_rbac_default_space_saved_objects_all_user: { + kibana: [ + { + base: [], + feature: { + savedObjectsManagement: ['all'], + }, + spaces: ['default'], + }, + ], + elasticsearch: {}, + }, + kibana_rbac_default_space_saved_objects_read_user: { + kibana: [ + { + base: [], + feature: { + savedObjectsManagement: ['read'], + }, + spaces: ['default'], + }, + ], + elasticsearch: {}, + }, + kibana_rbac_space_1_saved_objects_all_user: { + kibana: [ + { + base: [], + feature: { + savedObjectsManagement: ['all'], + }, + spaces: ['space_1'], + }, + ], + elasticsearch: {}, + }, + kibana_rbac_space_1_saved_objects_read_user: { + kibana: [ + { + base: [], + feature: { + savedObjectsManagement: ['read'], + }, + spaces: ['space_1'], + }, + ], + elasticsearch: {}, + }, + no_access: { + kibana: [], + elasticsearch: {}, + }, +}; + +export const isBuiltInRole = (role: string) => + [ + 'admin', + 'viewer', + 'editor', + 'system_indices_superuser', + 'monitoring_user', + 'machine_learning_admin', + 'machine_learning_user', + 'apm_user', + ].includes(role); + +export const getRoleDefinitionForUser = (user: { role: string }) => { + return ROLES[user.role as keyof typeof ROLES]; +}; diff --git a/x-pack/test/spaces_api_integration/common/lib/types.ts b/x-pack/test/spaces_api_integration/common/lib/types.ts index 34a96c3d6daad..1a372d136c36f 100644 --- a/x-pack/test/spaces_api_integration/common/lib/types.ts +++ b/x-pack/test/spaces_api_integration/common/lib/types.ts @@ -10,4 +10,5 @@ export type DescribeFn = (text: string, fn: () => void) => void; export interface TestDefinitionAuthentication { username?: string; password?: string; + role: string; } diff --git a/x-pack/test/spaces_api_integration/common/suites/common.ts b/x-pack/test/spaces_api_integration/common/suites/common.ts new file mode 100644 index 0000000000000..53546e4962ef3 --- /dev/null +++ b/x-pack/test/spaces_api_integration/common/suites/common.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { Agent as SuperTestAgent } from 'supertest'; + +import type { + DeploymentAgnosticFtrProviderContext, + SupertestWithRoleScopeType, +} from '../../deployment_agnostic/ftr_provider_context'; +import { getRoleDefinitionForUser, isBuiltInRole } from '../lib/authentication'; +import type { TestDefinitionAuthentication } from '../lib/types'; + +export async function getSupertest( + { getService }: DeploymentAgnosticFtrProviderContext, + user?: TestDefinitionAuthentication +): Promise { + const roleScopedSupertest = getService('roleScopedSupertest'); + const samlAuth = getService('samlAuth'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + if (user) { + const isBuiltIn = isBuiltInRole(user.role); + if (!isBuiltIn) { + await samlAuth.setCustomRole(getRoleDefinitionForUser(user)); + } + + return await roleScopedSupertest.getSupertestWithRoleScope( + isBuiltIn ? user.role : 'customRole', + { + useCookieHeader: true, + withInternalHeaders: true, + } + ); + } + + return supertestWithoutAuth; +} + +export function maybeDestroySupertest(supertest: SupertestWithRoleScopeType | SuperTestAgent) { + // @ts-expect-error + if (typeof supertest.destroy === 'function') { + (supertest as SupertestWithRoleScopeType).destroy(); + } +} diff --git a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.ts b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts similarity index 96% rename from x-pack/test/spaces_api_integration/common/suites/copy_to_space.ts rename to x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts index 68e1ea401cbf3..f73c52d71fb92 100644 --- a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.ts +++ b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts @@ -5,18 +5,22 @@ * 2.0. */ import type * as estypes from '@elastic/elasticsearch/lib/api/types'; -import type { SuperTest } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import type { SavedObjectsImportAmbiguousConflictError, SavedObjectsImportFailure, } from '@kbn/core/server'; -import expect from '@kbn/expect'; +import expect from '@kbn/expect/expect'; import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; import type { CopyResponse } from '@kbn/spaces-plugin/server/lib/copy_to_spaces'; import { getTestDataLoader, SPACE_1, SPACE_2 } from '../../../common/lib/test_data_loader'; -import type { FtrProviderContext } from '../ftr_provider_context'; +import type { + DeploymentAgnosticFtrProviderContext, + SupertestWithRoleScopeType, +} from '../../deployment_agnostic/ftr_provider_context'; +import { getRoleDefinitionForUser, isBuiltInRole } from '../lib/authentication'; import { getAggregatedSpaceData, getUrlPrefix } from '../lib/space_test_utils'; import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; @@ -99,12 +103,12 @@ const getDestinationWithConflicts = (originSpaceId?: string) => interface Aggs extends estypes.AggregationsMultiBucketAggregateBase { buckets: SpaceBucket[]; } -export function copyToSpaceTestSuiteFactory(context: FtrProviderContext) { +export function copyToSpaceTestSuiteFactory(context: DeploymentAgnosticFtrProviderContext) { const testDataLoader = getTestDataLoader(context); - const supertestWithoutAuth = context.getService( - 'supertestWithoutAuth' - ) as unknown as SuperTest; const es = context.getService('es'); + const roleScopedSupertest = context.getService('roleScopedSupertest'); + const samlAuth = context.getService('samlAuth'); + const supertestWithoutAuth = context.getService('supertestWithoutAuth'); const collectSpaceContents = async () => { const response = await getAggregatedSpaceData(es, [ @@ -797,18 +801,37 @@ export function copyToSpaceTestSuiteFactory(context: FtrProviderContext) { (describeFn: DescribeFn) => ( description: string, - { user = {}, spaceId = DEFAULT_SPACE_ID, tests }: CopyToSpaceTestDefinition + { user, spaceId = DEFAULT_SPACE_ID, tests }: CopyToSpaceTestDefinition ) => { describeFn(description, () => { + let supertest: SupertestWithRoleScopeType | SuperTestAgent; before(async () => { // test data only allows for the following spaces as the copy origin expect(['default', 'space_1']).to.contain(spaceId); - await testDataLoader.createFtrSpaces(); + + if (user) { + const isBuiltIn = isBuiltInRole(user.role); + if (!isBuiltIn) { + await samlAuth.setCustomRole(getRoleDefinitionForUser(user)); + } + supertest = await roleScopedSupertest.getSupertestWithRoleScope( + isBuiltIn ? user.role : 'customRole', + { + useCookieHeader: true, + withInternalHeaders: true, + } + ); + } else { + supertest = supertestWithoutAuth; + } }); after(async () => { await testDataLoader.deleteFtrSpaces(); + if (user) { + (supertest as SupertestWithRoleScopeType).destroy(); + } }); describe('single-namespace types', () => { @@ -825,9 +848,8 @@ export function copyToSpaceTestSuiteFactory(context: FtrProviderContext) { await assertSpaceCounts(destination, INITIAL_COUNTS[destination]); - return supertestWithoutAuth + return supertest .post(`${getUrlPrefix(spaceId)}/api/spaces/_copy_saved_objects`) - .auth(user.username, user.password) .send({ objects: [dashboardObject], spaces: [destination], @@ -844,9 +866,8 @@ export function copyToSpaceTestSuiteFactory(context: FtrProviderContext) { await assertSpaceCounts(destination, INITIAL_COUNTS[destination]); - return supertestWithoutAuth + return supertest .post(`${getUrlPrefix(spaceId)}/api/spaces/_copy_saved_objects`) - .auth(user.username, user.password) .send({ objects: [dashboardObject], spaces: [destination], @@ -863,9 +884,8 @@ export function copyToSpaceTestSuiteFactory(context: FtrProviderContext) { await assertSpaceCounts(destination, INITIAL_COUNTS[destination]); - return supertestWithoutAuth + return supertest .post(`${getUrlPrefix(spaceId)}/api/spaces/_copy_saved_objects`) - .auth(user.username, user.password) .send({ objects: [dashboardObject], spaces: [destination], @@ -882,9 +902,8 @@ export function copyToSpaceTestSuiteFactory(context: FtrProviderContext) { await assertSpaceCounts(destination, INITIAL_COUNTS[destination]); - return supertestWithoutAuth + return supertest .post(`${getUrlPrefix(spaceId)}/api/spaces/_copy_saved_objects`) - .auth(user.username, user.password) .send({ objects: [dashboardObject], spaces: [destination], @@ -900,9 +919,8 @@ export function copyToSpaceTestSuiteFactory(context: FtrProviderContext) { const conflictDestination = getDestinationWithConflicts(spaceId); const noConflictDestination = getDestinationWithoutConflicts(); - return supertestWithoutAuth + return supertest .post(`${getUrlPrefix(spaceId)}/api/spaces/_copy_saved_objects`) - .auth(user.username, user.password) .send({ objects: [dashboardObject], spaces: [conflictDestination, noConflictDestination], @@ -933,9 +951,8 @@ export function copyToSpaceTestSuiteFactory(context: FtrProviderContext) { }); it(`should return ${tests.nonExistentSpace.statusCode} when copying to non-existent space`, async () => { - return supertestWithoutAuth + return supertest .post(`${getUrlPrefix(spaceId)}/api/spaces/_copy_saved_objects`) - .auth(user.username, user.password) .send({ objects: [dashboardObject], spaces: ['non_existent_space'], @@ -963,9 +980,8 @@ export function copyToSpaceTestSuiteFactory(context: FtrProviderContext) { const testCases = tests.multiNamespaceTestCases(overwrite, createNewCopies); testCases.forEach(({ testTitle, objects, statusCode, response }) => { it(`should return ${statusCode} when ${testTitle}`, async () => { - return supertestWithoutAuth + return supertest .post(`${getUrlPrefix(spaceId)}/api/spaces/_copy_saved_objects`) - .auth(user.username, user.password) .send({ objects, spaces, includeReferences, createNewCopies, overwrite }) .expect(statusCode) .then(response); diff --git a/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts new file mode 100644 index 0000000000000..922431432423f --- /dev/null +++ b/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts @@ -0,0 +1,228 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Agent as SuperTestAgent } from 'supertest'; + +import expect from '@kbn/expect'; + +import type { + DeploymentAgnosticFtrProviderContext, + SupertestWithRoleScopeType, +} from '../../deployment_agnostic/ftr_provider_context'; +import { getRoleDefinitionForUser, isBuiltInRole } from '../lib/authentication'; +import { getTestScenariosForSpace } from '../lib/space_test_utils'; +import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; + +interface CreateTest { + statusCode: number; + response: (resp: { [key: string]: any }) => void; +} + +interface CreateTests { + newSpace: CreateTest; + alreadyExists: CreateTest; + reservedSpecified: CreateTest; + solutionSpecified: CreateTest; +} + +interface CreateTestDefinition { + user?: TestDefinitionAuthentication; + spaceId: string; + tests: CreateTests; +} + +export function createTestSuiteFactory({ getService }: DeploymentAgnosticFtrProviderContext) { + const esArchiver = getService('esArchiver'); + const roleScopedSupertest = getService('roleScopedSupertest'); + const samlAuth = getService('samlAuth'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + const expectConflictResponse = (resp: { [key: string]: any }) => { + expect(resp.body).to.only.have.keys(['error', 'message', 'statusCode']); + expect(resp.body.error).to.equal('Conflict'); + expect(resp.body.statusCode).to.equal(409); + expect(resp.body.message).to.match(new RegExp(`A space with the identifier .*`)); + }; + + const expectNewSpaceResult = (resp: { [key: string]: any }) => { + expect(resp.body).to.eql({ + name: 'marketing', + id: 'marketing', + description: 'a description', + color: '#5c5959', + disabledFeatures: [], + }); + }; + + const expectRbacForbiddenResponse = (resp: { [key: string]: any }) => { + expect(resp.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to create spaces', + }); + }; + + const expectReservedSpecifiedResult = (resp: { [key: string]: any }) => { + expect(resp.body).to.eql({ + name: 'reserved space', + id: 'reserved', + description: 'a description', + color: '#5c5959', + disabledFeatures: [], + }); + }; + + const expectSolutionSpecifiedResult = (resp: Record) => { + const disabledFeatures = resp.body.disabledFeatures.sort(); + + const expected = { + id: 'solution', + name: 'space with solution', + description: 'a description', + color: '#5c5959', + disabledFeatures: [ + // Disabled features are automatically added to the space when a solution is set + 'apm', + 'infrastructure', + 'inventory', + 'logs', + 'observabilityCases', + 'securitySolutionAssistant', + 'securitySolutionAttackDiscovery', + 'securitySolutionCases', + 'siem', + 'slo', + 'uptime', + ], + solution: 'es', + }; + + expect({ ...resp.body, disabledFeatures }).to.eql(expected); + }; + + const makeCreateTest = + (describeFn: DescribeFn) => + (description: string, { user, spaceId, tests }: CreateTestDefinition) => { + describeFn(description, () => { + let supertest: SupertestWithRoleScopeType | SuperTestAgent; + + before(async () => { + if (user) { + const isBuiltIn = isBuiltInRole(user.role); + if (!isBuiltIn) { + await samlAuth.setCustomRole(getRoleDefinitionForUser(user)); + } + supertest = await roleScopedSupertest.getSupertestWithRoleScope( + isBuiltIn ? user.role : 'customRole', + { + useCookieHeader: true, + withInternalHeaders: true, + } + ); + } else { + supertest = supertestWithoutAuth; + } + }); + + after(async () => { + if (user) { + (supertest as SupertestWithRoleScopeType).destroy(); + } + }); + + beforeEach(() => + esArchiver.load( + 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' + ) + ); + afterEach(() => + esArchiver.unload( + 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' + ) + ); + + getTestScenariosForSpace(spaceId).forEach(({ urlPrefix, scenario }) => { + it(`should return ${tests.newSpace.statusCode} ${scenario}`, async () => { + return supertest + .post(`${urlPrefix}/api/spaces/space`) + .send({ + name: 'marketing', + id: 'marketing', + description: 'a description', + color: '#5c5959', + disabledFeatures: [], + }) + .expect(tests.newSpace.statusCode) + .then(tests.newSpace.response); + }); + + describe('when it already exists', () => { + it(`should return ${tests.alreadyExists.statusCode} ${scenario}`, async () => { + return supertest + .post(`${urlPrefix}/api/spaces/space`) + .send({ + name: 'space_1', + id: 'space_1', + color: '#ffffff', + description: 'a description', + disabledFeatures: [], + }) + .expect(tests.alreadyExists.statusCode) + .then(tests.alreadyExists.response); + }); + }); + + describe('when _reserved is specified', () => { + it(`should return ${tests.reservedSpecified.statusCode} and ignore _reserved ${scenario}`, async () => { + return supertest + .post(`${urlPrefix}/api/spaces/space`) + .send({ + name: 'reserved space', + id: 'reserved', + description: 'a description', + color: '#5c5959', + _reserved: true, + disabledFeatures: [], + }) + .expect(tests.reservedSpecified.statusCode) + .then(tests.reservedSpecified.response); + }); + }); + + describe('when solution is specified', () => { + it(`should return ${tests.solutionSpecified.statusCode}`, async () => { + return supertest + .post(`${urlPrefix}/api/spaces/space`) + .send({ + name: 'space with solution', + id: 'solution', + description: 'a description', + color: '#5c5959', + solution: 'es', + disabledFeatures: [], + }) + .expect(tests.solutionSpecified.statusCode) + .then(tests.solutionSpecified.response); + }); + }); + }); + }); + }; + + const createTest = makeCreateTest(describe); + // @ts-ignore + createTest.only = makeCreateTest(describe.only); + + return { + createTest, + expectConflictResponse, + expectNewSpaceResult, + expectRbacForbiddenResponse, + expectReservedSpecifiedResult, + expectSolutionSpecifiedResult, + }; +} diff --git a/x-pack/test/spaces_api_integration/common/suites/create.ts b/x-pack/test/spaces_api_integration/common/suites/create.ts index 5599d065eb93c..bc6bdd21a549a 100644 --- a/x-pack/test/spaces_api_integration/common/suites/create.ts +++ b/x-pack/test/spaces_api_integration/common/suites/create.ts @@ -25,7 +25,7 @@ interface CreateTests { } interface CreateTestDefinition { - user?: TestDefinitionAuthentication; + user: TestDefinitionAuthentication; spaceId: string; tests: CreateTests; } @@ -101,7 +101,7 @@ export function createTestSuiteFactory(esArchiver: any, supertest: SuperTest - (description: string, { user = {}, spaceId, tests }: CreateTestDefinition) => { + (description: string, { user, spaceId, tests }: CreateTestDefinition) => { describeFn(description, () => { beforeEach(() => esArchiver.load( diff --git a/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts new file mode 100644 index 0000000000000..69e8242d19cab --- /dev/null +++ b/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts @@ -0,0 +1,240 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { Agent as SuperTestAgent } from 'supertest'; + +import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; +import expect from '@kbn/expect'; + +import type { + DeploymentAgnosticFtrProviderContext, + SupertestWithRoleScopeType, +} from '../../deployment_agnostic/ftr_provider_context'; +import { getRoleDefinitionForUser, isBuiltInRole } from '../lib/authentication'; +import { MULTI_NAMESPACE_SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; +import { getAggregatedSpaceData, getTestScenariosForSpace } from '../lib/space_test_utils'; +import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; + +interface DeleteTest { + statusCode: number; + response: (resp: { [key: string]: any }) => void; +} + +interface DeleteTests { + exists: DeleteTest; + reservedSpace: DeleteTest; + doesntExist: DeleteTest; +} + +interface DeleteTestDefinition { + user?: TestDefinitionAuthentication; + spaceId: string; + tests: DeleteTests; +} + +export function deleteTestSuiteFactory({ getService }: DeploymentAgnosticFtrProviderContext) { + const roleScopedSupertest = getService('roleScopedSupertest'); + const samlAuth = getService('samlAuth'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const esArchiver = getService('esArchiver'); + const es = getService('es'); + + const createExpectResult = (expectedResult: any) => (resp: { [key: string]: any }) => { + expect(resp.body).to.eql(expectedResult); + }; + + const expectEmptyResult = async (resp: { [key: string]: any }) => { + expect(resp.body).to.eql(''); + + // Query ES to ensure that we deleted everything we expected, and nothing we didn't + // Grouping first by namespace, then by saved object type + const response = await getAggregatedSpaceData(es, [ + 'visualization', + 'dashboard', + 'space', + 'index-pattern', + 'legacy-url-alias', + // TODO: add assertions for config objects -- these assertions were removed because of flaky behavior in #92358, but we should + // consider adding them again at some point, especially if we convert config objects to `namespaceType: 'multiple-isolated'` in + // the future. + ]); + + // @ts-expect-error @elastic/elasticsearch doesn't defined `count.buckets`. + const buckets = response.aggregations?.count.buckets; + + // The test fixture contains six legacy URL aliases: + // (1) two for "default", (2) two for "space_2", and (3) two for "other_space", which is a non-existent space. + // Each test deletes "space_2", so the agg buckets should reflect that aliases (1) and (3) still exist afterwards. + + // Space 2 deleted, all others should exist + const expectedBuckets = [ + { + key: 'default', + doc_count: 10, + countByType: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'space', doc_count: 3 }, // since space objects are namespace-agnostic, they appear in the "default" agg bucket + { key: 'visualization', doc_count: 3 }, + { key: 'legacy-url-alias', doc_count: 2 }, // aliases (1) + { key: 'dashboard', doc_count: 1 }, + { key: 'index-pattern', doc_count: 1 }, + ], + }, + }, + { + doc_count: 5, + key: 'space_1', + countByType: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'visualization', doc_count: 3 }, + { key: 'dashboard', doc_count: 1 }, + { key: 'index-pattern', doc_count: 1 }, + // no legacy url alias objects exist in space_1 + ], + }, + }, + { + doc_count: 2, + key: 'other_space', + countByType: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'legacy-url-alias', doc_count: 2 }], // aliases (3) + }, + }, + ]; + + expect(buckets).to.eql(expectedBuckets); + + // There were 24 multi-namespace objects. + // Since Space 2 was deleted, any multi-namespace objects that existed in that space + // are updated to remove it, and of those, any that don't exist in any space are deleted. + const multiNamespaceResponse = await es.search>({ + index: ALL_SAVED_OBJECT_INDICES, + size: 100, + body: { query: { terms: { type: ['sharedtype'] } } }, + }); + const docs = multiNamespaceResponse.hits.hits; + // Just 19 results, since spaces_2_only, conflict_1a_space_2, conflict_1b_space_2, conflict_1c_space_2, and conflict_2_space_2 got deleted. + expect(docs).length(19); + docs.forEach((doc) => () => { + const containsSpace2 = doc?._source?.namespaces.includes('space_2'); + expect(containsSpace2).to.eql(false); + }); + const space2OnlyObjExists = docs.some((x) => x._id === CASES.SPACE_2_ONLY.id); + expect(space2OnlyObjExists).to.eql(false); + }; + + const expectNotFound = (resp: { [key: string]: any }) => { + expect(resp.body).to.eql({ + error: 'Not Found', + statusCode: 404, + message: 'Not Found', + }); + }; + + const expectRbacForbidden = (resp: { [key: string]: any }) => { + expect(resp.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to delete spaces', + }); + }; + + const expectReservedSpaceResult = (resp: { [key: string]: any }) => { + expect(resp.body).to.eql({ + error: 'Bad Request', + statusCode: 400, + message: `The default space cannot be deleted because it is reserved.`, + }); + }; + + const makeDeleteTest = + (describeFn: DescribeFn) => + (description: string, { user, spaceId, tests }: DeleteTestDefinition) => { + describeFn(description, () => { + let supertest: SupertestWithRoleScopeType | SuperTestAgent; + + before(async () => { + if (user) { + const isBuiltIn = isBuiltInRole(user.role); + if (!isBuiltIn) { + await samlAuth.setCustomRole(getRoleDefinitionForUser(user)); + } + supertest = await roleScopedSupertest.getSupertestWithRoleScope( + isBuiltIn ? user.role : 'customRole', + { + useCookieHeader: true, + withInternalHeaders: true, + } + ); + } else { + supertest = supertestWithoutAuth; + } + }); + after(async () => { + if (user) { + (supertest as SupertestWithRoleScopeType).destroy(); + } + }); + + beforeEach(async () => { + await esArchiver.load( + 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' + ); + }); + afterEach(() => + esArchiver.unload( + 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' + ) + ); + + getTestScenariosForSpace(spaceId).forEach(({ urlPrefix, scenario }) => { + it(`should return ${tests.exists.statusCode} ${scenario}`, async () => { + return supertest + .delete(`${urlPrefix}/api/spaces/space/space_2`) + .expect(tests.exists.statusCode) + .then(tests.exists.response); + }); + + describe(`when the space is reserved`, () => { + it(`should return ${tests.reservedSpace.statusCode} ${scenario}`, async () => { + return supertest + .delete(`${urlPrefix}/api/spaces/space/default`) + .expect(tests.reservedSpace.statusCode) + .then(tests.reservedSpace.response); + }); + }); + + describe(`when the space doesn't exist`, () => { + it(`should return ${tests.doesntExist.statusCode} ${scenario}`, async () => { + return supertest + .delete(`${urlPrefix}/api/spaces/space/space_7`) + .expect(tests.doesntExist.statusCode) + .then(tests.doesntExist.response); + }); + }); + }); + }); + }; + + const deleteTest = makeDeleteTest(describe); + // @ts-ignore + deleteTest.only = makeDeleteTest(describe.only); + + return { + createExpectResult, + deleteTest, + expectEmptyResult, + expectNotFound, + expectRbacForbidden, + expectReservedSpaceResult, + }; +} diff --git a/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.agnostic.ts new file mode 100644 index 0000000000000..a43f516d5c25b --- /dev/null +++ b/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.agnostic.ts @@ -0,0 +1,167 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Agent as SuperTestAgent } from 'supertest'; + +import type { LegacyUrlAlias } from '@kbn/core-saved-objects-base-server-internal'; +import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; +import expect from '@kbn/expect'; + +import { getUrlPrefix } from '../../../saved_object_api_integration/common/lib/saved_object_test_utils'; +import type { + ExpectResponseBody, + TestDefinition, + TestSuite, +} from '../../../saved_object_api_integration/common/lib/types'; +import type { + DeploymentAgnosticFtrProviderContext, + SupertestWithRoleScopeType, +} from '../../deployment_agnostic/ftr_provider_context'; +import { getRoleDefinitionForUser, isBuiltInRole } from '../lib/authentication'; +import { SPACES } from '../lib/spaces'; +import type { TestDefinitionAuthentication } from '../lib/types'; + +export interface DisableLegacyUrlAliasesTestDefinition extends TestDefinition { + request: { + aliases: Array<{ targetSpace: string; targetType: string; sourceId: string }>; + }; +} +export type DisableLegacyUrlAliasesTestSuite = TestSuite; +export interface DisableLegacyUrlAliasesTestCase { + targetSpace: string; + targetType: string; + sourceId: string; + expectFound: boolean; +} + +const LEGACY_URL_ALIAS_TYPE = 'legacy-url-alias'; +interface RawLegacyUrlAlias { + [LEGACY_URL_ALIAS_TYPE]: LegacyUrlAlias; +} + +export const TEST_CASE_TARGET_TYPE = 'sharedtype'; +export const TEST_CASE_SOURCE_ID = 'space_1_only'; // two aliases exist for space_1_only: one in the default spacd=e, and one in space_2 +const createRequest = ({ targetSpace, targetType, sourceId }: DisableLegacyUrlAliasesTestCase) => ({ + aliases: [{ targetSpace, targetType, sourceId }], +}); +const getTestTitle = ({ targetSpace, targetType, sourceId }: DisableLegacyUrlAliasesTestCase) => { + return `for alias '${targetSpace}:${targetType}:${sourceId}'`; +}; + +export function disableLegacyUrlAliasesTestSuiteFactory({ + getService, +}: DeploymentAgnosticFtrProviderContext) { + const roleScopedSupertest = getService('roleScopedSupertest'); + const samlAuth = getService('samlAuth'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const esArchiver = getService('esArchiver'); + const es = getService('es'); + + const expectResponseBody = + (testCase: DisableLegacyUrlAliasesTestCase, statusCode: 204 | 403): ExpectResponseBody => + async (response: Record) => { + const { targetSpace, targetType, sourceId, expectFound } = testCase; + if (statusCode === 403) { + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: `Unable to disable aliases: Unable to bulk_update ${targetType}`, + }); + } + const esResponse = await es.get( + { + // affected by the .kibana split, assumes LEGACY_URL_ALIAS_TYPE is stored in .kibana + index: MAIN_SAVED_OBJECT_INDEX, + id: `${LEGACY_URL_ALIAS_TYPE}:${targetSpace}:${targetType}:${sourceId}`, + }, + { ignore: [404] } + ); + if (expectFound) { + expect(esResponse.found).to.be(true); + const doc = esResponse._source!; + expect(doc).not.to.be(undefined); + expect(doc[LEGACY_URL_ALIAS_TYPE].disabled).to.be(statusCode === 204 ? true : undefined); + } else { + expect(esResponse.found).to.be(false); + } + }; + const createTestDefinitions = ( + testCases: DisableLegacyUrlAliasesTestCase | DisableLegacyUrlAliasesTestCase[], + forbidden: boolean, + options: { + responseBodyOverride?: ExpectResponseBody; + } = {} + ): DisableLegacyUrlAliasesTestDefinition[] => { + const cases = Array.isArray(testCases) ? testCases : [testCases]; + const responseStatusCode = forbidden ? 403 : 204; + return cases.map((x) => ({ + title: getTestTitle(x), + responseStatusCode, + request: createRequest(x), + responseBody: options?.responseBodyOverride || expectResponseBody(x, responseStatusCode), + })); + }; + + const makeDisableLegacyUrlAliasesTest = + (describeFn: Mocha.SuiteFunction) => + (description: string, definition: DisableLegacyUrlAliasesTestSuite) => { + const { spaceId = SPACES.DEFAULT.spaceId, tests } = definition; + const user = definition.user as unknown as TestDefinitionAuthentication; + + describeFn(description, () => { + let supertest: SupertestWithRoleScopeType | SuperTestAgent; + before(async () => { + if (user) { + const isBuiltIn = isBuiltInRole(user.role); + if (!isBuiltIn) { + await samlAuth.setCustomRole(getRoleDefinitionForUser(user)); + } + supertest = await roleScopedSupertest.getSupertestWithRoleScope( + isBuiltIn ? user.role : 'customRole', + { + useCookieHeader: true, + withInternalHeaders: true, + } + ); + } else { + supertest = supertestWithoutAuth; + } + esArchiver.load( + 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' + ); + }); + after(() => { + if (user) { + (supertest as SupertestWithRoleScopeType).destroy(); + } + esArchiver.unload( + 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' + ); + }); + + for (const test of tests) { + it(`should return ${test.responseStatusCode} ${test.title}`, async () => { + const requestBody = test.request; + await supertest + .post(`${getUrlPrefix(spaceId)}/api/spaces/_disable_legacy_url_aliases`) + .send(requestBody) + .expect(test.responseStatusCode) + .then(test.responseBody); + }); + } + }); + }; + + const addTests = makeDisableLegacyUrlAliasesTest(describe); + // @ts-ignore + addTests.only = makeDisableLegacyUrlAliasesTest(describe.only); + + return { + addTests, + createTestDefinitions, + }; +} diff --git a/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts new file mode 100644 index 0000000000000..bb10e3805be71 --- /dev/null +++ b/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts @@ -0,0 +1,156 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Agent as SuperTestAgent } from 'supertest'; + +import expect from '@kbn/expect'; + +import { getSupertest, maybeDestroySupertest } from './common'; +import type { + DeploymentAgnosticFtrProviderContext, + SupertestWithRoleScopeType, +} from '../../deployment_agnostic/ftr_provider_context'; +import { getTestScenariosForSpace } from '../lib/space_test_utils'; +import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; + +interface GetTest { + statusCode: number; + response: (resp: { [key: string]: any }) => void; +} + +interface GetTests { + default: GetTest; +} + +interface GetTestDefinition { + user?: TestDefinitionAuthentication; + currentSpaceId: string; + spaceId: string; + tests: GetTests; +} + +const nonExistantSpaceId = 'not-a-space'; + +export function getTestSuiteFactory(context: DeploymentAgnosticFtrProviderContext) { + const esArchiver = context.getService('esArchiver'); + const createExpectEmptyResult = () => (resp: { [key: string]: any }) => { + expect(resp.body).to.eql(''); + }; + + const createExpectNotFoundResult = () => (resp: { [key: string]: any }) => { + expect(resp.body).to.eql({ + error: 'Not Found', + message: 'Not Found', + statusCode: 404, + }); + }; + + const createExpectRbacForbidden = (spaceId: string) => (resp: { [key: string]: any }) => { + expect(resp.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: `Unauthorized to get ${spaceId} space`, + }); + }; + + const createExpectResults = (spaceId: string) => (resp: { [key: string]: any }) => { + const allSpaces = [ + { + id: 'default', + name: 'Default', + color: '#00bfb3', + description: 'This is your default space!', + _reserved: true, + disabledFeatures: [], + }, + { + id: 'space_1', + name: 'Space 1', + description: 'This is the first test space', + disabledFeatures: [], + }, + { + id: 'space_2', + name: 'Space 2', + description: 'This is the second test space', + disabledFeatures: [], + }, + { + id: 'space_3', + name: 'Space 3', + description: 'This is the third test space', + solution: 'es', + disabledFeatures: [ + // Disabled features are automatically added to the space when a solution is set + 'apm', + 'infrastructure', + 'inventory', + 'logs', + 'observabilityCases', + 'securitySolutionAssistant', + 'securitySolutionAttackDiscovery', + 'securitySolutionCases', + 'siem', + 'slo', + 'uptime', + ], + }, + ]; + + const disabledFeatures = (resp.body.disabledFeatures ?? []).sort(); + + const expectedSpace = allSpaces.find((space) => space.id === spaceId); + if (expectedSpace) { + expectedSpace.disabledFeatures.sort(); + } + + expect({ ...resp.body, disabledFeatures }).to.eql(expectedSpace); + }; + + const makeGetTest = + (describeFn: DescribeFn) => + (description: string, { user, currentSpaceId, spaceId, tests }: GetTestDefinition) => { + describeFn(description, () => { + let supertest: SupertestWithRoleScopeType | SuperTestAgent; + + before(async () => { + supertest = await getSupertest(context, user); + esArchiver.load( + 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' + ); + }); + after(() => { + maybeDestroySupertest(supertest); + esArchiver.unload( + 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' + ); + }); + + getTestScenariosForSpace(currentSpaceId).forEach(({ urlPrefix, scenario }) => { + it(`should return ${tests.default.statusCode} ${scenario}`, async () => { + return supertest + .get(`${urlPrefix}/api/spaces/space/${spaceId}`) + .expect(tests.default.statusCode) + .then(tests.default.response); + }); + }); + }); + }; + + const getTest = makeGetTest(describe); + // @ts-ignore + getTest.only = makeGetTest(describe); + + return { + createExpectResults, + createExpectRbacForbidden, + createExpectEmptyResult, + createExpectNotFoundResult, + getTest, + nonExistantSpaceId, + }; +} diff --git a/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts new file mode 100644 index 0000000000000..3f9c6f6eb3173 --- /dev/null +++ b/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts @@ -0,0 +1,217 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Agent as SuperTestAgent } from 'supertest'; + +import expect from '@kbn/expect'; + +import { getSupertest, maybeDestroySupertest } from './common'; +import type { + DeploymentAgnosticFtrProviderContext, + SupertestWithRoleScopeType, +} from '../../deployment_agnostic/ftr_provider_context'; +import { getTestScenariosForSpace } from '../lib/space_test_utils'; +import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; + +interface GetAllTest { + statusCode: number; + response: (resp: { [key: string]: any }) => void; +} + +interface GetAllTests { + exists: GetAllTest; + copySavedObjectsPurpose: GetAllTest; + shareSavedObjectsPurpose: GetAllTest; + includeAuthorizedPurposes: GetAllTest; +} + +interface GetAllTestDefinition { + user?: TestDefinitionAuthentication; + spaceId: string; + tests: GetAllTests; +} + +interface AuthorizedPurposes { + any: boolean; + copySavedObjectsIntoSpace: boolean; + findSavedObjects: boolean; + shareSavedObjectsIntoSpace: boolean; +} + +interface Space { + id: string; + name: string; + color?: string; + description: string; + solution?: string; + _reserved?: boolean; + disabledFeatures: string[]; +} + +const ALL_SPACE_RESULTS: Space[] = [ + { + id: 'default', + name: 'Default', + color: '#00bfb3', + description: 'This is your default space!', + disabledFeatures: [], + _reserved: true, + }, + { + id: 'space_1', + name: 'Space 1', + description: 'This is the first test space', + disabledFeatures: [], + }, + { + id: 'space_2', + name: 'Space 2', + description: 'This is the second test space', + disabledFeatures: [], + }, + { + id: 'space_3', + name: 'Space 3', + description: 'This is the third test space', + disabledFeatures: [ + // Disabled features are automatically added to the space when a solution is set + 'apm', + 'infrastructure', + 'inventory', + 'logs', + 'observabilityCases', + 'securitySolutionAssistant', + 'securitySolutionAttackDiscovery', + 'securitySolutionCases', + 'siem', + 'slo', + 'uptime', + ], + solution: 'es', + }, +]; + +export function getAllTestSuiteFactory(context: DeploymentAgnosticFtrProviderContext) { + const esArchiver = context.getService('esArchiver'); + + const createExpectResults = + (...spaceIds: string[]) => + (resp: { [key: string]: any }) => { + const expectedBody = ALL_SPACE_RESULTS.filter((entry) => spaceIds.includes(entry.id)); + for (const space of resp.body) { + const expectedSpace = expectedBody.find((x) => x.id === space.id); + expect(space.name).to.eql(expectedSpace?.name); + expect(space.description).to.eql(expectedSpace?.description); + expect(space.color).to.eql(expectedSpace?.color); + expect(space.solution).to.eql(expectedSpace?.solution); + expect(space.disabledFeatures.sort()).to.eql(expectedSpace?.disabledFeatures.sort()); + } + }; + + const createExpectAllPurposesResults = + (authorizedPurposes: AuthorizedPurposes, ...spaceIds: string[]) => + (resp: { [key: string]: any }) => { + const expectedBody = ALL_SPACE_RESULTS.filter((entry) => spaceIds.includes(entry.id)).map( + (x) => ({ ...x, authorizedPurposes }) + ); + + for (const space of resp.body) { + const expectedSpace = expectedBody.find((x) => x.id === space.id); + expect(space.name).to.eql(expectedSpace?.name); + expect(space.description).to.eql(expectedSpace?.description); + expect(space.color).to.eql(expectedSpace?.color); + expect(space.solution).to.eql(expectedSpace?.solution); + expect(space.disabledFeatures.sort()).to.eql(expectedSpace?.disabledFeatures.sort()); + expect(space.authorizedPurposes).to.eql(expectedSpace?.authorizedPurposes); + } + }; + + const expectEmptyResult = (resp: { [key: string]: any }) => { + expect(resp.body).to.eql(''); + }; + + const expectRbacForbidden = (resp: { [key: string]: any }) => { + expect(resp.body).to.eql({ + error: 'Forbidden', + message: 'Forbidden', + statusCode: 403, + }); + }; + + const makeGetAllTest = + (describeFn: DescribeFn) => + (description: string, { user, spaceId, tests }: GetAllTestDefinition) => { + describeFn(description, () => { + let supertest: SupertestWithRoleScopeType | SuperTestAgent; + before(async () => { + supertest = await getSupertest(context, user); + esArchiver.load( + 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' + ); + }); + after(() => { + esArchiver.unload( + 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' + ); + maybeDestroySupertest(supertest); + }); + + getTestScenariosForSpace(spaceId).forEach(({ scenario, urlPrefix }) => { + describe('undefined purpose', () => { + it(`should return ${tests.exists.statusCode} ${scenario}`, async () => { + return supertest + .get(`${urlPrefix}/api/spaces/space`) + .expect(tests.exists.statusCode) + .then(tests.exists.response); + }); + }); + + describe('copySavedObjectsIntoSpace purpose', () => { + it(`should return ${tests.copySavedObjectsPurpose.statusCode} ${scenario}`, async () => { + return supertest + .get(`${urlPrefix}/api/spaces/space`) + .query({ purpose: 'copySavedObjectsIntoSpace' }) + .expect(tests.copySavedObjectsPurpose.statusCode) + .then(tests.copySavedObjectsPurpose.response); + }); + }); + + describe('shareSavedObjectsIntoSpace purpose', () => { + it(`should return ${tests.shareSavedObjectsPurpose.statusCode} ${scenario}`, async () => { + return supertest + .get(`${urlPrefix}/api/spaces/space`) + .query({ purpose: 'shareSavedObjectsIntoSpace' }) + .expect(tests.copySavedObjectsPurpose.statusCode) + .then(tests.copySavedObjectsPurpose.response); + }); + }); + + describe('include_authorized_purposes=true', () => { + it(`should return ${tests.includeAuthorizedPurposes.statusCode} ${scenario}`, async () => { + return supertest + .get(`${urlPrefix}/api/spaces/space`) + .query({ include_authorized_purposes: true }) + .expect(tests.includeAuthorizedPurposes.statusCode) + .then(tests.includeAuthorizedPurposes.response); + }); + }); + }); + }); + }; + + const getAllTest = makeGetAllTest(describe); + // @ts-ignore + getAllTest.only = makeGetAllTest(describe.only); + + return { + createExpectResults, + createExpectAllPurposesResults, + expectRbacForbidden, + getAllTest, + expectEmptyResult, + }; +} diff --git a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts new file mode 100644 index 0000000000000..23d86b833216b --- /dev/null +++ b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts @@ -0,0 +1,703 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Agent as SuperTestAgent } from 'supertest'; + +import type { SavedObject } from '@kbn/core/server'; +import expect from '@kbn/expect'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; +import type { CopyResponse } from '@kbn/spaces-plugin/server/lib/copy_to_spaces'; + +import { getSupertest, maybeDestroySupertest } from './common'; +import { getTestDataLoader, SPACE_1, SPACE_2 } from '../../../common/lib/test_data_loader'; +import type { + DeploymentAgnosticFtrProviderContext, + SupertestWithRoleScopeType, +} from '../../deployment_agnostic/ftr_provider_context'; +import { getUrlPrefix } from '../lib/space_test_utils'; +import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; + +type TestResponse = Record; + +interface ResolveCopyToSpaceTest { + statusCode: number; + response: (resp: TestResponse) => Promise; +} + +interface ResolveCopyToSpaceMultiNamespaceTest extends ResolveCopyToSpaceTest { + testTitle: string; + objects: Array>; + retries: Record; +} + +interface ResolveCopyToSpaceTests { + withReferencesNotOverwriting: ResolveCopyToSpaceTest; + withReferencesOverwriting: ResolveCopyToSpaceTest; + withoutReferencesOverwriting: ResolveCopyToSpaceTest; + withoutReferencesNotOverwriting: ResolveCopyToSpaceTest; + nonExistentSpace: ResolveCopyToSpaceTest; + multiNamespaceTestCases: () => ResolveCopyToSpaceMultiNamespaceTest[]; +} + +interface ResolveCopyToSpaceTestDefinition { + user?: TestDefinitionAuthentication; + spaceId?: string; + tests: ResolveCopyToSpaceTests; +} + +const NON_EXISTENT_SPACE_ID = 'non_existent_space'; + +const SPACE_DATA_TO_LOAD: Array<{ spaceName: string | null; dataUrl: string }> = [ + { + spaceName: null, + dataUrl: 'x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/default_space.json', + }, + { + spaceName: SPACE_1.id, + dataUrl: 'x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/space_1.json', + }, + { + spaceName: SPACE_2.id, + dataUrl: 'x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/space_2.json', + }, +]; + +const getDestinationSpace = (originSpaceId?: string) => { + if (!originSpaceId || originSpaceId === DEFAULT_SPACE_ID) { + return 'space_1'; + } + return DEFAULT_SPACE_ID; +}; + +export function resolveCopyToSpaceConflictsSuite(context: DeploymentAgnosticFtrProviderContext) { + const testDataLoader = getTestDataLoader(context); + const supertestWithAuth = context.getService('supertest'); + + const getVisualizationAtSpace = async (spaceId: string): Promise> => { + return supertestWithAuth + .get(`${getUrlPrefix(spaceId)}/api/saved_objects/visualization/cts_vis_3_${spaceId}`) + .then((response: any) => response.body); + }; + const getDashboardAtSpace = async (spaceId: string): Promise> => { + return supertestWithAuth + .get(`${getUrlPrefix(spaceId)}/api/saved_objects/dashboard/cts_dashboard_${spaceId}`) + .then((response: any) => response.body); + }; + + const getObjectsAtSpace = async ( + spaceId: string + ): Promise<[SavedObject, SavedObject]> => { + const dashboard = await getDashboardAtSpace(spaceId); + const visualization = await getVisualizationAtSpace(spaceId); + return [dashboard, visualization]; + }; + + const createExpectOverriddenResponseWithReferences = + (sourceSpaceId: string) => async (response: TestResponse) => { + const destination = getDestinationSpace(sourceSpaceId); + const result = response.body; + expect(result).to.eql({ + [destination]: { + success: true, + successCount: 2, + successResults: [ + { + id: `cts_ip_1_${sourceSpaceId}`, + type: 'index-pattern', + meta: { + title: `Copy to Space index pattern 1 from ${sourceSpaceId} space`, + icon: 'indexPatternApp', + }, + destinationId: `cts_ip_1_${destination}`, // this conflicted with another index pattern in the destination space because of a shared originId + overwrite: true, + managed: false, + }, + { + id: `cts_vis_3_${sourceSpaceId}`, + type: 'visualization', + meta: { + title: `CTS vis 3 from ${sourceSpaceId} space`, + icon: 'visualizeApp', + }, + destinationId: `cts_vis_3_${destination}`, // this conflicted with another visualization in the destination space because of a shared originId + overwrite: true, + managed: false, + }, + ], + }, + }); + const [dashboard, visualization] = await getObjectsAtSpace(destination); + expect(dashboard.attributes.title).to.eql( + `This is the ${destination} test space CTS dashboard` + ); + expect(visualization.attributes.title).to.eql(`CTS vis 3 from ${sourceSpaceId} space`); + }; + + const createExpectOverriddenResponseWithoutReferences = + (sourceSpaceId: string, destinationSpaceId: string = getDestinationSpace(sourceSpaceId)) => + async (response: TestResponse) => { + const result = response.body; + expect(result).to.eql({ + [destinationSpaceId]: { + success: true, + successCount: 1, + successResults: [ + { + id: `cts_dashboard_${sourceSpaceId}`, + type: 'dashboard', + meta: { + title: `This is the ${sourceSpaceId} test space CTS dashboard`, + icon: 'dashboardApp', + }, + destinationId: `cts_dashboard_${destinationSpaceId}`, // this conflicted with another dashboard in the destination space because of a shared originId + overwrite: true, + managed: false, + }, + ], + }, + }); + const [dashboard, visualization] = await getObjectsAtSpace(destinationSpaceId); + expect(dashboard.attributes.title).to.eql( + `This is the ${sourceSpaceId} test space CTS dashboard` + ); + if (destinationSpaceId === NON_EXISTENT_SPACE_ID) { + expect((visualization as any).statusCode).to.eql(404); + } else { + expect(visualization.attributes.title).to.eql(`CTS vis 3 from ${destinationSpaceId} space`); + } + }; + + const createExpectNonOverriddenResponseWithReferences = + (sourceSpaceId: string) => async (response: TestResponse) => { + const destination = getDestinationSpace(sourceSpaceId); + + const result = response.body; + expect(result).to.eql({ + [destination]: { + success: false, + successCount: 0, + errors: [ + { + error: { + type: 'conflict', + destinationId: `cts_ip_1_${destination}`, // this conflicted with another index pattern in the destination space because of a shared originId + }, + id: `cts_ip_1_${sourceSpaceId}`, + meta: { + title: `Copy to Space index pattern 1 from ${sourceSpaceId} space`, + icon: 'indexPatternApp', + }, + type: 'index-pattern', + }, + { + error: { + type: 'conflict', + destinationId: `cts_vis_3_${destination}`, // this conflicted with another visualization in the destination space because of a shared originId + }, + id: `cts_vis_3_${sourceSpaceId}`, + meta: { + title: `CTS vis 3 from ${sourceSpaceId} space`, + icon: 'visualizeApp', + }, + type: 'visualization', + }, + ], + }, + }); + + const [dashboard, visualization] = await getObjectsAtSpace(destination); + expect(dashboard.attributes.title).to.eql( + `This is the ${destination} test space CTS dashboard` + ); + expect(visualization.attributes.title).to.eql(`CTS vis 3 from ${destination} space`); + }; + + const createExpectNonOverriddenResponseWithoutReferences = + (sourceSpaceId: string) => async (response: TestResponse) => { + const destination = getDestinationSpace(sourceSpaceId); + + const result = response.body; + expect(result).to.eql({ + [destination]: { + success: false, + successCount: 0, + errors: [ + { + error: { + type: 'conflict', + destinationId: `cts_dashboard_${destination}`, // this conflicted with another visualization in the destination space because of a shared originId + }, + id: `cts_dashboard_${sourceSpaceId}`, + type: 'dashboard', + meta: { + title: `This is the ${sourceSpaceId} test space CTS dashboard`, + icon: 'dashboardApp', + }, + }, + ], + }, + }); + + const [dashboard, visualization] = await getObjectsAtSpace(destination); + expect(dashboard.attributes.title).to.eql( + `This is the ${destination} test space CTS dashboard` + ); + expect(visualization.attributes.title).to.eql(`CTS vis 3 from ${destination} space`); + }; + + const expectRouteForbiddenResponse = async (resp: TestResponse) => { + expect(resp.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: + 'API [POST /api/spaces/_resolve_copy_saved_objects_errors] is unauthorized for user, this action is granted by the Kibana privileges [copySavedObjectsToSpaces]', + }); + }; + + const expectRouteNotFoundResponse = async (resp: TestResponse) => { + expect(resp.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: 'Not Found', + }); + }; + + const createExpectUnauthorizedAtSpaceWithReferencesResult = + (spaceId: string = DEFAULT_SPACE_ID) => + async (resp: TestResponse) => { + const destination = getDestinationSpace(spaceId); + + const result = resp.body as CopyResponse; + expect(result).to.eql({ + [destination]: { + success: false, + successCount: 0, + errors: [ + { + statusCode: 403, + error: 'Forbidden', + message: 'Unable to bulk_create index-pattern,visualization', + }, + ], + }, + } as CopyResponse); + + // Query ES to ensure that nothing was copied + const [dashboard, visualization] = await getObjectsAtSpace(destination); + expect(dashboard.attributes.title).to.eql( + `This is the ${destination} test space CTS dashboard` + ); + expect(visualization.attributes.title).to.eql(`CTS vis 3 from ${destination} space`); + }; + + const createExpectUnauthorizedAtSpaceWithoutReferencesResult = + ( + sourceSpaceId: string = DEFAULT_SPACE_ID, + destinationSpaceId: string = getDestinationSpace(sourceSpaceId) + ) => + async (resp: TestResponse) => { + const result = resp.body as CopyResponse; + expect(result).to.eql({ + [destinationSpaceId]: { + success: false, + successCount: 0, + errors: [ + { + statusCode: 403, + error: 'Forbidden', + message: 'Unable to bulk_create dashboard', + }, + ], + }, + } as CopyResponse); + + // Query ES to ensure that nothing was copied + const [dashboard, visualization] = await getObjectsAtSpace(destinationSpaceId); + + if (destinationSpaceId === NON_EXISTENT_SPACE_ID) { + expect((dashboard as any).statusCode).to.eql(404); + expect((visualization as any).statusCode).to.eql(404); + } else { + expect(dashboard.attributes.title).to.eql( + `This is the ${destinationSpaceId} test space CTS dashboard` + ); + expect(visualization.attributes.title).to.eql(`CTS vis 3 from ${destinationSpaceId} space`); + } + }; + + /** + * Creates test cases for multi-namespace saved object types. + * Note: these are written with the assumption that test data will only be reloaded between each group of test cases, *not* before every + * single test case. This saves time during test execution. + */ + const createMultiNamespaceTestCases = + ( + spaceId: string, + outcome: 'authorized' | 'unauthorizedRead' | 'unauthorizedWrite' | 'noAccess' = 'authorized' + ) => + (): ResolveCopyToSpaceMultiNamespaceTest[] => { + // the status code of the HTTP response differs depending on the error type + // a 403 error actually comes back as an HTTP 200 response + const statusCode = outcome === 'noAccess' ? 403 : 200; + const type = 'sharedtype'; + const exactMatchId = 'each_space'; + const inexactMatchIdA = `conflict_1a_${spaceId}`; + const inexactMatchIdB = `conflict_1b_${spaceId}`; + const inexactMatchIdC = `conflict_1c_default_and_space_1`; + const ambiguousConflictId = `conflict_2_${spaceId}`; + + const createRetries = (overwriteRetry: Record) => ({ + space_2: [overwriteRetry], + }); + const getResult = (response: TestResponse) => (response.body as CopyResponse).space_2; + const expectSavedObjectForbiddenResponse = (response: TestResponse) => { + expect(response.body).to.eql({ + space_2: { + success: false, + successCount: 0, + errors: [ + { statusCode: 403, error: 'Forbidden', message: `Unable to bulk_create sharedtype` }, + ], + }, + }); + }; + const expectSavedObjectSuccessResponse = ( + response: TestResponse, + id: string, + destinationId?: string + ) => { + const { success, successCount, successResults, errors } = getResult(response); + expect(success).to.eql(true); + expect(successCount).to.eql(1); + expect(errors).to.be(undefined); + const title = (() => { + switch (id) { + case exactMatchId: + return 'A shared saved-object in the default, space_1, and space_2 spaces'; + case inexactMatchIdA: + return 'This is used to test an inexact match conflict for an originId -> originId match'; + case inexactMatchIdB: + return 'This is used to test an inexact match conflict for an originId -> id match'; + case inexactMatchIdC: + return 'This is used to test an inexact match conflict for an id -> originId match'; + default: + return 'A shared saved-object in one space'; + } + })(); + const meta = { title, icon: 'beaker' }; + expect(successResults).to.eql([ + { + type, + id, + meta, + overwrite: true, + ...(destinationId && { destinationId }), + managed: false, + }, + ]); + }; + + return [ + { + testTitle: 'copying with an exact match conflict', + objects: [{ type, id: exactMatchId }], + retries: createRetries({ type, id: exactMatchId, overwrite: true }), + statusCode, + response: async (response: TestResponse) => { + if (outcome === 'authorized') { + expectSavedObjectSuccessResponse(response, exactMatchId); + } else if (outcome === 'noAccess') { + await expectRouteForbiddenResponse(response); + } else { + // unauthorized read/write + expectSavedObjectForbiddenResponse(response); + } + }, + }, + { + testTitle: + 'copying with an inexact match conflict (a) - originId matches existing originId', + objects: [{ type, id: inexactMatchIdA }], + retries: createRetries({ + type, + id: inexactMatchIdA, + overwrite: true, + destinationId: 'conflict_1a_space_2', + }), + statusCode, + response: async (response: TestResponse) => { + if (outcome === 'authorized') { + expectSavedObjectSuccessResponse(response, inexactMatchIdA, 'conflict_1a_space_2'); + } else if (outcome === 'noAccess') { + await expectRouteForbiddenResponse(response); + } else { + // unauthorized read/write + expectSavedObjectForbiddenResponse(response); + } + }, + }, + { + testTitle: 'copying with an inexact match conflict (b) - originId matches existing id', + objects: [{ type, id: inexactMatchIdB }], + retries: createRetries({ + type, + id: inexactMatchIdB, + overwrite: true, + destinationId: 'conflict_1b_space_2', + }), + statusCode, + response: async (response: TestResponse) => { + if (outcome === 'authorized') { + expectSavedObjectSuccessResponse(response, inexactMatchIdB, 'conflict_1b_space_2'); + } else if (outcome === 'noAccess') { + await expectRouteForbiddenResponse(response); + } else { + // unauthorized read/write + expectSavedObjectForbiddenResponse(response); + } + }, + }, + { + testTitle: 'copying with an inexact match conflict (c) - id matches existing originId', + objects: [{ type, id: inexactMatchIdC }], + retries: createRetries({ + type, + id: inexactMatchIdC, + overwrite: true, + destinationId: 'conflict_1c_space_2', + }), + statusCode, + response: async (response: TestResponse) => { + if (outcome === 'authorized') { + expectSavedObjectSuccessResponse(response, inexactMatchIdC, 'conflict_1c_space_2'); + } else if (outcome === 'noAccess') { + await expectRouteForbiddenResponse(response); + } else { + // unauthorized read/write + expectSavedObjectForbiddenResponse(response); + } + }, + }, + { + testTitle: 'copying with an ambiguous conflict', + objects: [{ type, id: ambiguousConflictId }], + retries: createRetries({ + type, + id: ambiguousConflictId, + overwrite: true, + destinationId: 'conflict_2_space_2', + }), + statusCode, + response: async (response: TestResponse) => { + if (outcome === 'authorized') { + expectSavedObjectSuccessResponse(response, ambiguousConflictId, 'conflict_2_space_2'); + } else if (outcome === 'noAccess') { + await expectRouteForbiddenResponse(response); + } else { + // unauthorized read/write + expectSavedObjectForbiddenResponse(response); + } + }, + }, + ]; + }; + + const makeResolveCopyToSpaceConflictsTest = + (describeFn: DescribeFn) => + ( + description: string, + { user, spaceId = DEFAULT_SPACE_ID, tests }: ResolveCopyToSpaceTestDefinition + ) => { + describeFn(description, () => { + let supertest: SupertestWithRoleScopeType | SuperTestAgent; + before(async () => { + supertest = await getSupertest(context, user); + // test data only allows for the following spaces as the copy origin + expect(['default', 'space_1']).to.contain(spaceId); + }); + + after(() => { + maybeDestroySupertest(supertest); + }); + + describe('single-namespace types', () => { + beforeEach( + async () => await testDataLoader.createFtrSavedObjectsData(SPACE_DATA_TO_LOAD) + ); + afterEach(async () => await testDataLoader.deleteFtrSavedObjectsData()); + + const dashboardObject = { type: 'dashboard', id: `cts_dashboard_${spaceId}` }; + const visualizationObject = { type: 'visualization', id: `cts_vis_3_${spaceId}` }; + const indexPatternObject = { type: 'index-pattern', id: `cts_ip_1_${spaceId}` }; + + it(`should return ${tests.withReferencesNotOverwriting.statusCode} when not overwriting, with references`, async () => { + const destination = getDestinationSpace(spaceId); + + return supertest + .post(`${getUrlPrefix(spaceId)}/api/spaces/_resolve_copy_saved_objects_errors`) + .send({ + objects: [dashboardObject], + includeReferences: true, + createNewCopies: false, + retries: { + [destination]: [ + { + ...indexPatternObject, + destinationId: `cts_ip_1_${destination}`, + overwrite: false, + }, + { + ...visualizationObject, + destinationId: `cts_vis_3_${destination}`, + overwrite: false, + }, + ], + }, + }) + .expect(tests.withReferencesNotOverwriting.statusCode) + .then(tests.withReferencesNotOverwriting.response); + }); + + it(`should return ${tests.withReferencesOverwriting.statusCode} when overwriting, with references`, async () => { + const destination = getDestinationSpace(spaceId); + + return supertest + .post(`${getUrlPrefix(spaceId)}/api/spaces/_resolve_copy_saved_objects_errors`) + .send({ + objects: [dashboardObject], + includeReferences: true, + createNewCopies: false, + retries: { + [destination]: [ + { + ...indexPatternObject, + destinationId: `cts_ip_1_${destination}`, + overwrite: true, + }, + { + ...visualizationObject, + destinationId: `cts_vis_3_${destination}`, + overwrite: true, + }, + ], + }, + }) + .expect(tests.withReferencesOverwriting.statusCode) + .then(tests.withReferencesOverwriting.response); + }); + + it(`should return ${tests.withoutReferencesOverwriting.statusCode} when overwriting, without references`, async () => { + const destination = getDestinationSpace(spaceId); + + return supertest + .post(`${getUrlPrefix(spaceId)}/api/spaces/_resolve_copy_saved_objects_errors`) + .send({ + objects: [dashboardObject], + includeReferences: false, + createNewCopies: false, + retries: { + [destination]: [ + { + ...dashboardObject, + destinationId: `cts_dashboard_${destination}`, + overwrite: true, + }, + ], + }, + }) + .expect(tests.withoutReferencesOverwriting.statusCode) + .then(tests.withoutReferencesOverwriting.response); + }); + + it(`should return ${tests.withoutReferencesNotOverwriting.statusCode} when not overwriting, without references`, async () => { + const destination = getDestinationSpace(spaceId); + + return supertest + .post(`${getUrlPrefix(spaceId)}/api/spaces/_resolve_copy_saved_objects_errors`) + .send({ + objects: [dashboardObject], + includeReferences: false, + createNewCopies: false, + retries: { + [destination]: [ + { + ...dashboardObject, + destinationId: `cts_dashboard_${destination}`, + overwrite: false, + }, + ], + }, + }) + .expect(tests.withoutReferencesNotOverwriting.statusCode) + .then(tests.withoutReferencesNotOverwriting.response); + }); + + it(`should return ${tests.nonExistentSpace.statusCode} when resolving within a non-existent space`, async () => { + const destination = NON_EXISTENT_SPACE_ID; + + return supertest + .post(`${getUrlPrefix(spaceId)}/api/spaces/_resolve_copy_saved_objects_errors`) + .send({ + objects: [dashboardObject], + includeReferences: false, + createNewCopies: false, + retries: { + [destination]: [ + { + ...dashboardObject, + destinationId: `cts_dashboard_${destination}`, + // realistically a retry wouldn't use a destinationId, because it wouldn't have an origin conflict with another + // object in a non-existent space, but for the simplicity of testing we'll use this here + overwrite: true, + }, + ], + }, + }) + .expect(tests.nonExistentSpace.statusCode) + .then(tests.nonExistentSpace.response); + }); + }); + + const includeReferences = false; + const createNewCopies = false; + describe(`multi-namespace types with "overwrite" retry`, () => { + before(async () => await testDataLoader.createFtrSavedObjectsData(SPACE_DATA_TO_LOAD)); + after(async () => await testDataLoader.deleteFtrSavedObjectsData()); + + const testCases = tests.multiNamespaceTestCases(); + testCases.forEach(({ testTitle, objects, retries, statusCode, response }) => { + it(`should return ${statusCode} when ${testTitle}`, async () => { + return supertest + .post(`${getUrlPrefix(spaceId)}/api/spaces/_resolve_copy_saved_objects_errors`) + .send({ objects, includeReferences, createNewCopies, retries }) + .expect(statusCode) + .then(response); + }); + }); + }); + }); + }; + + const resolveCopyToSpaceConflictsTest = makeResolveCopyToSpaceConflictsTest(describe); + // @ts-ignore + resolveCopyToSpaceConflictsTest.only = makeResolveCopyToSpaceConflictsTest(describe.only); + + return { + resolveCopyToSpaceConflictsTest, + expectRouteForbiddenResponse, + expectRouteNotFoundResponse, + createExpectOverriddenResponseWithReferences, + createExpectOverriddenResponseWithoutReferences, + createExpectNonOverriddenResponseWithReferences, + createExpectNonOverriddenResponseWithoutReferences, + createExpectUnauthorizedAtSpaceWithReferencesResult, + createExpectUnauthorizedAtSpaceWithoutReferencesResult, + createMultiNamespaceTestCases, + originSpaces: ['default', 'space_1'], + NON_EXISTENT_SPACE_ID, + }; +} diff --git a/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts new file mode 100644 index 0000000000000..4d303450bdbed --- /dev/null +++ b/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts @@ -0,0 +1,164 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Agent as SuperTestAgent } from 'supertest'; + +import expect from '@kbn/expect'; + +import { getSupertest, maybeDestroySupertest } from './common'; +import type { + DeploymentAgnosticFtrProviderContext, + SupertestWithRoleScopeType, +} from '../../deployment_agnostic/ftr_provider_context'; +import { getUrlPrefix } from '../lib/space_test_utils'; +import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; + +interface UpdateTest { + statusCode: number; + response: (resp: { [key: string]: any }) => void; +} + +interface UpdateTests { + alreadyExists: UpdateTest; + defaultSpace: UpdateTest; + newSpace: UpdateTest; +} + +interface UpdateTestDefinition { + user?: TestDefinitionAuthentication; + spaceId: string; + tests: UpdateTests; +} + +export function updateTestSuiteFactory(context: DeploymentAgnosticFtrProviderContext) { + const esArchiver = context.getService('esArchiver'); + + const expectRbacForbidden = (resp: { [key: string]: any }) => { + expect(resp.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to update spaces', + }); + }; + + const expectNotFound = (resp: { [key: string]: any }) => { + expect(resp.body).to.eql({ + error: 'Not Found', + message: 'Not Found', + statusCode: 404, + }); + }; + + const expectDefaultSpaceResult = (resp: { [key: string]: any }) => { + expect(resp.body).to.eql({ + name: 'the new default', + id: 'default', + description: 'a description', + color: '#ffffff', + disabledFeatures: [], + _reserved: true, + }); + }; + + const expectAlreadyExistsResult = (resp: { [key: string]: any }) => { + expect(resp.body).to.eql({ + name: 'space 1', + id: 'space_1', + description: 'a description', + color: '#5c5959', + disabledFeatures: [], + }); + }; + + const makeUpdateTest = + (describeFn: DescribeFn) => + (description: string, { user, spaceId, tests }: UpdateTestDefinition) => { + describeFn(description, () => { + let supertest: SupertestWithRoleScopeType | SuperTestAgent; + before(async () => { + supertest = await getSupertest(context, user); + esArchiver.load( + 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' + ); + }); + after(() => { + maybeDestroySupertest(supertest); + esArchiver.unload( + 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' + ); + }); + + describe('space_1', () => { + it(`should return ${tests.alreadyExists.statusCode}`, async () => { + return supertest + .put(`${getUrlPrefix(spaceId)}/api/spaces/space/space_1`) + .send({ + name: 'space 1', + id: 'space_1', + description: 'a description', + color: '#5c5959', + _reserved: true, + disabledFeatures: [], + }) + .expect(tests.alreadyExists.statusCode) + .then(tests.alreadyExists.response); + }); + }); + + describe(`default space`, () => { + it(`should return ${tests.defaultSpace.statusCode}`, async () => { + return supertest + .put(`${getUrlPrefix(spaceId)}/api/spaces/space/default`) + .send({ + name: 'the new default', + id: 'default', + description: 'a description', + color: '#ffffff', + _reserved: false, + disabledFeatures: [], + }) + .expect(tests.defaultSpace.statusCode) + .then(tests.defaultSpace.response); + }); + }); + + describe(`when space doesn't exist`, () => { + it(`should return ${tests.newSpace.statusCode}`, async () => { + return supertest + .put(`${getUrlPrefix(spaceId)}/api/spaces/space/marketing`) + .send({ + name: 'marketing', + id: 'marketing', + description: 'a description', + color: '#5c5959', + disabledFeatures: [], + }) + .expect(tests.newSpace.statusCode) + .then(tests.newSpace.response); + }); + }); + }); + }; + + const updateTest = makeUpdateTest(describe); + // @ts-ignore + updateTest.only = makeUpdateTest(describe.only); + + return { + expectAlreadyExistsResult, + expectDefaultSpaceResult, + expectNotFound, + expectRbacForbidden, + updateTest, + }; +} diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/ftr_provider_context.d.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/ftr_provider_context.d.ts new file mode 100644 index 0000000000000..3f5e391e0cb91 --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/ftr_provider_context.d.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { GenericFtrProviderContext } from '@kbn/test'; + +import type { services } from './services'; + +export type DeploymentAgnosticFtrProviderContext = GenericFtrProviderContext; +export type { SupertestWithRoleScopeType } from '../../api_integration/deployment_agnostic/services'; diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space/copy_to_space.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space.ts similarity index 92% rename from x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space/copy_to_space.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space.ts index b7e10cb500e80..090d69dd43510 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space/copy_to_space.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space.ts @@ -5,13 +5,20 @@ * 2.0. */ -import type { FtrProviderContext } from '../../../common/ftr_provider_context'; import { AUTHENTICATION } from '../../../common/lib/authentication'; import { SPACES } from '../../../common/lib/spaces'; -import { copyToSpaceTestSuiteFactory } from '../../../common/suites/copy_to_space'; +import { copyToSpaceTestSuiteFactory } from '../../../common/suites/copy_to_space.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export -export default function copyToSpaceSpacesAndSecuritySuite(context: FtrProviderContext) { +interface User { + username: string; + password: string; + role: string; +} + +export default function copyToSpaceSpacesAndSecuritySuite( + context: DeploymentAgnosticFtrProviderContext +) { const { copyToSpaceTest, expectNoConflictsWithoutReferencesResult, @@ -54,7 +61,7 @@ export default function copyToSpaceSpacesAndSecuritySuite(context: FtrProviderCo }, }, ].forEach(({ spaceId, ...scenario }) => { - const definitionNoAccess = (user: { username: string; password: string }) => ({ + const definitionNoAccess = (user: User) => ({ spaceId, user, tests: { @@ -117,7 +124,7 @@ export default function copyToSpaceSpacesAndSecuritySuite(context: FtrProviderCo response: createExpectUnauthorizedAtSpaceWithoutReferencesResult(spaceId, 'non-existent'), }, }; - const definitionUnauthorizedRead = (user: { username: string; password: string }) => ({ + const definitionUnauthorizedRead = (user: User) => ({ spaceId, user, tests: { @@ -125,7 +132,7 @@ export default function copyToSpaceSpacesAndSecuritySuite(context: FtrProviderCo multiNamespaceTestCases: createMultiNamespaceTestCases(spaceId, 'unauthorizedRead'), }, }); - const definitionUnauthorizedWrite = (user: { username: string; password: string }) => ({ + const definitionUnauthorizedWrite = (user: User) => ({ spaceId, user, tests: { @@ -133,7 +140,7 @@ export default function copyToSpaceSpacesAndSecuritySuite(context: FtrProviderCo multiNamespaceTestCases: createMultiNamespaceTestCases(spaceId, 'unauthorizedWrite'), }, }); - const definitionAuthorized = (user: { username: string; password: string }) => ({ + const definitionAuthorized = (user: User) => ({ spaceId, user, tests: { diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/create.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/create.ts similarity index 91% rename from x-pack/test/spaces_api_integration/security_and_spaces/apis/create.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/create.ts index 6e5bc1649cb3f..ea775aa0a50ce 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/create.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/create.ts @@ -5,18 +5,12 @@ * 2.0. */ -import type { SuperTest } from 'supertest'; - -import type { FtrProviderContext } from '../../common/ftr_provider_context'; -import { AUTHENTICATION } from '../../common/lib/authentication'; -import { SPACES } from '../../common/lib/spaces'; -import { createTestSuiteFactory } from '../../common/suites/create'; - -// eslint-disable-next-line import/no-default-export -export default function createSpacesOnlySuite({ getService }: FtrProviderContext) { - const supertestWithoutAuth = getService('supertestWithoutAuth'); - const esArchiver = getService('esArchiver'); +import { AUTHENTICATION } from '../../../common/lib/authentication'; +import { SPACES } from '../../../common/lib/spaces'; +import { createTestSuiteFactory } from '../../../common/suites/create.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; +export default function createSpacesOnlySuite(context: DeploymentAgnosticFtrProviderContext) { const { createTest, expectNewSpaceResult, @@ -24,7 +18,7 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext expectConflictResponse, expectRbacForbiddenResponse, expectSolutionSpecifiedResult, - } = createTestSuiteFactory(esArchiver, supertestWithoutAuth as unknown as SuperTest); + } = createTestSuiteFactory(context); describe('create', () => { [ @@ -100,7 +94,6 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext }, }, }); - createTest(`rbac user with all globally from the ${scenario.spaceId} space`, { spaceId: scenario.spaceId, user: scenario.users.allGlobally, @@ -169,7 +162,6 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext }, }, }); - createTest(`rbac user with read globally from the ${scenario.spaceId} space`, { spaceId: scenario.spaceId, user: scenario.users.readGlobally, diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/delete.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/delete.ts similarity index 89% rename from x-pack/test/spaces_api_integration/security_and_spaces/apis/delete.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/delete.ts index 01fd306a7f558..1c398b08fc311 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/delete.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/delete.ts @@ -5,26 +5,19 @@ * 2.0. */ -import type { SuperTest } from 'supertest'; - -import type { FtrProviderContext } from '../../common/ftr_provider_context'; -import { AUTHENTICATION } from '../../common/lib/authentication'; -import { SPACES } from '../../common/lib/spaces'; -import { deleteTestSuiteFactory } from '../../common/suites/delete'; - -// eslint-disable-next-line import/no-default-export -export default function deleteSpaceTestSuite({ getService }: FtrProviderContext) { - const supertestWithoutAuth = getService('supertestWithoutAuth'); - const esArchiver = getService('esArchiver'); - const es = getService('es'); +import { AUTHENTICATION } from '../../../common/lib/authentication'; +import { SPACES } from '../../../common/lib/spaces'; +import { deleteTestSuiteFactory } from '../../../common/suites/delete.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; +export default function deleteSpaceTestSuite(context: DeploymentAgnosticFtrProviderContext) { const { deleteTest, expectRbacForbidden, expectEmptyResult, expectNotFound, expectReservedSpaceResult, - } = deleteTestSuiteFactory(es, esArchiver, supertestWithoutAuth as unknown as SuperTest); + } = deleteTestSuiteFactory(context); describe('delete', () => { [ diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/disable_legacy_url_aliases.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/disable_legacy_url_aliases.ts new file mode 100644 index 0000000000000..97d89076f6a61 --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/disable_legacy_url_aliases.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getTestScenarios } from '../../../../saved_object_api_integration/common/lib/saved_object_test_utils'; +import type { TestUser } from '../../../../saved_object_api_integration/common/lib/types'; +import { SPACES } from '../../../common/lib/spaces'; +import type { DisableLegacyUrlAliasesTestDefinition } from '../../../common/suites/disable_legacy_url_aliases'; +import { + disableLegacyUrlAliasesTestSuiteFactory, + TEST_CASE_SOURCE_ID, + TEST_CASE_TARGET_TYPE, +} from '../../../common/suites/disable_legacy_url_aliases.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; + +const { + DEFAULT: { spaceId: DEFAULT_SPACE_ID }, + SPACE_1: { spaceId: SPACE_1_ID }, + SPACE_2: { spaceId: SPACE_2_ID }, +} = SPACES; + +const createTestCases = () => { + const baseCase = { targetType: TEST_CASE_TARGET_TYPE, sourceId: TEST_CASE_SOURCE_ID }; + return { + [DEFAULT_SPACE_ID]: { ...baseCase, targetSpace: DEFAULT_SPACE_ID, expectFound: true }, // alias exists in the default space and should have been disabled + [SPACE_1_ID]: { ...baseCase, targetSpace: SPACE_1_ID, expectFound: false }, // alias does not exist in space_1 + [SPACE_2_ID]: { ...baseCase, targetSpace: SPACE_2_ID, expectFound: true }, // alias exists in space_2 and should have been disabled + }; +}; + +export default function (context: DeploymentAgnosticFtrProviderContext) { + const { addTests, createTestDefinitions } = disableLegacyUrlAliasesTestSuiteFactory(context); + + describe('_disable_legacy_url_aliases', () => { + const _addTests = ( + user: TestUser & { role: string }, + tests: DisableLegacyUrlAliasesTestDefinition[] + ) => { + addTests(`${user.description}`, { user, tests }); + }; + getTestScenarios().security.forEach(({ users }) => { + // We are intentionally using "security" test scenarios here, *not* "securityAndSpaces", because of how these tests are structured. + + const testCases = createTestCases(); + + [ + { ...users.noAccess, role: 'no_access' }, + { ...users.legacyAll, role: 'kibana_legacy_user' }, + { ...users.dualRead, role: 'dual_privileges_read' }, + { ...users.readGlobally, role: 'kibana_rbac_user' }, + { ...users.readAtDefaultSpace, role: 'kibana_rbac_default_space_read_user' }, + { ...users.readAtSpace1, role: 'kibana_rbac_space_1_all_user' }, + ].forEach((user) => { + const unauthorized = createTestDefinitions(Object.values(testCases), true); + _addTests(user, unauthorized); + }); + + const authorizedDefaultSpace = [ + ...createTestDefinitions(testCases[DEFAULT_SPACE_ID], false), + ...createTestDefinitions([testCases[SPACE_1_ID], testCases[SPACE_2_ID]], true), + ]; + _addTests( + { ...users.allAtDefaultSpace, role: 'kibana_rbac_default_space_all_user' }, + authorizedDefaultSpace + ); + + const authorizedSpace1 = [ + ...createTestDefinitions(testCases[SPACE_1_ID], false), + ...createTestDefinitions([testCases[DEFAULT_SPACE_ID], testCases[SPACE_2_ID]], true), + ]; + _addTests({ ...users.allAtSpace1, role: 'kibana_rbac_space_1_all_user' }, authorizedSpace1); + + // [users.dualAll, users.allGlobally, users.superuser].forEach((user) => { + // const authorizedGlobally = createTestDefinitions(Object.values(testCases), false); + // _addTests(user, authorizedGlobally); + // }); + }); + }); +} diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/get.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get.ts similarity index 93% rename from x-pack/test/spaces_api_integration/security_and_spaces/apis/get.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get.ts index cc24ffa0d891a..ca9fac5a99dcf 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/get.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get.ts @@ -5,25 +5,19 @@ * 2.0. */ -import type { SuperTest } from 'supertest'; - -import type { FtrProviderContext } from '../../common/ftr_provider_context'; -import { AUTHENTICATION } from '../../common/lib/authentication'; -import { SPACES } from '../../common/lib/spaces'; -import { getTestSuiteFactory } from '../../common/suites/get'; - -// eslint-disable-next-line import/no-default-export -export default function getSpaceTestSuite({ getService }: FtrProviderContext) { - const supertestWithoutAuth = getService('supertestWithoutAuth'); - const esArchiver = getService('esArchiver'); +import { AUTHENTICATION } from '../../../common/lib/authentication'; +import { SPACES } from '../../../common/lib/spaces'; +import { getTestSuiteFactory } from '../../../common/suites/get.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; +export default function getSpaceTestSuite(context: DeploymentAgnosticFtrProviderContext) { const { getTest, createExpectResults, createExpectNotFoundResult, createExpectRbacForbidden, nonExistantSpaceId, - } = getTestSuiteFactory(esArchiver, supertestWithoutAuth as unknown as SuperTest); + } = getTestSuiteFactory(context); describe('get', () => { [ diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/get_all.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get_all.ts similarity index 92% rename from x-pack/test/spaces_api_integration/security_and_spaces/apis/get_all.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get_all.ts index d40413f9457e3..fd9ed8a2abb0d 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/get_all.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get_all.ts @@ -5,20 +5,14 @@ * 2.0. */ -import type { SuperTest } from 'supertest'; - -import type { FtrProviderContext } from '../../common/ftr_provider_context'; -import { AUTHENTICATION } from '../../common/lib/authentication'; -import { SPACES } from '../../common/lib/spaces'; -import { getAllTestSuiteFactory } from '../../common/suites/get_all'; - -// eslint-disable-next-line import/no-default-export -export default function getAllSpacesTestSuite({ getService }: FtrProviderContext) { - const supertestWithoutAuth = getService('supertestWithoutAuth'); - const esArchiver = getService('esArchiver'); +import { AUTHENTICATION } from '../../../common/lib/authentication'; +import { SPACES } from '../../../common/lib/spaces'; +import { getAllTestSuiteFactory } from '../../../common/suites/get_all.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; +export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProviderContext) { const { getAllTest, createExpectResults, createExpectAllPurposesResults, expectRbacForbidden } = - getAllTestSuiteFactory(esArchiver, supertestWithoutAuth as unknown as SuperTest); + getAllTestSuiteFactory(context); // these are used to determine expected results for tests where the `include_authorized_purposes` option is enabled const authorizedAll = { @@ -430,31 +424,31 @@ export default function getAllSpacesTestSuite({ getService }: FtrProviderContext } ); - getAllTest( - `rbac user with saved objects management all at space_1 space can access space_1 from ${scenario.spaceId}`, - { - spaceId: scenario.spaceId, - user: scenario.users.allSavedObjectsAtSpace_1, - tests: { - exists: { - statusCode: 200, - response: createExpectResults('space_1'), - }, - copySavedObjectsPurpose: { - statusCode: 200, - response: createExpectResults('space_1'), - }, - shareSavedObjectsPurpose: { - statusCode: 200, - response: createExpectResults('space_1'), - }, - includeAuthorizedPurposes: { - statusCode: 200, - response: createExpectAllPurposesResults(authorizedAll, 'space_1'), - }, - }, - } - ); + // getAllTest( + // `rbac user with saved objects management all at space_1 space can access space_1 from ${scenario.spaceId}`, + // { + // spaceId: scenario.spaceId, + // user: scenario.users.allSavedObjectsAtSpace_1, + // tests: { + // exists: { + // statusCode: 200, + // response: createExpectResults('space_1'), + // }, + // copySavedObjectsPurpose: { + // statusCode: 200, + // response: createExpectResults('space_1'), + // }, + // shareSavedObjectsPurpose: { + // statusCode: 200, + // response: createExpectResults('space_1'), + // }, + // includeAuthorizedPurposes: { + // statusCode: 200, + // response: createExpectAllPurposesResults(authorizedAll, 'space_1'), + // }, + // }, + // } + // ); getAllTest( `rbac user with saved objects management read at space_1 space can access space_1 from ${scenario.spaceId}`, diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts new file mode 100644 index 0000000000000..2cda14d3fc69c --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('spaces api with security', function () { + loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); + loadTestFile(require.resolve('./create')); + loadTestFile(require.resolve('./delete')); + loadTestFile(require.resolve('./get_all')); + loadTestFile(require.resolve('./get')); + loadTestFile(require.resolve('./update')); + loadTestFile(require.resolve('./disable_legacy_url_aliases')); + }); +} diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/resolve_copy_to_space_conflicts.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/resolve_copy_to_space_conflicts.ts similarity index 90% rename from x-pack/test/spaces_api_integration/security_and_spaces/apis/resolve_copy_to_space_conflicts.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/resolve_copy_to_space_conflicts.ts index da5054436dc69..391119e97513a 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/resolve_copy_to_space_conflicts.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/resolve_copy_to_space_conflicts.ts @@ -5,13 +5,20 @@ * 2.0. */ -import type { FtrProviderContext } from '../../common/ftr_provider_context'; -import { AUTHENTICATION } from '../../common/lib/authentication'; -import { SPACES } from '../../common/lib/spaces'; -import { resolveCopyToSpaceConflictsSuite } from '../../common/suites/resolve_copy_to_space_conflicts'; +import { AUTHENTICATION } from '../../../common/lib/authentication'; +import { SPACES } from '../../../common/lib/spaces'; +import { resolveCopyToSpaceConflictsSuite } from '../../../common/suites/resolve_copy_to_space_conflicts.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export -export default function resolveCopyToSpaceConflictsTestSuite(context: FtrProviderContext) { +interface TestUser { + username: string; + password: string; + role: string; +} + +export default function resolveCopyToSpaceConflictsTestSuite( + context: DeploymentAgnosticFtrProviderContext +) { const { resolveCopyToSpaceConflictsTest, createExpectNonOverriddenResponseWithReferences, @@ -54,7 +61,7 @@ export default function resolveCopyToSpaceConflictsTestSuite(context: FtrProvide }, }, ].forEach(({ spaceId, ...scenario }) => { - const definitionNoAccess = (user: { username: string; password: string }) => ({ + const definitionNoAccess = (user: TestUser) => ({ spaceId, user, tests: { @@ -81,7 +88,7 @@ export default function resolveCopyToSpaceConflictsTestSuite(context: FtrProvide multiNamespaceTestCases: createMultiNamespaceTestCases(spaceId, 'noAccess'), }, }); - const definitionUnauthorizedRead = (user: { username: string; password: string }) => ({ + const definitionUnauthorizedRead = (user: TestUser) => ({ spaceId, user, tests: { @@ -111,7 +118,7 @@ export default function resolveCopyToSpaceConflictsTestSuite(context: FtrProvide multiNamespaceTestCases: createMultiNamespaceTestCases(spaceId, 'unauthorizedRead'), }, }); - const definitionUnauthorizedWrite = (user: { username: string; password: string }) => ({ + const definitionUnauthorizedWrite = (user: TestUser) => ({ spaceId, user, tests: { @@ -141,7 +148,7 @@ export default function resolveCopyToSpaceConflictsTestSuite(context: FtrProvide multiNamespaceTestCases: createMultiNamespaceTestCases(spaceId, 'unauthorizedWrite'), }, }); - const definitionAuthorized = (user: { username: string; password: string }) => ({ + const definitionAuthorized = (user: TestUser) => ({ spaceId, user, tests: { diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/update.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/update.ts similarity index 90% rename from x-pack/test/spaces_api_integration/security_and_spaces/apis/update.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/update.ts index 57fd1c5b1a202..0fbfe6763a1a7 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/update.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/update.ts @@ -5,25 +5,19 @@ * 2.0. */ -import type { SuperTest } from 'supertest'; - -import type { FtrProviderContext } from '../../common/ftr_provider_context'; -import { AUTHENTICATION } from '../../common/lib/authentication'; -import { SPACES } from '../../common/lib/spaces'; -import { updateTestSuiteFactory } from '../../common/suites/update'; - -// eslint-disable-next-line import/no-default-export -export default function updateSpaceTestSuite({ getService }: FtrProviderContext) { - const supertestWithoutAuth = getService('supertestWithoutAuth'); - const esArchiver = getService('esArchiver'); +import { AUTHENTICATION } from '../../../common/lib/authentication'; +import { SPACES } from '../../../common/lib/spaces'; +import { updateTestSuiteFactory } from '../../../common/suites/update.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; +export default function updateSpaceTestSuite(context: DeploymentAgnosticFtrProviderContext) { const { updateTest, expectNotFound, expectAlreadyExistsResult, expectDefaultSpaceResult, expectRbacForbidden, - } = updateTestSuiteFactory(esArchiver, supertestWithoutAuth as unknown as SuperTest); + } = updateTestSuiteFactory(context); describe('update', () => { [ diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/services/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/services/index.ts new file mode 100644 index 0000000000000..0923a6ae4d2df --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/services/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { services as deploymentAgnosticServices } from '../../../api_integration/deployment_agnostic/services'; +import { services as apiIntegrationServices } from '../../../api_integration/services'; +import { services as commonServices } from '../../../common/services'; + +export const services = { + ...deploymentAgnosticServices, + ...commonServices, + usageAPI: apiIntegrationServices.usageAPI, +}; diff --git a/x-pack/test/spaces_api_integration/spaces_only/apis/copy_to_space.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/copy_to_space.ts similarity index 86% rename from x-pack/test/spaces_api_integration/spaces_only/apis/copy_to_space.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/copy_to_space.ts index 3867d528ef374..a159097800f31 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/apis/copy_to_space.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/copy_to_space.ts @@ -5,11 +5,10 @@ * 2.0. */ -import type { FtrProviderContext } from '../../common/ftr_provider_context'; -import { copyToSpaceTestSuiteFactory } from '../../common/suites/copy_to_space'; +import { copyToSpaceTestSuiteFactory } from '../../../common/suites/copy_to_space.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export -export default function copyToSpacesOnlySuite(context: FtrProviderContext) { +export default function copyToSpacesOnlySuite(context: DeploymentAgnosticFtrProviderContext) { const { copyToSpaceTest, expectNoConflictsWithoutReferencesResult, diff --git a/x-pack/test/spaces_api_integration/spaces_only/apis/create.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/create.ts similarity index 67% rename from x-pack/test/spaces_api_integration/spaces_only/apis/create.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/create.ts index c98754d60eec3..b9b0e67f9796d 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/apis/create.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/create.ts @@ -5,24 +5,18 @@ * 2.0. */ -import type { SuperTest } from 'supertest'; - -import type { FtrProviderContext } from '../../common/ftr_provider_context'; -import { SPACES } from '../../common/lib/spaces'; -import { createTestSuiteFactory } from '../../common/suites/create'; - -// eslint-disable-next-line import/no-default-export -export default function createSpacesOnlySuite({ getService }: FtrProviderContext) { - const supertestWithoutAuth = getService('supertestWithoutAuth'); - const esArchiver = getService('esArchiver'); +import { SPACES } from '../../../common/lib/spaces'; +import { createTestSuiteFactory } from '../../../common/suites/create.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; +export default function createSpacesOnlySuite(context: DeploymentAgnosticFtrProviderContext) { const { createTest, expectNewSpaceResult, expectConflictResponse, expectReservedSpecifiedResult, expectSolutionSpecifiedResult, - } = createTestSuiteFactory(esArchiver, supertestWithoutAuth as unknown as SuperTest); + } = createTestSuiteFactory(context); describe('create', () => { [ diff --git a/x-pack/test/spaces_api_integration/spaces_only/apis/delete.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/delete.ts similarity index 61% rename from x-pack/test/spaces_api_integration/spaces_only/apis/delete.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/delete.ts index da50b9c37f190..7550d856fdbe6 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/apis/delete.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/delete.ts @@ -5,20 +5,13 @@ * 2.0. */ -import type { SuperTest } from 'supertest'; - -import type { FtrProviderContext } from '../../common/ftr_provider_context'; -import { SPACES } from '../../common/lib/spaces'; -import { deleteTestSuiteFactory } from '../../common/suites/delete'; - -// eslint-disable-next-line import/no-default-export -export default function deleteSpaceTestSuite({ getService }: FtrProviderContext) { - const supertestWithoutAuth = getService('supertestWithoutAuth'); - const esArchiver = getService('esArchiver'); - const es = getService('es'); +import { SPACES } from '../../../common/lib/spaces'; +import { deleteTestSuiteFactory } from '../../../common/suites/delete.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; +export default function deleteSpaceTestSuite(context: DeploymentAgnosticFtrProviderContext) { const { deleteTest, expectEmptyResult, expectReservedSpaceResult, expectNotFound } = - deleteTestSuiteFactory(es, esArchiver, supertestWithoutAuth as unknown as SuperTest); + deleteTestSuiteFactory(context); describe('delete', () => { [ diff --git a/x-pack/test/spaces_api_integration/spaces_only/apis/get.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/get.ts similarity index 75% rename from x-pack/test/spaces_api_integration/spaces_only/apis/get.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/get.ts index 5cca880f2a7f4..7f73166b29e9e 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/apis/get.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/get.ts @@ -5,19 +5,13 @@ * 2.0. */ -import type { SuperTest } from 'supertest'; - -import type { FtrProviderContext } from '../../common/ftr_provider_context'; -import { SPACES } from '../../common/lib/spaces'; -import { getTestSuiteFactory } from '../../common/suites/get'; - -// eslint-disable-next-line import/no-default-export -export default function getSpaceTestSuite({ getService }: FtrProviderContext) { - const supertestWithoutAuth = getService('supertestWithoutAuth'); - const esArchiver = getService('esArchiver'); +import { SPACES } from '../../../common/lib/spaces'; +import { getTestSuiteFactory } from '../../../common/suites/get.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; +export default function getSpaceTestSuite(context: DeploymentAgnosticFtrProviderContext) { const { getTest, createExpectResults, createExpectNotFoundResult, nonExistantSpaceId } = - getTestSuiteFactory(esArchiver, supertestWithoutAuth as unknown as SuperTest); + getTestSuiteFactory(context); describe('get', () => { // valid spaces diff --git a/x-pack/test/spaces_api_integration/spaces_only/apis/get_all.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/get_all.ts similarity index 69% rename from x-pack/test/spaces_api_integration/spaces_only/apis/get_all.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/get_all.ts index 4b52f25eecbea..34e3f6c328e3d 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/apis/get_all.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/get_all.ts @@ -5,21 +5,12 @@ * 2.0. */ -import type { SuperTest } from 'supertest'; +import { SPACES } from '../../../common/lib/spaces'; +import { getAllTestSuiteFactory } from '../../../common/suites/get_all.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; -import type { FtrProviderContext } from '../../common/ftr_provider_context'; -import { SPACES } from '../../common/lib/spaces'; -import { getAllTestSuiteFactory } from '../../common/suites/get_all'; - -// eslint-disable-next-line import/no-default-export -export default function getAllSpacesTestSuite({ getService }: FtrProviderContext) { - const supertestWithoutAuth = getService('supertestWithoutAuth'); - const esArchiver = getService('esArchiver'); - - const { getAllTest, createExpectResults } = getAllTestSuiteFactory( - esArchiver, - supertestWithoutAuth as unknown as SuperTest - ); +export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProviderContext) { + const { getAllTest, createExpectResults } = getAllTestSuiteFactory(context); describe('get all', () => { [ diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/index.ts new file mode 100644 index 0000000000000..06b75d10c1099 --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/index.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; + +export default function spacesOnlyTestSuite({ + loadTestFile, +}: DeploymentAgnosticFtrProviderContext) { + describe('spaces api without security', function () { + this.tags('skipFIPS'); + loadTestFile(require.resolve('./copy_to_space')); + loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); + loadTestFile(require.resolve('./create')); + loadTestFile(require.resolve('./delete')); + loadTestFile(require.resolve('./get_all')); + loadTestFile(require.resolve('./get')); + loadTestFile(require.resolve('./update')); + }); +} diff --git a/x-pack/test/spaces_api_integration/spaces_only/apis/resolve_copy_to_space_conflicts.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/resolve_copy_to_space_conflicts.ts similarity index 84% rename from x-pack/test/spaces_api_integration/spaces_only/apis/resolve_copy_to_space_conflicts.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/resolve_copy_to_space_conflicts.ts index 9fc16ed64abb8..dacdeadf26f6e 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/apis/resolve_copy_to_space_conflicts.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/resolve_copy_to_space_conflicts.ts @@ -5,11 +5,12 @@ * 2.0. */ -import type { FtrProviderContext } from '../../common/ftr_provider_context'; -import { resolveCopyToSpaceConflictsSuite } from '../../common/suites/resolve_copy_to_space_conflicts'; +import { resolveCopyToSpaceConflictsSuite } from '../../../common/suites/resolve_copy_to_space_conflicts.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export -export default function resolveCopyToSpaceConflictsTestSuite(context: FtrProviderContext) { +export default function resolveCopyToSpaceConflictsTestSuite( + context: DeploymentAgnosticFtrProviderContext +) { const { resolveCopyToSpaceConflictsTest, createExpectNonOverriddenResponseWithReferences, diff --git a/x-pack/test/spaces_api_integration/spaces_only/apis/update.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/update.ts similarity index 63% rename from x-pack/test/spaces_api_integration/spaces_only/apis/update.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/update.ts index 4a4055896e546..9ff036f858999 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/apis/update.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/apis/update.ts @@ -5,19 +5,12 @@ * 2.0. */ -import type { SuperTest } from 'supertest'; - -import type { FtrProviderContext } from '../../common/ftr_provider_context'; -import { SPACES } from '../../common/lib/spaces'; -import { updateTestSuiteFactory } from '../../common/suites/update'; - -// eslint-disable-next-line import/no-default-export -export default function updateSpaceTestSuite({ getService }: FtrProviderContext) { - const supertestWithoutAuth = getService('supertestWithoutAuth'); - const esArchiver = getService('esArchiver'); - +import { SPACES } from '../../../common/lib/spaces'; +import { updateTestSuiteFactory } from '../../../common/suites/update.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; +export default function updateSpaceTestSuite(context: DeploymentAgnosticFtrProviderContext) { const { updateTest, expectAlreadyExistsResult, expectDefaultSpaceResult, expectNotFound } = - updateTestSuiteFactory(esArchiver, supertestWithoutAuth as unknown as SuperTest); + updateTestSuiteFactory(context); describe('update', () => { [ diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_basic.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/config.ts similarity index 56% rename from x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_basic.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/config.ts index 609d747fa8861..fe25ba69b3512 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_basic.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/config.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { createTestConfig } from '../common/config'; +import { createTestConfig } from '../../common/config'; -// eslint-disable-next-line import/no-default-export -export default createTestConfig('security_and_spaces', { +export default createTestConfig('spaces_only', { + disabledPlugins: ['security'], license: 'basic', - testFiles: [require.resolve('./apis/copy_to_space')], + testFiles: [require.resolve('./apis')], }); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config.ts new file mode 100644 index 0000000000000..da9ca0fc86865 --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import path from 'path'; + +import { REPO_ROOT } from '@kbn/repo-info'; +import type { FtrConfigProviderContext } from '@kbn/test'; + +import { services } from './services'; +import { createStatefulTestConfig } from '../../api_integration/deployment_agnostic/default_configs/stateful.config.base'; + +export function createTestConfig({ + license = 'trial', + testFiles, +}: { + license: string; + testFiles?: string[]; +}) { + return async (context: FtrConfigProviderContext) => { + const { readConfigFile } = context; + const config = { + kibana: { + api: await readConfigFile(path.resolve(REPO_ROOT, 'test/api_integration/config.js')), + functional: await readConfigFile( + require.resolve('@kbn/test-suites-src/functional/config.base') + ), + }, + xpack: { + api: await readConfigFile(require.resolve('../../api_integration/config.ts')), + }, + }; + + const testConfig = await createStatefulTestConfig({ + services: { + ...services, + es: config.kibana.api.get('services.es'), + esSupertestWithoutAuth: config.xpack.api.get('services.esSupertestWithoutAuth'), + supertest: config.kibana.api.get('services.supertest'), + supertestWithoutAuth: config.xpack.api.get('services.supertestWithoutAuth'), + retry: config.xpack.api.get('services.retry'), + esArchiver: config.kibana.functional.get('services.esArchiver'), + kibanaServer: config.kibana.functional.get('services.kibanaServer'), + spaces: config.xpack.api.get('services.spaces'), + usageAPI: config.xpack.api.get('services.usageAPI'), + }, + testFiles: testFiles ?? [require.resolve('./security_and_spaces/apis')], + junit: { + reportName: 'X-Pack Spaces API Integration Tests -- ', + }, + // esServerArgs: [`xpack.license.self_generated.type=${license}`], + })(context); + + return { + ...testConfig, + kbnTestServer: { + ...testConfig.kbnTestServer, + serverArgs: [ + ...testConfig.kbnTestServer.serverArgs, + '--status.allowAnonymous=false', + '--server.xsrf.disableProtection=true', + `--plugin-path=${path.resolve(__dirname, '../common/plugins/spaces_test_plugin')}`, + ], + }, + }; + }; +} diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_basic.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_basic.ts new file mode 100644 index 0000000000000..98b24b2f7915d --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_basic.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createTestConfig } from './stateful.config'; + +export default createTestConfig({ license: 'basic' }); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts new file mode 100644 index 0000000000000..014dfbb1e31ad --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createTestConfig } from './stateful.config'; + +export default createTestConfig({ license: 'trial' }); diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_trial.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts similarity index 56% rename from x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_trial.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts index da8834134f258..9e746c55c1404 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_trial.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts @@ -5,10 +5,9 @@ * 2.0. */ -import { createTestConfig } from '../common/config'; +import { createTestConfig } from './stateful.config'; -// eslint-disable-next-line import/no-default-export -export default createTestConfig('security_and_spaces', { +export default createTestConfig({ license: 'trial', - testFiles: [require.resolve('./apis/copy_to_space')], + testFiles: [require.resolve('./security_and_spaces/apis/copy_to_space')], }); diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space/index.ts b/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space/index.ts deleted file mode 100644 index 79a6e69cb6f32..0000000000000 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { createUsersAndRoles } from '../../../common/lib/create_users_and_roles'; - -// eslint-disable-next-line import/no-default-export -export default function ({ loadTestFile, getService }: FtrProviderContext) { - const es = getService('es'); - const supertest = getService('supertest'); - - describe('copy to space with security', function () { - before(async () => { - await createUsersAndRoles(es, supertest); - }); - - loadTestFile(require.resolve('./copy_to_space')); // ~ 19m 20s - }); -} diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts index 756d47308f7fc..197d276564a57 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts @@ -19,13 +19,7 @@ export default function ({ loadTestFile, getService }: FtrProviderContext) { }); // total runtime ~ 17m - loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); // ~ 10m - loadTestFile(require.resolve('./create')); // ~ 2m - loadTestFile(require.resolve('./delete')); // ~ 1m 20s - loadTestFile(require.resolve('./get_all')); // ~ 50s loadTestFile(require.resolve('./get_shareable_references')); // ~ 30s - loadTestFile(require.resolve('./get')); // ~ 30s - loadTestFile(require.resolve('./update')); // ~ 30s loadTestFile(require.resolve('./update_objects_spaces')); // ~ 1m loadTestFile(require.resolve('./disable_legacy_url_aliases')); // ~ 30s }); diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/config_trial.ts b/x-pack/test/spaces_api_integration/security_and_spaces/config_trial.ts index 0481a6009d722..c988a59d23a22 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/config_trial.ts +++ b/x-pack/test/spaces_api_integration/security_and_spaces/config_trial.ts @@ -8,4 +8,7 @@ import { createTestConfig } from '../common/config'; // eslint-disable-next-line import/no-default-export -export default createTestConfig('security_and_spaces', { license: 'trial' }); +export default createTestConfig('security_and_spaces', { + license: 'trial', + testFiles: [require.resolve('./apis')], +}); diff --git a/x-pack/test/spaces_api_integration/spaces_only/apis/index.ts b/x-pack/test/spaces_api_integration/spaces_only/apis/index.ts index 433ce2c6c444c..a85a84d3f723b 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/apis/index.ts +++ b/x-pack/test/spaces_api_integration/spaces_only/apis/index.ts @@ -11,14 +11,7 @@ import type { FtrProviderContext } from '../../common/ftr_provider_context'; export default function spacesOnlyTestSuite({ loadTestFile }: FtrProviderContext) { describe('spaces api without security', function () { this.tags('skipFIPS'); - loadTestFile(require.resolve('./copy_to_space')); - loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); - loadTestFile(require.resolve('./create')); - loadTestFile(require.resolve('./delete')); - loadTestFile(require.resolve('./get_all')); loadTestFile(require.resolve('./get_shareable_references')); - loadTestFile(require.resolve('./get')); - loadTestFile(require.resolve('./update')); loadTestFile(require.resolve('./update_objects_spaces')); loadTestFile(require.resolve('./disable_legacy_url_aliases')); }); From 41dfba2281f1db344c52caef1babcbdc258f57fb Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Mon, 18 Nov 2024 17:42:05 +0100 Subject: [PATCH 02/38] wip --- .../deployment_agnostic/stateful.config_basic.ts | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_basic.ts diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_basic.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_basic.ts deleted file mode 100644 index 98b24b2f7915d..0000000000000 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_basic.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { createTestConfig } from './stateful.config'; - -export default createTestConfig({ license: 'basic' }); From e463d1fc66477ac587974b9838ae10224ac91206 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Mon, 18 Nov 2024 18:45:57 +0100 Subject: [PATCH 03/38] Fixes --- .../common/suites/common.ts | 6 +- .../common/suites/copy_to_space.agnostic.ts | 2 +- .../common/suites/create.agnostic.ts | 2 +- .../common/suites/delete.agnostic.ts | 2 +- .../common/suites/delete.ts | 209 ------ .../disable_legacy_url_aliases.agnostic.ts | 4 +- .../common/suites/get.agnostic.ts | 4 +- .../common/suites/get_all.agnostic.ts | 4 +- .../common/suites/get_all.ts | 2 +- ...esolve_copy_to_space_conflicts.agnostic.ts | 4 +- .../suites/resolve_copy_to_space_conflicts.ts | 702 ------------------ .../common/suites/update.agnostic.ts | 4 +- .../common/suites/update.ts | 151 ---- 13 files changed, 18 insertions(+), 1078 deletions(-) delete mode 100644 x-pack/test/spaces_api_integration/common/suites/delete.ts delete mode 100644 x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts delete mode 100644 x-pack/test/spaces_api_integration/common/suites/update.ts diff --git a/x-pack/test/spaces_api_integration/common/suites/common.ts b/x-pack/test/spaces_api_integration/common/suites/common.ts index 53546e4962ef3..6c8e41501ec92 100644 --- a/x-pack/test/spaces_api_integration/common/suites/common.ts +++ b/x-pack/test/spaces_api_integration/common/suites/common.ts @@ -39,9 +39,11 @@ export async function getSupertest( return supertestWithoutAuth; } -export function maybeDestroySupertest(supertest: SupertestWithRoleScopeType | SuperTestAgent) { +export async function maybeDestroySupertest( + supertest: SupertestWithRoleScopeType | SuperTestAgent +) { // @ts-expect-error if (typeof supertest.destroy === 'function') { - (supertest as SupertestWithRoleScopeType).destroy(); + await (supertest as SupertestWithRoleScopeType).destroy(); } } diff --git a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts index f73c52d71fb92..c1df89b402d36 100644 --- a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts @@ -830,7 +830,7 @@ export function copyToSpaceTestSuiteFactory(context: DeploymentAgnosticFtrProvid after(async () => { await testDataLoader.deleteFtrSpaces(); if (user) { - (supertest as SupertestWithRoleScopeType).destroy(); + await (supertest as SupertestWithRoleScopeType).destroy(); } }); diff --git a/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts index 922431432423f..7e99431fd2a44 100644 --- a/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts @@ -130,7 +130,7 @@ export function createTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv after(async () => { if (user) { - (supertest as SupertestWithRoleScopeType).destroy(); + await (supertest as SupertestWithRoleScopeType).destroy(); } }); diff --git a/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts index 69e8242d19cab..cf7b337697f6a 100644 --- a/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts @@ -181,7 +181,7 @@ export function deleteTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv }); after(async () => { if (user) { - (supertest as SupertestWithRoleScopeType).destroy(); + await (supertest as SupertestWithRoleScopeType).destroy(); } }); diff --git a/x-pack/test/spaces_api_integration/common/suites/delete.ts b/x-pack/test/spaces_api_integration/common/suites/delete.ts deleted file mode 100644 index 1a75bc219ae1f..0000000000000 --- a/x-pack/test/spaces_api_integration/common/suites/delete.ts +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { Client } from '@elastic/elasticsearch'; -import type { SuperTest } from 'supertest'; - -import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; -import expect from '@kbn/expect'; - -import { MULTI_NAMESPACE_SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; -import { getAggregatedSpaceData, getTestScenariosForSpace } from '../lib/space_test_utils'; -import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; - -interface DeleteTest { - statusCode: number; - response: (resp: { [key: string]: any }) => void; -} - -interface DeleteTests { - exists: DeleteTest; - reservedSpace: DeleteTest; - doesntExist: DeleteTest; -} - -interface DeleteTestDefinition { - user?: TestDefinitionAuthentication; - spaceId: string; - tests: DeleteTests; -} - -export function deleteTestSuiteFactory(es: Client, esArchiver: any, supertest: SuperTest) { - const createExpectResult = (expectedResult: any) => (resp: { [key: string]: any }) => { - expect(resp.body).to.eql(expectedResult); - }; - - const expectEmptyResult = async (resp: { [key: string]: any }) => { - expect(resp.body).to.eql(''); - - // Query ES to ensure that we deleted everything we expected, and nothing we didn't - // Grouping first by namespace, then by saved object type - const response = await getAggregatedSpaceData(es, [ - 'visualization', - 'dashboard', - 'space', - 'index-pattern', - 'legacy-url-alias', - // TODO: add assertions for config objects -- these assertions were removed because of flaky behavior in #92358, but we should - // consider adding them again at some point, especially if we convert config objects to `namespaceType: 'multiple-isolated'` in - // the future. - ]); - - // @ts-expect-error @elastic/elasticsearch doesn't defined `count.buckets`. - const buckets = response.aggregations?.count.buckets; - - // The test fixture contains six legacy URL aliases: - // (1) two for "default", (2) two for "space_2", and (3) two for "other_space", which is a non-existent space. - // Each test deletes "space_2", so the agg buckets should reflect that aliases (1) and (3) still exist afterwards. - - // Space 2 deleted, all others should exist - const expectedBuckets = [ - { - key: 'default', - doc_count: 10, - countByType: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { key: 'space', doc_count: 3 }, // since space objects are namespace-agnostic, they appear in the "default" agg bucket - { key: 'visualization', doc_count: 3 }, - { key: 'legacy-url-alias', doc_count: 2 }, // aliases (1) - { key: 'dashboard', doc_count: 1 }, - { key: 'index-pattern', doc_count: 1 }, - ], - }, - }, - { - doc_count: 5, - key: 'space_1', - countByType: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { key: 'visualization', doc_count: 3 }, - { key: 'dashboard', doc_count: 1 }, - { key: 'index-pattern', doc_count: 1 }, - // no legacy url alias objects exist in space_1 - ], - }, - }, - { - doc_count: 2, - key: 'other_space', - countByType: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [{ key: 'legacy-url-alias', doc_count: 2 }], // aliases (3) - }, - }, - ]; - - expect(buckets).to.eql(expectedBuckets); - - // There were 24 multi-namespace objects. - // Since Space 2 was deleted, any multi-namespace objects that existed in that space - // are updated to remove it, and of those, any that don't exist in any space are deleted. - const multiNamespaceResponse = await es.search>({ - index: ALL_SAVED_OBJECT_INDICES, - size: 100, - body: { query: { terms: { type: ['sharedtype'] } } }, - }); - const docs = multiNamespaceResponse.hits.hits; - // Just 19 results, since spaces_2_only, conflict_1a_space_2, conflict_1b_space_2, conflict_1c_space_2, and conflict_2_space_2 got deleted. - expect(docs).length(19); - docs.forEach((doc) => () => { - const containsSpace2 = doc?._source?.namespaces.includes('space_2'); - expect(containsSpace2).to.eql(false); - }); - const space2OnlyObjExists = docs.some((x) => x._id === CASES.SPACE_2_ONLY.id); - expect(space2OnlyObjExists).to.eql(false); - }; - - const expectNotFound = (resp: { [key: string]: any }) => { - expect(resp.body).to.eql({ - error: 'Not Found', - statusCode: 404, - message: 'Not Found', - }); - }; - - const expectRbacForbidden = (resp: { [key: string]: any }) => { - expect(resp.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to delete spaces', - }); - }; - - const expectReservedSpaceResult = (resp: { [key: string]: any }) => { - expect(resp.body).to.eql({ - error: 'Bad Request', - statusCode: 400, - message: `The default space cannot be deleted because it is reserved.`, - }); - }; - - const makeDeleteTest = - (describeFn: DescribeFn) => - (description: string, { user = {}, spaceId, tests }: DeleteTestDefinition) => { - describeFn(description, () => { - beforeEach(async () => { - await esArchiver.load( - 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' - ); - }); - afterEach(() => - esArchiver.unload( - 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' - ) - ); - - getTestScenariosForSpace(spaceId).forEach(({ urlPrefix, scenario }) => { - it(`should return ${tests.exists.statusCode} ${scenario}`, async () => { - return supertest - .delete(`${urlPrefix}/api/spaces/space/space_2`) - .auth(user.username, user.password) - .expect(tests.exists.statusCode) - .then(tests.exists.response); - }); - - describe(`when the space is reserved`, () => { - it(`should return ${tests.reservedSpace.statusCode} ${scenario}`, async () => { - return supertest - .delete(`${urlPrefix}/api/spaces/space/default`) - .auth(user.username, user.password) - .expect(tests.reservedSpace.statusCode) - .then(tests.reservedSpace.response); - }); - }); - - describe(`when the space doesn't exist`, () => { - it(`should return ${tests.doesntExist.statusCode} ${scenario}`, async () => { - return supertest - .delete(`${urlPrefix}/api/spaces/space/space_7`) - .auth(user.username, user.password) - .expect(tests.doesntExist.statusCode) - .then(tests.doesntExist.response); - }); - }); - }); - }); - }; - - const deleteTest = makeDeleteTest(describe); - // @ts-ignore - deleteTest.only = makeDeleteTest(describe.only); - - return { - createExpectResult, - deleteTest, - expectEmptyResult, - expectNotFound, - expectRbacForbidden, - expectReservedSpaceResult, - }; -} diff --git a/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.agnostic.ts index a43f516d5c25b..007731e9c69b2 100644 --- a/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.agnostic.ts @@ -134,9 +134,9 @@ export function disableLegacyUrlAliasesTestSuiteFactory({ 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); }); - after(() => { + after(async () => { if (user) { - (supertest as SupertestWithRoleScopeType).destroy(); + await (supertest as SupertestWithRoleScopeType).destroy(); } esArchiver.unload( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' diff --git a/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts index bb10e3805be71..e45368b3523cc 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts @@ -123,8 +123,8 @@ export function getTestSuiteFactory(context: DeploymentAgnosticFtrProviderContex 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); }); - after(() => { - maybeDestroySupertest(supertest); + after(async () => { + await maybeDestroySupertest(supertest); esArchiver.unload( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); diff --git a/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts index 3f9c6f6eb3173..0ba63863b000e 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts @@ -153,11 +153,11 @@ export function getAllTestSuiteFactory(context: DeploymentAgnosticFtrProviderCon 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); }); - after(() => { + after(async () => { esArchiver.unload( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); - maybeDestroySupertest(supertest); + await maybeDestroySupertest(supertest); }); getTestScenariosForSpace(spaceId).forEach(({ scenario, urlPrefix }) => { diff --git a/x-pack/test/spaces_api_integration/common/suites/get_all.ts b/x-pack/test/spaces_api_integration/common/suites/get_all.ts index 9f4abd8001f6d..a8517b43ec888 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get_all.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get_all.ts @@ -25,7 +25,7 @@ interface GetAllTests { } interface GetAllTestDefinition { - user?: TestDefinitionAuthentication; + user?: Omit; spaceId: string; tests: GetAllTests; } diff --git a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts index 23d86b833216b..dc6c93324ca0e 100644 --- a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts @@ -520,8 +520,8 @@ export function resolveCopyToSpaceConflictsSuite(context: DeploymentAgnosticFtrP expect(['default', 'space_1']).to.contain(spaceId); }); - after(() => { - maybeDestroySupertest(supertest); + after(async () => { + await maybeDestroySupertest(supertest); }); describe('single-namespace types', () => { diff --git a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts deleted file mode 100644 index 845d41d1431b9..0000000000000 --- a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts +++ /dev/null @@ -1,702 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { SuperTest } from 'supertest'; - -import type { SavedObject } from '@kbn/core/server'; -import expect from '@kbn/expect'; -import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; -import type { CopyResponse } from '@kbn/spaces-plugin/server/lib/copy_to_spaces'; - -import { getTestDataLoader, SPACE_1, SPACE_2 } from '../../../common/lib/test_data_loader'; -import type { FtrProviderContext } from '../ftr_provider_context'; -import { getUrlPrefix } from '../lib/space_test_utils'; -import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; - -type TestResponse = Record; - -interface ResolveCopyToSpaceTest { - statusCode: number; - response: (resp: TestResponse) => Promise; -} - -interface ResolveCopyToSpaceMultiNamespaceTest extends ResolveCopyToSpaceTest { - testTitle: string; - objects: Array>; - retries: Record; -} - -interface ResolveCopyToSpaceTests { - withReferencesNotOverwriting: ResolveCopyToSpaceTest; - withReferencesOverwriting: ResolveCopyToSpaceTest; - withoutReferencesOverwriting: ResolveCopyToSpaceTest; - withoutReferencesNotOverwriting: ResolveCopyToSpaceTest; - nonExistentSpace: ResolveCopyToSpaceTest; - multiNamespaceTestCases: () => ResolveCopyToSpaceMultiNamespaceTest[]; -} - -interface ResolveCopyToSpaceTestDefinition { - user?: TestDefinitionAuthentication; - spaceId?: string; - tests: ResolveCopyToSpaceTests; -} - -const NON_EXISTENT_SPACE_ID = 'non_existent_space'; - -const SPACE_DATA_TO_LOAD: Array<{ spaceName: string | null; dataUrl: string }> = [ - { - spaceName: null, - dataUrl: 'x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/default_space.json', - }, - { - spaceName: SPACE_1.id, - dataUrl: 'x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/space_1.json', - }, - { - spaceName: SPACE_2.id, - dataUrl: 'x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/space_2.json', - }, -]; - -const getDestinationSpace = (originSpaceId?: string) => { - if (!originSpaceId || originSpaceId === DEFAULT_SPACE_ID) { - return 'space_1'; - } - return DEFAULT_SPACE_ID; -}; - -export function resolveCopyToSpaceConflictsSuite(context: FtrProviderContext) { - const testDataLoader = getTestDataLoader(context); - const supertestWithAuth = context.getService('supertest'); - const supertestWithoutAuth = context.getService( - 'supertestWithoutAuth' - ) as unknown as SuperTest; - - const getVisualizationAtSpace = async (spaceId: string): Promise> => { - return supertestWithAuth - .get(`${getUrlPrefix(spaceId)}/api/saved_objects/visualization/cts_vis_3_${spaceId}`) - .then((response: any) => response.body); - }; - const getDashboardAtSpace = async (spaceId: string): Promise> => { - return supertestWithAuth - .get(`${getUrlPrefix(spaceId)}/api/saved_objects/dashboard/cts_dashboard_${spaceId}`) - .then((response: any) => response.body); - }; - - const getObjectsAtSpace = async ( - spaceId: string - ): Promise<[SavedObject, SavedObject]> => { - const dashboard = await getDashboardAtSpace(spaceId); - const visualization = await getVisualizationAtSpace(spaceId); - return [dashboard, visualization]; - }; - - const createExpectOverriddenResponseWithReferences = - (sourceSpaceId: string) => async (response: TestResponse) => { - const destination = getDestinationSpace(sourceSpaceId); - const result = response.body; - expect(result).to.eql({ - [destination]: { - success: true, - successCount: 2, - successResults: [ - { - id: `cts_ip_1_${sourceSpaceId}`, - type: 'index-pattern', - meta: { - title: `Copy to Space index pattern 1 from ${sourceSpaceId} space`, - icon: 'indexPatternApp', - }, - destinationId: `cts_ip_1_${destination}`, // this conflicted with another index pattern in the destination space because of a shared originId - overwrite: true, - managed: false, - }, - { - id: `cts_vis_3_${sourceSpaceId}`, - type: 'visualization', - meta: { - title: `CTS vis 3 from ${sourceSpaceId} space`, - icon: 'visualizeApp', - }, - destinationId: `cts_vis_3_${destination}`, // this conflicted with another visualization in the destination space because of a shared originId - overwrite: true, - managed: false, - }, - ], - }, - }); - const [dashboard, visualization] = await getObjectsAtSpace(destination); - expect(dashboard.attributes.title).to.eql( - `This is the ${destination} test space CTS dashboard` - ); - expect(visualization.attributes.title).to.eql(`CTS vis 3 from ${sourceSpaceId} space`); - }; - - const createExpectOverriddenResponseWithoutReferences = - (sourceSpaceId: string, destinationSpaceId: string = getDestinationSpace(sourceSpaceId)) => - async (response: TestResponse) => { - const result = response.body; - expect(result).to.eql({ - [destinationSpaceId]: { - success: true, - successCount: 1, - successResults: [ - { - id: `cts_dashboard_${sourceSpaceId}`, - type: 'dashboard', - meta: { - title: `This is the ${sourceSpaceId} test space CTS dashboard`, - icon: 'dashboardApp', - }, - destinationId: `cts_dashboard_${destinationSpaceId}`, // this conflicted with another dashboard in the destination space because of a shared originId - overwrite: true, - managed: false, - }, - ], - }, - }); - const [dashboard, visualization] = await getObjectsAtSpace(destinationSpaceId); - expect(dashboard.attributes.title).to.eql( - `This is the ${sourceSpaceId} test space CTS dashboard` - ); - if (destinationSpaceId === NON_EXISTENT_SPACE_ID) { - expect((visualization as any).statusCode).to.eql(404); - } else { - expect(visualization.attributes.title).to.eql(`CTS vis 3 from ${destinationSpaceId} space`); - } - }; - - const createExpectNonOverriddenResponseWithReferences = - (sourceSpaceId: string) => async (response: TestResponse) => { - const destination = getDestinationSpace(sourceSpaceId); - - const result = response.body; - expect(result).to.eql({ - [destination]: { - success: false, - successCount: 0, - errors: [ - { - error: { - type: 'conflict', - destinationId: `cts_ip_1_${destination}`, // this conflicted with another index pattern in the destination space because of a shared originId - }, - id: `cts_ip_1_${sourceSpaceId}`, - meta: { - title: `Copy to Space index pattern 1 from ${sourceSpaceId} space`, - icon: 'indexPatternApp', - }, - type: 'index-pattern', - }, - { - error: { - type: 'conflict', - destinationId: `cts_vis_3_${destination}`, // this conflicted with another visualization in the destination space because of a shared originId - }, - id: `cts_vis_3_${sourceSpaceId}`, - meta: { - title: `CTS vis 3 from ${sourceSpaceId} space`, - icon: 'visualizeApp', - }, - type: 'visualization', - }, - ], - }, - }); - - const [dashboard, visualization] = await getObjectsAtSpace(destination); - expect(dashboard.attributes.title).to.eql( - `This is the ${destination} test space CTS dashboard` - ); - expect(visualization.attributes.title).to.eql(`CTS vis 3 from ${destination} space`); - }; - - const createExpectNonOverriddenResponseWithoutReferences = - (sourceSpaceId: string) => async (response: TestResponse) => { - const destination = getDestinationSpace(sourceSpaceId); - - const result = response.body; - expect(result).to.eql({ - [destination]: { - success: false, - successCount: 0, - errors: [ - { - error: { - type: 'conflict', - destinationId: `cts_dashboard_${destination}`, // this conflicted with another visualization in the destination space because of a shared originId - }, - id: `cts_dashboard_${sourceSpaceId}`, - type: 'dashboard', - meta: { - title: `This is the ${sourceSpaceId} test space CTS dashboard`, - icon: 'dashboardApp', - }, - }, - ], - }, - }); - - const [dashboard, visualization] = await getObjectsAtSpace(destination); - expect(dashboard.attributes.title).to.eql( - `This is the ${destination} test space CTS dashboard` - ); - expect(visualization.attributes.title).to.eql(`CTS vis 3 from ${destination} space`); - }; - - const expectRouteForbiddenResponse = async (resp: TestResponse) => { - expect(resp.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: - 'API [POST /api/spaces/_resolve_copy_saved_objects_errors] is unauthorized for user, this action is granted by the Kibana privileges [copySavedObjectsToSpaces]', - }); - }; - - const expectRouteNotFoundResponse = async (resp: TestResponse) => { - expect(resp.body).to.eql({ - statusCode: 404, - error: 'Not Found', - message: 'Not Found', - }); - }; - - const createExpectUnauthorizedAtSpaceWithReferencesResult = - (spaceId: string = DEFAULT_SPACE_ID) => - async (resp: TestResponse) => { - const destination = getDestinationSpace(spaceId); - - const result = resp.body as CopyResponse; - expect(result).to.eql({ - [destination]: { - success: false, - successCount: 0, - errors: [ - { - statusCode: 403, - error: 'Forbidden', - message: 'Unable to bulk_create index-pattern,visualization', - }, - ], - }, - } as CopyResponse); - - // Query ES to ensure that nothing was copied - const [dashboard, visualization] = await getObjectsAtSpace(destination); - expect(dashboard.attributes.title).to.eql( - `This is the ${destination} test space CTS dashboard` - ); - expect(visualization.attributes.title).to.eql(`CTS vis 3 from ${destination} space`); - }; - - const createExpectUnauthorizedAtSpaceWithoutReferencesResult = - ( - sourceSpaceId: string = DEFAULT_SPACE_ID, - destinationSpaceId: string = getDestinationSpace(sourceSpaceId) - ) => - async (resp: TestResponse) => { - const result = resp.body as CopyResponse; - expect(result).to.eql({ - [destinationSpaceId]: { - success: false, - successCount: 0, - errors: [ - { - statusCode: 403, - error: 'Forbidden', - message: 'Unable to bulk_create dashboard', - }, - ], - }, - } as CopyResponse); - - // Query ES to ensure that nothing was copied - const [dashboard, visualization] = await getObjectsAtSpace(destinationSpaceId); - - if (destinationSpaceId === NON_EXISTENT_SPACE_ID) { - expect((dashboard as any).statusCode).to.eql(404); - expect((visualization as any).statusCode).to.eql(404); - } else { - expect(dashboard.attributes.title).to.eql( - `This is the ${destinationSpaceId} test space CTS dashboard` - ); - expect(visualization.attributes.title).to.eql(`CTS vis 3 from ${destinationSpaceId} space`); - } - }; - - /** - * Creates test cases for multi-namespace saved object types. - * Note: these are written with the assumption that test data will only be reloaded between each group of test cases, *not* before every - * single test case. This saves time during test execution. - */ - const createMultiNamespaceTestCases = - ( - spaceId: string, - outcome: 'authorized' | 'unauthorizedRead' | 'unauthorizedWrite' | 'noAccess' = 'authorized' - ) => - (): ResolveCopyToSpaceMultiNamespaceTest[] => { - // the status code of the HTTP response differs depending on the error type - // a 403 error actually comes back as an HTTP 200 response - const statusCode = outcome === 'noAccess' ? 403 : 200; - const type = 'sharedtype'; - const exactMatchId = 'each_space'; - const inexactMatchIdA = `conflict_1a_${spaceId}`; - const inexactMatchIdB = `conflict_1b_${spaceId}`; - const inexactMatchIdC = `conflict_1c_default_and_space_1`; - const ambiguousConflictId = `conflict_2_${spaceId}`; - - const createRetries = (overwriteRetry: Record) => ({ - space_2: [overwriteRetry], - }); - const getResult = (response: TestResponse) => (response.body as CopyResponse).space_2; - const expectSavedObjectForbiddenResponse = (response: TestResponse) => { - expect(response.body).to.eql({ - space_2: { - success: false, - successCount: 0, - errors: [ - { statusCode: 403, error: 'Forbidden', message: `Unable to bulk_create sharedtype` }, - ], - }, - }); - }; - const expectSavedObjectSuccessResponse = ( - response: TestResponse, - id: string, - destinationId?: string - ) => { - const { success, successCount, successResults, errors } = getResult(response); - expect(success).to.eql(true); - expect(successCount).to.eql(1); - expect(errors).to.be(undefined); - const title = (() => { - switch (id) { - case exactMatchId: - return 'A shared saved-object in the default, space_1, and space_2 spaces'; - case inexactMatchIdA: - return 'This is used to test an inexact match conflict for an originId -> originId match'; - case inexactMatchIdB: - return 'This is used to test an inexact match conflict for an originId -> id match'; - case inexactMatchIdC: - return 'This is used to test an inexact match conflict for an id -> originId match'; - default: - return 'A shared saved-object in one space'; - } - })(); - const meta = { title, icon: 'beaker' }; - expect(successResults).to.eql([ - { - type, - id, - meta, - overwrite: true, - ...(destinationId && { destinationId }), - managed: false, - }, - ]); - }; - - return [ - { - testTitle: 'copying with an exact match conflict', - objects: [{ type, id: exactMatchId }], - retries: createRetries({ type, id: exactMatchId, overwrite: true }), - statusCode, - response: async (response: TestResponse) => { - if (outcome === 'authorized') { - expectSavedObjectSuccessResponse(response, exactMatchId); - } else if (outcome === 'noAccess') { - await expectRouteForbiddenResponse(response); - } else { - // unauthorized read/write - expectSavedObjectForbiddenResponse(response); - } - }, - }, - { - testTitle: - 'copying with an inexact match conflict (a) - originId matches existing originId', - objects: [{ type, id: inexactMatchIdA }], - retries: createRetries({ - type, - id: inexactMatchIdA, - overwrite: true, - destinationId: 'conflict_1a_space_2', - }), - statusCode, - response: async (response: TestResponse) => { - if (outcome === 'authorized') { - expectSavedObjectSuccessResponse(response, inexactMatchIdA, 'conflict_1a_space_2'); - } else if (outcome === 'noAccess') { - await expectRouteForbiddenResponse(response); - } else { - // unauthorized read/write - expectSavedObjectForbiddenResponse(response); - } - }, - }, - { - testTitle: 'copying with an inexact match conflict (b) - originId matches existing id', - objects: [{ type, id: inexactMatchIdB }], - retries: createRetries({ - type, - id: inexactMatchIdB, - overwrite: true, - destinationId: 'conflict_1b_space_2', - }), - statusCode, - response: async (response: TestResponse) => { - if (outcome === 'authorized') { - expectSavedObjectSuccessResponse(response, inexactMatchIdB, 'conflict_1b_space_2'); - } else if (outcome === 'noAccess') { - await expectRouteForbiddenResponse(response); - } else { - // unauthorized read/write - expectSavedObjectForbiddenResponse(response); - } - }, - }, - { - testTitle: 'copying with an inexact match conflict (c) - id matches existing originId', - objects: [{ type, id: inexactMatchIdC }], - retries: createRetries({ - type, - id: inexactMatchIdC, - overwrite: true, - destinationId: 'conflict_1c_space_2', - }), - statusCode, - response: async (response: TestResponse) => { - if (outcome === 'authorized') { - expectSavedObjectSuccessResponse(response, inexactMatchIdC, 'conflict_1c_space_2'); - } else if (outcome === 'noAccess') { - await expectRouteForbiddenResponse(response); - } else { - // unauthorized read/write - expectSavedObjectForbiddenResponse(response); - } - }, - }, - { - testTitle: 'copying with an ambiguous conflict', - objects: [{ type, id: ambiguousConflictId }], - retries: createRetries({ - type, - id: ambiguousConflictId, - overwrite: true, - destinationId: 'conflict_2_space_2', - }), - statusCode, - response: async (response: TestResponse) => { - if (outcome === 'authorized') { - expectSavedObjectSuccessResponse(response, ambiguousConflictId, 'conflict_2_space_2'); - } else if (outcome === 'noAccess') { - await expectRouteForbiddenResponse(response); - } else { - // unauthorized read/write - expectSavedObjectForbiddenResponse(response); - } - }, - }, - ]; - }; - - const makeResolveCopyToSpaceConflictsTest = - (describeFn: DescribeFn) => - ( - description: string, - { user = {}, spaceId = DEFAULT_SPACE_ID, tests }: ResolveCopyToSpaceTestDefinition - ) => { - describeFn(description, () => { - before(() => { - // test data only allows for the following spaces as the copy origin - expect(['default', 'space_1']).to.contain(spaceId); - }); - - describe('single-namespace types', () => { - beforeEach( - async () => await testDataLoader.createFtrSavedObjectsData(SPACE_DATA_TO_LOAD) - ); - afterEach(async () => await testDataLoader.deleteFtrSavedObjectsData()); - - const dashboardObject = { type: 'dashboard', id: `cts_dashboard_${spaceId}` }; - const visualizationObject = { type: 'visualization', id: `cts_vis_3_${spaceId}` }; - const indexPatternObject = { type: 'index-pattern', id: `cts_ip_1_${spaceId}` }; - - it(`should return ${tests.withReferencesNotOverwriting.statusCode} when not overwriting, with references`, async () => { - const destination = getDestinationSpace(spaceId); - - return supertestWithoutAuth - .post(`${getUrlPrefix(spaceId)}/api/spaces/_resolve_copy_saved_objects_errors`) - .auth(user.username, user.password) - .send({ - objects: [dashboardObject], - includeReferences: true, - createNewCopies: false, - retries: { - [destination]: [ - { - ...indexPatternObject, - destinationId: `cts_ip_1_${destination}`, - overwrite: false, - }, - { - ...visualizationObject, - destinationId: `cts_vis_3_${destination}`, - overwrite: false, - }, - ], - }, - }) - .expect(tests.withReferencesNotOverwriting.statusCode) - .then(tests.withReferencesNotOverwriting.response); - }); - - it(`should return ${tests.withReferencesOverwriting.statusCode} when overwriting, with references`, async () => { - const destination = getDestinationSpace(spaceId); - - return supertestWithoutAuth - .post(`${getUrlPrefix(spaceId)}/api/spaces/_resolve_copy_saved_objects_errors`) - .auth(user.username, user.password) - .send({ - objects: [dashboardObject], - includeReferences: true, - createNewCopies: false, - retries: { - [destination]: [ - { - ...indexPatternObject, - destinationId: `cts_ip_1_${destination}`, - overwrite: true, - }, - { - ...visualizationObject, - destinationId: `cts_vis_3_${destination}`, - overwrite: true, - }, - ], - }, - }) - .expect(tests.withReferencesOverwriting.statusCode) - .then(tests.withReferencesOverwriting.response); - }); - - it(`should return ${tests.withoutReferencesOverwriting.statusCode} when overwriting, without references`, async () => { - const destination = getDestinationSpace(spaceId); - - return supertestWithoutAuth - .post(`${getUrlPrefix(spaceId)}/api/spaces/_resolve_copy_saved_objects_errors`) - .auth(user.username, user.password) - .send({ - objects: [dashboardObject], - includeReferences: false, - createNewCopies: false, - retries: { - [destination]: [ - { - ...dashboardObject, - destinationId: `cts_dashboard_${destination}`, - overwrite: true, - }, - ], - }, - }) - .expect(tests.withoutReferencesOverwriting.statusCode) - .then(tests.withoutReferencesOverwriting.response); - }); - - it(`should return ${tests.withoutReferencesNotOverwriting.statusCode} when not overwriting, without references`, async () => { - const destination = getDestinationSpace(spaceId); - - return supertestWithoutAuth - .post(`${getUrlPrefix(spaceId)}/api/spaces/_resolve_copy_saved_objects_errors`) - .auth(user.username, user.password) - .send({ - objects: [dashboardObject], - includeReferences: false, - createNewCopies: false, - retries: { - [destination]: [ - { - ...dashboardObject, - destinationId: `cts_dashboard_${destination}`, - overwrite: false, - }, - ], - }, - }) - .expect(tests.withoutReferencesNotOverwriting.statusCode) - .then(tests.withoutReferencesNotOverwriting.response); - }); - - it(`should return ${tests.nonExistentSpace.statusCode} when resolving within a non-existent space`, async () => { - const destination = NON_EXISTENT_SPACE_ID; - - return supertestWithoutAuth - .post(`${getUrlPrefix(spaceId)}/api/spaces/_resolve_copy_saved_objects_errors`) - .auth(user.username, user.password) - .send({ - objects: [dashboardObject], - includeReferences: false, - createNewCopies: false, - retries: { - [destination]: [ - { - ...dashboardObject, - destinationId: `cts_dashboard_${destination}`, - // realistically a retry wouldn't use a destinationId, because it wouldn't have an origin conflict with another - // object in a non-existent space, but for the simplicity of testing we'll use this here - overwrite: true, - }, - ], - }, - }) - .expect(tests.nonExistentSpace.statusCode) - .then(tests.nonExistentSpace.response); - }); - }); - - const includeReferences = false; - const createNewCopies = false; - describe(`multi-namespace types with "overwrite" retry`, () => { - before(async () => await testDataLoader.createFtrSavedObjectsData(SPACE_DATA_TO_LOAD)); - after(async () => await testDataLoader.deleteFtrSavedObjectsData()); - - const testCases = tests.multiNamespaceTestCases(); - testCases.forEach(({ testTitle, objects, retries, statusCode, response }) => { - it(`should return ${statusCode} when ${testTitle}`, async () => { - return supertestWithoutAuth - .post(`${getUrlPrefix(spaceId)}/api/spaces/_resolve_copy_saved_objects_errors`) - .auth(user.username, user.password) - .send({ objects, includeReferences, createNewCopies, retries }) - .expect(statusCode) - .then(response); - }); - }); - }); - }); - }; - - const resolveCopyToSpaceConflictsTest = makeResolveCopyToSpaceConflictsTest(describe); - // @ts-ignore - resolveCopyToSpaceConflictsTest.only = makeResolveCopyToSpaceConflictsTest(describe.only); - - return { - resolveCopyToSpaceConflictsTest, - expectRouteForbiddenResponse, - expectRouteNotFoundResponse, - createExpectOverriddenResponseWithReferences, - createExpectOverriddenResponseWithoutReferences, - createExpectNonOverriddenResponseWithReferences, - createExpectNonOverriddenResponseWithoutReferences, - createExpectUnauthorizedAtSpaceWithReferencesResult, - createExpectUnauthorizedAtSpaceWithoutReferencesResult, - createMultiNamespaceTestCases, - originSpaces: ['default', 'space_1'], - NON_EXISTENT_SPACE_ID, - }; -} diff --git a/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts index 4d303450bdbed..b1097ad422450 100644 --- a/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts @@ -91,8 +91,8 @@ export function updateTestSuiteFactory(context: DeploymentAgnosticFtrProviderCon 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); }); - after(() => { - maybeDestroySupertest(supertest); + after(async () => { + await maybeDestroySupertest(supertest); esArchiver.unload( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); diff --git a/x-pack/test/spaces_api_integration/common/suites/update.ts b/x-pack/test/spaces_api_integration/common/suites/update.ts deleted file mode 100644 index 62226bf4dbb8d..0000000000000 --- a/x-pack/test/spaces_api_integration/common/suites/update.ts +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { SuperTest } from 'supertest'; - -import expect from '@kbn/expect'; - -import { getUrlPrefix } from '../lib/space_test_utils'; -import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; - -interface UpdateTest { - statusCode: number; - response: (resp: { [key: string]: any }) => void; -} - -interface UpdateTests { - alreadyExists: UpdateTest; - defaultSpace: UpdateTest; - newSpace: UpdateTest; -} - -interface UpdateTestDefinition { - user?: TestDefinitionAuthentication; - spaceId: string; - tests: UpdateTests; -} - -export function updateTestSuiteFactory(esArchiver: any, supertest: SuperTest) { - const expectRbacForbidden = (resp: { [key: string]: any }) => { - expect(resp.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to update spaces', - }); - }; - - const expectNotFound = (resp: { [key: string]: any }) => { - expect(resp.body).to.eql({ - error: 'Not Found', - message: 'Not Found', - statusCode: 404, - }); - }; - - const expectDefaultSpaceResult = (resp: { [key: string]: any }) => { - expect(resp.body).to.eql({ - name: 'the new default', - id: 'default', - description: 'a description', - color: '#ffffff', - disabledFeatures: [], - _reserved: true, - }); - }; - - const expectAlreadyExistsResult = (resp: { [key: string]: any }) => { - expect(resp.body).to.eql({ - name: 'space 1', - id: 'space_1', - description: 'a description', - color: '#5c5959', - disabledFeatures: [], - }); - }; - - const makeUpdateTest = - (describeFn: DescribeFn) => - (description: string, { user = {}, spaceId, tests }: UpdateTestDefinition) => { - describeFn(description, () => { - before(() => - esArchiver.load( - 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' - ) - ); - after(() => - esArchiver.unload( - 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' - ) - ); - - describe('space_1', () => { - it(`should return ${tests.alreadyExists.statusCode}`, async () => { - return supertest - .put(`${getUrlPrefix(spaceId)}/api/spaces/space/space_1`) - .auth(user.username, user.password) - .send({ - name: 'space 1', - id: 'space_1', - description: 'a description', - color: '#5c5959', - _reserved: true, - disabledFeatures: [], - }) - .expect(tests.alreadyExists.statusCode) - .then(tests.alreadyExists.response); - }); - }); - - describe(`default space`, () => { - it(`should return ${tests.defaultSpace.statusCode}`, async () => { - return supertest - .put(`${getUrlPrefix(spaceId)}/api/spaces/space/default`) - .auth(user.username, user.password) - .send({ - name: 'the new default', - id: 'default', - description: 'a description', - color: '#ffffff', - _reserved: false, - disabledFeatures: [], - }) - .expect(tests.defaultSpace.statusCode) - .then(tests.defaultSpace.response); - }); - }); - - describe(`when space doesn't exist`, () => { - it(`should return ${tests.newSpace.statusCode}`, async () => { - return supertest - .put(`${getUrlPrefix(spaceId)}/api/spaces/space/marketing`) - .auth(user.username, user.password) - .send({ - name: 'marketing', - id: 'marketing', - description: 'a description', - color: '#5c5959', - disabledFeatures: [], - }) - .expect(tests.newSpace.statusCode) - .then(tests.newSpace.response); - }); - }); - }); - }; - - const updateTest = makeUpdateTest(describe); - // @ts-ignore - updateTest.only = makeUpdateTest(describe.only); - - return { - expectAlreadyExistsResult, - expectDefaultSpaceResult, - expectNotFound, - expectRbacForbidden, - updateTest, - }; -} From f9d1d70444ce104dd84ee65b4e9971f36e70b4b5 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Mon, 18 Nov 2024 20:17:50 +0100 Subject: [PATCH 04/38] Fixes --- .../disable_legacy_url_aliases.agnostic.ts | 167 ------------------ .../common/suites/get.agnostic.ts | 4 +- .../common/suites/get_all.agnostic.ts | 4 +- .../common/suites/update.agnostic.ts | 4 +- 4 files changed, 6 insertions(+), 173 deletions(-) delete mode 100644 x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.agnostic.ts diff --git a/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.agnostic.ts deleted file mode 100644 index 007731e9c69b2..0000000000000 --- a/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.agnostic.ts +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { Agent as SuperTestAgent } from 'supertest'; - -import type { LegacyUrlAlias } from '@kbn/core-saved-objects-base-server-internal'; -import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; -import expect from '@kbn/expect'; - -import { getUrlPrefix } from '../../../saved_object_api_integration/common/lib/saved_object_test_utils'; -import type { - ExpectResponseBody, - TestDefinition, - TestSuite, -} from '../../../saved_object_api_integration/common/lib/types'; -import type { - DeploymentAgnosticFtrProviderContext, - SupertestWithRoleScopeType, -} from '../../deployment_agnostic/ftr_provider_context'; -import { getRoleDefinitionForUser, isBuiltInRole } from '../lib/authentication'; -import { SPACES } from '../lib/spaces'; -import type { TestDefinitionAuthentication } from '../lib/types'; - -export interface DisableLegacyUrlAliasesTestDefinition extends TestDefinition { - request: { - aliases: Array<{ targetSpace: string; targetType: string; sourceId: string }>; - }; -} -export type DisableLegacyUrlAliasesTestSuite = TestSuite; -export interface DisableLegacyUrlAliasesTestCase { - targetSpace: string; - targetType: string; - sourceId: string; - expectFound: boolean; -} - -const LEGACY_URL_ALIAS_TYPE = 'legacy-url-alias'; -interface RawLegacyUrlAlias { - [LEGACY_URL_ALIAS_TYPE]: LegacyUrlAlias; -} - -export const TEST_CASE_TARGET_TYPE = 'sharedtype'; -export const TEST_CASE_SOURCE_ID = 'space_1_only'; // two aliases exist for space_1_only: one in the default spacd=e, and one in space_2 -const createRequest = ({ targetSpace, targetType, sourceId }: DisableLegacyUrlAliasesTestCase) => ({ - aliases: [{ targetSpace, targetType, sourceId }], -}); -const getTestTitle = ({ targetSpace, targetType, sourceId }: DisableLegacyUrlAliasesTestCase) => { - return `for alias '${targetSpace}:${targetType}:${sourceId}'`; -}; - -export function disableLegacyUrlAliasesTestSuiteFactory({ - getService, -}: DeploymentAgnosticFtrProviderContext) { - const roleScopedSupertest = getService('roleScopedSupertest'); - const samlAuth = getService('samlAuth'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - const esArchiver = getService('esArchiver'); - const es = getService('es'); - - const expectResponseBody = - (testCase: DisableLegacyUrlAliasesTestCase, statusCode: 204 | 403): ExpectResponseBody => - async (response: Record) => { - const { targetSpace, targetType, sourceId, expectFound } = testCase; - if (statusCode === 403) { - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: `Unable to disable aliases: Unable to bulk_update ${targetType}`, - }); - } - const esResponse = await es.get( - { - // affected by the .kibana split, assumes LEGACY_URL_ALIAS_TYPE is stored in .kibana - index: MAIN_SAVED_OBJECT_INDEX, - id: `${LEGACY_URL_ALIAS_TYPE}:${targetSpace}:${targetType}:${sourceId}`, - }, - { ignore: [404] } - ); - if (expectFound) { - expect(esResponse.found).to.be(true); - const doc = esResponse._source!; - expect(doc).not.to.be(undefined); - expect(doc[LEGACY_URL_ALIAS_TYPE].disabled).to.be(statusCode === 204 ? true : undefined); - } else { - expect(esResponse.found).to.be(false); - } - }; - const createTestDefinitions = ( - testCases: DisableLegacyUrlAliasesTestCase | DisableLegacyUrlAliasesTestCase[], - forbidden: boolean, - options: { - responseBodyOverride?: ExpectResponseBody; - } = {} - ): DisableLegacyUrlAliasesTestDefinition[] => { - const cases = Array.isArray(testCases) ? testCases : [testCases]; - const responseStatusCode = forbidden ? 403 : 204; - return cases.map((x) => ({ - title: getTestTitle(x), - responseStatusCode, - request: createRequest(x), - responseBody: options?.responseBodyOverride || expectResponseBody(x, responseStatusCode), - })); - }; - - const makeDisableLegacyUrlAliasesTest = - (describeFn: Mocha.SuiteFunction) => - (description: string, definition: DisableLegacyUrlAliasesTestSuite) => { - const { spaceId = SPACES.DEFAULT.spaceId, tests } = definition; - const user = definition.user as unknown as TestDefinitionAuthentication; - - describeFn(description, () => { - let supertest: SupertestWithRoleScopeType | SuperTestAgent; - before(async () => { - if (user) { - const isBuiltIn = isBuiltInRole(user.role); - if (!isBuiltIn) { - await samlAuth.setCustomRole(getRoleDefinitionForUser(user)); - } - supertest = await roleScopedSupertest.getSupertestWithRoleScope( - isBuiltIn ? user.role : 'customRole', - { - useCookieHeader: true, - withInternalHeaders: true, - } - ); - } else { - supertest = supertestWithoutAuth; - } - esArchiver.load( - 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' - ); - }); - after(async () => { - if (user) { - await (supertest as SupertestWithRoleScopeType).destroy(); - } - esArchiver.unload( - 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' - ); - }); - - for (const test of tests) { - it(`should return ${test.responseStatusCode} ${test.title}`, async () => { - const requestBody = test.request; - await supertest - .post(`${getUrlPrefix(spaceId)}/api/spaces/_disable_legacy_url_aliases`) - .send(requestBody) - .expect(test.responseStatusCode) - .then(test.responseBody); - }); - } - }); - }; - - const addTests = makeDisableLegacyUrlAliasesTest(describe); - // @ts-ignore - addTests.only = makeDisableLegacyUrlAliasesTest(describe.only); - - return { - addTests, - createTestDefinitions, - }; -} diff --git a/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts index e45368b3523cc..b4630e750e55c 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts @@ -119,13 +119,13 @@ export function getTestSuiteFactory(context: DeploymentAgnosticFtrProviderContex before(async () => { supertest = await getSupertest(context, user); - esArchiver.load( + await esArchiver.load( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); }); after(async () => { await maybeDestroySupertest(supertest); - esArchiver.unload( + await esArchiver.unload( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); }); diff --git a/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts index 0ba63863b000e..2e833b492289c 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts @@ -149,12 +149,12 @@ export function getAllTestSuiteFactory(context: DeploymentAgnosticFtrProviderCon let supertest: SupertestWithRoleScopeType | SuperTestAgent; before(async () => { supertest = await getSupertest(context, user); - esArchiver.load( + await esArchiver.load( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); }); after(async () => { - esArchiver.unload( + await esArchiver.unload( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); await maybeDestroySupertest(supertest); diff --git a/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts index b1097ad422450..9d7446cebd397 100644 --- a/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts @@ -87,13 +87,13 @@ export function updateTestSuiteFactory(context: DeploymentAgnosticFtrProviderCon let supertest: SupertestWithRoleScopeType | SuperTestAgent; before(async () => { supertest = await getSupertest(context, user); - esArchiver.load( + await esArchiver.load( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); }); after(async () => { await maybeDestroySupertest(supertest); - esArchiver.unload( + await esArchiver.unload( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); }); From b2b4847ce92f3edd21254b8231dddebc6ba092e9 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Mon, 18 Nov 2024 21:00:10 +0100 Subject: [PATCH 05/38] fixes --- .../apis/disable_legacy_url_aliases.ts | 82 ------------------- .../security_and_spaces/apis/index.ts | 1 - 2 files changed, 83 deletions(-) delete mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/disable_legacy_url_aliases.ts diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/disable_legacy_url_aliases.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/disable_legacy_url_aliases.ts deleted file mode 100644 index 97d89076f6a61..0000000000000 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/disable_legacy_url_aliases.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { getTestScenarios } from '../../../../saved_object_api_integration/common/lib/saved_object_test_utils'; -import type { TestUser } from '../../../../saved_object_api_integration/common/lib/types'; -import { SPACES } from '../../../common/lib/spaces'; -import type { DisableLegacyUrlAliasesTestDefinition } from '../../../common/suites/disable_legacy_url_aliases'; -import { - disableLegacyUrlAliasesTestSuiteFactory, - TEST_CASE_SOURCE_ID, - TEST_CASE_TARGET_TYPE, -} from '../../../common/suites/disable_legacy_url_aliases.agnostic'; -import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; - -const { - DEFAULT: { spaceId: DEFAULT_SPACE_ID }, - SPACE_1: { spaceId: SPACE_1_ID }, - SPACE_2: { spaceId: SPACE_2_ID }, -} = SPACES; - -const createTestCases = () => { - const baseCase = { targetType: TEST_CASE_TARGET_TYPE, sourceId: TEST_CASE_SOURCE_ID }; - return { - [DEFAULT_SPACE_ID]: { ...baseCase, targetSpace: DEFAULT_SPACE_ID, expectFound: true }, // alias exists in the default space and should have been disabled - [SPACE_1_ID]: { ...baseCase, targetSpace: SPACE_1_ID, expectFound: false }, // alias does not exist in space_1 - [SPACE_2_ID]: { ...baseCase, targetSpace: SPACE_2_ID, expectFound: true }, // alias exists in space_2 and should have been disabled - }; -}; - -export default function (context: DeploymentAgnosticFtrProviderContext) { - const { addTests, createTestDefinitions } = disableLegacyUrlAliasesTestSuiteFactory(context); - - describe('_disable_legacy_url_aliases', () => { - const _addTests = ( - user: TestUser & { role: string }, - tests: DisableLegacyUrlAliasesTestDefinition[] - ) => { - addTests(`${user.description}`, { user, tests }); - }; - getTestScenarios().security.forEach(({ users }) => { - // We are intentionally using "security" test scenarios here, *not* "securityAndSpaces", because of how these tests are structured. - - const testCases = createTestCases(); - - [ - { ...users.noAccess, role: 'no_access' }, - { ...users.legacyAll, role: 'kibana_legacy_user' }, - { ...users.dualRead, role: 'dual_privileges_read' }, - { ...users.readGlobally, role: 'kibana_rbac_user' }, - { ...users.readAtDefaultSpace, role: 'kibana_rbac_default_space_read_user' }, - { ...users.readAtSpace1, role: 'kibana_rbac_space_1_all_user' }, - ].forEach((user) => { - const unauthorized = createTestDefinitions(Object.values(testCases), true); - _addTests(user, unauthorized); - }); - - const authorizedDefaultSpace = [ - ...createTestDefinitions(testCases[DEFAULT_SPACE_ID], false), - ...createTestDefinitions([testCases[SPACE_1_ID], testCases[SPACE_2_ID]], true), - ]; - _addTests( - { ...users.allAtDefaultSpace, role: 'kibana_rbac_default_space_all_user' }, - authorizedDefaultSpace - ); - - const authorizedSpace1 = [ - ...createTestDefinitions(testCases[SPACE_1_ID], false), - ...createTestDefinitions([testCases[DEFAULT_SPACE_ID], testCases[SPACE_2_ID]], true), - ]; - _addTests({ ...users.allAtSpace1, role: 'kibana_rbac_space_1_all_user' }, authorizedSpace1); - - // [users.dualAll, users.allGlobally, users.superuser].forEach((user) => { - // const authorizedGlobally = createTestDefinitions(Object.values(testCases), false); - // _addTests(user, authorizedGlobally); - // }); - }); - }); -} diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts index 2cda14d3fc69c..4bb9c73c70b87 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts @@ -15,6 +15,5 @@ export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) loadTestFile(require.resolve('./get_all')); loadTestFile(require.resolve('./get')); loadTestFile(require.resolve('./update')); - loadTestFile(require.resolve('./disable_legacy_url_aliases')); }); } From a20cb078369037be8b18cfc5a73dd48f15711896 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Mon, 18 Nov 2024 23:54:23 +0100 Subject: [PATCH 06/38] Fixes --- .buildkite/ftr_platform_stateful_configs.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.buildkite/ftr_platform_stateful_configs.yml b/.buildkite/ftr_platform_stateful_configs.yml index ddcfffafc7c79..09b2feced215b 100644 --- a/.buildkite/ftr_platform_stateful_configs.yml +++ b/.buildkite/ftr_platform_stateful_configs.yml @@ -343,9 +343,7 @@ enabled: - x-pack/test/security_functional/expired_session.config.ts - x-pack/test/session_view/basic/config.ts - x-pack/test/spaces_api_integration/security_and_spaces/config_basic.ts - - x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_basic.ts - x-pack/test/spaces_api_integration/security_and_spaces/config_trial.ts - - x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_trial.ts - x-pack/test/spaces_api_integration/spaces_only/config.ts - x-pack/test/task_manager_claimer_update_by_query/config.ts - x-pack/test/ui_capabilities/security_and_spaces/config.ts From 9d8b9a93bc44ea84d6d6598efbb04c3e7ea4a28c Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Tue, 19 Nov 2024 08:56:57 +0100 Subject: [PATCH 07/38] fixes --- .../{stateful.config.ts => stateful.common.config.ts} | 0 .../deployment_agnostic/stateful.config_trial.ts | 2 +- .../deployment_agnostic/stateful.copy_to_space.config_trial.ts | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename x-pack/test/spaces_api_integration/deployment_agnostic/{stateful.config.ts => stateful.common.config.ts} (100%) diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts similarity index 100% rename from x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts index 014dfbb1e31ad..4f17b0f8b7f77 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts @@ -5,6 +5,6 @@ * 2.0. */ -import { createTestConfig } from './stateful.config'; +import { createTestConfig } from './stateful.common.config'; export default createTestConfig({ license: 'trial' }); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts index 9e746c55c1404..ba437a4b9317a 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { createTestConfig } from './stateful.config'; +import { createTestConfig } from './stateful.common.config'; export default createTestConfig({ license: 'trial', From daec53f40f0f359e51e6046ef9b8eb596abe8a4a Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Tue, 19 Nov 2024 13:05:39 +0100 Subject: [PATCH 08/38] cleaned up apm_user --- .../kbn-es/src/stateful_resources/roles.yml | 65 ------------------- .../security_and_spaces/apis/get_all.ts | 50 +++++++------- 2 files changed, 25 insertions(+), 90 deletions(-) diff --git a/packages/kbn-es/src/stateful_resources/roles.yml b/packages/kbn-es/src/stateful_resources/roles.yml index bc11627a83b09..a527603aafae2 100644 --- a/packages/kbn-es/src/stateful_resources/roles.yml +++ b/packages/kbn-es/src/stateful_resources/roles.yml @@ -159,71 +159,6 @@ machine_learning_user: resources: ['*'] run_as: [] -apm_user: - cluster: [] - indices: - - names: - - 'apm-*' - privileges: - - 'read' - - 'view_index_metadata' - allow_restricted_indices: false - - names: - - 'logs-apm.*' - privileges: - - 'read' - - 'view_index_metadata' - allow_restricted_indices: false - - names: - - 'logs-apm-*' - privileges: - - 'read' - - 'view_index_metadata' - allow_restricted_indices: false - - names: - - 'metrics-apm.*' - privileges: - - 'read' - - 'view_index_metadata' - allow_restricted_indices: false - - names: - - 'metrics-apm-*' - privileges: - - 'read' - - 'view_index_metadata' - allow_restricted_indices: false - - names: - - 'traces-apm.*' - privileges: - - 'read' - - 'view_index_metadata' - allow_restricted_indices: false - - names: - - 'traces-apm-*' - privileges: - - 'read' - - 'view_index_metadata' - allow_restricted_indices: false - - names: - - '.ml-anomalies*' - privileges: - - 'read' - - 'view_index_metadata' - allow_restricted_indices: false - - names: - - 'observability-annotations' - privileges: - - 'read' - - 'view_index_metadata' - allow_restricted_indices: false - applications: - - application: 'kibana-*' - privileges: - - 'reserved_ml_apm_user' - resources: - - '*' - run_as: [] - monitoring_user: cluster: - "cluster:monitor/main" diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get_all.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get_all.ts index fd9ed8a2abb0d..a0ec35b1f4e3d 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get_all.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get_all.ts @@ -424,31 +424,31 @@ export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProv } ); - // getAllTest( - // `rbac user with saved objects management all at space_1 space can access space_1 from ${scenario.spaceId}`, - // { - // spaceId: scenario.spaceId, - // user: scenario.users.allSavedObjectsAtSpace_1, - // tests: { - // exists: { - // statusCode: 200, - // response: createExpectResults('space_1'), - // }, - // copySavedObjectsPurpose: { - // statusCode: 200, - // response: createExpectResults('space_1'), - // }, - // shareSavedObjectsPurpose: { - // statusCode: 200, - // response: createExpectResults('space_1'), - // }, - // includeAuthorizedPurposes: { - // statusCode: 200, - // response: createExpectAllPurposesResults(authorizedAll, 'space_1'), - // }, - // }, - // } - // ); + getAllTest( + `rbac user with saved objects management all at space_1 space can access space_1 from ${scenario.spaceId}`, + { + spaceId: scenario.spaceId, + user: scenario.users.allSavedObjectsAtSpace_1, + tests: { + exists: { + statusCode: 200, + response: createExpectResults('space_1'), + }, + copySavedObjectsPurpose: { + statusCode: 200, + response: createExpectResults('space_1'), + }, + shareSavedObjectsPurpose: { + statusCode: 200, + response: createExpectResults('space_1'), + }, + includeAuthorizedPurposes: { + statusCode: 200, + response: createExpectAllPurposesResults(authorizedAll, 'space_1'), + }, + }, + } + ); getAllTest( `rbac user with saved objects management read at space_1 space can access space_1 from ${scenario.spaceId}`, From 111b4b318fbf29a515a91e7a76d82b3c7a3b7e5e Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Tue, 19 Nov 2024 13:06:15 +0100 Subject: [PATCH 09/38] basic license - discuss with the team --- .buildkite/ftr_security_stateful_configs.yml | 1 + .../common/suites/common.ts | 29 +++++++++++++++++-- .../common/suites/copy_to_space.agnostic.ts | 26 ++--------------- .../apis/{ => copy_to_space}/copy_to_space.ts | 8 ++--- .../apis/copy_to_space/index.ts | 25 ++++++++++++++++ .../security_and_spaces/apis/index.ts | 13 ++++++++- .../stateful.copy_to_space.config_basic.ts | 13 +++++++++ 7 files changed, 84 insertions(+), 31 deletions(-) rename x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/{ => copy_to_space}/copy_to_space.ts (96%) create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/index.ts create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_basic.ts diff --git a/.buildkite/ftr_security_stateful_configs.yml b/.buildkite/ftr_security_stateful_configs.yml index 01827494d95df..9084cac58a933 100644 --- a/.buildkite/ftr_security_stateful_configs.yml +++ b/.buildkite/ftr_security_stateful_configs.yml @@ -109,4 +109,5 @@ enabled: - x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config.ts - x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts - x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts + - x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_basic.ts - x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/config.ts diff --git a/x-pack/test/spaces_api_integration/common/suites/common.ts b/x-pack/test/spaces_api_integration/common/suites/common.ts index 6c8e41501ec92..fc51528eed205 100644 --- a/x-pack/test/spaces_api_integration/common/suites/common.ts +++ b/x-pack/test/spaces_api_integration/common/suites/common.ts @@ -20,23 +20,46 @@ export async function getSupertest( const roleScopedSupertest = getService('roleScopedSupertest'); const samlAuth = getService('samlAuth'); const supertestWithoutAuth = getService('supertestWithoutAuth'); + const config = getService('config'); + const license = config.get('esTestCluster.license'); + let scopedSupertest: SupertestWithRoleScopeType | SuperTestAgent; - if (user) { + if (user && license === 'trial') { const isBuiltIn = isBuiltInRole(user.role); if (!isBuiltIn) { await samlAuth.setCustomRole(getRoleDefinitionForUser(user)); } - return await roleScopedSupertest.getSupertestWithRoleScope( + scopedSupertest = await roleScopedSupertest.getSupertestWithRoleScope( isBuiltIn ? user.role : 'customRole', { useCookieHeader: true, withInternalHeaders: true, } ); + } else { + scopedSupertest = supertestWithoutAuth; } - return supertestWithoutAuth; + // TODO: fix types + const wrapRequestWithAuth = (method: keyof SuperTestAgent) => { + return (...args: any[]) => { + const request = (scopedSupertest as any)[method](...args); + if (user && license === 'basic') { + return request.auth(user.username, user.password); + } + return request; + }; + }; + + return new Proxy(scopedSupertest, { + get(target, prop: string) { + if (['post', 'get', 'put', 'delete', 'patch', 'head'].includes(prop)) { + return wrapRequestWithAuth(prop as keyof SuperTestAgent); + } + return Reflect.get(target, prop); + }, + }); } export async function maybeDestroySupertest( diff --git a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts index c1df89b402d36..3681ddb7cc658 100644 --- a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts @@ -15,12 +15,12 @@ import expect from '@kbn/expect/expect'; import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; import type { CopyResponse } from '@kbn/spaces-plugin/server/lib/copy_to_spaces'; +import { getSupertest, maybeDestroySupertest } from './common'; import { getTestDataLoader, SPACE_1, SPACE_2 } from '../../../common/lib/test_data_loader'; import type { DeploymentAgnosticFtrProviderContext, SupertestWithRoleScopeType, } from '../../deployment_agnostic/ftr_provider_context'; -import { getRoleDefinitionForUser, isBuiltInRole } from '../lib/authentication'; import { getAggregatedSpaceData, getUrlPrefix } from '../lib/space_test_utils'; import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; @@ -106,9 +106,6 @@ interface Aggs extends estypes.AggregationsMultiBucketAggregateBase { export function copyToSpaceTestSuiteFactory(context: DeploymentAgnosticFtrProviderContext) { const testDataLoader = getTestDataLoader(context); const es = context.getService('es'); - const roleScopedSupertest = context.getService('roleScopedSupertest'); - const samlAuth = context.getService('samlAuth'); - const supertestWithoutAuth = context.getService('supertestWithoutAuth'); const collectSpaceContents = async () => { const response = await getAggregatedSpaceData(es, [ @@ -809,29 +806,12 @@ export function copyToSpaceTestSuiteFactory(context: DeploymentAgnosticFtrProvid // test data only allows for the following spaces as the copy origin expect(['default', 'space_1']).to.contain(spaceId); await testDataLoader.createFtrSpaces(); - - if (user) { - const isBuiltIn = isBuiltInRole(user.role); - if (!isBuiltIn) { - await samlAuth.setCustomRole(getRoleDefinitionForUser(user)); - } - supertest = await roleScopedSupertest.getSupertestWithRoleScope( - isBuiltIn ? user.role : 'customRole', - { - useCookieHeader: true, - withInternalHeaders: true, - } - ); - } else { - supertest = supertestWithoutAuth; - } + supertest = await getSupertest(context, user); }); after(async () => { await testDataLoader.deleteFtrSpaces(); - if (user) { - await (supertest as SupertestWithRoleScopeType).destroy(); - } + await maybeDestroySupertest(supertest); }); describe('single-namespace types', () => { diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/copy_to_space.ts similarity index 96% rename from x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/copy_to_space.ts index 090d69dd43510..3e769698d7f55 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/copy_to_space.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { AUTHENTICATION } from '../../../common/lib/authentication'; -import { SPACES } from '../../../common/lib/spaces'; -import { copyToSpaceTestSuiteFactory } from '../../../common/suites/copy_to_space.agnostic'; -import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; +import { AUTHENTICATION } from '../../../../common/lib/authentication'; +import { SPACES } from '../../../../common/lib/spaces'; +import { copyToSpaceTestSuiteFactory } from '../../../../common/suites/copy_to_space.agnostic'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context'; interface User { username: string; diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/index.ts new file mode 100644 index 0000000000000..04ca969807e2d --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createUsersAndRoles } from '../../../../common/lib/create_users_and_roles'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile, getService }: DeploymentAgnosticFtrProviderContext) { + const config = getService('config'); + const license = config.get('esTestCluster.license'); + const es = getService('es'); + const supertest = getService('supertest'); + + describe('spaces api with security', function () { + before(async () => { + if (license === 'basic') { + await createUsersAndRoles(es, supertest); + } + }); + loadTestFile(require.resolve('./copy_to_space')); + }); +} diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts index 4bb9c73c70b87..18d0a16853c6b 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts @@ -5,10 +5,21 @@ * 2.0. */ +import { createUsersAndRoles } from '../../../common/lib/create_users_and_roles'; import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; -export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { +export default function ({ loadTestFile, getService }: DeploymentAgnosticFtrProviderContext) { + const config = getService('config'); + const license = config.get('esTestCluster.license'); + const es = getService('es'); + const supertest = getService('supertest'); + describe('spaces api with security', function () { + before(async () => { + if (license === 'basic') { + await createUsersAndRoles(es, supertest); + } + }); loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); loadTestFile(require.resolve('./create')); loadTestFile(require.resolve('./delete')); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_basic.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_basic.ts new file mode 100644 index 0000000000000..a079bd64edb98 --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_basic.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createTestConfig } from '../common/config'; + +export default createTestConfig('copy_to_space - basic license', { + license: 'basic', + testFiles: [require.resolve('./security_and_spaces/apis/copy_to_space')], +}); From 9190e14ebe4dc350a6de6fddb5cf4906ab05fa50 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Thu, 21 Nov 2024 16:38:58 +0100 Subject: [PATCH 10/38] SO types change --- .../saved_objects/spaces/data.json | 194 +++++++++--------- .../security_and_spaces/apis/index.ts | 10 +- .../stateful.common.config.ts | 2 +- 3 files changed, 103 insertions(+), 103 deletions(-) diff --git a/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json b/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json index fcf6043a30012..0a7d3f783958f 100644 --- a/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json +++ b/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json @@ -68,11 +68,11 @@ { "type": "_doc", "value": { - "id": "isolatedtype:my_isolated_object", + "id": "url:my_isolated_object", "index": ".kibana", "source": { "namespace": "space_2", - "type": "isolatedtype", + "type": "url", "updated_at": "2017-09-21T18:49:16.270Z" }, "type": "_doc" @@ -82,11 +82,11 @@ { "type": "_doc", "value": { - "id": "isolatedtype:my_isolated_object", + "id": "url:my_isolated_object", "index": ".kibana", "source": { "namespace": "space_1", - "type": "isolatedtype", + "type": "url", "updated_at": "2017-09-21T18:49:16.270Z" }, "type": "_doc" @@ -96,10 +96,10 @@ { "type": "_doc", "value": { - "id": "isolatedtype:my_isolated_object", + "id": "url:my_isolated_object", "index": ".kibana", "source": { - "type": "isolatedtype", + "type": "url", "updated_at": "2017-09-21T18:49:16.270Z" }, "type": "_doc" @@ -411,16 +411,16 @@ { "type": "doc", "value": { - "id": "sharedtype:default_only", + "id": "index-pattern:default_only", "index": ".kibana", "source": { - "sharedtype": { + "index-pattern": { "title": "A shared saved-object in one space" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["default"], "references": [ - { "type": "sharedtype", "id": "each_space", "name": "refname" } + { "type": "index-pattern", "id": "each_space", "name": "refname" } ], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -431,7 +431,7 @@ { "type": "doc", "value": { - "id": "legacy-url-alias:default:sharedtype:space_1_only", + "id": "legacy-url-alias:default:index-pattern:space_1_only", "index": ".kibana", "source": { "type": "legacy-url-alias", @@ -439,7 +439,7 @@ "legacy-url-alias": { "sourceId": "space_1_only", "targetNamespace": "default", - "targetType": "sharedtype", + "targetType": "index-pattern", "targetId": "default_only" }, "coreMigrationVersion": "8.8.0", @@ -451,16 +451,16 @@ { "type": "doc", "value": { - "id": "sharedtype:space_1_only", + "id": "index-pattern:space_1_only", "index": ".kibana", "source": { - "sharedtype": { + "index-pattern": { "title": "A shared saved-object in one space" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["space_1"], "references": [ - { "type": "sharedtype", "id": "each_space", "name": "refname" } + { "type": "index-pattern", "id": "each_space", "name": "refname" } ], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -471,14 +471,14 @@ { "type": "doc", "value": { - "id": "sharedtype:space_1_only_matching_origin", + "id": "index-pattern:space_1_only_matching_origin", "index": ".kibana", "source": { "originId": "space_1_only", - "sharedtype": { + "index-pattern": { "title": "This object only exists to test the second assertion for spacesWithMatchingOrigins in get_shareable_references" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["other_space"], "references": [], "updated_at": "2017-09-21T18:59:16.270Z" @@ -490,16 +490,16 @@ { "type": "doc", "value": { - "id": "sharedtype:space_2_only", + "id": "index-pattern:space_2_only", "index": ".kibana", "source": { - "sharedtype": { + "index-pattern": { "title": "A shared saved-object in one space" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["space_2"], "references": [ - { "type": "sharedtype", "id": "each_space", "name": "refname" } + { "type": "index-pattern", "id": "each_space", "name": "refname" } ], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -510,14 +510,14 @@ { "type": "doc", "value": { - "id": "sharedtype:space_2_only_matching_origin", + "id": "index-pattern:space_2_only_matching_origin", "index": ".kibana", "source": { "originId": "space_2_only", - "sharedtype": { + "index-pattern": { "title": "This object only exists to test the third assertion for spacesWithMatchingOrigins in get_shareable_references" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["*"], "references": [], "updated_at": "2017-09-21T18:59:16.270Z" @@ -529,7 +529,7 @@ { "type": "doc", "value": { - "id": "legacy-url-alias:space_2:sharedtype:space_1_only", + "id": "legacy-url-alias:space_2:index-pattern:space_1_only", "index": ".kibana", "source": { "type": "legacy-url-alias", @@ -537,7 +537,7 @@ "legacy-url-alias": { "sourceId": "space_1_only", "targetNamespace": "space_2", - "targetType": "sharedtype", + "targetType": "index-pattern", "targetId": "space_2_only" }, "coreMigrationVersion": "8.8.0", @@ -549,7 +549,7 @@ { "type": "doc", "value": { - "id": "legacy-url-alias:other_space:sharedtype:default_only", + "id": "legacy-url-alias:other_space:index-pattern:default_only", "index": ".kibana", "source": { "type": "legacy-url-alias", @@ -557,7 +557,7 @@ "legacy-url-alias": { "sourceId": "default_only", "targetNamespace": "other_space", - "targetType": "sharedtype", + "targetType": "index-pattern", "targetId": "other_id", "disabled": true } @@ -568,13 +568,13 @@ { "type": "doc", "value": { - "id": "sharedtype:default_and_space_1", + "id": "index-pattern:default_and_space_1", "index": ".kibana", "source": { - "sharedtype": { + "index-pattern": { "title": "A shared saved-object in the default and space_1 spaces" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["default", "space_1"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -585,13 +585,13 @@ { "type": "doc", "value": { - "id": "sharedtype:default_and_space_2", + "id": "index-pattern:default_and_space_2", "index": ".kibana", "source": { - "sharedtype": { + "index-pattern": { "title": "A shared saved-object in the default and space_2 spaces" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["default", "space_2"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -602,13 +602,13 @@ { "type": "doc", "value": { - "id": "sharedtype:space_1_and_space_2", + "id": "index-pattern:space_1_and_space_2", "index": ".kibana", "source": { - "sharedtype": { + "index-pattern": { "title": "A shared saved-object in the space_1 and space_2 spaces" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["space_1", "space_2"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -619,19 +619,19 @@ { "type": "doc", "value": { - "id": "sharedtype:each_space", + "id": "index-pattern:each_space", "index": ".kibana", "source": { - "sharedtype": { + "index-pattern": { "title": "A shared saved-object in the default, space_1, and space_2 spaces" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["default", "space_1", "space_2"], "references": [ - { "type": "sharedtype", "id": "default_only", "name": "refname" }, - { "type": "sharedtype", "id": "space_1_only", "name": "refname" }, - { "type": "sharedtype", "id": "space_2_only", "name": "refname" }, - { "type": "sharedtype", "id": "all_spaces", "name": "refname" } + { "type": "index-pattern", "id": "default_only", "name": "refname" }, + { "type": "index-pattern", "id": "space_1_only", "name": "refname" }, + { "type": "index-pattern", "id": "space_2_only", "name": "refname" }, + { "type": "index-pattern", "id": "all_spaces", "name": "refname" } ], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -642,13 +642,13 @@ { "type": "doc", "value": { - "id": "sharedtype:all_spaces", + "id": "index-pattern:all_spaces", "index": ".kibana", "source": { - "sharedtype": { + "index-pattern": { "title": "A shared saved-object in all spaces" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["*"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -659,13 +659,13 @@ { "type": "doc", "value": { - "id": "sharedtype:alias_delete_inclusive", + "id": "index-pattern:alias_delete_inclusive", "index": ".kibana", "source": { - "sharedtype": { + "index-pattern": { "title": "This is used to test that when an object is unshared from a space, inbound aliases for just those spaces are removed" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["default", "space_1", "space_2"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -676,7 +676,7 @@ { "type": "doc", "value": { - "id": "legacy-url-alias:default:sharedtype:doesnt-matter", + "id": "legacy-url-alias:default:index-pattern:doesnt-matter", "index": ".kibana", "source": { "type": "legacy-url-alias", @@ -684,7 +684,7 @@ "legacy-url-alias": { "sourceId": "doesnt-matter", "targetNamespace": "default", - "targetType": "sharedtype", + "targetType": "index-pattern", "targetId": "alias_delete_inclusive" } } @@ -694,7 +694,7 @@ { "type": "doc", "value": { - "id": "legacy-url-alias:space_2:sharedtype:doesnt-matter", + "id": "legacy-url-alias:space_2:index-pattern:doesnt-matter", "index": ".kibana", "source": { "type": "legacy-url-alias", @@ -702,7 +702,7 @@ "legacy-url-alias": { "sourceId": "doesnt-matter", "targetNamespace": "space_2", - "targetType": "sharedtype", + "targetType": "index-pattern", "targetId": "alias_delete_inclusive" } } @@ -712,13 +712,13 @@ { "type": "doc", "value": { - "id": "sharedtype:alias_delete_exclusive", + "id": "index-pattern:alias_delete_exclusive", "index": ".kibana", "source": { - "sharedtype": { + "index-pattern": { "title": "This is used to test that when an object is unshared from all space, inbound aliases for all spaces are removed" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["*"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -729,7 +729,7 @@ { "type": "doc", "value": { - "id": "legacy-url-alias:other_space:sharedtype:doesnt-matter", + "id": "legacy-url-alias:other_space:index-pattern:doesnt-matter", "index": ".kibana", "source": { "type": "legacy-url-alias", @@ -737,7 +737,7 @@ "legacy-url-alias": { "sourceId": "doesnt-matter", "targetNamespace": "other_space", - "targetType": "sharedtype", + "targetType": "index-pattern", "targetId": "alias_delete_exclusive" } } @@ -747,14 +747,14 @@ { "type": "doc", "value": { - "id": "sharedtype:conflict_1a_default", + "id": "index-pattern:conflict_1a_default", "index": ".kibana", "source": { "originId": "conflict_1a", - "sharedtype": { + "index-pattern": { "title": "This is used to test an inexact match conflict for an originId -> originId match" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["default"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -765,14 +765,14 @@ { "type": "doc", "value": { - "id": "sharedtype:conflict_1a_space_1", + "id": "index-pattern:conflict_1a_space_1", "index": ".kibana", "source": { "originId": "conflict_1a", - "sharedtype": { + "index-pattern": { "title": "This is used to test an inexact match conflict for an originId -> originId match" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["space_1"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -783,14 +783,14 @@ { "type": "doc", "value": { - "id": "sharedtype:conflict_1a_space_2", + "id": "index-pattern:conflict_1a_space_2", "index": ".kibana", "source": { "originId": "conflict_1a", - "sharedtype": { + "index-pattern": { "title": "This is used to test an inexact match conflict for an originId -> originId match" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["space_2"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -801,14 +801,14 @@ { "type": "doc", "value": { - "id": "sharedtype:conflict_1b_default", + "id": "index-pattern:conflict_1b_default", "index": ".kibana", "source": { "originId": "conflict_1b_space_2", - "sharedtype": { + "index-pattern": { "title": "This is used to test an inexact match conflict for an originId -> id match" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["default"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -819,14 +819,14 @@ { "type": "doc", "value": { - "id": "sharedtype:conflict_1b_space_1", + "id": "index-pattern:conflict_1b_space_1", "index": ".kibana", "source": { "originId": "conflict_1b_space_2", - "sharedtype": { + "index-pattern": { "title": "This is used to test an inexact match conflict for an originId -> id match" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["space_1"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -837,13 +837,13 @@ { "type": "doc", "value": { - "id": "sharedtype:conflict_1b_space_2", + "id": "index-pattern:conflict_1b_space_2", "index": ".kibana", "source": { - "sharedtype": { + "index-pattern": { "title": "This is used to test an inexact match conflict for an originId -> id match" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["space_2"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -854,13 +854,13 @@ { "type": "doc", "value": { - "id": "sharedtype:conflict_1c_default_and_space_1", + "id": "index-pattern:conflict_1c_default_and_space_1", "index": ".kibana", "source": { - "sharedtype": { + "index-pattern": { "title": "This is used to test an inexact match conflict for an id -> originId match" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["default", "space_1"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -871,14 +871,14 @@ { "type": "doc", "value": { - "id": "sharedtype:conflict_1c_space_2", + "id": "index-pattern:conflict_1c_space_2", "index": ".kibana", "source": { "originId": "conflict_1c_default_and_space_1", - "sharedtype": { + "index-pattern": { "title": "This is used to test an inexact match conflict for an id -> originId match" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["space_2"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -889,14 +889,14 @@ { "type": "doc", "value": { - "id": "sharedtype:conflict_2_default", + "id": "index-pattern:conflict_2_default", "index": ".kibana", "source": { "originId": "conflict_2", - "sharedtype": { + "index-pattern": { "title": "A shared saved-object in one space" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["default"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -907,14 +907,14 @@ { "type": "doc", "value": { - "id": "sharedtype:conflict_2_space_1", + "id": "index-pattern:conflict_2_space_1", "index": ".kibana", "source": { "originId": "conflict_2", - "sharedtype": { + "index-pattern": { "title": "A shared saved-object in one space" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["space_1"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -925,14 +925,14 @@ { "type": "doc", "value": { - "id": "sharedtype:conflict_2_space_2", + "id": "index-pattern:conflict_2_space_2", "index": ".kibana", "source": { "originId": "conflict_2", - "sharedtype": { + "index-pattern": { "title": "A shared saved-object in one space" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["space_2"], "updated_at": "2017-09-21T18:59:16.270Z" }, @@ -943,14 +943,14 @@ { "type": "doc", "value": { - "id": "sharedtype:conflict_2_all", + "id": "index-pattern:conflict_2_all", "index": ".kibana", "source": { "originId": "conflict_2", - "sharedtype": { + "index-pattern": { "title": "A shared saved-object in all spaces" }, - "type": "sharedtype", + "type": "index-pattern", "namespaces": ["default", "space_1", "space_2"], "updated_at": "2017-09-21T18:59:16.270Z" }, diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts index 18d0a16853c6b..90395f6c1f1de 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts @@ -20,11 +20,11 @@ export default function ({ loadTestFile, getService }: DeploymentAgnosticFtrProv await createUsersAndRoles(es, supertest); } }); - loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); + // loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); loadTestFile(require.resolve('./create')); - loadTestFile(require.resolve('./delete')); - loadTestFile(require.resolve('./get_all')); - loadTestFile(require.resolve('./get')); - loadTestFile(require.resolve('./update')); + // loadTestFile(require.resolve('./delete')); + // loadTestFile(require.resolve('./get_all')); + // loadTestFile(require.resolve('./get')); + // loadTestFile(require.resolve('./update')); }); } diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts index da9ca0fc86865..6f03cf33d2eaf 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts @@ -62,7 +62,7 @@ export function createTestConfig({ ...testConfig.kbnTestServer.serverArgs, '--status.allowAnonymous=false', '--server.xsrf.disableProtection=true', - `--plugin-path=${path.resolve(__dirname, '../common/plugins/spaces_test_plugin')}`, + // `--plugin-path=${path.resolve(__dirname, '../common/plugins/spaces_test_plugin')}`, ], }, }; From 28ba05ae4a64e5d2d9f8e1b39c7bed982133ec99 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Fri, 22 Nov 2024 11:41:40 +0100 Subject: [PATCH 11/38] wip --- .../saved_objects/spaces/data.json | 48 +++++++++---------- .../common/suites/delete.agnostic.ts | 15 +++--- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json b/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json index 0a7d3f783958f..546ed5401b244 100644 --- a/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json +++ b/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json @@ -412,7 +412,7 @@ "type": "doc", "value": { "id": "index-pattern:default_only", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "title": "A shared saved-object in one space" @@ -452,7 +452,7 @@ "type": "doc", "value": { "id": "index-pattern:space_1_only", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "title": "A shared saved-object in one space" @@ -472,7 +472,7 @@ "type": "doc", "value": { "id": "index-pattern:space_1_only_matching_origin", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "space_1_only", "index-pattern": { @@ -491,7 +491,7 @@ "type": "doc", "value": { "id": "index-pattern:space_2_only", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "title": "A shared saved-object in one space" @@ -511,7 +511,7 @@ "type": "doc", "value": { "id": "index-pattern:space_2_only_matching_origin", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "space_2_only", "index-pattern": { @@ -569,7 +569,7 @@ "type": "doc", "value": { "id": "index-pattern:default_and_space_1", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "title": "A shared saved-object in the default and space_1 spaces" @@ -586,7 +586,7 @@ "type": "doc", "value": { "id": "index-pattern:default_and_space_2", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "title": "A shared saved-object in the default and space_2 spaces" @@ -603,7 +603,7 @@ "type": "doc", "value": { "id": "index-pattern:space_1_and_space_2", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "title": "A shared saved-object in the space_1 and space_2 spaces" @@ -620,7 +620,7 @@ "type": "doc", "value": { "id": "index-pattern:each_space", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "title": "A shared saved-object in the default, space_1, and space_2 spaces" @@ -643,7 +643,7 @@ "type": "doc", "value": { "id": "index-pattern:all_spaces", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "title": "A shared saved-object in all spaces" @@ -660,7 +660,7 @@ "type": "doc", "value": { "id": "index-pattern:alias_delete_inclusive", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "title": "This is used to test that when an object is unshared from a space, inbound aliases for just those spaces are removed" @@ -713,7 +713,7 @@ "type": "doc", "value": { "id": "index-pattern:alias_delete_exclusive", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "title": "This is used to test that when an object is unshared from all space, inbound aliases for all spaces are removed" @@ -748,7 +748,7 @@ "type": "doc", "value": { "id": "index-pattern:conflict_1a_default", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "conflict_1a", "index-pattern": { @@ -766,7 +766,7 @@ "type": "doc", "value": { "id": "index-pattern:conflict_1a_space_1", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "conflict_1a", "index-pattern": { @@ -784,7 +784,7 @@ "type": "doc", "value": { "id": "index-pattern:conflict_1a_space_2", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "conflict_1a", "index-pattern": { @@ -802,7 +802,7 @@ "type": "doc", "value": { "id": "index-pattern:conflict_1b_default", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "conflict_1b_space_2", "index-pattern": { @@ -820,7 +820,7 @@ "type": "doc", "value": { "id": "index-pattern:conflict_1b_space_1", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "conflict_1b_space_2", "index-pattern": { @@ -838,7 +838,7 @@ "type": "doc", "value": { "id": "index-pattern:conflict_1b_space_2", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "title": "This is used to test an inexact match conflict for an originId -> id match" @@ -855,7 +855,7 @@ "type": "doc", "value": { "id": "index-pattern:conflict_1c_default_and_space_1", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "title": "This is used to test an inexact match conflict for an id -> originId match" @@ -872,7 +872,7 @@ "type": "doc", "value": { "id": "index-pattern:conflict_1c_space_2", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "conflict_1c_default_and_space_1", "index-pattern": { @@ -890,7 +890,7 @@ "type": "doc", "value": { "id": "index-pattern:conflict_2_default", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "conflict_2", "index-pattern": { @@ -908,7 +908,7 @@ "type": "doc", "value": { "id": "index-pattern:conflict_2_space_1", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "conflict_2", "index-pattern": { @@ -926,7 +926,7 @@ "type": "doc", "value": { "id": "index-pattern:conflict_2_space_2", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "conflict_2", "index-pattern": { @@ -944,7 +944,7 @@ "type": "doc", "value": { "id": "index-pattern:conflict_2_all", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "conflict_2", "index-pattern": { diff --git a/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts index cf7b337697f6a..d1f2a7aa236d9 100644 --- a/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts @@ -73,7 +73,7 @@ export function deleteTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv const expectedBuckets = [ { key: 'default', - doc_count: 10, + doc_count: 20, countByType: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -82,12 +82,12 @@ export function deleteTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv { key: 'visualization', doc_count: 3 }, { key: 'legacy-url-alias', doc_count: 2 }, // aliases (1) { key: 'dashboard', doc_count: 1 }, - { key: 'index-pattern', doc_count: 1 }, + { key: 'index-pattern', doc_count: 11 }, ], }, }, { - doc_count: 5, + doc_count: 10, key: 'space_1', countByType: { doc_count_error_upper_bound: 0, @@ -95,18 +95,21 @@ export function deleteTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv buckets: [ { key: 'visualization', doc_count: 3 }, { key: 'dashboard', doc_count: 1 }, - { key: 'index-pattern', doc_count: 1 }, + { key: 'index-pattern', doc_count: 6 }, // no legacy url alias objects exist in space_1 ], }, }, { - doc_count: 2, + doc_count: 3, key: 'other_space', countByType: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, - buckets: [{ key: 'legacy-url-alias', doc_count: 2 }], // aliases (3) + buckets: [ + { key: 'legacy-url-alias', doc_count: 2 }, + { key: 'index-pattern', doc_count: 1 }, + ], // aliases (3) }, }, ]; From e6b456359bc77e1a8d31beee5f42f00172631e35 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Mon, 2 Dec 2024 17:10:26 +0100 Subject: [PATCH 12/38] Tests --- .../fixtures/kbn_archiver/default_space.json | 40 +++++----- .../common/fixtures/kbn_archiver/space_1.json | 12 +-- .../common/fixtures/kbn_archiver/space_2.json | 14 ++-- .../common/suites/delete.agnostic.ts | 75 +++++++++++++++---- ...esolve_copy_to_space_conflicts.agnostic.ts | 8 +- .../security_and_spaces/apis/index.ts | 8 +- 6 files changed, 102 insertions(+), 55 deletions(-) diff --git a/x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/default_space.json b/x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/default_space.json index 9179a846066f1..1822fac324bde 100644 --- a/x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/default_space.json +++ b/x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/default_space.json @@ -139,7 +139,7 @@ "id": "conflict_2_default", "originId": "conflict_2", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzUxMCwxXQ==" } @@ -151,7 +151,7 @@ "id": "conflict_2_all", "originId": "conflict_2", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzUxMSwxXQ==" } @@ -163,7 +163,7 @@ "id": "conflict_1b_default", "originId": "conflict_1b_space_2", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzUwMywxXQ==" } @@ -175,7 +175,7 @@ "id": "conflict_1a_default", "originId": "conflict_1a", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzUwMCwxXQ==" } @@ -186,7 +186,7 @@ }, "id": "my_isolated_object", "references": [], - "type": "isolatedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:49:16.270Z", "version": "WzQ4NywxXQ==" } @@ -197,7 +197,7 @@ }, "id": "all_spaces", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzQ5NywxXQ==" } @@ -211,10 +211,10 @@ { "id": "each_space", "name": "refname", - "type": "sharedtype" + "type": "index-pattern" } ], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzQ4OCwxXQ==" } @@ -228,25 +228,25 @@ { "id": "default_only", "name": "refname", - "type": "sharedtype" + "type": "index-pattern" }, { "id": "space_1_only", "name": "refname", - "type": "sharedtype" + "type": "index-pattern" }, { "id": "space_2_only", "name": "refname", - "type": "sharedtype" + "type": "index-pattern" }, { "id": "all_spaces", "name": "refname", - "type": "sharedtype" + "type": "index-pattern" } ], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzQ5NiwxXQ==" } @@ -257,7 +257,7 @@ }, "id": "conflict_1c_default_and_space_1", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzUwNiwxXQ==" } @@ -268,7 +268,7 @@ }, "id": "default_and_space_1", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzQ5MywxXQ==" } @@ -279,7 +279,7 @@ }, "id": "default_and_space_2", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzQ5NCwxXQ==" } @@ -291,7 +291,7 @@ "id": "space_2_only_matching_origin", "originId": "space_2_only", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzQ5MiwxXQ==" } @@ -302,7 +302,7 @@ }, "id": "alias_delete_inclusive", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzQ5OCwxXQ==" } @@ -313,7 +313,7 @@ }, "id": "alias_delete_exclusive", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzQ5OSwxXQ==" } @@ -324,7 +324,7 @@ }, "id": "space_1_and_space_2", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzQ5NSwxXQ==" } diff --git a/x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/space_1.json b/x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/space_1.json index d037b9d1bd24c..06884f70a81d5 100644 --- a/x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/space_1.json +++ b/x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/space_1.json @@ -139,7 +139,7 @@ "id": "conflict_2_space_1", "originId": "conflict_2", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzUxMCwxXQ==" } @@ -151,7 +151,7 @@ "id": "conflict_1b_space_1", "originId": "conflict_1b_space_2", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzUwNCwxXQ==" } @@ -163,7 +163,7 @@ "id": "conflict_1a_space_1", "originId": "conflict_1a", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzUwMSwxXQ==" } @@ -177,10 +177,10 @@ { "id": "each_space", "name": "refname", - "type": "sharedtype" + "type": "index-pattern" } ], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzQ4OSwxXQ==" } @@ -191,7 +191,7 @@ }, "id": "my_isolated_object", "references": [], - "type": "isolatedtype", + "type": "url", "updated_at": "2017-09-21T18:49:16.270Z", "version": "WzQ4NywxXQ==" } diff --git a/x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/space_2.json b/x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/space_2.json index c68269eb1b335..5e60123795f7d 100644 --- a/x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/space_2.json +++ b/x-pack/test/spaces_api_integration/common/fixtures/kbn_archiver/space_2.json @@ -5,7 +5,7 @@ "id": "conflict_2_space_2", "originId": "conflict_2", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzUxMCwxXQ==" } @@ -17,7 +17,7 @@ "id": "conflict_1c_space_2", "originId": "conflict_1c_default_and_space_1", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzUwNywxXQ==" } @@ -28,7 +28,7 @@ }, "id": "conflict_1b_space_2", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzUwNSwxXQ==" } @@ -40,7 +40,7 @@ "id": "conflict_1a_space_2", "originId": "conflict_1a", "references": [], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzUwMiwxXQ==" } @@ -54,10 +54,10 @@ { "id": "each_space", "name": "refname", - "type": "sharedtype" + "type": "index-pattern" } ], - "type": "sharedtype", + "type": "index-pattern", "updated_at": "2017-09-21T18:59:16.270Z", "version": "WzQ5MSwxXQ==" } @@ -68,7 +68,7 @@ }, "id": "my_isolated_object", "references": [], - "type": "isolatedtype", + "type": "url", "updated_at": "2017-09-21T18:49:16.270Z", "version": "WzQ4NywxXQ==" } diff --git a/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts index d1f2a7aa236d9..87fbfa5722de4 100644 --- a/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts @@ -78,38 +78,81 @@ export function deleteTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [ - { key: 'space', doc_count: 3 }, // since space objects are namespace-agnostic, they appear in the "default" agg bucket - { key: 'visualization', doc_count: 3 }, - { key: 'legacy-url-alias', doc_count: 2 }, // aliases (1) - { key: 'dashboard', doc_count: 1 }, - { key: 'index-pattern', doc_count: 11 }, + { + key: 'index-pattern', + doc_count: 11, + }, + { + key: 'space', + doc_count: 3, + }, + { + key: 'visualization', + doc_count: 3, + }, + { + key: 'legacy-url-alias', + doc_count: 2, + }, + { + key: 'dashboard', + doc_count: 1, + }, ], }, }, { - doc_count: 10, key: 'space_1', + doc_count: 10, countByType: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [ - { key: 'visualization', doc_count: 3 }, - { key: 'dashboard', doc_count: 1 }, - { key: 'index-pattern', doc_count: 6 }, - // no legacy url alias objects exist in space_1 + { + key: 'index-pattern', + doc_count: 6, + }, + { + key: 'visualization', + doc_count: 3, + }, + { + key: 'dashboard', + doc_count: 1, + }, ], }, }, { + key: '*', doc_count: 3, + countByType: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'index-pattern', + doc_count: 3, + }, + ], + }, + }, + { key: 'other_space', + doc_count: 3, countByType: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [ - { key: 'legacy-url-alias', doc_count: 2 }, - { key: 'index-pattern', doc_count: 1 }, - ], // aliases (3) + { + key: 'legacy-url-alias', + doc_count: 2, + }, + { + key: 'index-pattern', + doc_count: 1, + }, + ], }, }, ]; @@ -122,11 +165,11 @@ export function deleteTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv const multiNamespaceResponse = await es.search>({ index: ALL_SAVED_OBJECT_INDICES, size: 100, - body: { query: { terms: { type: ['sharedtype'] } } }, + body: { query: { terms: { type: ['index-pattern'] } } }, }); const docs = multiNamespaceResponse.hits.hits; - // Just 19 results, since spaces_2_only, conflict_1a_space_2, conflict_1b_space_2, conflict_1c_space_2, and conflict_2_space_2 got deleted. - expect(docs).length(19); + // Just 21 results, since spaces_2_only, conflict_1a_space_2, conflict_1b_space_2, conflict_1c_space_2, and conflict_2_space_2 got deleted. + expect(docs).length(21); docs.forEach((doc) => () => { const containsSpace2 = doc?._source?.namespaces.includes('space_2'); expect(containsSpace2).to.eql(false); diff --git a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts index dc6c93324ca0e..2958620d63a16 100644 --- a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts @@ -343,7 +343,7 @@ export function resolveCopyToSpaceConflictsSuite(context: DeploymentAgnosticFtrP // the status code of the HTTP response differs depending on the error type // a 403 error actually comes back as an HTTP 200 response const statusCode = outcome === 'noAccess' ? 403 : 200; - const type = 'sharedtype'; + const type = 'index-pattern'; const exactMatchId = 'each_space'; const inexactMatchIdA = `conflict_1a_${spaceId}`; const inexactMatchIdB = `conflict_1b_${spaceId}`; @@ -360,7 +360,11 @@ export function resolveCopyToSpaceConflictsSuite(context: DeploymentAgnosticFtrP success: false, successCount: 0, errors: [ - { statusCode: 403, error: 'Forbidden', message: `Unable to bulk_create sharedtype` }, + { + statusCode: 403, + error: 'Forbidden', + message: `Unable to bulk_create index-pattern`, + }, ], }, }); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts index 90395f6c1f1de..ef9a70fd961a1 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts @@ -22,9 +22,9 @@ export default function ({ loadTestFile, getService }: DeploymentAgnosticFtrProv }); // loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); loadTestFile(require.resolve('./create')); - // loadTestFile(require.resolve('./delete')); - // loadTestFile(require.resolve('./get_all')); - // loadTestFile(require.resolve('./get')); - // loadTestFile(require.resolve('./update')); + loadTestFile(require.resolve('./delete')); + loadTestFile(require.resolve('./get_all')); + loadTestFile(require.resolve('./get')); + loadTestFile(require.resolve('./update')); }); } From 0db0033d6b358b687839f6b9ca1744799ffb57ac Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Mon, 23 Dec 2024 22:16:44 +0100 Subject: [PATCH 13/38] wip --- .../default_configs/stateful.config.base.ts | 10 ++- .../fixtures/kbn_archiver/default_space.json | 40 +++++------ .../common/fixtures/kbn_archiver/space_1.json | 10 +-- .../common/fixtures/kbn_archiver/space_2.json | 12 ++-- .../common/suites/copy_to_space.agnostic.ts | 66 ++++++++++--------- .../common/suites/create.agnostic.ts | 4 +- .../common/suites/get.agnostic.ts | 4 +- .../security_and_spaces/apis/index.ts | 8 +-- .../stateful.common.config.ts | 7 ++ 9 files changed, 87 insertions(+), 74 deletions(-) diff --git a/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts b/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts index 951adbc34a395..51be28e97f0a2 100644 --- a/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts +++ b/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts @@ -39,10 +39,10 @@ export function createStatefulTestConfig { if (options.esServerArgs || options.kbnServerArgs) { - // throw new Error( - // `FTR doesn't provision custom ES/Kibana server arguments into the ESS deployment. - // It may lead to unexpected test failures on Cloud. Please contact #appex-qa.` - // ); + throw new Error( + `FTR doesn't provision custom ES/Kibana server arguments into the ESS deployment. + It may lead to unexpected test failures on Cloud. Please contact #appex-qa.` + ); } // if config is executed on CI or locally @@ -118,7 +118,6 @@ export function createStatefulTestConfig { - const spaces = ['space_2']; - const includeReferences = false; - describe(`multi-namespace types with overwrite=${overwrite} and createNewCopies=${createNewCopies}`, () => { - before(async () => await testDataLoader.createFtrSavedObjectsData(SPACE_DATA_TO_LOAD)); - after(async () => await testDataLoader.deleteFtrSavedObjectsData()); - - const testCases = tests.multiNamespaceTestCases(overwrite, createNewCopies); - testCases.forEach(({ testTitle, objects, statusCode, response }) => { - it(`should return ${statusCode} when ${testTitle}`, async () => { - return supertest - .post(`${getUrlPrefix(spaceId)}/api/spaces/_copy_saved_objects`) - .send({ objects, spaces, includeReferences, createNewCopies, overwrite }) - .expect(statusCode) - .then(response); - }); - }); - }); - }); + // [ + // [false, false], + // [false, true], // createNewCopies enabled + // [true, false], // overwrite enabled + // // we don't specify tese cases with both overwrite and createNewCopies enabled, since overwrite won't matter in that scenario + // ].forEach(([overwrite, createNewCopies]) => { + // const spaces = ['space_2']; + // const includeReferences = false; + // describe(`multi-namespace types with overwrite=${overwrite} and createNewCopies=${createNewCopies}`, () => { + // before(async () => await testDataLoader.createFtrSavedObjectsData(SPACE_DATA_TO_LOAD)); + // after(async () => await testDataLoader.deleteFtrSavedObjectsData()); + + // const testCases = tests.multiNamespaceTestCases(overwrite, createNewCopies); + // testCases.forEach(({ testTitle, objects, statusCode, response }) => { + // it(`should return ${statusCode} when ${testTitle}`, async () => { + // return supertest + // .post(`${getUrlPrefix(spaceId)}/api/spaces/_copy_saved_objects`) + // .send({ objects, spaces, includeReferences, createNewCopies, overwrite }) + // .expect(statusCode) + // .then(response); + // }); + // }); + // }); + // }); }); }; diff --git a/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts index 7e99431fd2a44..9e300b40eecd1 100644 --- a/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts @@ -7,7 +7,7 @@ import type { Agent as SuperTestAgent } from 'supertest'; -import expect from '@kbn/expect'; +import expect from '@kbn/expect/expect'; import type { DeploymentAgnosticFtrProviderContext, @@ -91,9 +91,11 @@ export function createTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv 'inventory', 'logs', 'observabilityCases', + 'observabilityCasesV2', 'securitySolutionAssistant', 'securitySolutionAttackDiscovery', 'securitySolutionCases', + 'securitySolutionCasesV2', 'siem', 'slo', 'uptime', diff --git a/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts index b4630e750e55c..a0a5f1eeb553a 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts @@ -7,7 +7,7 @@ import type { Agent as SuperTestAgent } from 'supertest'; -import expect from '@kbn/expect'; +import expect from '@kbn/expect/expect'; import { getSupertest, maybeDestroySupertest } from './common'; import type { @@ -91,9 +91,11 @@ export function getTestSuiteFactory(context: DeploymentAgnosticFtrProviderContex 'inventory', 'logs', 'observabilityCases', + 'observabilityCasesV2', 'securitySolutionAssistant', 'securitySolutionAttackDiscovery', 'securitySolutionCases', + 'securitySolutionCasesV2', 'siem', 'slo', 'uptime', diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts index ef9a70fd961a1..c1911110409c7 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts @@ -21,10 +21,10 @@ export default function ({ loadTestFile, getService }: DeploymentAgnosticFtrProv } }); // loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); - loadTestFile(require.resolve('./create')); - loadTestFile(require.resolve('./delete')); - loadTestFile(require.resolve('./get_all')); + // loadTestFile(require.resolve('./create')); + // loadTestFile(require.resolve('./delete')); + // loadTestFile(require.resolve('./get_all')); loadTestFile(require.resolve('./get')); - loadTestFile(require.resolve('./update')); + // loadTestFile(require.resolve('./update')); }); } diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts index 6f03cf33d2eaf..67bcdbdaea7ea 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts @@ -65,6 +65,13 @@ export function createTestConfig({ // `--plugin-path=${path.resolve(__dirname, '../common/plugins/spaces_test_plugin')}`, ], }, + esTestCluster: { + ...testConfig.esTestCluster, + serverArgs: [ + ...testConfig.esTestCluster.serverArgs, + `xpack.license.self_generated.type=${license}`, + ], + }, }; }; } From c771a11d30433c84c41c8a317d0a5d85af21d908 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Fri, 27 Dec 2024 13:54:52 +0100 Subject: [PATCH 14/38] wip --- .../common/suites/create.agnostic.ts | 32 +-- .../common/suites/create.ts | 201 ----------------- .../common/suites/delete.agnostic.ts | 30 +-- .../common/suites/get.agnostic.ts | 12 +- .../common/suites/get.ts | 152 ------------- .../common/suites/get_all.agnostic.ts | 13 +- .../common/suites/get_all.ts | 207 ------------------ ...esolve_copy_to_space_conflicts.agnostic.ts | 9 +- .../common/suites/update.agnostic.ts | 12 +- .../ftr_provider_context.d.ts | 2 +- .../security_and_spaces/apis/index.ts | 10 +- .../services/basic_auth_supertest.ts | 54 +++++ .../deployment_agnostic/services/index.ts | 4 + .../services/role_scoped_supertest.ts | 174 +++++++++++++++ 14 files changed, 275 insertions(+), 637 deletions(-) delete mode 100644 x-pack/test/spaces_api_integration/common/suites/create.ts delete mode 100644 x-pack/test/spaces_api_integration/common/suites/get.ts delete mode 100644 x-pack/test/spaces_api_integration/common/suites/get_all.ts create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts diff --git a/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts index 9e300b40eecd1..255fa5e07b950 100644 --- a/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts @@ -5,15 +5,12 @@ * 2.0. */ -import type { Agent as SuperTestAgent } from 'supertest'; - -import expect from '@kbn/expect/expect'; +import expect from '@kbn/expect'; import type { DeploymentAgnosticFtrProviderContext, SupertestWithRoleScopeType, } from '../../deployment_agnostic/ftr_provider_context'; -import { getRoleDefinitionForUser, isBuiltInRole } from '../lib/authentication'; import { getTestScenariosForSpace } from '../lib/space_test_utils'; import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; @@ -38,8 +35,6 @@ interface CreateTestDefinition { export function createTestSuiteFactory({ getService }: DeploymentAgnosticFtrProviderContext) { const esArchiver = getService('esArchiver'); const roleScopedSupertest = getService('roleScopedSupertest'); - const samlAuth = getService('samlAuth'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); const expectConflictResponse = (resp: { [key: string]: any }) => { expect(resp.body).to.only.have.keys(['error', 'message', 'statusCode']); @@ -96,7 +91,10 @@ export function createTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv 'securitySolutionAttackDiscovery', 'securitySolutionCases', 'securitySolutionCasesV2', + 'securitySolutionNotes', + 'securitySolutionTimeline', 'siem', + 'siemV2', 'slo', 'uptime', ], @@ -110,30 +108,14 @@ export function createTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv (describeFn: DescribeFn) => (description: string, { user, spaceId, tests }: CreateTestDefinition) => { describeFn(description, () => { - let supertest: SupertestWithRoleScopeType | SuperTestAgent; + let supertest: SupertestWithRoleScopeType; before(async () => { - if (user) { - const isBuiltIn = isBuiltInRole(user.role); - if (!isBuiltIn) { - await samlAuth.setCustomRole(getRoleDefinitionForUser(user)); - } - supertest = await roleScopedSupertest.getSupertestWithRoleScope( - isBuiltIn ? user.role : 'customRole', - { - useCookieHeader: true, - withInternalHeaders: true, - } - ); - } else { - supertest = supertestWithoutAuth; - } + supertest = await roleScopedSupertest.getSupertestWithRoleScope(user!); }); after(async () => { - if (user) { - await (supertest as SupertestWithRoleScopeType).destroy(); - } + await supertest.destroy(); }); beforeEach(() => diff --git a/x-pack/test/spaces_api_integration/common/suites/create.ts b/x-pack/test/spaces_api_integration/common/suites/create.ts deleted file mode 100644 index bc6bdd21a549a..0000000000000 --- a/x-pack/test/spaces_api_integration/common/suites/create.ts +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { SuperTest } from 'supertest'; - -import expect from '@kbn/expect'; - -import { getTestScenariosForSpace } from '../lib/space_test_utils'; -import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; - -interface CreateTest { - statusCode: number; - response: (resp: { [key: string]: any }) => void; -} - -interface CreateTests { - newSpace: CreateTest; - alreadyExists: CreateTest; - reservedSpecified: CreateTest; - solutionSpecified: CreateTest; -} - -interface CreateTestDefinition { - user: TestDefinitionAuthentication; - spaceId: string; - tests: CreateTests; -} - -export function createTestSuiteFactory(esArchiver: any, supertest: SuperTest) { - const expectConflictResponse = (resp: { [key: string]: any }) => { - expect(resp.body).to.only.have.keys(['error', 'message', 'statusCode']); - expect(resp.body.error).to.equal('Conflict'); - expect(resp.body.statusCode).to.equal(409); - expect(resp.body.message).to.match(new RegExp(`A space with the identifier .*`)); - }; - - const expectNewSpaceResult = (resp: { [key: string]: any }) => { - expect(resp.body).to.eql({ - name: 'marketing', - id: 'marketing', - description: 'a description', - color: '#5c5959', - disabledFeatures: [], - }); - }; - - const expectRbacForbiddenResponse = (resp: { [key: string]: any }) => { - expect(resp.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to create spaces', - }); - }; - - const expectReservedSpecifiedResult = (resp: { [key: string]: any }) => { - expect(resp.body).to.eql({ - name: 'reserved space', - id: 'reserved', - description: 'a description', - color: '#5c5959', - disabledFeatures: [], - }); - }; - - const expectSolutionSpecifiedResult = (resp: Record) => { - const disabledFeatures = resp.body.disabledFeatures.sort(); - - const expected = { - id: 'solution', - name: 'space with solution', - description: 'a description', - color: '#5c5959', - disabledFeatures: [ - // Disabled features are automatically added to the space when a solution is set - 'apm', - 'infrastructure', - 'inventory', - 'logs', - 'observabilityCases', - 'observabilityCasesV2', - 'securitySolutionAssistant', - 'securitySolutionAttackDiscovery', - 'securitySolutionCases', - 'securitySolutionCasesV2', - 'securitySolutionNotes', - 'securitySolutionTimeline', - 'siem', - 'siemV2', - 'slo', - 'uptime', - ], - solution: 'es', - }; - - expect({ ...resp.body, disabledFeatures }).to.eql(expected); - }; - - const makeCreateTest = - (describeFn: DescribeFn) => - (description: string, { user, spaceId, tests }: CreateTestDefinition) => { - describeFn(description, () => { - beforeEach(() => - esArchiver.load( - 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' - ) - ); - afterEach(() => - esArchiver.unload( - 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' - ) - ); - - getTestScenariosForSpace(spaceId).forEach(({ urlPrefix, scenario }) => { - it(`should return ${tests.newSpace.statusCode} ${scenario}`, async () => { - return supertest - .post(`${urlPrefix}/api/spaces/space`) - .auth(user.username, user.password) - .send({ - name: 'marketing', - id: 'marketing', - description: 'a description', - color: '#5c5959', - disabledFeatures: [], - }) - .expect(tests.newSpace.statusCode) - .then(tests.newSpace.response); - }); - - describe('when it already exists', () => { - it(`should return ${tests.alreadyExists.statusCode} ${scenario}`, async () => { - return supertest - .post(`${urlPrefix}/api/spaces/space`) - .auth(user.username, user.password) - .send({ - name: 'space_1', - id: 'space_1', - color: '#ffffff', - description: 'a description', - disabledFeatures: [], - }) - .expect(tests.alreadyExists.statusCode) - .then(tests.alreadyExists.response); - }); - }); - - describe('when _reserved is specified', () => { - it(`should return ${tests.reservedSpecified.statusCode} and ignore _reserved ${scenario}`, async () => { - return supertest - .post(`${urlPrefix}/api/spaces/space`) - .auth(user.username, user.password) - .send({ - name: 'reserved space', - id: 'reserved', - description: 'a description', - color: '#5c5959', - _reserved: true, - disabledFeatures: [], - }) - .expect(tests.reservedSpecified.statusCode) - .then(tests.reservedSpecified.response); - }); - }); - - describe('when solution is specified', () => { - it(`should return ${tests.solutionSpecified.statusCode}`, async () => { - return supertest - .post(`${urlPrefix}/api/spaces/space`) - .auth(user.username, user.password) - .send({ - name: 'space with solution', - id: 'solution', - description: 'a description', - color: '#5c5959', - solution: 'es', - disabledFeatures: [], - }) - .expect(tests.solutionSpecified.statusCode) - .then(tests.solutionSpecified.response); - }); - }); - }); - }); - }; - - const createTest = makeCreateTest(describe); - // @ts-ignore - createTest.only = makeCreateTest(describe.only); - - return { - createTest, - expectConflictResponse, - expectNewSpaceResult, - expectRbacForbiddenResponse, - expectReservedSpecifiedResult, - expectSolutionSpecifiedResult, - }; -} diff --git a/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts index 87fbfa5722de4..4f591742fbb2a 100644 --- a/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts @@ -4,16 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { Agent as SuperTestAgent } from 'supertest'; import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; -import expect from '@kbn/expect'; +import expect from '@kbn/expect/expect'; import type { DeploymentAgnosticFtrProviderContext, SupertestWithRoleScopeType, } from '../../deployment_agnostic/ftr_provider_context'; -import { getRoleDefinitionForUser, isBuiltInRole } from '../lib/authentication'; import { MULTI_NAMESPACE_SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { getAggregatedSpaceData, getTestScenariosForSpace } from '../lib/space_test_utils'; import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; @@ -36,11 +34,9 @@ interface DeleteTestDefinition { } export function deleteTestSuiteFactory({ getService }: DeploymentAgnosticFtrProviderContext) { - const roleScopedSupertest = getService('roleScopedSupertest'); - const samlAuth = getService('samlAuth'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); const esArchiver = getService('esArchiver'); const es = getService('es'); + const roleScopedSupertest = getService('roleScopedSupertest'); const createExpectResult = (expectedResult: any) => (resp: { [key: string]: any }) => { expect(resp.body).to.eql(expectedResult); @@ -206,29 +202,13 @@ export function deleteTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv (describeFn: DescribeFn) => (description: string, { user, spaceId, tests }: DeleteTestDefinition) => { describeFn(description, () => { - let supertest: SupertestWithRoleScopeType | SuperTestAgent; + let supertest: SupertestWithRoleScopeType; before(async () => { - if (user) { - const isBuiltIn = isBuiltInRole(user.role); - if (!isBuiltIn) { - await samlAuth.setCustomRole(getRoleDefinitionForUser(user)); - } - supertest = await roleScopedSupertest.getSupertestWithRoleScope( - isBuiltIn ? user.role : 'customRole', - { - useCookieHeader: true, - withInternalHeaders: true, - } - ); - } else { - supertest = supertestWithoutAuth; - } + supertest = await roleScopedSupertest.getSupertestWithRoleScope(user!); }); after(async () => { - if (user) { - await (supertest as SupertestWithRoleScopeType).destroy(); - } + await supertest.destroy(); }); beforeEach(async () => { diff --git a/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts index a0a5f1eeb553a..6edb4fbb7514b 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts @@ -7,7 +7,7 @@ import type { Agent as SuperTestAgent } from 'supertest'; -import expect from '@kbn/expect/expect'; +import expect from '@kbn/expect'; import { getSupertest, maybeDestroySupertest } from './common'; import type { @@ -96,7 +96,10 @@ export function getTestSuiteFactory(context: DeploymentAgnosticFtrProviderContex 'securitySolutionAttackDiscovery', 'securitySolutionCases', 'securitySolutionCasesV2', + 'securitySolutionNotes', + 'securitySolutionTimeline', 'siem', + 'siemV2', 'slo', 'uptime', ], @@ -117,16 +120,17 @@ export function getTestSuiteFactory(context: DeploymentAgnosticFtrProviderContex (describeFn: DescribeFn) => (description: string, { user, currentSpaceId, spaceId, tests }: GetTestDefinition) => { describeFn(description, () => { - let supertest: SupertestWithRoleScopeType | SuperTestAgent; + const roleScopedSupertest = context.getService('roleScopedSupertest'); + let supertest: SupertestWithRoleScopeType; before(async () => { - supertest = await getSupertest(context, user); + supertest = await roleScopedSupertest.getSupertestWithRoleScope(user!); await esArchiver.load( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); }); after(async () => { - await maybeDestroySupertest(supertest); + await supertest.destroy(); await esArchiver.unload( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); diff --git a/x-pack/test/spaces_api_integration/common/suites/get.ts b/x-pack/test/spaces_api_integration/common/suites/get.ts deleted file mode 100644 index 3c3a81d171f7b..0000000000000 --- a/x-pack/test/spaces_api_integration/common/suites/get.ts +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { SuperAgent } from 'superagent'; - -import expect from '@kbn/expect'; - -import { getTestScenariosForSpace } from '../lib/space_test_utils'; -import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; - -interface GetTest { - statusCode: number; - response: (resp: { [key: string]: any }) => void; -} - -interface GetTests { - default: GetTest; -} - -interface GetTestDefinition { - user?: TestDefinitionAuthentication; - currentSpaceId: string; - spaceId: string; - tests: GetTests; -} - -const nonExistantSpaceId = 'not-a-space'; - -export function getTestSuiteFactory(esArchiver: any, supertest: SuperAgent) { - const createExpectEmptyResult = () => (resp: { [key: string]: any }) => { - expect(resp.body).to.eql(''); - }; - - const createExpectNotFoundResult = () => (resp: { [key: string]: any }) => { - expect(resp.body).to.eql({ - error: 'Not Found', - message: 'Not Found', - statusCode: 404, - }); - }; - - const createExpectRbacForbidden = (spaceId: string) => (resp: { [key: string]: any }) => { - expect(resp.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: `Unauthorized to get ${spaceId} space`, - }); - }; - - const createExpectResults = (spaceId: string) => (resp: { [key: string]: any }) => { - const allSpaces = [ - { - id: 'default', - name: 'Default', - color: '#00bfb3', - description: 'This is your default space!', - _reserved: true, - disabledFeatures: [], - }, - { - id: 'space_1', - name: 'Space 1', - description: 'This is the first test space', - disabledFeatures: [], - }, - { - id: 'space_2', - name: 'Space 2', - description: 'This is the second test space', - disabledFeatures: [], - }, - { - id: 'space_3', - name: 'Space 3', - description: 'This is the third test space', - solution: 'es', - disabledFeatures: [ - // Disabled features are automatically added to the space when a solution is set - 'apm', - 'infrastructure', - 'inventory', - 'logs', - 'observabilityCases', - 'observabilityCasesV2', - 'securitySolutionAssistant', - 'securitySolutionAttackDiscovery', - 'securitySolutionCases', - 'securitySolutionCasesV2', - 'securitySolutionNotes', - 'securitySolutionTimeline', - 'siem', - 'siemV2', - 'slo', - 'uptime', - ], - }, - ]; - - const disabledFeatures = (resp.body.disabledFeatures ?? []).sort(); - - const expectedSpace = allSpaces.find((space) => space.id === spaceId); - if (expectedSpace) { - expectedSpace.disabledFeatures.sort(); - } - - expect({ ...resp.body, disabledFeatures }).to.eql(expectedSpace); - }; - - const makeGetTest = - (describeFn: DescribeFn) => - (description: string, { user = {}, currentSpaceId, spaceId, tests }: GetTestDefinition) => { - describeFn(description, () => { - before(() => - esArchiver.load( - 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' - ) - ); - after(() => - esArchiver.unload( - 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' - ) - ); - - getTestScenariosForSpace(currentSpaceId).forEach(({ urlPrefix, scenario }) => { - it(`should return ${tests.default.statusCode} ${scenario}`, async () => { - return supertest - .get(`${urlPrefix}/api/spaces/space/${spaceId}`) - .auth(user.username, user.password) - .expect(tests.default.statusCode) - .then(tests.default.response); - }); - }); - }); - }; - - const getTest = makeGetTest(describe); - // @ts-ignore - getTest.only = makeGetTest(describe); - - return { - createExpectResults, - createExpectRbacForbidden, - createExpectEmptyResult, - createExpectNotFoundResult, - getTest, - nonExistantSpaceId, - }; -} diff --git a/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts index 2e833b492289c..1d388f13d4328 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts @@ -5,11 +5,8 @@ * 2.0. */ -import type { Agent as SuperTestAgent } from 'supertest'; - import expect from '@kbn/expect'; -import { getSupertest, maybeDestroySupertest } from './common'; import type { DeploymentAgnosticFtrProviderContext, SupertestWithRoleScopeType, @@ -84,9 +81,11 @@ const ALL_SPACE_RESULTS: Space[] = [ 'inventory', 'logs', 'observabilityCases', + 'observabilityCasesV2', 'securitySolutionAssistant', 'securitySolutionAttackDiscovery', 'securitySolutionCases', + 'securitySolutionCasesV2', 'siem', 'slo', 'uptime', @@ -146,9 +145,10 @@ export function getAllTestSuiteFactory(context: DeploymentAgnosticFtrProviderCon (describeFn: DescribeFn) => (description: string, { user, spaceId, tests }: GetAllTestDefinition) => { describeFn(description, () => { - let supertest: SupertestWithRoleScopeType | SuperTestAgent; + const roleScopedSupertest = context.getService('roleScopedSupertest'); + let supertest: SupertestWithRoleScopeType; before(async () => { - supertest = await getSupertest(context, user); + supertest = await roleScopedSupertest.getSupertestWithRoleScope(user!); await esArchiver.load( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); @@ -157,7 +157,8 @@ export function getAllTestSuiteFactory(context: DeploymentAgnosticFtrProviderCon await esArchiver.unload( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); - await maybeDestroySupertest(supertest); + + await supertest.destroy(); }); getTestScenariosForSpace(spaceId).forEach(({ scenario, urlPrefix }) => { diff --git a/x-pack/test/spaces_api_integration/common/suites/get_all.ts b/x-pack/test/spaces_api_integration/common/suites/get_all.ts deleted file mode 100644 index a8517b43ec888..0000000000000 --- a/x-pack/test/spaces_api_integration/common/suites/get_all.ts +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { SuperTest } from 'supertest'; - -import expect from '@kbn/expect'; - -import { getTestScenariosForSpace } from '../lib/space_test_utils'; -import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; - -interface GetAllTest { - statusCode: number; - response: (resp: { [key: string]: any }) => void; -} - -interface GetAllTests { - exists: GetAllTest; - copySavedObjectsPurpose: GetAllTest; - shareSavedObjectsPurpose: GetAllTest; - includeAuthorizedPurposes: GetAllTest; -} - -interface GetAllTestDefinition { - user?: Omit; - spaceId: string; - tests: GetAllTests; -} - -interface AuthorizedPurposes { - any: boolean; - copySavedObjectsIntoSpace: boolean; - findSavedObjects: boolean; - shareSavedObjectsIntoSpace: boolean; -} - -interface Space { - id: string; - name: string; - color?: string; - description: string; - solution?: string; - _reserved?: boolean; - disabledFeatures: string[]; -} - -const ALL_SPACE_RESULTS: Space[] = [ - { - id: 'default', - name: 'Default', - color: '#00bfb3', - description: 'This is your default space!', - _reserved: true, - disabledFeatures: [], - }, - { - id: 'space_1', - name: 'Space 1', - description: 'This is the first test space', - disabledFeatures: [], - }, - { - id: 'space_2', - name: 'Space 2', - description: 'This is the second test space', - disabledFeatures: [], - }, - { - id: 'space_3', - name: 'Space 3', - description: 'This is the third test space', - solution: 'es', - disabledFeatures: [ - // Disabled features are automatically added to the space when a solution is set - 'apm', - 'infrastructure', - 'inventory', - 'logs', - 'observabilityCases', - 'observabilityCasesV2', - 'securitySolutionAssistant', - 'securitySolutionAttackDiscovery', - 'securitySolutionCases', - 'securitySolutionCasesV2', - 'securitySolutionNotes', - 'securitySolutionTimeline', - 'siem', - 'siemV2', - 'slo', - 'uptime', - ], - }, -]; - -const sortDisabledFeatures = (space: Space) => { - return { - ...space, - disabledFeatures: [...space.disabledFeatures].sort(), - }; -}; - -export function getAllTestSuiteFactory(esArchiver: any, supertest: SuperTest) { - const createExpectResults = - (...spaceIds: string[]) => - (resp: { [key: string]: any }) => { - const expectedBody = ALL_SPACE_RESULTS.filter((entry) => spaceIds.includes(entry.id)); - expect(resp.body.map(sortDisabledFeatures)).to.eql(expectedBody.map(sortDisabledFeatures)); - }; - - const createExpectAllPurposesResults = - (authorizedPurposes: AuthorizedPurposes, ...spaceIds: string[]) => - (resp: { [key: string]: any }) => { - const expectedBody = ALL_SPACE_RESULTS.filter((entry) => spaceIds.includes(entry.id)).map( - (x) => ({ ...x, authorizedPurposes }) - ); - expect(resp.body.map(sortDisabledFeatures)).to.eql(expectedBody.map(sortDisabledFeatures)); - }; - - const expectEmptyResult = (resp: { [key: string]: any }) => { - expect(resp.body).to.eql(''); - }; - - const expectRbacForbidden = (resp: { [key: string]: any }) => { - expect(resp.body).to.eql({ - error: 'Forbidden', - message: 'Forbidden', - statusCode: 403, - }); - }; - - const makeGetAllTest = - (describeFn: DescribeFn) => - (description: string, { user = {}, spaceId, tests }: GetAllTestDefinition) => { - describeFn(description, () => { - before(() => - esArchiver.load( - 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' - ) - ); - after(() => - esArchiver.unload( - 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' - ) - ); - - getTestScenariosForSpace(spaceId).forEach(({ scenario, urlPrefix }) => { - describe('undefined purpose', () => { - it(`should return ${tests.exists.statusCode} ${scenario}`, async () => { - return supertest - .get(`${urlPrefix}/api/spaces/space`) - .auth(user.username, user.password) - .expect(tests.exists.statusCode) - .then(tests.exists.response); - }); - }); - - describe('copySavedObjectsIntoSpace purpose', () => { - it(`should return ${tests.copySavedObjectsPurpose.statusCode} ${scenario}`, async () => { - return supertest - .get(`${urlPrefix}/api/spaces/space`) - .query({ purpose: 'copySavedObjectsIntoSpace' }) - .auth(user.username, user.password) - .expect(tests.copySavedObjectsPurpose.statusCode) - .then(tests.copySavedObjectsPurpose.response); - }); - }); - - describe('shareSavedObjectsIntoSpace purpose', () => { - it(`should return ${tests.shareSavedObjectsPurpose.statusCode} ${scenario}`, async () => { - return supertest - .get(`${urlPrefix}/api/spaces/space`) - .query({ purpose: 'shareSavedObjectsIntoSpace' }) - .auth(user.username, user.password) - .expect(tests.copySavedObjectsPurpose.statusCode) - .then(tests.copySavedObjectsPurpose.response); - }); - }); - - describe('include_authorized_purposes=true', () => { - it(`should return ${tests.includeAuthorizedPurposes.statusCode} ${scenario}`, async () => { - return supertest - .get(`${urlPrefix}/api/spaces/space`) - .query({ include_authorized_purposes: true }) - .auth(user.username, user.password) - .expect(tests.includeAuthorizedPurposes.statusCode) - .then(tests.includeAuthorizedPurposes.response); - }); - }); - }); - }); - }; - - const getAllTest = makeGetAllTest(describe); - // @ts-ignore - getAllTest.only = makeGetAllTest(describe.only); - - return { - createExpectResults, - createExpectAllPurposesResults, - expectRbacForbidden, - getAllTest, - expectEmptyResult, - }; -} diff --git a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts index 2958620d63a16..dae2959714e41 100644 --- a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts @@ -8,7 +8,7 @@ import type { Agent as SuperTestAgent } from 'supertest'; import type { SavedObject } from '@kbn/core/server'; -import expect from '@kbn/expect'; +import expect from '@kbn/expect/expect'; import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; import type { CopyResponse } from '@kbn/spaces-plugin/server/lib/copy_to_spaces'; @@ -76,6 +76,7 @@ const getDestinationSpace = (originSpaceId?: string) => { export function resolveCopyToSpaceConflictsSuite(context: DeploymentAgnosticFtrProviderContext) { const testDataLoader = getTestDataLoader(context); const supertestWithAuth = context.getService('supertest'); + const roleScopedSupertest = context.getService('roleScopedSupertest'); const getVisualizationAtSpace = async (spaceId: string): Promise> => { return supertestWithAuth @@ -517,15 +518,15 @@ export function resolveCopyToSpaceConflictsSuite(context: DeploymentAgnosticFtrP { user, spaceId = DEFAULT_SPACE_ID, tests }: ResolveCopyToSpaceTestDefinition ) => { describeFn(description, () => { - let supertest: SupertestWithRoleScopeType | SuperTestAgent; + let supertest: SupertestWithRoleScopeType; before(async () => { - supertest = await getSupertest(context, user); + supertest = await roleScopedSupertest.getSupertestWithRoleScope(user!); // test data only allows for the following spaces as the copy origin expect(['default', 'space_1']).to.contain(spaceId); }); after(async () => { - await maybeDestroySupertest(supertest); + await supertest.destroy(); }); describe('single-namespace types', () => { diff --git a/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts index 9d7446cebd397..6ab6c3eca60e7 100644 --- a/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts @@ -11,11 +11,8 @@ * 2.0. */ -import type { Agent as SuperTestAgent } from 'supertest'; +import expect from '@kbn/expect/expect'; -import expect from '@kbn/expect'; - -import { getSupertest, maybeDestroySupertest } from './common'; import type { DeploymentAgnosticFtrProviderContext, SupertestWithRoleScopeType, @@ -42,6 +39,7 @@ interface UpdateTestDefinition { export function updateTestSuiteFactory(context: DeploymentAgnosticFtrProviderContext) { const esArchiver = context.getService('esArchiver'); + const roleScopedSupertest = context.getService('roleScopedSupertest'); const expectRbacForbidden = (resp: { [key: string]: any }) => { expect(resp.body).to.eql({ @@ -84,15 +82,15 @@ export function updateTestSuiteFactory(context: DeploymentAgnosticFtrProviderCon (describeFn: DescribeFn) => (description: string, { user, spaceId, tests }: UpdateTestDefinition) => { describeFn(description, () => { - let supertest: SupertestWithRoleScopeType | SuperTestAgent; + let supertest: SupertestWithRoleScopeType; before(async () => { - supertest = await getSupertest(context, user); + supertest = await roleScopedSupertest.getSupertestWithRoleScope(user!); await esArchiver.load( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); }); after(async () => { - await maybeDestroySupertest(supertest); + await supertest.destroy(); await esArchiver.unload( 'x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces' ); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/ftr_provider_context.d.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/ftr_provider_context.d.ts index 3f5e391e0cb91..c8d0b14c5786f 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/ftr_provider_context.d.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/ftr_provider_context.d.ts @@ -10,4 +10,4 @@ import type { GenericFtrProviderContext } from '@kbn/test'; import type { services } from './services'; export type DeploymentAgnosticFtrProviderContext = GenericFtrProviderContext; -export type { SupertestWithRoleScopeType } from '../../api_integration/deployment_agnostic/services'; +export type { SupertestWithRoleScopeType } from './services'; diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts index c1911110409c7..7652834480220 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts @@ -21,10 +21,10 @@ export default function ({ loadTestFile, getService }: DeploymentAgnosticFtrProv } }); // loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); - // loadTestFile(require.resolve('./create')); - // loadTestFile(require.resolve('./delete')); - // loadTestFile(require.resolve('./get_all')); - loadTestFile(require.resolve('./get')); - // loadTestFile(require.resolve('./update')); + // loadTestFile(require.resolve('./create')); // PASS + loadTestFile(require.resolve('./delete')); + // loadTestFile(require.resolve('./get_all')); // PASS + // loadTestFile(require.resolve('./get')); // PASS + // loadTestFile(require.resolve('./update')); // PASS }); } diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts new file mode 100644 index 0000000000000..02410fa09da0d --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Test } from 'supertest'; + +import type { SupertestWithoutAuthProviderType } from '@kbn/ftr-common-functional-services'; + +import type { TestDefinitionAuthentication as User } from '../../common/lib/types'; + +export class SupertestWithBasicAuth { + private readonly supertestWithoutAuth: SupertestWithoutAuthProviderType; + private readonly user: User; + + constructor(supertestWithoutAuth: SupertestWithoutAuthProviderType, user: User) { + this.supertestWithoutAuth = supertestWithoutAuth; + this.user = user; + } + + async destroy() {} + + private request(method: 'post' | 'get' | 'put' | 'delete' | 'patch', url: string): Test { + const agent = this.supertestWithoutAuth[method](url); + + if (this.user) { + agent.auth(this.user.username!, this.user.password!); + } + + return agent; + } + + post(url: string) { + return this.request('post', url); + } + + get(url: string) { + return this.request('get', url); + } + + put(url: string) { + return this.request('put', url); + } + + delete(url: string) { + return this.request('delete', url); + } + + patch(url: string) { + return this.request('patch', url); + } +} diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/services/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/services/index.ts index 0923a6ae4d2df..b0166ffbd4ed8 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/services/index.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/services/index.ts @@ -5,12 +5,16 @@ * 2.0. */ +import { RoleScopedSupertestProvider } from './role_scoped_supertest'; import { services as deploymentAgnosticServices } from '../../../api_integration/deployment_agnostic/services'; import { services as apiIntegrationServices } from '../../../api_integration/services'; import { services as commonServices } from '../../../common/services'; +export type { SupertestWithRoleScopeType } from './role_scoped_supertest'; + export const services = { ...deploymentAgnosticServices, ...commonServices, usageAPI: apiIntegrationServices.usageAPI, + roleScopedSupertest: RoleScopedSupertestProvider, }; diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts new file mode 100644 index 0000000000000..49e16ca1b2b7a --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts @@ -0,0 +1,174 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Test } from 'supertest'; + +import type { + CookieCredentials, + RoleCredentials, + SamlAuthProviderType, + SupertestWithoutAuthProviderType, +} from '@kbn/ftr-common-functional-services'; + +import { SupertestWithBasicAuth } from './basic_auth_supertest'; +import { getRoleDefinitionForUser, isBuiltInRole } from '../../common/lib/authentication'; +import type { TestDefinitionAuthentication as User } from '../../common/lib/types'; +import type { DeploymentAgnosticFtrProviderContext } from '../ftr_provider_context'; + +export interface RequestHeadersOptions { + useCookieHeader?: boolean; + withInternalHeaders?: boolean; + withCommonHeaders?: boolean; + withCustomHeaders?: Record; +} + +export type SupertestWithRoleScopeType = SupertestWithBasicAuth | SupertestWithRoleScope; + +export class SupertestWithRoleScope { + private authValue: RoleCredentials | CookieCredentials | null; + private readonly supertestWithoutAuth: SupertestWithoutAuthProviderType; + private samlAuth: SamlAuthProviderType; + private readonly options: RequestHeadersOptions; + + constructor( + authValue: RoleCredentials | CookieCredentials | null, + supertestWithoutAuth: SupertestWithoutAuthProviderType, + samlAuth: SamlAuthProviderType, + options: RequestHeadersOptions + ) { + this.authValue = authValue; + this.supertestWithoutAuth = supertestWithoutAuth; + this.samlAuth = samlAuth; + this.options = options; + } + + private isRoleCredentials(value: any): value is RoleCredentials { + return value && typeof value === 'object' && 'apiKey' in value && 'apiKeyHeader' in value; + } + + async destroy() { + if (this.isRoleCredentials(this.authValue)) { + await this.samlAuth.invalidateM2mApiKeyWithRoleScope(this.authValue); + this.authValue = null; + } + } + + private addHeaders(agent: Test): Test { + const { useCookieHeader, withInternalHeaders, withCommonHeaders, withCustomHeaders } = + this.options; + + if (useCookieHeader) { + if (!this.authValue || !('Cookie' in this.authValue)) { + throw new Error('The instance has already been destroyed or cookieHeader is missing.'); + } + // set cookie header + void agent.set(this.authValue); + } else { + if (!this.authValue || !this.isRoleCredentials(this.authValue)) { + throw new Error('The instance has already been destroyed or roleAuthc is missing.'); + } + // set API key header + void agent.set(this.authValue.apiKeyHeader); + } + + if (withInternalHeaders) { + void agent.set(this.samlAuth.getInternalRequestHeader()); + } + + if (withCommonHeaders) { + void agent.set(this.samlAuth.getCommonRequestHeader()); + } + + if (withCustomHeaders) { + void agent.set(withCustomHeaders); + } + + return agent; + } + + private request(method: 'post' | 'get' | 'put' | 'delete' | 'patch', url: string): Test { + if (!this.authValue) { + throw new Error('Instance has been destroyed and cannot be used for making requests.'); + } + const agent = this.supertestWithoutAuth[method](url); + return this.addHeaders(agent); + } + + post(url: string) { + return this.request('post', url); + } + + get(url: string) { + return this.request('get', url); + } + + put(url: string) { + return this.request('put', url); + } + + delete(url: string) { + return this.request('delete', url); + } + + patch(url: string) { + return this.request('patch', url); + } +} + +/** + * Provides a customized 'supertest' instance that is authenticated using a role-based API key + * and enriched with the appropriate request headers. This service allows you to perform + * HTTP requests with specific authentication and header configurations, ensuring that + * the requests are scoped to the provided role and environment. + * + * Use this service to easily test API endpoints with role-specific authorization and + * custom headers, both in serverless and stateful environments. + * + * Pass '{ useCookieHeader: true }' to use Cookie header for authentication instead of API key. + * It is the correct way to perform HTTP requests for internal end-points. + */ +export function RoleScopedSupertestProvider({ getService }: DeploymentAgnosticFtrProviderContext) { + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const samlAuth = getService('samlAuth'); + const config = getService('config'); + + return { + async getSupertestWithRoleScope( + user: User, + options: RequestHeadersOptions = { + useCookieHeader: true, + withCommonHeaders: false, + withInternalHeaders: true, + } + ) { + const license = config.get('esTestCluster.license'); + + if (!user || license !== 'trial') { + return new SupertestWithBasicAuth(supertestWithoutAuth, user); + } + + const isBuiltIn = isBuiltInRole(user.role); + + if (!isBuiltIn) { + await samlAuth.setCustomRole(getRoleDefinitionForUser(user)); + } + + if (options.useCookieHeader) { + const cookieHeader = await samlAuth.getM2MApiCookieCredentialsWithRoleScope( + isBuiltIn ? user.role : 'customRole' + ); + return new SupertestWithRoleScope(cookieHeader, supertestWithoutAuth, samlAuth, options); + } + + // HTTP requests will be called with API key in header by default + const roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope( + isBuiltIn ? user.role : 'customRole' + ); + return new SupertestWithRoleScope(roleAuthc, supertestWithoutAuth, samlAuth, options); + }, + }; +} From 0e19cd1c8faff8ed181a9347577255f9f96c040f Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Fri, 27 Dec 2024 18:02:23 +0100 Subject: [PATCH 15/38] Test fixes --- .../spaces_api_integration/common/config.ts | 4 +- .../common/suites/common.ts | 72 ------ .../common/suites/copy_to_space.agnostic.ts | 79 ++++--- .../common/suites/delete.agnostic.ts | 2 +- .../common/suites/get.agnostic.ts | 3 - ...esolve_copy_to_space_conflicts.agnostic.ts | 17 +- .../common/suites/update.agnostic.ts | 2 +- .../security_and_spaces/apis/index.ts | 12 +- .../services/test_data_loader.ts | 209 ++++++++++++++++++ .../stateful.common.config.ts | 9 - 10 files changed, 272 insertions(+), 137 deletions(-) delete mode 100644 x-pack/test/spaces_api_integration/common/suites/common.ts create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/services/test_data_loader.ts diff --git a/x-pack/test/spaces_api_integration/common/config.ts b/x-pack/test/spaces_api_integration/common/config.ts index 0204fed730126..29efed8626663 100644 --- a/x-pack/test/spaces_api_integration/common/config.ts +++ b/x-pack/test/spaces_api_integration/common/config.ts @@ -10,6 +10,8 @@ import path from 'path'; import { REPO_ROOT } from '@kbn/repo-info'; import type { FtrConfigProviderContext } from '@kbn/test'; +import { RoleScopedSupertestProvider } from '../deployment_agnostic/services/role_scoped_supertest'; + interface CreateTestConfigOptions { license: string; disabledPlugins?: string[]; @@ -45,7 +47,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) kibanaServer: config.kibana.functional.get('services.kibanaServer'), spaces: config.xpack.api.get('services.spaces'), usageAPI: config.xpack.api.get('services.usageAPI'), - roleScopedSupertest: () => {}, + roleScopedSupertest: RoleScopedSupertestProvider, samlAuth: () => {}, }, junit: { diff --git a/x-pack/test/spaces_api_integration/common/suites/common.ts b/x-pack/test/spaces_api_integration/common/suites/common.ts deleted file mode 100644 index fc51528eed205..0000000000000 --- a/x-pack/test/spaces_api_integration/common/suites/common.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import type { Agent as SuperTestAgent } from 'supertest'; - -import type { - DeploymentAgnosticFtrProviderContext, - SupertestWithRoleScopeType, -} from '../../deployment_agnostic/ftr_provider_context'; -import { getRoleDefinitionForUser, isBuiltInRole } from '../lib/authentication'; -import type { TestDefinitionAuthentication } from '../lib/types'; - -export async function getSupertest( - { getService }: DeploymentAgnosticFtrProviderContext, - user?: TestDefinitionAuthentication -): Promise { - const roleScopedSupertest = getService('roleScopedSupertest'); - const samlAuth = getService('samlAuth'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - const config = getService('config'); - const license = config.get('esTestCluster.license'); - let scopedSupertest: SupertestWithRoleScopeType | SuperTestAgent; - - if (user && license === 'trial') { - const isBuiltIn = isBuiltInRole(user.role); - if (!isBuiltIn) { - await samlAuth.setCustomRole(getRoleDefinitionForUser(user)); - } - - scopedSupertest = await roleScopedSupertest.getSupertestWithRoleScope( - isBuiltIn ? user.role : 'customRole', - { - useCookieHeader: true, - withInternalHeaders: true, - } - ); - } else { - scopedSupertest = supertestWithoutAuth; - } - - // TODO: fix types - const wrapRequestWithAuth = (method: keyof SuperTestAgent) => { - return (...args: any[]) => { - const request = (scopedSupertest as any)[method](...args); - if (user && license === 'basic') { - return request.auth(user.username, user.password); - } - return request; - }; - }; - - return new Proxy(scopedSupertest, { - get(target, prop: string) { - if (['post', 'get', 'put', 'delete', 'patch', 'head'].includes(prop)) { - return wrapRequestWithAuth(prop as keyof SuperTestAgent); - } - return Reflect.get(target, prop); - }, - }); -} - -export async function maybeDestroySupertest( - supertest: SupertestWithRoleScopeType | SuperTestAgent -) { - // @ts-expect-error - if (typeof supertest.destroy === 'function') { - await (supertest as SupertestWithRoleScopeType).destroy(); - } -} diff --git a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts index f5ef4dc937a92..d28654dace40f 100644 --- a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts @@ -5,22 +5,24 @@ * 2.0. */ import type * as estypes from '@elastic/elasticsearch/lib/api/types'; -import type { Agent as SuperTestAgent } from 'supertest'; import type { SavedObjectsImportAmbiguousConflictError, SavedObjectsImportFailure, } from '@kbn/core/server'; -import expect from '@kbn/expect/expect'; +import expect from '@kbn/expect'; import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; import type { CopyResponse } from '@kbn/spaces-plugin/server/lib/copy_to_spaces'; -import { getSupertest, maybeDestroySupertest } from './common'; -import { getTestDataLoader, SPACE_1, SPACE_2 } from '../../../common/lib/test_data_loader'; import type { DeploymentAgnosticFtrProviderContext, SupertestWithRoleScopeType, } from '../../deployment_agnostic/ftr_provider_context'; +import { + getTestDataLoader, + SPACE_1, + SPACE_2, +} from '../../deployment_agnostic/services/test_data_loader'; import { getAggregatedSpaceData, getUrlPrefix } from '../lib/space_test_utils'; import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; @@ -106,6 +108,10 @@ interface Aggs extends estypes.AggregationsMultiBucketAggregateBase { export function copyToSpaceTestSuiteFactory(context: DeploymentAgnosticFtrProviderContext) { const testDataLoader = getTestDataLoader(context); const es = context.getService('es'); + const roleScopedSupertest = context.getService('roleScopedSupertest'); + const log = context.getService('log'); + const kbnServer = context.getService('kibanaServer'); + const spacesService = context.getService('spaces'); const collectSpaceContents = async () => { const response = await getAggregatedSpaceData(es, [ @@ -583,6 +589,7 @@ export function copyToSpaceTestSuiteFactory(context: DeploymentAgnosticFtrProvid // 'unauthorizedRead'), the copy attempt will proceed because they are not aware that the object already exists in the // destination space. In that case, they will encounter a 403 error. const { success, successCount, successResults, errors } = getResult(response); + const title = 'A shared saved-object in the default, space_1, and space_2 spaces'; if (createNewCopies) { expectNewCopyResponse(response, exactMatchId, title); @@ -805,25 +812,25 @@ export function copyToSpaceTestSuiteFactory(context: DeploymentAgnosticFtrProvid { user, spaceId = DEFAULT_SPACE_ID, tests }: CopyToSpaceTestDefinition ) => { describeFn(description, () => { - let supertest: SupertestWithRoleScopeType | SuperTestAgent; + let supertest: SupertestWithRoleScopeType; before(async () => { // test data only allows for the following spaces as the copy origin expect(['default', 'space_1']).to.contain(spaceId); await testDataLoader.createFtrSpaces(); - supertest = await getSupertest(context, user); + supertest = await roleScopedSupertest.getSupertestWithRoleScope(user!); }); after(async () => { await testDataLoader.deleteFtrSpaces(); - await maybeDestroySupertest(supertest); + await supertest.destroy(); }); - describe('single-namespace types', () => { - beforeEach(async () => { - await testDataLoader.createFtrSavedObjectsData(SPACE_DATA_TO_LOAD); - }); + describe.skip('single-namespace types', () => { + beforeEach( + async () => await testDataLoader.createFtrSavedObjectsData(SPACE_DATA_TO_LOAD) + ); - afterEach(async () => await testDataLoader.deleteFtrSavedObjectsData()); + afterEach(async () => await await testDataLoader.deleteFtrSavedObjectsData()); const dashboardObject = { type: 'dashboard', id: `cts_dashboard_${spaceId}` }; @@ -949,30 +956,30 @@ export function copyToSpaceTestSuiteFactory(context: DeploymentAgnosticFtrProvid }); }); - // [ - // [false, false], - // [false, true], // createNewCopies enabled - // [true, false], // overwrite enabled - // // we don't specify tese cases with both overwrite and createNewCopies enabled, since overwrite won't matter in that scenario - // ].forEach(([overwrite, createNewCopies]) => { - // const spaces = ['space_2']; - // const includeReferences = false; - // describe(`multi-namespace types with overwrite=${overwrite} and createNewCopies=${createNewCopies}`, () => { - // before(async () => await testDataLoader.createFtrSavedObjectsData(SPACE_DATA_TO_LOAD)); - // after(async () => await testDataLoader.deleteFtrSavedObjectsData()); - - // const testCases = tests.multiNamespaceTestCases(overwrite, createNewCopies); - // testCases.forEach(({ testTitle, objects, statusCode, response }) => { - // it(`should return ${statusCode} when ${testTitle}`, async () => { - // return supertest - // .post(`${getUrlPrefix(spaceId)}/api/spaces/_copy_saved_objects`) - // .send({ objects, spaces, includeReferences, createNewCopies, overwrite }) - // .expect(statusCode) - // .then(response); - // }); - // }); - // }); - // }); + [ + [false, false], + [false, true], // createNewCopies enabled + [true, false], // overwrite enabled + // we don't specify tese cases with both overwrite and createNewCopies enabled, since overwrite won't matter in that scenario + ].forEach(([overwrite, createNewCopies]) => { + const spaces = ['space_2']; + const includeReferences = false; + describe(`multi-namespace types with overwrite=${overwrite} and createNewCopies=${createNewCopies}`, () => { + before(async () => await testDataLoader.createFtrSavedObjectsData(SPACE_DATA_TO_LOAD)); + after(async () => await testDataLoader.deleteFtrSavedObjectsData()); + + const testCases = tests.multiNamespaceTestCases(overwrite, createNewCopies); + testCases.forEach(({ testTitle, objects, statusCode, response }) => { + it(`should return ${statusCode} when ${testTitle}`, async () => { + return supertest + .post(`${getUrlPrefix(spaceId)}/api/spaces/_copy_saved_objects`) + .send({ objects, spaces, includeReferences, createNewCopies, overwrite }) + .expect(statusCode) + .then(response); + }); + }); + }); + }); }); }; diff --git a/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts index 4f591742fbb2a..f7712ef170d41 100644 --- a/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts @@ -6,7 +6,7 @@ */ import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; -import expect from '@kbn/expect/expect'; +import expect from '@kbn/expect'; import type { DeploymentAgnosticFtrProviderContext, diff --git a/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts index 6edb4fbb7514b..4e36769deba99 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts @@ -5,11 +5,8 @@ * 2.0. */ -import type { Agent as SuperTestAgent } from 'supertest'; - import expect from '@kbn/expect'; -import { getSupertest, maybeDestroySupertest } from './common'; import type { DeploymentAgnosticFtrProviderContext, SupertestWithRoleScopeType, diff --git a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts index dae2959714e41..8f40f21331131 100644 --- a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts @@ -5,19 +5,20 @@ * 2.0. */ -import type { Agent as SuperTestAgent } from 'supertest'; - import type { SavedObject } from '@kbn/core/server'; -import expect from '@kbn/expect/expect'; +import expect from '@kbn/expect'; import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; import type { CopyResponse } from '@kbn/spaces-plugin/server/lib/copy_to_spaces'; -import { getSupertest, maybeDestroySupertest } from './common'; -import { getTestDataLoader, SPACE_1, SPACE_2 } from '../../../common/lib/test_data_loader'; import type { DeploymentAgnosticFtrProviderContext, SupertestWithRoleScopeType, } from '../../deployment_agnostic/ftr_provider_context'; +import { + getTestDataLoader, + SPACE_1, + SPACE_2, +} from '../../deployment_agnostic/services/test_data_loader'; import { getUrlPrefix } from '../lib/space_test_utils'; import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; @@ -344,7 +345,7 @@ export function resolveCopyToSpaceConflictsSuite(context: DeploymentAgnosticFtrP // the status code of the HTTP response differs depending on the error type // a 403 error actually comes back as an HTTP 200 response const statusCode = outcome === 'noAccess' ? 403 : 200; - const type = 'index-pattern'; + const type = 'event-annotation-group'; const exactMatchId = 'each_space'; const inexactMatchIdA = `conflict_1a_${spaceId}`; const inexactMatchIdB = `conflict_1b_${spaceId}`; @@ -364,7 +365,7 @@ export function resolveCopyToSpaceConflictsSuite(context: DeploymentAgnosticFtrP { statusCode: 403, error: 'Forbidden', - message: `Unable to bulk_create index-pattern`, + message: `Unable to bulk_create event-annotation-group`, }, ], }, @@ -393,7 +394,7 @@ export function resolveCopyToSpaceConflictsSuite(context: DeploymentAgnosticFtrP return 'A shared saved-object in one space'; } })(); - const meta = { title, icon: 'beaker' }; + const meta = { title, icon: 'flag' }; expect(successResults).to.eql([ { type, diff --git a/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts index 6ab6c3eca60e7..b3e1972912f23 100644 --- a/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/update.agnostic.ts @@ -11,7 +11,7 @@ * 2.0. */ -import expect from '@kbn/expect/expect'; +import expect from '@kbn/expect'; import type { DeploymentAgnosticFtrProviderContext, diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts index 7652834480220..71234644328af 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts @@ -20,11 +20,11 @@ export default function ({ loadTestFile, getService }: DeploymentAgnosticFtrProv await createUsersAndRoles(es, supertest); } }); - // loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); - // loadTestFile(require.resolve('./create')); // PASS - loadTestFile(require.resolve('./delete')); - // loadTestFile(require.resolve('./get_all')); // PASS - // loadTestFile(require.resolve('./get')); // PASS - // loadTestFile(require.resolve('./update')); // PASS + loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); // PASS + loadTestFile(require.resolve('./create')); // PASS + // loadTestFile(require.resolve('./delete')); + loadTestFile(require.resolve('./get_all')); // PASS + loadTestFile(require.resolve('./get')); // PASS + loadTestFile(require.resolve('./update')); // PASS }); } diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/services/test_data_loader.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/services/test_data_loader.ts new file mode 100644 index 0000000000000..d6478b2c1ff08 --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/services/test_data_loader.ts @@ -0,0 +1,209 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import Fs from 'fs/promises'; + +import type { LegacyUrlAlias } from '@kbn/core-saved-objects-base-server-internal'; +import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; + +import type { DeploymentAgnosticFtrProviderContext } from '../ftr_provider_context'; + +export const SPACE_1 = { + id: 'space_1', + name: 'Space 1', + description: 'This is the first test space', + disabledFeatures: [], +}; + +export const SPACE_2 = { + id: 'space_2', + name: 'Space 2', + description: 'This is the second test space', + disabledFeatures: [], +}; + +async function parseLegacyUrlAliases(path: string): Promise { + return (await Fs.readFile(path, 'utf-8')) + .split(/\r?\n\r?\n/) + .filter((line) => !!line) + .map((line) => JSON.parse(line)); +} + +// Objects can only be imported in one space at a time. To have test saved objects +// that are shared in multiple spaces we should import all objects in the "original" +// spaces first and then share them to other spaces as a subsequent operation. +const OBJECTS_TO_SHARE: Array<{ + spacesToAdd?: string[]; + spacesToRemove?: string[]; + objects: Array<{ type: string; id: string }>; +}> = [ + { + spacesToAdd: ['*'], + spacesToRemove: ['default'], + objects: [ + { type: 'event-annotation-group', id: 'all_spaces' }, + { type: 'event-annotation-group', id: 'space_2_only_matching_origin' }, + { type: 'event-annotation-group', id: 'alias_delete_exclusive' }, + ], + }, + { + spacesToRemove: ['default'], + spacesToAdd: [SPACE_1.id, SPACE_2.id], + objects: [{ type: 'event-annotation-group', id: 'space_1_and_space_2' }], + }, + { + spacesToAdd: [SPACE_1.id, SPACE_2.id], + objects: [ + { type: 'event-annotation-group', id: 'each_space' }, + { type: 'event-annotation-group', id: 'conflict_2_all' }, + { type: 'event-annotation-group', id: 'alias_delete_inclusive' }, + ], + }, + { + spacesToAdd: [SPACE_1.id], + objects: [ + { type: 'event-annotation-group', id: 'conflict_1c_default_and_space_1' }, + { type: 'event-annotation-group', id: 'default_and_space_1' }, + ], + }, + { + spacesToAdd: [SPACE_2.id], + objects: [{ type: 'event-annotation-group', id: 'default_and_space_2' }], + }, + { + spacesToAdd: [SPACE_1.id, SPACE_2.id], + objects: [{ type: 'resolvetype', id: 'conflict-newid' }], + }, +]; + +export function getTestDataLoader({ + getService, +}: Pick) { + const spacesService = getService('spaces'); + const kbnServer = getService('kibanaServer'); + const supertest = getService('supertest'); + const log = getService('log'); + const es = getService('es'); + + return { + createFtrSpaces: async () => { + await Promise.all([await spacesService.create(SPACE_1), await spacesService.create(SPACE_2)]); + }, + + deleteFtrSpaces: async () => { + await Promise.all([spacesService.delete(SPACE_1.id), spacesService.delete(SPACE_2.id)]); + }, + + createFtrSavedObjectsData: async ( + spaceData: Array<{ spaceName: string | null; dataUrl: string }> + ) => { + log.debug('Loading test data for the following spaces: default, space_1 and space_2'); + + await Promise.all( + spaceData.map((spaceDataObj) => { + if (spaceDataObj.spaceName) { + return kbnServer.importExport.load(spaceDataObj.dataUrl, { + space: spaceDataObj.spaceName, + }); + } else { + return kbnServer.importExport.load(spaceDataObj.dataUrl); + } + }) + ); + + // Adjust spaces for the imported saved objects. + for (const { objects, spacesToAdd = [], spacesToRemove = [] } of OBJECTS_TO_SHARE) { + log.debug( + `Updating spaces for the following objects (add: [${spacesToAdd.join( + ', ' + )}], remove: [${spacesToRemove.join(', ')}]): ${objects + .map(({ type, id }) => `${type}:${id}`) + .join(', ')}` + ); + await supertest + .post('/api/spaces/_update_objects_spaces') + .send({ objects, spacesToAdd, spacesToRemove }) + .expect(200); + } + }, + + createLegacyUrlAliases: async ( + spaceData: Array<{ spaceName: string | null; dataUrl: string; disabled?: boolean }> + ) => { + await Promise.all( + spaceData.map(async (data) => { + const spaceString = data.spaceName ?? 'default'; + + const aliases = await parseLegacyUrlAliases(data.dataUrl); + log.info('creating', aliases.length, 'legacy URL aliases', { + space: spaceString, + }); + + await Promise.all( + aliases.map(async (alias) => { + await es.create({ + id: `legacy-url-alias:${spaceString}:${alias.targetType}:${alias.sourceId}`, + index: '.kibana', + refresh: 'wait_for', + document: { + type: 'legacy-url-alias', + updated_at: '2017-09-21T18:51:23.794Z', + 'legacy-url-alias': { + ...alias, + targetNamespace: spaceString, + ...(data.disabled && { disabled: data.disabled }), + }, + }, + }); + }) + ); + }) + ); + }, + + deleteFtrSavedObjectsData: async () => { + const allSpacesIds = [ + ...(await spacesService.getAll()).map((space: { id: string }) => space.id), + 'non_existent_space', + ]; + log.debug(`Removing data from the following spaces: ${allSpacesIds.join(', ')}`); + await Promise.all( + allSpacesIds.flatMap((spaceId) => [ + kbnServer.savedObjects.cleanStandardList({ space: spaceId }), + kbnServer.savedObjects.clean({ + space: spaceId, + types: ['event-annotation-group', 'url'], + }), + ]) + ); + }, + + deleteAllSavedObjectsFromKibanaIndex: async () => { + await es.deleteByQuery({ + index: ALL_SAVED_OBJECT_INDICES, + wait_for_completion: true, + body: { + // @ts-expect-error + conflicts: 'proceed', + query: { + bool: { + must_not: [ + { + term: { + type: { + value: 'space', + }, + }, + }, + ], + }, + }, + }, + }); + }, + }; +} diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts index 67bcdbdaea7ea..8df28696f41ef 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts @@ -51,7 +51,6 @@ export function createTestConfig({ junit: { reportName: 'X-Pack Spaces API Integration Tests -- ', }, - // esServerArgs: [`xpack.license.self_generated.type=${license}`], })(context); return { @@ -62,14 +61,6 @@ export function createTestConfig({ ...testConfig.kbnTestServer.serverArgs, '--status.allowAnonymous=false', '--server.xsrf.disableProtection=true', - // `--plugin-path=${path.resolve(__dirname, '../common/plugins/spaces_test_plugin')}`, - ], - }, - esTestCluster: { - ...testConfig.esTestCluster, - serverArgs: [ - ...testConfig.esTestCluster.serverArgs, - `xpack.license.self_generated.type=${license}`, ], }, }; From cac3073aec3a0fef962c7ad9e9bc6c77296ebcbd Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Fri, 27 Dec 2024 21:35:50 +0100 Subject: [PATCH 16/38] Fixes --- .../common/suites/copy_to_space.agnostic.ts | 3 --- .../deployment_agnostic/services/basic_auth_supertest.ts | 2 +- .../deployment_agnostic/stateful.common.config.ts | 1 + 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts index d28654dace40f..22d4527c33c21 100644 --- a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts @@ -109,9 +109,6 @@ export function copyToSpaceTestSuiteFactory(context: DeploymentAgnosticFtrProvid const testDataLoader = getTestDataLoader(context); const es = context.getService('es'); const roleScopedSupertest = context.getService('roleScopedSupertest'); - const log = context.getService('log'); - const kbnServer = context.getService('kibanaServer'); - const spacesService = context.getService('spaces'); const collectSpaceContents = async () => { const response = await getAggregatedSpaceData(es, [ diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts index 02410fa09da0d..40622c3efc8c7 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts @@ -26,7 +26,7 @@ export class SupertestWithBasicAuth { const agent = this.supertestWithoutAuth[method](url); if (this.user) { - agent.auth(this.user.username!, this.user.password!); + agent.auth(this.user.username!, this.user.password!).catch(() => {}); // to silence @typescript-eslint/no-floating-promises; } return agent; diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts index 8df28696f41ef..2f712a23565d3 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts @@ -38,6 +38,7 @@ export function createTestConfig({ services: { ...services, es: config.kibana.api.get('services.es'), + // @ts-expect-error esSupertestWithoutAuth: config.xpack.api.get('services.esSupertestWithoutAuth'), supertest: config.kibana.api.get('services.supertest'), supertestWithoutAuth: config.xpack.api.get('services.supertestWithoutAuth'), From 5df6357455c55f0372687988f40327561b4d7056 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Mon, 30 Dec 2024 11:33:19 +0100 Subject: [PATCH 17/38] Fixes --- .../{stateful.common.config.ts => common/config.ts} | 8 ++++---- .../deployment_agnostic/stateful.config_trial.ts | 2 +- .../stateful.copy_to_space.config_trial.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) rename x-pack/test/spaces_api_integration/deployment_agnostic/{stateful.common.config.ts => common/config.ts} (85%) diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/common/config.ts similarity index 85% rename from x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/common/config.ts index 2f712a23565d3..f3f61e54d9237 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.common.config.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/common/config.ts @@ -10,8 +10,8 @@ import path from 'path'; import { REPO_ROOT } from '@kbn/repo-info'; import type { FtrConfigProviderContext } from '@kbn/test'; -import { services } from './services'; -import { createStatefulTestConfig } from '../../api_integration/deployment_agnostic/default_configs/stateful.config.base'; +import { createStatefulTestConfig } from '../../../api_integration/deployment_agnostic/default_configs/stateful.config.base'; +import { services } from '../services'; export function createTestConfig({ license = 'trial', @@ -30,7 +30,7 @@ export function createTestConfig({ ), }, xpack: { - api: await readConfigFile(require.resolve('../../api_integration/config.ts')), + api: await readConfigFile(require.resolve('../../../api_integration/config.ts')), }, }; @@ -48,7 +48,7 @@ export function createTestConfig({ spaces: config.xpack.api.get('services.spaces'), usageAPI: config.xpack.api.get('services.usageAPI'), }, - testFiles: testFiles ?? [require.resolve('./security_and_spaces/apis')], + testFiles: testFiles ?? [require.resolve('../security_and_spaces/apis')], junit: { reportName: 'X-Pack Spaces API Integration Tests -- ', }, diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts index 4f17b0f8b7f77..d28070aa237fc 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts @@ -5,6 +5,6 @@ * 2.0. */ -import { createTestConfig } from './stateful.common.config'; +import { createTestConfig } from './common/config'; export default createTestConfig({ license: 'trial' }); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts index ba437a4b9317a..f4ef249df1911 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { createTestConfig } from './stateful.common.config'; +import { createTestConfig } from './common/config'; export default createTestConfig({ license: 'trial', From e9c6a362be4215b2aae24089caa6a4598536e6b4 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Mon, 30 Dec 2024 14:24:17 +0100 Subject: [PATCH 18/38] get_shareable_references test fix --- .buildkite/ftr_security_stateful_configs.yml | 1 - .../common/suites/get_shareable_references.ts | 36 +++++++++---------- .../security_and_spaces/apis/index.ts | 4 +-- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/.buildkite/ftr_security_stateful_configs.yml b/.buildkite/ftr_security_stateful_configs.yml index 9084cac58a933..9597aa6bbac4a 100644 --- a/.buildkite/ftr_security_stateful_configs.yml +++ b/.buildkite/ftr_security_stateful_configs.yml @@ -106,7 +106,6 @@ enabled: - x-pack/test/automatic_import_api_integration/apis/config_basic.ts - x-pack/test/automatic_import_api_integration/apis/config_graphs.ts - x-pack/test/security_solution_api_integration/test_suites/asset_inventory/entity_store/trial_license_complete_tier/configs/ess.config.ts - - x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config.ts - x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts - x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts - x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_basic.ts diff --git a/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts b/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts index 553aedbab958b..a384abab72dc0 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts @@ -43,9 +43,9 @@ const { SPACE_2: { spaceId: SPACE_2_ID }, } = SPACES; export const TEST_CASE_OBJECTS: Record = deepFreeze({ - SHAREABLE_TYPE: { type: 'sharedtype', id: CASES.EACH_SPACE.id }, // contains references to four other objects - SHAREABLE_TYPE_DOES_NOT_EXIST: { type: 'sharedtype', id: 'does-not-exist' }, - NON_SHAREABLE_TYPE: { type: 'isolatedtype', id: 'my_isolated_object' }, // one of these exists in each space + SHAREABLE_TYPE: { type: 'index-pattern', id: CASES.EACH_SPACE.id }, // contains references to four other objects + SHAREABLE_TYPE_DOES_NOT_EXIST: { type: 'index-pattern', id: 'does-not-exist' }, + NON_SHAREABLE_TYPE: { type: 'url', id: 'my_isolated_object' }, // one of these exists in each space }); // Expected results for each space are defined here since they are used in multiple test suites export const EXPECTED_RESULTS: Record = { @@ -54,7 +54,7 @@ export const EXPECTED_RESULTS: Record ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, spaces: [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID], // No matching origins because there are no copies of the object in another space (we no longer consider a raw ID match to be an origin match) - inboundReferences: [{ type: 'sharedtype', id: CASES.DEFAULT_ONLY.id, name: 'refname' }], // only reflects inbound reference that exist in the default space + inboundReferences: [{ type: 'index-pattern', id: CASES.DEFAULT_ONLY.id, name: 'refname' }], // only reflects inbound reference that exist in the default space }, { ...TEST_CASE_OBJECTS.SHAREABLE_TYPE_DOES_NOT_EXIST, @@ -64,28 +64,28 @@ export const EXPECTED_RESULTS: Record }, { ...TEST_CASE_OBJECTS.NON_SHAREABLE_TYPE, spaces: [], inboundReferences: [] }, // not missing, but has an empty spaces array because it is not a shareable type { - type: 'sharedtype', + type: 'index-pattern', id: CASES.DEFAULT_ONLY.id, spaces: [DEFAULT_SPACE_ID], // No matching origins because there are no copies of the object in another space (we no longer consider a raw ID match to be an origin match) inboundReferences: [{ ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, name: 'refname' }], }, { - type: 'sharedtype', + type: 'index-pattern', id: CASES.SPACE_1_ONLY.id, spaces: [], inboundReferences: [{ ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, name: 'refname' }], isMissing: true, // doesn't exist in the default space }, { - type: 'sharedtype', + type: 'index-pattern', id: CASES.SPACE_2_ONLY.id, spaces: [], inboundReferences: [{ ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, name: 'refname' }], isMissing: true, // doesn't exist in the default space }, { - type: 'sharedtype', + type: 'index-pattern', id: CASES.ALL_SPACES.id, spaces: ['*'], // No matching origins because there are no copies of the object in another space (we no longer consider a raw ID match to be an origin match) @@ -97,7 +97,7 @@ export const EXPECTED_RESULTS: Record ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, spaces: [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID], // No matching origins because there are no copies of the object in another space (we no longer consider a raw ID match to be an origin match) - inboundReferences: [{ type: 'sharedtype', id: CASES.SPACE_1_ONLY.id, name: 'refname' }], // only reflects inbound reference that exist in space 1 + inboundReferences: [{ type: 'index-pattern', id: CASES.SPACE_1_ONLY.id, name: 'refname' }], // only reflects inbound reference that exist in space 1 }, { ...TEST_CASE_OBJECTS.SHAREABLE_TYPE_DOES_NOT_EXIST, @@ -107,14 +107,14 @@ export const EXPECTED_RESULTS: Record }, { ...TEST_CASE_OBJECTS.NON_SHAREABLE_TYPE, spaces: [], inboundReferences: [] }, // not missing, but has an empty spaces array because it is not a shareable type { - type: 'sharedtype', + type: 'index-pattern', id: CASES.DEFAULT_ONLY.id, spaces: [], inboundReferences: [{ ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, name: 'refname' }], isMissing: true, // doesn't exist in space 1 }, { - type: 'sharedtype', + type: 'index-pattern', id: CASES.SPACE_1_ONLY.id, spaces: [SPACE_1_ID], spacesWithMatchingAliases: [DEFAULT_SPACE_ID, SPACE_2_ID], // aliases with a matching targetType and sourceId exist in two other spaces @@ -122,14 +122,14 @@ export const EXPECTED_RESULTS: Record inboundReferences: [{ ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, name: 'refname' }], }, { - type: 'sharedtype', + type: 'index-pattern', id: CASES.SPACE_2_ONLY.id, spaces: [], inboundReferences: [{ ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, name: 'refname' }], isMissing: true, // doesn't exist in space 1 }, { - type: 'sharedtype', + type: 'index-pattern', id: CASES.ALL_SPACES.id, spaces: ['*'], // No matching origins because there are no copies of the object in another space (we no longer consider a raw ID match to be an origin match) @@ -141,7 +141,7 @@ export const EXPECTED_RESULTS: Record ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, spaces: [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID], // No matching origins because there are no copies of the object in another space (we no longer consider a raw ID match to be an origin match) - inboundReferences: [{ type: 'sharedtype', id: CASES.SPACE_2_ONLY.id, name: 'refname' }], // only reflects inbound reference that exist in space 2 + inboundReferences: [{ type: 'index-pattern', id: CASES.SPACE_2_ONLY.id, name: 'refname' }], // only reflects inbound reference that exist in space 2 }, { ...TEST_CASE_OBJECTS.SHAREABLE_TYPE_DOES_NOT_EXIST, @@ -151,28 +151,28 @@ export const EXPECTED_RESULTS: Record }, { ...TEST_CASE_OBJECTS.NON_SHAREABLE_TYPE, spaces: [], inboundReferences: [] }, // not missing, but has an empty spaces array because it is not a shareable type { - type: 'sharedtype', + type: 'index-pattern', id: CASES.DEFAULT_ONLY.id, spaces: [], inboundReferences: [{ ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, name: 'refname' }], isMissing: true, // doesn't exist in space 2 }, { - type: 'sharedtype', + type: 'index-pattern', id: CASES.SPACE_1_ONLY.id, spaces: [], inboundReferences: [{ ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, name: 'refname' }], isMissing: true, // doesn't exist in space 2 }, { - type: 'sharedtype', + type: 'index-pattern', id: CASES.SPACE_2_ONLY.id, spaces: [SPACE_2_ID], spacesWithMatchingOrigins: ['*'], // The third test assertion for spacesWithMatchingOrigins is an object that has a matching origin in all spaces (this takes precedence, causing SPACE_2_ID to be omitted) inboundReferences: [{ ...TEST_CASE_OBJECTS.SHAREABLE_TYPE, name: 'refname' }], }, { - type: 'sharedtype', + type: 'index-pattern', id: CASES.ALL_SPACES.id, spaces: ['*'], // No matching origins because there are no copies of the object in another space (we no longer consider a raw ID match to be an origin match) diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts index 197d276564a57..f3cd16f41a9d8 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts @@ -20,7 +20,7 @@ export default function ({ loadTestFile, getService }: FtrProviderContext) { // total runtime ~ 17m loadTestFile(require.resolve('./get_shareable_references')); // ~ 30s - loadTestFile(require.resolve('./update_objects_spaces')); // ~ 1m - loadTestFile(require.resolve('./disable_legacy_url_aliases')); // ~ 30s + // loadTestFile(require.resolve('./update_objects_spaces')); // ~ 1m + // loadTestFile(require.resolve('./disable_legacy_url_aliases')); // ~ 30s }); } From b892778d8173e9f3217086a36bfcef5fdde15ec0 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Mon, 30 Dec 2024 17:13:34 +0100 Subject: [PATCH 19/38] wip --- .../test/spaces_api_integration/common/lib/space_test_utils.ts | 1 + .../deployment_agnostic/security_and_spaces/apis/index.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts b/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts index 3c30208933550..70a597141f1c2 100644 --- a/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts +++ b/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts @@ -42,6 +42,7 @@ export function getTestScenariosForSpace(spaceId: string) { export function getAggregatedSpaceData(es: Client, objectTypes: string[]) { return es.search({ index: ALL_SAVED_OBJECT_INDICES, + request_cache: false, body: { size: 0, runtime_mappings: { diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts index 71234644328af..245e13a8a4583 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts @@ -22,7 +22,7 @@ export default function ({ loadTestFile, getService }: DeploymentAgnosticFtrProv }); loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); // PASS loadTestFile(require.resolve('./create')); // PASS - // loadTestFile(require.resolve('./delete')); + loadTestFile(require.resolve('./delete')); loadTestFile(require.resolve('./get_all')); // PASS loadTestFile(require.resolve('./get')); // PASS loadTestFile(require.resolve('./update')); // PASS From d0bd5d6689e14a8a5b5270845e935f8ca6bd709c Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Tue, 31 Dec 2024 15:01:59 +0100 Subject: [PATCH 20/38] update_objects_spaces and disable_legacy_url_aliases tests --- .../common/suites/disable_legacy_url_aliases.ts | 2 +- .../common/suites/update_objects_spaces.ts | 2 +- .../spaces_api_integration/security_and_spaces/apis/index.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts b/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts index a00729e7eca8c..10cb486ac2633 100644 --- a/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts +++ b/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts @@ -38,7 +38,7 @@ interface RawLegacyUrlAlias { [LEGACY_URL_ALIAS_TYPE]: LegacyUrlAlias; } -export const TEST_CASE_TARGET_TYPE = 'sharedtype'; +export const TEST_CASE_TARGET_TYPE = 'index-pattern'; export const TEST_CASE_SOURCE_ID = 'space_1_only'; // two aliases exist for space_1_only: one in the default spacd=e, and one in space_2 const createRequest = ({ targetSpace, targetType, sourceId }: DisableLegacyUrlAliasesTestCase) => ({ aliases: [{ targetSpace, targetType, sourceId }], diff --git a/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts b/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts index 05ed64e1e6047..c1f5bf68c5da2 100644 --- a/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts +++ b/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts @@ -45,7 +45,7 @@ export interface UpdateObjectsSpacesTestCase { spacesToRemove: string[]; } -const TYPE = 'sharedtype'; +const TYPE = 'index-pattern'; const createRequest = ({ objects, spacesToAdd, spacesToRemove }: UpdateObjectsSpacesTestCase) => ({ objects: objects.map(({ id }) => ({ type: TYPE, id })), spacesToAdd, diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts index f3cd16f41a9d8..197d276564a57 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts @@ -20,7 +20,7 @@ export default function ({ loadTestFile, getService }: FtrProviderContext) { // total runtime ~ 17m loadTestFile(require.resolve('./get_shareable_references')); // ~ 30s - // loadTestFile(require.resolve('./update_objects_spaces')); // ~ 1m - // loadTestFile(require.resolve('./disable_legacy_url_aliases')); // ~ 30s + loadTestFile(require.resolve('./update_objects_spaces')); // ~ 1m + loadTestFile(require.resolve('./disable_legacy_url_aliases')); // ~ 30s }); } From 99c18fb2566d25313796f7b1abe3bb058972198e Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Fri, 3 Jan 2025 12:21:39 +0100 Subject: [PATCH 21/38] Fixes --- src/dev/eslint/types.eslint.config.template.cjs | 6 ++++++ .../common/suites/copy_to_space.agnostic.ts | 6 +++--- .../deployment_agnostic/services/basic_auth_supertest.ts | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/dev/eslint/types.eslint.config.template.cjs b/src/dev/eslint/types.eslint.config.template.cjs index ef7572d5626b2..42a032c383671 100644 --- a/src/dev/eslint/types.eslint.config.template.cjs +++ b/src/dev/eslint/types.eslint.config.template.cjs @@ -34,5 +34,11 @@ module.exports = { '@typescript-eslint/no-floating-promises': 'error', }, }, + { + files: ['*spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts'], + rules: { + '@typescript-eslint/no-floating-promises': 'off', + }, + }, ], }; diff --git a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts index 22d4527c33c21..38ab78e054414 100644 --- a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts @@ -822,12 +822,12 @@ export function copyToSpaceTestSuiteFactory(context: DeploymentAgnosticFtrProvid await supertest.destroy(); }); - describe.skip('single-namespace types', () => { + describe('single-namespace types', () => { beforeEach( async () => await testDataLoader.createFtrSavedObjectsData(SPACE_DATA_TO_LOAD) ); - afterEach(async () => await await testDataLoader.deleteFtrSavedObjectsData()); + afterEach(async () => await testDataLoader.deleteFtrSavedObjectsData()); const dashboardObject = { type: 'dashboard', id: `cts_dashboard_${spaceId}` }; @@ -957,7 +957,7 @@ export function copyToSpaceTestSuiteFactory(context: DeploymentAgnosticFtrProvid [false, false], [false, true], // createNewCopies enabled [true, false], // overwrite enabled - // we don't specify tese cases with both overwrite and createNewCopies enabled, since overwrite won't matter in that scenario + // we don't specify test cases with both overwrite and createNewCopies enabled, since overwrite won't matter in that scenario ].forEach(([overwrite, createNewCopies]) => { const spaces = ['space_2']; const includeReferences = false; diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts index 40622c3efc8c7..02410fa09da0d 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts @@ -26,7 +26,7 @@ export class SupertestWithBasicAuth { const agent = this.supertestWithoutAuth[method](url); if (this.user) { - agent.auth(this.user.username!, this.user.password!).catch(() => {}); // to silence @typescript-eslint/no-floating-promises; + agent.auth(this.user.username!, this.user.password!); } return agent; From 24c15487a5be9ec836531ea2ba6e749b598f613a Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Fri, 3 Jan 2025 13:09:09 +0100 Subject: [PATCH 22/38] Added waitFor for delete test --- .../common/suites/delete.agnostic.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts index f7712ef170d41..fe97e062b7f5f 100644 --- a/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/delete.agnostic.ts @@ -37,6 +37,7 @@ export function deleteTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv const esArchiver = getService('esArchiver'); const es = getService('es'); const roleScopedSupertest = getService('roleScopedSupertest'); + const retry = getService('retry'); const createExpectResult = (expectedResult: any) => (resp: { [key: string]: any }) => { expect(resp.body).to.eql(expectedResult); @@ -45,6 +46,23 @@ export function deleteTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv const expectEmptyResult = async (resp: { [key: string]: any }) => { expect(resp.body).to.eql(''); + await retry.waitForWithTimeout('space_2 to be deleted', 5000, async () => { + const response = await getAggregatedSpaceData(es, [ + 'visualization', + 'dashboard', + 'space', + 'index-pattern', + 'legacy-url-alias', + ]); + + // @ts-expect-error @elastic/elasticsearch doesn't defined `count.buckets`. + const buckets = response.aggregations?.count.buckets; + + const isSpace2Found = buckets?.some((bucket: any) => bucket.key === 'space_2'); + + return !isSpace2Found; + }); + // Query ES to ensure that we deleted everything we expected, and nothing we didn't // Grouping first by namespace, then by saved object type const response = await getAggregatedSpaceData(es, [ From dda347de013081bb13717eade948c8c860d38332 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Fri, 3 Jan 2025 14:50:38 +0100 Subject: [PATCH 23/38] Fixes --- .buildkite/ftr_security_stateful_configs.yml | 7 ++++--- .../common/lib/authentication.ts | 6 ++---- .../services/basic_auth_supertest.ts | 2 +- .../services/test_data_loader.ts | 6 ++---- .../common/suites/copy_to_space.agnostic.ts | 6 +----- .../resolve_copy_to_space_conflicts.agnostic.ts | 2 +- .../deployment_agnostic/common/config.ts | 10 ++-------- .../security_and_spaces/apis/index.ts | 10 +++++----- .../security_and_spaces/stateful.config_basic.ts | 16 ++++++++++++++++ .../stateful.config_trial.ts | 4 ++-- .../stateful.copy_to_space.config_basic.ts | 4 ++-- .../stateful.copy_to_space.config_trial.ts | 9 ++++----- .../services/role_scoped_supertest.ts | 2 +- 13 files changed, 43 insertions(+), 41 deletions(-) rename x-pack/test/spaces_api_integration/{deployment_agnostic => common}/services/basic_auth_supertest.ts (93%) rename x-pack/test/spaces_api_integration/{deployment_agnostic => common}/services/test_data_loader.ts (97%) create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_basic.ts rename x-pack/test/spaces_api_integration/deployment_agnostic/{ => security_and_spaces}/stateful.config_trial.ts (67%) rename x-pack/test/spaces_api_integration/deployment_agnostic/{ => security_and_spaces}/stateful.copy_to_space.config_basic.ts (72%) rename x-pack/test/spaces_api_integration/deployment_agnostic/{ => security_and_spaces}/stateful.copy_to_space.config_trial.ts (57%) diff --git a/.buildkite/ftr_security_stateful_configs.yml b/.buildkite/ftr_security_stateful_configs.yml index 9597aa6bbac4a..5ccc1911c992b 100644 --- a/.buildkite/ftr_security_stateful_configs.yml +++ b/.buildkite/ftr_security_stateful_configs.yml @@ -106,7 +106,8 @@ enabled: - x-pack/test/automatic_import_api_integration/apis/config_basic.ts - x-pack/test/automatic_import_api_integration/apis/config_graphs.ts - x-pack/test/security_solution_api_integration/test_suites/asset_inventory/entity_store/trial_license_complete_tier/configs/ess.config.ts - - x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts - - x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts - - x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_basic.ts - x-pack/test/spaces_api_integration/deployment_agnostic/spaces_only/config.ts + - x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_basic.ts + - x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_trial.ts + - x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.copy_to_space.config_trial.ts + - x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.copy_to_space.config_basic.ts diff --git a/x-pack/test/spaces_api_integration/common/lib/authentication.ts b/x-pack/test/spaces_api_integration/common/lib/authentication.ts index 76bdf582fbd2e..a0472f51bd38c 100644 --- a/x-pack/test/spaces_api_integration/common/lib/authentication.ts +++ b/x-pack/test/spaces_api_integration/common/lib/authentication.ts @@ -343,9 +343,7 @@ export const isBuiltInRole = (role: string) => 'monitoring_user', 'machine_learning_admin', 'machine_learning_user', - 'apm_user', ].includes(role); -export const getRoleDefinitionForUser = (user: { role: string }) => { - return ROLES[user.role as keyof typeof ROLES]; -}; +export const getRoleDefinitionForUser = (user: { role: string }) => + ROLES[user.role as keyof typeof ROLES]; diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts b/x-pack/test/spaces_api_integration/common/services/basic_auth_supertest.ts similarity index 93% rename from x-pack/test/spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts rename to x-pack/test/spaces_api_integration/common/services/basic_auth_supertest.ts index 02410fa09da0d..f3fc944925287 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts +++ b/x-pack/test/spaces_api_integration/common/services/basic_auth_supertest.ts @@ -9,7 +9,7 @@ import type { Test } from 'supertest'; import type { SupertestWithoutAuthProviderType } from '@kbn/ftr-common-functional-services'; -import type { TestDefinitionAuthentication as User } from '../../common/lib/types'; +import type { TestDefinitionAuthentication as User } from '../lib/types'; export class SupertestWithBasicAuth { private readonly supertestWithoutAuth: SupertestWithoutAuthProviderType; diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/services/test_data_loader.ts b/x-pack/test/spaces_api_integration/common/services/test_data_loader.ts similarity index 97% rename from x-pack/test/spaces_api_integration/deployment_agnostic/services/test_data_loader.ts rename to x-pack/test/spaces_api_integration/common/services/test_data_loader.ts index d6478b2c1ff08..761758545f267 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/services/test_data_loader.ts +++ b/x-pack/test/spaces_api_integration/common/services/test_data_loader.ts @@ -10,7 +10,7 @@ import Fs from 'fs/promises'; import type { LegacyUrlAlias } from '@kbn/core-saved-objects-base-server-internal'; import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; -import type { DeploymentAgnosticFtrProviderContext } from '../ftr_provider_context'; +import type { FtrProviderContext } from '../ftr_provider_context'; export const SPACE_1 = { id: 'space_1', @@ -80,9 +80,7 @@ const OBJECTS_TO_SHARE: Array<{ }, ]; -export function getTestDataLoader({ - getService, -}: Pick) { +export function getTestDataLoader({ getService }: Pick) { const spacesService = getService('spaces'); const kbnServer = getService('kibanaServer'); const supertest = getService('supertest'); diff --git a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts index 38ab78e054414..3d6496ed556a9 100644 --- a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.agnostic.ts @@ -18,13 +18,9 @@ import type { DeploymentAgnosticFtrProviderContext, SupertestWithRoleScopeType, } from '../../deployment_agnostic/ftr_provider_context'; -import { - getTestDataLoader, - SPACE_1, - SPACE_2, -} from '../../deployment_agnostic/services/test_data_loader'; import { getAggregatedSpaceData, getUrlPrefix } from '../lib/space_test_utils'; import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; +import { getTestDataLoader, SPACE_1, SPACE_2 } from '../services/test_data_loader'; type TestResponse = Record; diff --git a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts index 8f40f21331131..e0ecf6256c2f5 100644 --- a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts @@ -18,7 +18,7 @@ import { getTestDataLoader, SPACE_1, SPACE_2, -} from '../../deployment_agnostic/services/test_data_loader'; +} from '../services/test_data_loader'; import { getUrlPrefix } from '../lib/space_test_utils'; import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/common/config.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/common/config.ts index f3f61e54d9237..a08ead4945891 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/common/config.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/common/config.ts @@ -13,13 +13,7 @@ import type { FtrConfigProviderContext } from '@kbn/test'; import { createStatefulTestConfig } from '../../../api_integration/deployment_agnostic/default_configs/stateful.config.base'; import { services } from '../services'; -export function createTestConfig({ - license = 'trial', - testFiles, -}: { - license: string; - testFiles?: string[]; -}) { +export function createTestConfig(name: string, testFiles?: string[]) { return async (context: FtrConfigProviderContext) => { const { readConfigFile } = context; const config = { @@ -50,7 +44,7 @@ export function createTestConfig({ }, testFiles: testFiles ?? [require.resolve('../security_and_spaces/apis')], junit: { - reportName: 'X-Pack Spaces API Integration Tests -- ', + reportName: `X-Pack Spaces API Deployment Agnostic Integration Tests -- ${name}`, }, })(context); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts index 245e13a8a4583..18d0a16853c6b 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts @@ -20,11 +20,11 @@ export default function ({ loadTestFile, getService }: DeploymentAgnosticFtrProv await createUsersAndRoles(es, supertest); } }); - loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); // PASS - loadTestFile(require.resolve('./create')); // PASS + loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); + loadTestFile(require.resolve('./create')); loadTestFile(require.resolve('./delete')); - loadTestFile(require.resolve('./get_all')); // PASS - loadTestFile(require.resolve('./get')); // PASS - loadTestFile(require.resolve('./update')); // PASS + loadTestFile(require.resolve('./get_all')); + loadTestFile(require.resolve('./get')); + loadTestFile(require.resolve('./update')); }); } diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_basic.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_basic.ts new file mode 100644 index 0000000000000..83f61fa006eb4 --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_basic.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createTestConfig } from '../common/config'; + +export default createTestConfig('security_and_spaces - basic license', [require.resolve('./apis')]); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_trial.ts similarity index 67% rename from x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_trial.ts index d28070aa237fc..cd449df1d54e3 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.config_trial.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_trial.ts @@ -5,6 +5,6 @@ * 2.0. */ -import { createTestConfig } from './common/config'; +import { createTestConfig } from '../common/config'; -export default createTestConfig({ license: 'trial' }); +export default createTestConfig('security_and_spaces - trial license'); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_basic.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.copy_to_space.config_basic.ts similarity index 72% rename from x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_basic.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.copy_to_space.config_basic.ts index a079bd64edb98..c6dbb5f0eabbf 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_basic.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.copy_to_space.config_basic.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { createTestConfig } from '../common/config'; +import { createTestConfig } from '../../common/config'; export default createTestConfig('copy_to_space - basic license', { license: 'basic', - testFiles: [require.resolve('./security_and_spaces/apis/copy_to_space')], + testFiles: [require.resolve('./apis/copy_to_space')], }); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.copy_to_space.config_trial.ts similarity index 57% rename from x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts rename to x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.copy_to_space.config_trial.ts index f4ef249df1911..d188520658843 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/stateful.copy_to_space.config_trial.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.copy_to_space.config_trial.ts @@ -5,9 +5,8 @@ * 2.0. */ -import { createTestConfig } from './common/config'; +import { createTestConfig } from '../common/config'; -export default createTestConfig({ - license: 'trial', - testFiles: [require.resolve('./security_and_spaces/apis/copy_to_space')], -}); +export default createTestConfig('copy_to_space - trial license', [ + require.resolve('./apis/copy_to_space'), +]); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts index 49e16ca1b2b7a..2275435c63abe 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts @@ -14,9 +14,9 @@ import type { SupertestWithoutAuthProviderType, } from '@kbn/ftr-common-functional-services'; -import { SupertestWithBasicAuth } from './basic_auth_supertest'; import { getRoleDefinitionForUser, isBuiltInRole } from '../../common/lib/authentication'; import type { TestDefinitionAuthentication as User } from '../../common/lib/types'; +import { SupertestWithBasicAuth } from '../../common/services/basic_auth_supertest'; import type { DeploymentAgnosticFtrProviderContext } from '../ftr_provider_context'; export interface RequestHeadersOptions { From f089b0259ba14a7b0683dd9b100afd18a25f2d0f Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Fri, 3 Jan 2025 14:50:47 +0100 Subject: [PATCH 24/38] Fixes --- .../security_and_spaces/stateful.config_basic.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_basic.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_basic.ts index 83f61fa006eb4..f1e1c562bdd61 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_basic.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_basic.ts @@ -11,6 +11,9 @@ * 2.0. */ -import { createTestConfig } from '../common/config'; +import { createTestConfig } from '../../common/config'; -export default createTestConfig('security_and_spaces - basic license', [require.resolve('./apis')]); +export default createTestConfig('security_and_spaces - basic license', { + license: 'basic', + testFiles: [require.resolve('./apis')], +}); From 008ed23d7ebddcb4663c05d802f5d1b61a67aeb0 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Fri, 3 Jan 2025 15:54:04 +0100 Subject: [PATCH 25/38] Lint fix --- src/dev/eslint/types.eslint.config.template.cjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dev/eslint/types.eslint.config.template.cjs b/src/dev/eslint/types.eslint.config.template.cjs index 42a032c383671..4ad2391055f5d 100644 --- a/src/dev/eslint/types.eslint.config.template.cjs +++ b/src/dev/eslint/types.eslint.config.template.cjs @@ -35,7 +35,7 @@ module.exports = { }, }, { - files: ['*spaces_api_integration/deployment_agnostic/services/basic_auth_supertest.ts'], + files: ['*spaces_api_integration/common/services/basic_auth_supertest.ts'], rules: { '@typescript-eslint/no-floating-promises': 'off', }, From 0e6c69ca96c46fd444056269907ad86ef0ecd2dd Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:14:30 +0000 Subject: [PATCH 26/38] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../suites/resolve_copy_to_space_conflicts.agnostic.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts index e0ecf6256c2f5..af1d74ceeba46 100644 --- a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts @@ -14,13 +14,9 @@ import type { DeploymentAgnosticFtrProviderContext, SupertestWithRoleScopeType, } from '../../deployment_agnostic/ftr_provider_context'; -import { - getTestDataLoader, - SPACE_1, - SPACE_2, -} from '../services/test_data_loader'; import { getUrlPrefix } from '../lib/space_test_utils'; import type { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; +import { getTestDataLoader, SPACE_1, SPACE_2 } from '../services/test_data_loader'; type TestResponse = Record; From 6e362b3a6ee053ab8329fafe4d67099c4f29b40c Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Fri, 3 Jan 2025 18:37:18 +0100 Subject: [PATCH 27/38] Fixup --- .../services/role_scoped_supertest.ts | 121 +----------------- 1 file changed, 2 insertions(+), 119 deletions(-) diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts index 2275435c63abe..503b6c0e191ec 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts @@ -5,132 +5,15 @@ * 2.0. */ -import type { Test } from 'supertest'; - -import type { - CookieCredentials, - RoleCredentials, - SamlAuthProviderType, - SupertestWithoutAuthProviderType, -} from '@kbn/ftr-common-functional-services'; - +import type { RequestHeadersOptions } from '../../../api_integration/deployment_agnostic/services/role_scoped_supertest'; +import { SupertestWithRoleScope } from '../../../api_integration/deployment_agnostic/services/role_scoped_supertest'; import { getRoleDefinitionForUser, isBuiltInRole } from '../../common/lib/authentication'; import type { TestDefinitionAuthentication as User } from '../../common/lib/types'; import { SupertestWithBasicAuth } from '../../common/services/basic_auth_supertest'; import type { DeploymentAgnosticFtrProviderContext } from '../ftr_provider_context'; -export interface RequestHeadersOptions { - useCookieHeader?: boolean; - withInternalHeaders?: boolean; - withCommonHeaders?: boolean; - withCustomHeaders?: Record; -} - export type SupertestWithRoleScopeType = SupertestWithBasicAuth | SupertestWithRoleScope; -export class SupertestWithRoleScope { - private authValue: RoleCredentials | CookieCredentials | null; - private readonly supertestWithoutAuth: SupertestWithoutAuthProviderType; - private samlAuth: SamlAuthProviderType; - private readonly options: RequestHeadersOptions; - - constructor( - authValue: RoleCredentials | CookieCredentials | null, - supertestWithoutAuth: SupertestWithoutAuthProviderType, - samlAuth: SamlAuthProviderType, - options: RequestHeadersOptions - ) { - this.authValue = authValue; - this.supertestWithoutAuth = supertestWithoutAuth; - this.samlAuth = samlAuth; - this.options = options; - } - - private isRoleCredentials(value: any): value is RoleCredentials { - return value && typeof value === 'object' && 'apiKey' in value && 'apiKeyHeader' in value; - } - - async destroy() { - if (this.isRoleCredentials(this.authValue)) { - await this.samlAuth.invalidateM2mApiKeyWithRoleScope(this.authValue); - this.authValue = null; - } - } - - private addHeaders(agent: Test): Test { - const { useCookieHeader, withInternalHeaders, withCommonHeaders, withCustomHeaders } = - this.options; - - if (useCookieHeader) { - if (!this.authValue || !('Cookie' in this.authValue)) { - throw new Error('The instance has already been destroyed or cookieHeader is missing.'); - } - // set cookie header - void agent.set(this.authValue); - } else { - if (!this.authValue || !this.isRoleCredentials(this.authValue)) { - throw new Error('The instance has already been destroyed or roleAuthc is missing.'); - } - // set API key header - void agent.set(this.authValue.apiKeyHeader); - } - - if (withInternalHeaders) { - void agent.set(this.samlAuth.getInternalRequestHeader()); - } - - if (withCommonHeaders) { - void agent.set(this.samlAuth.getCommonRequestHeader()); - } - - if (withCustomHeaders) { - void agent.set(withCustomHeaders); - } - - return agent; - } - - private request(method: 'post' | 'get' | 'put' | 'delete' | 'patch', url: string): Test { - if (!this.authValue) { - throw new Error('Instance has been destroyed and cannot be used for making requests.'); - } - const agent = this.supertestWithoutAuth[method](url); - return this.addHeaders(agent); - } - - post(url: string) { - return this.request('post', url); - } - - get(url: string) { - return this.request('get', url); - } - - put(url: string) { - return this.request('put', url); - } - - delete(url: string) { - return this.request('delete', url); - } - - patch(url: string) { - return this.request('patch', url); - } -} - -/** - * Provides a customized 'supertest' instance that is authenticated using a role-based API key - * and enriched with the appropriate request headers. This service allows you to perform - * HTTP requests with specific authentication and header configurations, ensuring that - * the requests are scoped to the provided role and environment. - * - * Use this service to easily test API endpoints with role-specific authorization and - * custom headers, both in serverless and stateful environments. - * - * Pass '{ useCookieHeader: true }' to use Cookie header for authentication instead of API key. - * It is the correct way to perform HTTP requests for internal end-points. - */ export function RoleScopedSupertestProvider({ getService }: DeploymentAgnosticFtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); const samlAuth = getService('samlAuth'); From c7c91eb0705e62772eabc88a9f72096e0a9b7a70 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Fri, 3 Jan 2025 20:20:17 +0100 Subject: [PATCH 28/38] Serverless config --- .../ftr_security_serverless_configs.yml | 1 + .../common/suites/create.agnostic.ts | 2 +- .../apis/index.serverless.ts | 19 +++++++++++++++++++ .../security_and_spaces/serverless.config.ts | 19 +++++++++++++++++++ .../deployment_agnostic/services/index.ts | 2 +- .../services/role_scoped_supertest.ts | 3 ++- 6 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/serverless.config.ts diff --git a/.buildkite/ftr_security_serverless_configs.yml b/.buildkite/ftr_security_serverless_configs.yml index eca2fc6bdf0a6..b3aed37c19d74 100644 --- a/.buildkite/ftr_security_serverless_configs.yml +++ b/.buildkite/ftr_security_serverless_configs.yml @@ -126,3 +126,4 @@ enabled: - x-pack/test/security_solution_endpoint/configs/serverless.integrations.config.ts # serverless config files that run deployment-agnostic tests - x-pack/test/api_integration/deployment_agnostic/configs/serverless/security.serverless.config.ts + - x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/serverless.config.ts diff --git a/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts index 255fa5e07b950..6085b96702452 100644 --- a/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/create.agnostic.ts @@ -177,7 +177,7 @@ export function createTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv }); }); - describe('when solution is specified', () => { + describe('@skipInServerless when solution is specified', () => { it(`should return ${tests.solutionSpecified.statusCode}`, async () => { return supertest .post(`${urlPrefix}/api/spaces/space`) diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts new file mode 100644 index 0000000000000..3554b5c872396 --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('spaces api with security', function () { + // loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); + loadTestFile(require.resolve('./create')); + loadTestFile(require.resolve('./delete')); + // loadTestFile(require.resolve('./get_all')); + // loadTestFile(require.resolve('./get')); + loadTestFile(require.resolve('./update')); + }); +} diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/serverless.config.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/serverless.config.ts new file mode 100644 index 0000000000000..e9e47d92c2d45 --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/serverless.config.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createServerlessTestConfig } from '../../../api_integration/deployment_agnostic/default_configs/serverless.config.base'; +import { services } from '../services'; + +export default createServerlessTestConfig({ + // @ts-expect-error + services, + serverlessProject: 'security', + testFiles: [require.resolve('./apis/index.serverless')], + junit: { + reportName: 'Serverless Security - Deployment-agnostic API Integration Tests', + }, +}); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/services/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/services/index.ts index b0166ffbd4ed8..656aab1cab731 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/services/index.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/services/index.ts @@ -13,8 +13,8 @@ import { services as commonServices } from '../../../common/services'; export type { SupertestWithRoleScopeType } from './role_scoped_supertest'; export const services = { - ...deploymentAgnosticServices, ...commonServices, + ...deploymentAgnosticServices, usageAPI: apiIntegrationServices.usageAPI, roleScopedSupertest: RoleScopedSupertestProvider, }; diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts index 503b6c0e191ec..5a88257918e96 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts @@ -29,8 +29,9 @@ export function RoleScopedSupertestProvider({ getService }: DeploymentAgnosticFt } ) { const license = config.get('esTestCluster.license'); + const isServerless = config.get('serverless'); - if (!user || license !== 'trial') { + if (!user || (license === 'basic' && !isServerless)) { return new SupertestWithBasicAuth(supertestWithoutAuth, user); } From 9cd8a5d68590f7f26a025e241dd3ecea94254bf6 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Mon, 6 Jan 2025 14:32:30 +0100 Subject: [PATCH 29/38] Fixes --- .../common/services/test_data_loader.ts | 1 + .../common/suites/create.agnostic.ts | 11 +- .../common/suites/get.agnostic.ts | 48 +++-- .../common/suites/get_all.agnostic.ts | 22 +- .../deployment_agnostic/common/config.ts | 12 +- .../security_and_spaces/apis/get_all.ts | 195 ++++++++---------- .../apis/index.serverless.ts | 4 +- 7 files changed, 145 insertions(+), 148 deletions(-) diff --git a/x-pack/test/spaces_api_integration/common/services/test_data_loader.ts b/x-pack/test/spaces_api_integration/common/services/test_data_loader.ts index 761758545f267..13a420b372354 100644 --- a/x-pack/test/spaces_api_integration/common/services/test_data_loader.ts +++ b/x-pack/test/spaces_api_integration/common/services/test_data_loader.ts @@ -124,6 +124,7 @@ export function getTestDataLoader({ getService }: Pick undefined; const expectConflictResponse = (resp: { [key: string]: any }) => { expect(resp.body).to.only.have.keys(['error', 'message', 'statusCode']); @@ -177,8 +180,10 @@ export function createTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv }); }); - describe('@skipInServerless when solution is specified', () => { + describe('when solution is specified', () => { it(`should return ${tests.solutionSpecified.statusCode}`, async () => { + const statusCode = isServerless ? 400 : tests.solutionSpecified.statusCode; + return supertest .post(`${urlPrefix}/api/spaces/space`) .send({ @@ -189,8 +194,8 @@ export function createTestSuiteFactory({ getService }: DeploymentAgnosticFtrProv solution: 'es', disabledFeatures: [], }) - .expect(tests.solutionSpecified.statusCode) - .then(tests.solutionSpecified.response); + .expect(statusCode) + .then(isServerless ? noop : tests.solutionSpecified.response); }); }); }); diff --git a/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts index 4e36769deba99..9c7a8e6b53152 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get.agnostic.ts @@ -34,6 +34,9 @@ const nonExistantSpaceId = 'not-a-space'; export function getTestSuiteFactory(context: DeploymentAgnosticFtrProviderContext) { const esArchiver = context.getService('esArchiver'); + const config = context.getService('config'); + const isServerless = config.get('serverless'); + const createExpectEmptyResult = () => (resp: { [key: string]: any }) => { expect(resp.body).to.eql(''); }; @@ -80,26 +83,29 @@ export function getTestSuiteFactory(context: DeploymentAgnosticFtrProviderContex id: 'space_3', name: 'Space 3', description: 'This is the third test space', - solution: 'es', - disabledFeatures: [ - // Disabled features are automatically added to the space when a solution is set - 'apm', - 'infrastructure', - 'inventory', - 'logs', - 'observabilityCases', - 'observabilityCasesV2', - 'securitySolutionAssistant', - 'securitySolutionAttackDiscovery', - 'securitySolutionCases', - 'securitySolutionCasesV2', - 'securitySolutionNotes', - 'securitySolutionTimeline', - 'siem', - 'siemV2', - 'slo', - 'uptime', - ], + disabledFeatures: [], + ...(!isServerless && { + solution: 'es', + disabledFeatures: [ + // Disabled features are automatically added to the space when a solution is set + 'apm', + 'infrastructure', + 'inventory', + 'logs', + 'observabilityCases', + 'observabilityCasesV2', + 'securitySolutionAssistant', + 'securitySolutionAttackDiscovery', + 'securitySolutionCases', + 'securitySolutionCasesV2', + 'securitySolutionNotes', + 'securitySolutionTimeline', + 'siem', + 'siemV2', + 'slo', + 'uptime', + ], + }), }, ]; @@ -107,7 +113,7 @@ export function getTestSuiteFactory(context: DeploymentAgnosticFtrProviderContex const expectedSpace = allSpaces.find((space) => space.id === spaceId); if (expectedSpace) { - expectedSpace.disabledFeatures.sort(); + expectedSpace.disabledFeatures?.sort(); } expect({ ...resp.body, disabledFeatures }).to.eql(expectedSpace); diff --git a/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts index 1d388f13d4328..b2cc9edf36946 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts @@ -96,11 +96,25 @@ const ALL_SPACE_RESULTS: Space[] = [ export function getAllTestSuiteFactory(context: DeploymentAgnosticFtrProviderContext) { const esArchiver = context.getService('esArchiver'); + const config = context.getService('config'); + const isServerless = config.get('serverless'); + + const maybeNormalizeSpace = (space: Space) => { + if (isServerless && space.solution) { + const { id, name, description } = space; + + return { id, name, description, disabledFeatures: [] }; + } + return space; + }; const createExpectResults = (...spaceIds: string[]) => (resp: { [key: string]: any }) => { - const expectedBody = ALL_SPACE_RESULTS.filter((entry) => spaceIds.includes(entry.id)); + const expectedBody = ALL_SPACE_RESULTS.filter((entry) => spaceIds.includes(entry.id)).map( + maybeNormalizeSpace + ); + for (const space of resp.body) { const expectedSpace = expectedBody.find((x) => x.id === space.id); expect(space.name).to.eql(expectedSpace?.name); @@ -115,7 +129,11 @@ export function getAllTestSuiteFactory(context: DeploymentAgnosticFtrProviderCon (authorizedPurposes: AuthorizedPurposes, ...spaceIds: string[]) => (resp: { [key: string]: any }) => { const expectedBody = ALL_SPACE_RESULTS.filter((entry) => spaceIds.includes(entry.id)).map( - (x) => ({ ...x, authorizedPurposes }) + (entry) => { + const space = maybeNormalizeSpace(entry); + + return { ...space, authorizedPurposes }; + } ); for (const space of resp.body) { diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/common/config.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/common/config.ts index a08ead4945891..bf406104216de 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/common/config.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/common/config.ts @@ -48,16 +48,6 @@ export function createTestConfig(name: string, testFiles?: string[]) { }, })(context); - return { - ...testConfig, - kbnTestServer: { - ...testConfig.kbnTestServer, - serverArgs: [ - ...testConfig.kbnTestServer.serverArgs, - '--status.allowAnonymous=false', - '--server.xsrf.disableProtection=true', - ], - }, - }; + return testConfig; }; } diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get_all.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get_all.ts index a0ec35b1f4e3d..5af99492a5c93 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get_all.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get_all.ts @@ -14,6 +14,11 @@ export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProv const { getAllTest, createExpectResults, createExpectAllPurposesResults, expectRbacForbidden } = getAllTestSuiteFactory(context); + const config = context.getService('config'); + const isServerless = config.get('serverless'); + + const spaces = ['default', 'space_1', 'space_2', 'space_3']; + // these are used to determine expected results for tests where the `include_authorized_purposes` option is enabled const authorizedAll = { any: true, @@ -112,25 +117,19 @@ export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProv tests: { exists: { statusCode: 200, - response: createExpectResults('default', 'space_1', 'space_2', 'space_3'), + response: createExpectResults(...spaces), }, copySavedObjectsPurpose: { statusCode: 200, - response: createExpectResults('default', 'space_1', 'space_2', 'space_3'), + response: createExpectResults(...spaces), }, shareSavedObjectsPurpose: { statusCode: 200, - response: createExpectResults('default', 'space_1', 'space_2', 'space_3'), + response: createExpectResults(...spaces), }, includeAuthorizedPurposes: { statusCode: 200, - response: createExpectAllPurposesResults( - authorizedAll, - 'default', - 'space_1', - 'space_2', - 'space_3' - ), + response: createExpectAllPurposesResults(authorizedAll, ...spaces), }, }, }); @@ -141,25 +140,19 @@ export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProv tests: { exists: { statusCode: 200, - response: createExpectResults('default', 'space_1', 'space_2', 'space_3'), + response: createExpectResults(...spaces), }, copySavedObjectsPurpose: { statusCode: 200, - response: createExpectResults('default', 'space_1', 'space_2', 'space_3'), + response: createExpectResults(...spaces), }, shareSavedObjectsPurpose: { statusCode: 200, - response: createExpectResults('default', 'space_1', 'space_2', 'space_3'), + response: createExpectResults(...spaces), }, includeAuthorizedPurposes: { statusCode: 200, - response: createExpectAllPurposesResults( - authorizedAll, - 'default', - 'space_1', - 'space_2', - 'space_3' - ), + response: createExpectAllPurposesResults(authorizedAll, ...spaces), }, }, }); @@ -170,25 +163,19 @@ export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProv tests: { exists: { statusCode: 200, - response: createExpectResults('default', 'space_1', 'space_2', 'space_3'), + response: createExpectResults(...spaces), }, copySavedObjectsPurpose: { statusCode: 200, - response: createExpectResults('default', 'space_1', 'space_2', 'space_3'), + response: createExpectResults(...spaces), }, shareSavedObjectsPurpose: { statusCode: 200, - response: createExpectResults('default', 'space_1', 'space_2', 'space_3'), + response: createExpectResults(...spaces), }, includeAuthorizedPurposes: { statusCode: 200, - response: createExpectAllPurposesResults( - authorizedAll, - 'default', - 'space_1', - 'space_2', - 'space_3' - ), + response: createExpectAllPurposesResults(authorizedAll, ...spaces), }, }, }); @@ -222,7 +209,7 @@ export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProv tests: { exists: { statusCode: 200, - response: createExpectResults('default', 'space_1', 'space_2', 'space_3'), + response: createExpectResults(...spaces), }, copySavedObjectsPurpose: { statusCode: 403, @@ -234,13 +221,7 @@ export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProv }, includeAuthorizedPurposes: { statusCode: 200, - response: createExpectAllPurposesResults( - authorizedRead, - 'default', - 'space_1', - 'space_2', - 'space_3' - ), + response: createExpectAllPurposesResults(authorizedRead, ...spaces), }, }, }); @@ -251,7 +232,7 @@ export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProv tests: { exists: { statusCode: 200, - response: createExpectResults('default', 'space_1', 'space_2', 'space_3'), + response: createExpectResults(...spaces), }, copySavedObjectsPurpose: { statusCode: 403, @@ -263,13 +244,7 @@ export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProv }, includeAuthorizedPurposes: { statusCode: 200, - response: createExpectAllPurposesResults( - authorizedRead, - 'default', - 'space_1', - 'space_2', - 'space_3' - ), + response: createExpectAllPurposesResults(authorizedRead, ...spaces), }, }, }); @@ -476,74 +451,76 @@ export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProv } ); - getAllTest(`machine_learning_admin can't access any spaces from ${scenario.spaceId}`, { - spaceId: scenario.spaceId, - user: scenario.users.machineLearningAdmin, - tests: { - exists: { - statusCode: 403, - response: expectRbacForbidden, - }, - copySavedObjectsPurpose: { - statusCode: 403, - response: expectRbacForbidden, - }, - shareSavedObjectsPurpose: { - statusCode: 403, - response: expectRbacForbidden, - }, - includeAuthorizedPurposes: { - statusCode: 403, - response: expectRbacForbidden, + if (!isServerless) { + getAllTest(`machine_learning_admin can't access any spaces from ${scenario.spaceId}`, { + spaceId: scenario.spaceId, + user: scenario.users.machineLearningAdmin, + tests: { + exists: { + statusCode: 403, + response: expectRbacForbidden, + }, + copySavedObjectsPurpose: { + statusCode: 403, + response: expectRbacForbidden, + }, + shareSavedObjectsPurpose: { + statusCode: 403, + response: expectRbacForbidden, + }, + includeAuthorizedPurposes: { + statusCode: 403, + response: expectRbacForbidden, + }, }, - }, - }); + }); - getAllTest(`machine_learning_user can't access any spaces from ${scenario.spaceId}`, { - spaceId: scenario.spaceId, - user: scenario.users.machineLearningUser, - tests: { - exists: { - statusCode: 403, - response: expectRbacForbidden, - }, - copySavedObjectsPurpose: { - statusCode: 403, - response: expectRbacForbidden, - }, - shareSavedObjectsPurpose: { - statusCode: 403, - response: expectRbacForbidden, - }, - includeAuthorizedPurposes: { - statusCode: 403, - response: expectRbacForbidden, + getAllTest(`machine_learning_user can't access any spaces from ${scenario.spaceId}`, { + spaceId: scenario.spaceId, + user: scenario.users.machineLearningUser, + tests: { + exists: { + statusCode: 403, + response: expectRbacForbidden, + }, + copySavedObjectsPurpose: { + statusCode: 403, + response: expectRbacForbidden, + }, + shareSavedObjectsPurpose: { + statusCode: 403, + response: expectRbacForbidden, + }, + includeAuthorizedPurposes: { + statusCode: 403, + response: expectRbacForbidden, + }, }, - }, - }); + }); - getAllTest(`monitoring_user can't access any spaces from ${scenario.spaceId}`, { - spaceId: scenario.spaceId, - user: scenario.users.monitoringUser, - tests: { - exists: { - statusCode: 403, - response: expectRbacForbidden, - }, - copySavedObjectsPurpose: { - statusCode: 403, - response: expectRbacForbidden, - }, - shareSavedObjectsPurpose: { - statusCode: 403, - response: expectRbacForbidden, - }, - includeAuthorizedPurposes: { - statusCode: 403, - response: expectRbacForbidden, + getAllTest(`monitoring_user can't access any spaces from ${scenario.spaceId}`, { + spaceId: scenario.spaceId, + user: scenario.users.monitoringUser, + tests: { + exists: { + statusCode: 403, + response: expectRbacForbidden, + }, + copySavedObjectsPurpose: { + statusCode: 403, + response: expectRbacForbidden, + }, + shareSavedObjectsPurpose: { + statusCode: 403, + response: expectRbacForbidden, + }, + includeAuthorizedPurposes: { + statusCode: 403, + response: expectRbacForbidden, + }, }, - }, - }); + }); + } }); }); } diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts index 3554b5c872396..556a2f35462a8 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts @@ -12,8 +12,8 @@ export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) // loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); loadTestFile(require.resolve('./create')); loadTestFile(require.resolve('./delete')); - // loadTestFile(require.resolve('./get_all')); - // loadTestFile(require.resolve('./get')); + loadTestFile(require.resolve('./get_all')); + loadTestFile(require.resolve('./get')); loadTestFile(require.resolve('./update')); }); } From 82064198d8ae189e51ea160f475a488c159d572a Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Mon, 6 Jan 2025 18:28:50 +0100 Subject: [PATCH 30/38] Fixes --- .../common/services/test_data_loader.ts | 19 ++++++++++++++++-- ...esolve_copy_to_space_conflicts.agnostic.ts | 20 ++++++++++++++++--- .../apis/index.serverless.ts | 2 +- .../stateful.config_trial.ts | 13 ++++++++++-- .../services/role_scoped_supertest.ts | 5 ++++- 5 files changed, 50 insertions(+), 9 deletions(-) diff --git a/x-pack/test/spaces_api_integration/common/services/test_data_loader.ts b/x-pack/test/spaces_api_integration/common/services/test_data_loader.ts index 13a420b372354..8c691031dce1b 100644 --- a/x-pack/test/spaces_api_integration/common/services/test_data_loader.ts +++ b/x-pack/test/spaces_api_integration/common/services/test_data_loader.ts @@ -10,7 +10,7 @@ import Fs from 'fs/promises'; import type { LegacyUrlAlias } from '@kbn/core-saved-objects-base-server-internal'; import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; -import type { FtrProviderContext } from '../ftr_provider_context'; +import type { FtrProviderContext } from '../deployment_agnostic/ftr_provider_context'; export const SPACE_1 = { id: 'space_1', @@ -86,6 +86,9 @@ export function getTestDataLoader({ getService }: Pick { @@ -122,7 +125,19 @@ export function getTestDataLoader({ getService }: Pick `${type}:${id}`) .join(', ')}` ); - await supertest + + // _update_objects_spaces route is internal in serverless and public in stateful + const supertestWithScope = isServerless + ? await roleScopedSupertest.getSupertestWithRoleScope( + { role: 'admin' }, + { + withCommonHeaders: true, + useCookieHeader: false, + withInternalHeaders: true, + } + ) + : supertest; + await supertestWithScope .post('/api/spaces/_update_objects_spaces') .set('kbn-xsrf', 'xxx') .send({ objects, spacesToAdd, spacesToRemove }) diff --git a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts index af1d74ceeba46..20d479c60c1c5 100644 --- a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts @@ -72,16 +72,29 @@ const getDestinationSpace = (originSpaceId?: string) => { export function resolveCopyToSpaceConflictsSuite(context: DeploymentAgnosticFtrProviderContext) { const testDataLoader = getTestDataLoader(context); - const supertestWithAuth = context.getService('supertest'); const roleScopedSupertest = context.getService('roleScopedSupertest'); + const getSupertestWithAuth = async () => + await roleScopedSupertest.getSupertestWithRoleScope( + { role: 'admin' }, + { + withCommonHeaders: true, + useCookieHeader: false, + withInternalHeaders: true, + } + ); + const getVisualizationAtSpace = async (spaceId: string): Promise> => { - return supertestWithAuth + const supertest = await getSupertestWithAuth(); + + return supertest .get(`${getUrlPrefix(spaceId)}/api/saved_objects/visualization/cts_vis_3_${spaceId}`) .then((response: any) => response.body); }; const getDashboardAtSpace = async (spaceId: string): Promise> => { - return supertestWithAuth + const supertest = await getSupertestWithAuth(); + + return supertest .get(`${getUrlPrefix(spaceId)}/api/saved_objects/dashboard/cts_dashboard_${spaceId}`) .then((response: any) => response.body); }; @@ -208,6 +221,7 @@ export function resolveCopyToSpaceConflictsSuite(context: DeploymentAgnosticFtrP }); const [dashboard, visualization] = await getObjectsAtSpace(destination); + expect(dashboard.attributes.title).to.eql( `This is the ${destination} test space CTS dashboard` ); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts index 556a2f35462a8..4bb9c73c70b87 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts @@ -9,7 +9,7 @@ import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_co export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { describe('spaces api with security', function () { - // loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); + loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); loadTestFile(require.resolve('./create')); loadTestFile(require.resolve('./delete')); loadTestFile(require.resolve('./get_all')); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_trial.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_trial.ts index cd449df1d54e3..a4c22b32cb857 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_trial.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_trial.ts @@ -5,6 +5,15 @@ * 2.0. */ -import { createTestConfig } from '../common/config'; +import { createStatefulTestConfig } from '../../../api_integration/deployment_agnostic/default_configs/stateful.config.base'; +import { services } from '../services'; -export default createTestConfig('security_and_spaces - trial license'); +export default createStatefulTestConfig({ + // @ts-expect-error + services, + testFiles: [require.resolve('./apis')], + junit: { + reportName: + 'X-Pack Spaces API Deployment Agnostic Integration Tests -- security_and_spaces - trial license', + }, +}); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts index 5a88257918e96..895f16a83c3aa 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts @@ -18,6 +18,7 @@ export function RoleScopedSupertestProvider({ getService }: DeploymentAgnosticFt const supertestWithoutAuth = getService('supertestWithoutAuth'); const samlAuth = getService('samlAuth'); const config = getService('config'); + const supertestWithAuth = getService('supertest'); return { async getSupertestWithRoleScope( @@ -32,7 +33,9 @@ export function RoleScopedSupertestProvider({ getService }: DeploymentAgnosticFt const isServerless = config.get('serverless'); if (!user || (license === 'basic' && !isServerless)) { - return new SupertestWithBasicAuth(supertestWithoutAuth, user); + return user?.role === 'admin' + ? supertestWithAuth + : new SupertestWithBasicAuth(supertestWithoutAuth, user); } const isBuiltIn = isBuiltInRole(user.role); From f289e054bcd156080034dd086d4ff5b242f5866a Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Mon, 6 Jan 2025 19:34:35 +0100 Subject: [PATCH 31/38] Fixes --- .../spaces_api_integration/common/config.ts | 4 +--- .../spaces_api_integration/common/services.ts | 2 ++ .../common/services/test_data_loader.ts | 2 +- ...esolve_copy_to_space_conflicts.agnostic.ts | 22 ++++++++++++------- .../services/role_scoped_supertest.ts | 10 +++------ 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/x-pack/test/spaces_api_integration/common/config.ts b/x-pack/test/spaces_api_integration/common/config.ts index 29efed8626663..93d6066b01dde 100644 --- a/x-pack/test/spaces_api_integration/common/config.ts +++ b/x-pack/test/spaces_api_integration/common/config.ts @@ -10,8 +10,6 @@ import path from 'path'; import { REPO_ROOT } from '@kbn/repo-info'; import type { FtrConfigProviderContext } from '@kbn/test'; -import { RoleScopedSupertestProvider } from '../deployment_agnostic/services/role_scoped_supertest'; - interface CreateTestConfigOptions { license: string; disabledPlugins?: string[]; @@ -47,7 +45,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) kibanaServer: config.kibana.functional.get('services.kibanaServer'), spaces: config.xpack.api.get('services.spaces'), usageAPI: config.xpack.api.get('services.usageAPI'), - roleScopedSupertest: RoleScopedSupertestProvider, + roleScopedSupertest: config.xpack.api.get('services.roleScopedSupertest'), samlAuth: () => {}, }, junit: { diff --git a/x-pack/test/spaces_api_integration/common/services.ts b/x-pack/test/spaces_api_integration/common/services.ts index 44ca7ec39c582..0e9428124bc2b 100644 --- a/x-pack/test/spaces_api_integration/common/services.ts +++ b/x-pack/test/spaces_api_integration/common/services.ts @@ -7,8 +7,10 @@ import { services as apiIntegrationServices } from '../../api_integration/services'; import { services as commonServices } from '../../common/services'; +import { RoleScopedSupertestProvider } from '../deployment_agnostic/services/role_scoped_supertest'; export const services = { ...commonServices, usageAPI: apiIntegrationServices.usageAPI, + roleScopedSupertest: RoleScopedSupertestProvider, }; diff --git a/x-pack/test/spaces_api_integration/common/services/test_data_loader.ts b/x-pack/test/spaces_api_integration/common/services/test_data_loader.ts index 8c691031dce1b..5d8868edfd113 100644 --- a/x-pack/test/spaces_api_integration/common/services/test_data_loader.ts +++ b/x-pack/test/spaces_api_integration/common/services/test_data_loader.ts @@ -10,7 +10,7 @@ import Fs from 'fs/promises'; import type { LegacyUrlAlias } from '@kbn/core-saved-objects-base-server-internal'; import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; -import type { FtrProviderContext } from '../deployment_agnostic/ftr_provider_context'; +import type { FtrProviderContext } from '../ftr_provider_context'; export const SPACE_1 = { id: 'space_1', diff --git a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts index 20d479c60c1c5..2edbcaddcfae3 100644 --- a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.agnostic.ts @@ -73,16 +73,22 @@ const getDestinationSpace = (originSpaceId?: string) => { export function resolveCopyToSpaceConflictsSuite(context: DeploymentAgnosticFtrProviderContext) { const testDataLoader = getTestDataLoader(context); const roleScopedSupertest = context.getService('roleScopedSupertest'); + const supertestWithAuth = context.getService('supertest'); + const config = context.getService('config'); + const license = config.get('esTestCluster.license'); + const isServerless = config.get('serverless'); const getSupertestWithAuth = async () => - await roleScopedSupertest.getSupertestWithRoleScope( - { role: 'admin' }, - { - withCommonHeaders: true, - useCookieHeader: false, - withInternalHeaders: true, - } - ); + license === 'basic' && !isServerless + ? supertestWithAuth + : await roleScopedSupertest.getSupertestWithRoleScope( + { role: 'admin' }, + { + withCommonHeaders: true, + useCookieHeader: false, + withInternalHeaders: true, + } + ); const getVisualizationAtSpace = async (spaceId: string): Promise> => { const supertest = await getSupertestWithAuth(); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts index 895f16a83c3aa..0be90b8497f1f 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/services/role_scoped_supertest.ts @@ -18,7 +18,8 @@ export function RoleScopedSupertestProvider({ getService }: DeploymentAgnosticFt const supertestWithoutAuth = getService('supertestWithoutAuth'); const samlAuth = getService('samlAuth'); const config = getService('config'); - const supertestWithAuth = getService('supertest'); + const license = config.get('esTestCluster.license'); + const isServerless = config.get('serverless'); return { async getSupertestWithRoleScope( @@ -29,13 +30,8 @@ export function RoleScopedSupertestProvider({ getService }: DeploymentAgnosticFt withInternalHeaders: true, } ) { - const license = config.get('esTestCluster.license'); - const isServerless = config.get('serverless'); - if (!user || (license === 'basic' && !isServerless)) { - return user?.role === 'admin' - ? supertestWithAuth - : new SupertestWithBasicAuth(supertestWithoutAuth, user); + return new SupertestWithBasicAuth(supertestWithoutAuth, user); } const isBuiltIn = isBuiltInRole(user.role); From 670e1d1d8d7eb534483bc1d079cfb0569ba13a4a Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Mon, 6 Jan 2025 21:03:39 +0100 Subject: [PATCH 32/38] Service fix --- x-pack/test/spaces_api_integration/common/config.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/test/spaces_api_integration/common/config.ts b/x-pack/test/spaces_api_integration/common/config.ts index 93d6066b01dde..324a3607e3513 100644 --- a/x-pack/test/spaces_api_integration/common/config.ts +++ b/x-pack/test/spaces_api_integration/common/config.ts @@ -10,6 +10,8 @@ import path from 'path'; import { REPO_ROOT } from '@kbn/repo-info'; import type { FtrConfigProviderContext } from '@kbn/test'; +import { services } from './services'; + interface CreateTestConfigOptions { license: string; disabledPlugins?: string[]; @@ -45,7 +47,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) kibanaServer: config.kibana.functional.get('services.kibanaServer'), spaces: config.xpack.api.get('services.spaces'), usageAPI: config.xpack.api.get('services.usageAPI'), - roleScopedSupertest: config.xpack.api.get('services.roleScopedSupertest'), + roleScopedSupertest: services.roleScopedSupertest, samlAuth: () => {}, }, junit: { From 703f1e5820272e19adcf8979c848027a441fd852 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Tue, 7 Jan 2025 11:58:03 +0100 Subject: [PATCH 33/38] Fixes --- .../default_configs/stateful.config.base.ts | 2 +- .../deployment_agnostic/common/config.ts | 53 ------------------- .../stateful.config_trial.ts | 2 +- .../stateful.copy_to_space.config_trial.ts | 15 ++++-- 4 files changed, 13 insertions(+), 59 deletions(-) delete mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/common/config.ts diff --git a/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts b/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts index 51be28e97f0a2..1d879c856c7c8 100644 --- a/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts +++ b/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts @@ -41,7 +41,7 @@ export function createStatefulTestConfig { - const { readConfigFile } = context; - const config = { - kibana: { - api: await readConfigFile(path.resolve(REPO_ROOT, 'test/api_integration/config.js')), - functional: await readConfigFile( - require.resolve('@kbn/test-suites-src/functional/config.base') - ), - }, - xpack: { - api: await readConfigFile(require.resolve('../../../api_integration/config.ts')), - }, - }; - - const testConfig = await createStatefulTestConfig({ - services: { - ...services, - es: config.kibana.api.get('services.es'), - // @ts-expect-error - esSupertestWithoutAuth: config.xpack.api.get('services.esSupertestWithoutAuth'), - supertest: config.kibana.api.get('services.supertest'), - supertestWithoutAuth: config.xpack.api.get('services.supertestWithoutAuth'), - retry: config.xpack.api.get('services.retry'), - esArchiver: config.kibana.functional.get('services.esArchiver'), - kibanaServer: config.kibana.functional.get('services.kibanaServer'), - spaces: config.xpack.api.get('services.spaces'), - usageAPI: config.xpack.api.get('services.usageAPI'), - }, - testFiles: testFiles ?? [require.resolve('../security_and_spaces/apis')], - junit: { - reportName: `X-Pack Spaces API Deployment Agnostic Integration Tests -- ${name}`, - }, - })(context); - - return testConfig; - }; -} diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_trial.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_trial.ts index a4c22b32cb857..d2ed6598dade0 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_trial.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.config_trial.ts @@ -9,7 +9,7 @@ import { createStatefulTestConfig } from '../../../api_integration/deployment_ag import { services } from '../services'; export default createStatefulTestConfig({ - // @ts-expect-error + // @ts-expect-error roleScopedSupertest service accepts a user not just a user role and is different from the one in the common services services, testFiles: [require.resolve('./apis')], junit: { diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.copy_to_space.config_trial.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.copy_to_space.config_trial.ts index d188520658843..ac80beaea2372 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.copy_to_space.config_trial.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/stateful.copy_to_space.config_trial.ts @@ -5,8 +5,15 @@ * 2.0. */ -import { createTestConfig } from '../common/config'; +import { createStatefulTestConfig } from '../../../api_integration/deployment_agnostic/default_configs/stateful.config.base'; +import { services } from '../services'; -export default createTestConfig('copy_to_space - trial license', [ - require.resolve('./apis/copy_to_space'), -]); +export default createStatefulTestConfig({ + // @ts-expect-error roleScopedSupertest service accepts a user not just a user role and is different from the one in the common services + services, + testFiles: [require.resolve('./apis/copy_to_space')], + junit: { + reportName: + 'X-Pack Spaces API Deployment Agnostic Integration Tests -- copy_to_space - trial license', + }, +}); From 094aaee8d668092d24642ce1360755934a464a74 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Fri, 17 Jan 2025 18:30:14 +0100 Subject: [PATCH 34/38] Fixes --- .../kbn-es/src/stateful_resources/roles.yml | 61 ---------- .../security_and_spaces/apis/get_all.ts | 80 ------------- .../security_and_spaces/apis/get_all.ts | 113 ++++++++++++++++++ .../security_and_spaces/config_basic.ts | 5 +- 4 files changed, 117 insertions(+), 142 deletions(-) create mode 100644 x-pack/test/spaces_api_integration/security_and_spaces/apis/get_all.ts diff --git a/packages/kbn-es/src/stateful_resources/roles.yml b/packages/kbn-es/src/stateful_resources/roles.yml index a527603aafae2..2bf66f99859d3 100644 --- a/packages/kbn-es/src/stateful_resources/roles.yml +++ b/packages/kbn-es/src/stateful_resources/roles.yml @@ -129,64 +129,3 @@ system_indices_superuser: resources: ['*'] run_as: ['*'] -machine_learning_admin: - cluster: ['manage_ml'] - indices: - - names: ['.ml-anomalies*','.ml-notifications*','.ml-state*','.ml-meta*','.ml-stats-*'] - privileges: ['view_index_metadata', 'read'] - allow_restricted_indices: true - - names: ['.ml-annotations*'] - privileges: ['write', 'read', 'view_index_metadata'] - allow_restricted_indices: true - applications: - - application: 'kibana-*' - privileges: ['reserved_ml_admin'] - resources: ['*'] - run_as: [] - -machine_learning_user: - cluster: ['monitor_ml'] - indices: - - names: ['.ml-anomalies*','.ml-notifications*',] - privileges: ['view_index_metadata', 'read'] - allow_restricted_indices: false - - names: ['.ml-annotations*'] - privileges: ['write', 'read', 'view_index_metadata'] - allow_restricted_indices: false - applications: - - application: 'kibana-*' - privileges: ['reserved_ml_user'] - resources: ['*'] - run_as: [] - -monitoring_user: - cluster: - - "cluster:monitor/main" - - "cluster:monitor/xpack/info" - - "cluster:monitor/remote/info" - indices: - - names: - - ".monitoring-*" - privileges: - - "read" - - "read_cross_cluster" - allow_restricted_indices: false - - names: - - "/metrics-(beats|elasticsearch|enterprisesearch|kibana|logstash).*/" - privileges: - - "read" - - "read_cross_cluster" - allow_restricted_indices: false - - names: - - "metricbeat-*" - privileges: - - "read" - - "read_cross_cluster" - allow_restricted_indices: false - applications: - - application: "kibana-*" - privileges: - - "reserved_monitoring" - resources: - - "*" - run_as: [] diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get_all.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get_all.ts index 5af99492a5c93..39dcf12e9a696 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get_all.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/get_all.ts @@ -14,9 +14,6 @@ export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProv const { getAllTest, createExpectResults, createExpectAllPurposesResults, expectRbacForbidden } = getAllTestSuiteFactory(context); - const config = context.getService('config'); - const isServerless = config.get('serverless'); - const spaces = ['default', 'space_1', 'space_2', 'space_3']; // these are used to determine expected results for tests where the `include_authorized_purposes` option is enabled @@ -56,9 +53,6 @@ export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProv legacyAll: AUTHENTICATION.KIBANA_LEGACY_USER, dualAll: AUTHENTICATION.KIBANA_DUAL_PRIVILEGES_USER, dualRead: AUTHENTICATION.KIBANA_DUAL_PRIVILEGES_DASHBOARD_ONLY_USER, - machineLearningAdmin: AUTHENTICATION.MACHINE_LEARING_ADMIN, - machineLearningUser: AUTHENTICATION.MACHINE_LEARNING_USER, - monitoringUser: AUTHENTICATION.MONITORING_USER, }, }, { @@ -81,9 +75,6 @@ export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProv legacyAll: AUTHENTICATION.KIBANA_LEGACY_USER, dualAll: AUTHENTICATION.KIBANA_DUAL_PRIVILEGES_USER, dualRead: AUTHENTICATION.KIBANA_DUAL_PRIVILEGES_DASHBOARD_ONLY_USER, - machineLearningAdmin: AUTHENTICATION.MACHINE_LEARING_ADMIN, - machineLearningUser: AUTHENTICATION.MACHINE_LEARNING_USER, - monitoringUser: AUTHENTICATION.MONITORING_USER, }, }, /* eslint-enable @typescript-eslint/naming-convention */ @@ -450,77 +441,6 @@ export default function getAllSpacesTestSuite(context: DeploymentAgnosticFtrProv }, } ); - - if (!isServerless) { - getAllTest(`machine_learning_admin can't access any spaces from ${scenario.spaceId}`, { - spaceId: scenario.spaceId, - user: scenario.users.machineLearningAdmin, - tests: { - exists: { - statusCode: 403, - response: expectRbacForbidden, - }, - copySavedObjectsPurpose: { - statusCode: 403, - response: expectRbacForbidden, - }, - shareSavedObjectsPurpose: { - statusCode: 403, - response: expectRbacForbidden, - }, - includeAuthorizedPurposes: { - statusCode: 403, - response: expectRbacForbidden, - }, - }, - }); - - getAllTest(`machine_learning_user can't access any spaces from ${scenario.spaceId}`, { - spaceId: scenario.spaceId, - user: scenario.users.machineLearningUser, - tests: { - exists: { - statusCode: 403, - response: expectRbacForbidden, - }, - copySavedObjectsPurpose: { - statusCode: 403, - response: expectRbacForbidden, - }, - shareSavedObjectsPurpose: { - statusCode: 403, - response: expectRbacForbidden, - }, - includeAuthorizedPurposes: { - statusCode: 403, - response: expectRbacForbidden, - }, - }, - }); - - getAllTest(`monitoring_user can't access any spaces from ${scenario.spaceId}`, { - spaceId: scenario.spaceId, - user: scenario.users.monitoringUser, - tests: { - exists: { - statusCode: 403, - response: expectRbacForbidden, - }, - copySavedObjectsPurpose: { - statusCode: 403, - response: expectRbacForbidden, - }, - shareSavedObjectsPurpose: { - statusCode: 403, - response: expectRbacForbidden, - }, - includeAuthorizedPurposes: { - statusCode: 403, - response: expectRbacForbidden, - }, - }, - }); - } }); }); } diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/get_all.ts b/x-pack/test/spaces_api_integration/security_and_spaces/apis/get_all.ts new file mode 100644 index 0000000000000..4392eb86e6125 --- /dev/null +++ b/x-pack/test/spaces_api_integration/security_and_spaces/apis/get_all.ts @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../common/ftr_provider_context'; +import { AUTHENTICATION } from '../../common/lib/authentication'; +import { SPACES } from '../../common/lib/spaces'; +import { getAllTestSuiteFactory } from '../../common/suites/get_all.agnostic'; + +// eslint-disable-next-line import/no-default-export +export default function getAllSpacesTestSuite(context: FtrProviderContext) { + // @ts-expect-error getAllTestSuiteFactory expects only DeploymentAgnosticFtrProviderContext + const { getAllTest, expectRbacForbidden } = getAllTestSuiteFactory(context); + + describe('get all', () => { + [ + { + spaceId: SPACES.DEFAULT.spaceId, + users: { + machineLearningAdmin: AUTHENTICATION.MACHINE_LEARING_ADMIN, + machineLearningUser: AUTHENTICATION.MACHINE_LEARNING_USER, + monitoringUser: AUTHENTICATION.MONITORING_USER, + }, + }, + { + spaceId: SPACES.SPACE_1.spaceId, + users: { + machineLearningAdmin: AUTHENTICATION.MACHINE_LEARING_ADMIN, + machineLearningUser: AUTHENTICATION.MACHINE_LEARNING_USER, + monitoringUser: AUTHENTICATION.MONITORING_USER, + }, + }, + ].forEach((scenario) => { + getAllTest(`machine_learning_admin can't access any spaces from ${scenario.spaceId}`, { + spaceId: scenario.spaceId, + user: scenario.users.machineLearningAdmin, + tests: { + exists: { + statusCode: 403, + response: expectRbacForbidden, + }, + copySavedObjectsPurpose: { + statusCode: 403, + response: expectRbacForbidden, + }, + shareSavedObjectsPurpose: { + statusCode: 403, + response: expectRbacForbidden, + }, + includeAuthorizedPurposes: { + statusCode: 403, + response: expectRbacForbidden, + }, + }, + }); + + getAllTest(`machine_learning_user can't access any spaces from ${scenario.spaceId}`, { + spaceId: scenario.spaceId, + user: scenario.users.machineLearningUser, + tests: { + exists: { + statusCode: 403, + response: expectRbacForbidden, + }, + copySavedObjectsPurpose: { + statusCode: 403, + response: expectRbacForbidden, + }, + shareSavedObjectsPurpose: { + statusCode: 403, + response: expectRbacForbidden, + }, + includeAuthorizedPurposes: { + statusCode: 403, + response: expectRbacForbidden, + }, + }, + }); + + getAllTest(`monitoring_user can't access any spaces from ${scenario.spaceId}`, { + spaceId: scenario.spaceId, + user: scenario.users.monitoringUser, + tests: { + exists: { + statusCode: 403, + response: expectRbacForbidden, + }, + copySavedObjectsPurpose: { + statusCode: 403, + response: expectRbacForbidden, + }, + shareSavedObjectsPurpose: { + statusCode: 403, + response: expectRbacForbidden, + }, + includeAuthorizedPurposes: { + statusCode: 403, + response: expectRbacForbidden, + }, + }, + }); + }); + }); +} diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/config_basic.ts b/x-pack/test/spaces_api_integration/security_and_spaces/config_basic.ts index 902f8baf026a8..aba9bc9a92d08 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/config_basic.ts +++ b/x-pack/test/spaces_api_integration/security_and_spaces/config_basic.ts @@ -8,4 +8,7 @@ import { createTestConfig } from '../common/config'; // eslint-disable-next-line import/no-default-export -export default createTestConfig('security_and_spaces', { license: 'basic' }); +export default createTestConfig('security_and_spaces', { + license: 'basic', + testFiles: [require.resolve('./apis'), require.resolve('./apis/get_all')], +}); From 23bc2952f41ddac70ae8a9973c15ec67f9520fa7 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Tue, 21 Jan 2025 11:42:47 +0100 Subject: [PATCH 35/38] Added skipInMKI tag and dropped unnecessary builtin roles from check --- .../common/lib/authentication.ts | 10 +--------- .../security_and_spaces/apis/index.serverless.ts | 1 + .../security_and_spaces/apis/index.ts | 1 + 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/x-pack/test/spaces_api_integration/common/lib/authentication.ts b/x-pack/test/spaces_api_integration/common/lib/authentication.ts index a0472f51bd38c..cacc3881ade3f 100644 --- a/x-pack/test/spaces_api_integration/common/lib/authentication.ts +++ b/x-pack/test/spaces_api_integration/common/lib/authentication.ts @@ -335,15 +335,7 @@ export const ROLES = { }; export const isBuiltInRole = (role: string) => - [ - 'admin', - 'viewer', - 'editor', - 'system_indices_superuser', - 'monitoring_user', - 'machine_learning_admin', - 'machine_learning_user', - ].includes(role); + ['admin', 'viewer', 'system_indices_superuser'].includes(role); export const getRoleDefinitionForUser = (user: { role: string }) => ROLES[user.role as keyof typeof ROLES]; diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts index 4bb9c73c70b87..9b0105f37bae2 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts @@ -9,6 +9,7 @@ import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_co export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { describe('spaces api with security', function () { + this.tags('skipMKI'); loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); loadTestFile(require.resolve('./create')); loadTestFile(require.resolve('./delete')); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts index 18d0a16853c6b..cc74a4a404ccc 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts @@ -15,6 +15,7 @@ export default function ({ loadTestFile, getService }: DeploymentAgnosticFtrProv const supertest = getService('supertest'); describe('spaces api with security', function () { + this.tags('skipMKI'); before(async () => { if (license === 'basic') { await createUsersAndRoles(es, supertest); From 12a11e916e88c3b1e0f18075d2d9c7dc25f1dd34 Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Tue, 21 Jan 2025 12:39:34 +0100 Subject: [PATCH 36/38] copy_to_space serverless config --- .../ftr_security_serverless_configs.yml | 1 + .../apis/copy_to_space/copy_to_space.ts | 1 + .../apis/copy_to_space/index.ts | 4 +++- .../serverless.copy_to_space.config.ts | 20 +++++++++++++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/serverless.copy_to_space.config.ts diff --git a/.buildkite/ftr_security_serverless_configs.yml b/.buildkite/ftr_security_serverless_configs.yml index b3aed37c19d74..7337addf34ac9 100644 --- a/.buildkite/ftr_security_serverless_configs.yml +++ b/.buildkite/ftr_security_serverless_configs.yml @@ -127,3 +127,4 @@ enabled: # serverless config files that run deployment-agnostic tests - x-pack/test/api_integration/deployment_agnostic/configs/serverless/security.serverless.config.ts - x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/serverless.config.ts + - x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/serverless.copy_to_space.config.ts diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/copy_to_space.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/copy_to_space.ts index 3e769698d7f55..9827d9b03c455 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/copy_to_space.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/copy_to_space.ts @@ -33,6 +33,7 @@ export default function copyToSpaceSpacesAndSecuritySuite( } = copyToSpaceTestSuiteFactory(context); describe('copy to spaces', () => { + this.tags('skipMKI'); [ { spaceId: SPACES.DEFAULT.spaceId, diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/index.ts index 04ca969807e2d..006ae3c32018c 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/index.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/index.ts @@ -13,10 +13,12 @@ export default function ({ loadTestFile, getService }: DeploymentAgnosticFtrProv const license = config.get('esTestCluster.license'); const es = getService('es'); const supertest = getService('supertest'); + const isServerless = config.get('serverless'); describe('spaces api with security', function () { + this.tags('skipMKI'); before(async () => { - if (license === 'basic') { + if (license === 'basic' && !isServerless) { await createUsersAndRoles(es, supertest); } }); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/serverless.copy_to_space.config.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/serverless.copy_to_space.config.ts new file mode 100644 index 0000000000000..fbfe85fb5b9e0 --- /dev/null +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/serverless.copy_to_space.config.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createServerlessTestConfig } from '../../../api_integration/deployment_agnostic/default_configs/serverless.config.base'; +import { services } from '../services'; + +export default createServerlessTestConfig({ + // @ts-expect-error roleScopedSupertest service accepts a user not just a user role and is different from the one in the common services + services, + serverlessProject: 'security', + testFiles: [require.resolve('./apis/copy_to_space')], + junit: { + reportName: + 'X-Pack Spaces API Deployment Agnostic Integration Tests -- copy_to_space - serverless', + }, +}); From 2064f156afafd6149d0ede09d66895593120e0aa Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Tue, 21 Jan 2025 13:21:24 +0100 Subject: [PATCH 37/38] Fix --- .../security_and_spaces/apis/copy_to_space/copy_to_space.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/copy_to_space.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/copy_to_space.ts index 9827d9b03c455..3e769698d7f55 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/copy_to_space.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/copy_to_space.ts @@ -33,7 +33,6 @@ export default function copyToSpaceSpacesAndSecuritySuite( } = copyToSpaceTestSuiteFactory(context); describe('copy to spaces', () => { - this.tags('skipMKI'); [ { spaceId: SPACES.DEFAULT.spaceId, From 2bd07ce8b0319efce2986baf480bd52803224a3c Mon Sep 17 00:00:00 2001 From: Elena Shostak Date: Tue, 21 Jan 2025 15:02:06 +0100 Subject: [PATCH 38/38] Fixes --- .../spaces_api_integration/common/suites/get_all.agnostic.ts | 3 +++ .../security_and_spaces/apis/copy_to_space/index.ts | 2 ++ .../security_and_spaces/apis/index.serverless.ts | 2 ++ .../deployment_agnostic/security_and_spaces/apis/index.ts | 2 ++ 4 files changed, 9 insertions(+) diff --git a/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts b/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts index b2cc9edf36946..4976f739f3765 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get_all.agnostic.ts @@ -86,7 +86,10 @@ const ALL_SPACE_RESULTS: Space[] = [ 'securitySolutionAttackDiscovery', 'securitySolutionCases', 'securitySolutionCasesV2', + 'securitySolutionNotes', + 'securitySolutionTimeline', 'siem', + 'siemV2', 'slo', 'uptime', ], diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/index.ts index 006ae3c32018c..3dab3cfbf708c 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/index.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/copy_to_space/index.ts @@ -16,6 +16,8 @@ export default function ({ loadTestFile, getService }: DeploymentAgnosticFtrProv const isServerless = config.get('serverless'); describe('spaces api with security', function () { + // Should be enabled when custom roles can be provisioned for MKI + // See: https://github.com/elastic/kibana/issues/207361 this.tags('skipMKI'); before(async () => { if (license === 'basic' && !isServerless) { diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts index 9b0105f37bae2..8a5a03dfa8333 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.serverless.ts @@ -9,6 +9,8 @@ import type { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_co export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { describe('spaces api with security', function () { + // Should be enabled when custom roles can be provisioned for MKI + // See: https://github.com/elastic/kibana/issues/207361 this.tags('skipMKI'); loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); loadTestFile(require.resolve('./create')); diff --git a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts index cc74a4a404ccc..392ae720c9330 100644 --- a/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/deployment_agnostic/security_and_spaces/apis/index.ts @@ -15,6 +15,8 @@ export default function ({ loadTestFile, getService }: DeploymentAgnosticFtrProv const supertest = getService('supertest'); describe('spaces api with security', function () { + // Should we enabled when custom roles can be provisioned for MKI + // See: https://github.com/elastic/kibana/issues/207361 this.tags('skipMKI'); before(async () => { if (license === 'basic') {