From 2f2bde2b48cbaf1958bb091498aad39e17184f33 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Tue, 31 Mar 2026 18:39:45 +0200 Subject: [PATCH] feat(security,serverless): enable UIAM mode by default --- src/cli/serve/serve.js | 8 +- src/cli/serve/serve.test.js | 42 ++-- .../private/kbn-mock-idp-utils/src/utils.ts | 9 +- .../kbn-es/src/cli_commands/serverless.ts | 3 +- .../kbn-es/src/utils/docker_uiam.test.ts | 2 + .../shared/kbn-es/src/utils/docker_uiam.ts | 2 + .../services/saml_auth/saml_auth_provider.ts | 14 ++ .../security_complete.serverless.config.ts | 1 - .../serverless/serverless.base.config.ts | 24 ++- ...bservability_complete.serverless.config.ts | 27 +-- ...ility_logs_essentials.serverless.config.ts | 29 +++ .../serverless/search.serverless.config.ts | 29 +++ .../security_complete.serverless.config.ts | 38 +--- .../security_ease.serverless.config.ts | 29 +++ .../security_essentials.serverless.config.ts | 29 +++ .../workplaceai.serverless.config.ts | 29 +++ .../kbn-test-saml-auth/src/session_manager.ts | 5 +- .../.meta/api/parallel.json | 10 +- .../api/fixtures/constants.ts | 0 .../api/fixtures/index.ts | 0 .../api/lib/wait_for_successful_event_log.ts | 0 .../api/parallel.playwright.config.ts | 0 .../api/parallel_tests/rules.spec.ts | 0 .../uiam_access_token_errors.spec.ts | 4 +- .../api/parallel_tests/uiam_api_keys.spec.ts | 2 +- .../uiam_convert_api_keys.spec.ts | 190 ++++++++--------- .../uiam_create_session.spec.ts | 2 +- .../uiam_invalidate_session.spec.ts | 149 +++++++------ .../uiam_refresh_session.spec.ts | 201 +++++++++--------- .../uiam_use_uiam_credentials.spec.ts | 3 +- .../serverless/api_integration/config.base.ts | 9 - .../configs/security/config.group1.ts | 2 +- .../test_suites/data_usage/mock_api.ts | 3 +- .../test/serverless/shared/config.base.ts | 35 ++- .../complete/functions/summarize.spec.ts | 6 +- .../apis/synthetics/inspect_monitor.ts | 8 +- .../endpoint_exceptions_per_policy_opt_in.ts | 3 +- .../api_integration/configs/config.graph.ts | 2 +- .../api_integration/configs/config.ts | 2 +- .../cloud_security_metering.ts | 2 +- 40 files changed, 565 insertions(+), 388 deletions(-) create mode 100644 src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/observability_logs_essentials.serverless.config.ts create mode 100644 src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/search.serverless.config.ts create mode 100644 src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/security_ease.serverless.config.ts create mode 100644 src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/security_essentials.serverless.config.ts create mode 100644 src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/workplaceai.serverless.config.ts rename x-pack/platform/plugins/shared/alerting/test/{scout_uiam_local => scout}/.meta/api/parallel.json (90%) rename x-pack/platform/plugins/shared/alerting/test/{scout_uiam_local => scout}/api/fixtures/constants.ts (100%) rename x-pack/platform/plugins/shared/alerting/test/{scout_uiam_local => scout}/api/fixtures/index.ts (100%) rename x-pack/platform/plugins/shared/alerting/test/{scout_uiam_local => scout}/api/lib/wait_for_successful_event_log.ts (100%) rename x-pack/platform/plugins/shared/alerting/test/{scout_uiam_local => scout}/api/parallel.playwright.config.ts (100%) rename x-pack/platform/plugins/shared/alerting/test/{scout_uiam_local => scout}/api/parallel_tests/rules.spec.ts (100%) diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index 5c8be1990190c..95b3a8522c283 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -279,8 +279,8 @@ export default function (program) { 'Collect more complete stack traces. See src/cli/dev.js for explanation.' ) .option( - '--uiam', - 'Configure Kibana with Universal Identity and Access Management (UIAM) support when running in serverless project mode.' + '--no-uiam', + 'Prevents configuring Kibana with Universal Identity and Access Management (UIAM) support when running in serverless project mode.' ); } @@ -323,7 +323,7 @@ export default function (program) { cache: !!opts.cache, dist: !!opts.dist, serverless: isServerlessMode, - uiam: isServerlessSamlSupported && !!opts.uiam, + uiam: isServerlessSamlSupported && opts.uiam !== false, }; // In development mode, the main process uses the @kbn/dev-cli-mode @@ -442,7 +442,7 @@ function tryConfigureServerlessSamlProvider(rawConfig, opts, extraCliOptions) { }); } - if (opts.uiam && DEV_UTILS_SUPPORTED) { + if (opts.uiam !== false && DEV_UTILS_SUPPORTED) { // Ensure the key/cert pair is loaded dynamically to exclude it from the production build. // eslint-disable-next-line import/no-dynamic-require const { KBN_CERT_PATH, KBN_KEY_PATH } = require(DEV_UTILS_PATH); diff --git a/src/cli/serve/serve.test.js b/src/cli/serve/serve.test.js index ab430c84d9881..9b7766946b696 100644 --- a/src/cli/serve/serve.test.js +++ b/src/cli/serve/serve.test.js @@ -83,15 +83,22 @@ describe('applyConfigOverrides', () => { }); }); - it('alters config to enable SAML Mock IdP in serverless dev mode', () => { - expect(applyConfigOverrides({}, { dev: true, serverless: true }, {}, {})).toEqual({ + it('alters config to enable SAML Mock IdP and UIAM in serverless dev mode', () => { + expect(applyConfigOverrides({}, { dev: true, serverless: true, uiam: true }, {}, {})).toEqual({ elasticsearch: { hosts: ['https://localhost:9200'], serviceAccountToken: kibanaDevServiceAccount.token, ssl: { certificateAuthorities: expect.stringContaining('ca.crt') }, }, + mockIdpPlugin: { uiam: { enabled: true } }, plugins: { paths: [] }, xpack: { + cloud: { + id: 'local-dev:ZG9ja2VyLmludGVybmFsOjkyMDAkaG9zdDo5MjAwJGtpYmFuYTo5MjAw', + organization_id: 'org1234567890', + projects_url: '', + serverless: { project_id: 'abcdef12345678901234567890123456' }, + }, security: { authc: { providers: { @@ -108,27 +115,30 @@ describe('applyConfigOverrides', () => { }, selector: { enabled: false }, }, + uiam: { + enabled: true, + sharedSecret: 'Dw7eRt5yU2iO9pL3aS4dF6gH8jK0lZ1xC2vB3nM4qW5=', + url: 'https://localhost:8443', + ssl: { + certificate: KBN_CERT_PATH, + key: KBN_KEY_PATH, + verificationMode: 'none', + }, + }, }, }, }); }); - it('alters config to enable UIAM if `--uiam` flag is passed in serverless dev mode', () => { - expect(applyConfigOverrides({}, { dev: true, serverless: true, uiam: true }, {}, {})).toEqual({ + it('omits UIAM config if `--no-uiam` flag is passed in serverless dev mode', () => { + expect(applyConfigOverrides({}, { dev: true, serverless: true, uiam: false }, {}, {})).toEqual({ elasticsearch: { hosts: ['https://localhost:9200'], serviceAccountToken: kibanaDevServiceAccount.token, ssl: { certificateAuthorities: expect.stringContaining('ca.crt') }, }, - mockIdpPlugin: { uiam: { enabled: true } }, plugins: { paths: [] }, xpack: { - cloud: { - id: 'local-dev:ZG9ja2VyLmludGVybmFsOjkyMDAkaG9zdDo5MjAwJGtpYmFuYTo5MjAw', - organization_id: 'org1234567890', - projects_url: '', - serverless: { project_id: 'abcdef12345678901234567890123456' }, - }, security: { authc: { providers: { @@ -145,16 +155,6 @@ describe('applyConfigOverrides', () => { }, selector: { enabled: false }, }, - uiam: { - enabled: true, - sharedSecret: 'Dw7eRt5yU2iO9pL3aS4dF6gH8jK0lZ1xC2vB3nM4qW5=', - url: 'https://localhost:8443', - ssl: { - certificate: KBN_CERT_PATH, - key: KBN_KEY_PATH, - verificationMode: 'none', - }, - }, }, }, }); diff --git a/src/platform/packages/private/kbn-mock-idp-utils/src/utils.ts b/src/platform/packages/private/kbn-mock-idp-utils/src/utils.ts index 1365fb85ffbb0..5f2e0380810a9 100644 --- a/src/platform/packages/private/kbn-mock-idp-utils/src/utils.ts +++ b/src/platform/packages/private/kbn-mock-idp-utils/src/utils.ts @@ -318,11 +318,16 @@ export async function createUiamSessionTokens({ const givenName = fullName ? fullName.split(' ')[0] : 'Test'; const familyName = fullName ? fullName.split(' ').slice(1).join(' ') : 'User'; + // UIAM expects project types to be in a specific format, so we need to convert the project type + // if it's one of the known types. + const uiamProjectType = + projectType === 'oblt' ? 'observability' : projectType === 'es' ? 'elasticsearch' : projectType; + const userSeedResult = await seedTestUser({ userId: username, organizationId, roleId: 'cloud-role-id', - projectType, + projectType: uiamProjectType, applicationRoles: roles, email, firstName: givenName, @@ -358,7 +363,7 @@ export async function createUiamSessionTokens({ { role_id: 'cloud-role-id', organization_id: organizationId, - project_type: projectType, + project_type: uiamProjectType, application_roles: roles, project_scope: { scope: 'all' }, }, diff --git a/src/platform/packages/shared/kbn-es/src/cli_commands/serverless.ts b/src/platform/packages/shared/kbn-es/src/cli_commands/serverless.ts index 0cbba5d3e4ddb..0d291615b5df8 100644 --- a/src/platform/packages/shared/kbn-es/src/cli_commands/serverless.ts +++ b/src/platform/packages/shared/kbn-es/src/cli_commands/serverless.ts @@ -64,7 +64,7 @@ export const serverless: Command = { ${SERVERLESS_RESOURCES_PATHS.map((filePath) => basename(filePath)).join( ' | ' )} - --uiam Configure ES serverless with Universal Identity and Access Management (UIAM) support. + --uiam Configure ES serverless with Universal Identity and Access Management (UIAM) support [default: true]. -E Additional key=value settings to pass to ES -F Absolute paths for files to mount into containers @@ -118,6 +118,7 @@ export const serverless: Command = { kibanaUrl: 'http://localhost:5601/', dataPath: 'stateless', ssl: true, + uiam: true, }, }) as unknown as ServerlessOptions; diff --git a/src/platform/packages/shared/kbn-es/src/utils/docker_uiam.test.ts b/src/platform/packages/shared/kbn-es/src/utils/docker_uiam.test.ts index 8bdfba06a8f84..85b5d17d1422b 100644 --- a/src/platform/packages/shared/kbn-es/src/utils/docker_uiam.test.ts +++ b/src/platform/packages/shared/kbn-es/src/utils/docker_uiam.test.ts @@ -190,6 +190,8 @@ describe(`#runUiamContainer()`, () => { "--env", "quarkus.log.category.\\"co.elastic.cloud.uiam\\".level=DEBUG", "--env", + "quarkus.log.category.\\"co.elastic.cloud.uiam.app.authentication.ClientCertificateExtractor\\".level=INFO", + "--env", "quarkus.log.console.json.enabled=false", "--env", "quarkus.log.level=INFO", diff --git a/src/platform/packages/shared/kbn-es/src/utils/docker_uiam.ts b/src/platform/packages/shared/kbn-es/src/utils/docker_uiam.ts index 37edcd877b26d..087fbb6da0c24 100644 --- a/src/platform/packages/shared/kbn-es/src/utils/docker_uiam.ts +++ b/src/platform/packages/shared/kbn-es/src/utils/docker_uiam.ts @@ -154,6 +154,8 @@ export const UIAM_CONTAINERS = [ '--env', `quarkus.log.category."co.elastic.cloud.uiam".level=${env.UIAM_APP_LOGGING_LEVEL}`, '--env', + `quarkus.log.category."co.elastic.cloud.uiam.app.authentication.ClientCertificateExtractor".level=${env.UIAM_LOGGING_LEVEL}`, + '--env', 'quarkus.log.console.json.enabled=false', '--env', `quarkus.log.level=${env.UIAM_LOGGING_LEVEL}`, diff --git a/src/platform/packages/shared/kbn-ftr-common-functional-services/services/saml_auth/saml_auth_provider.ts b/src/platform/packages/shared/kbn-ftr-common-functional-services/services/saml_auth/saml_auth_provider.ts index 0a406c2d49bb6..67673c3f5315b 100644 --- a/src/platform/packages/shared/kbn-ftr-common-functional-services/services/saml_auth/saml_auth_provider.ts +++ b/src/platform/packages/shared/kbn-ftr-common-functional-services/services/saml_auth/saml_auth_provider.ts @@ -12,6 +12,8 @@ import { SamlSessionManager } from '@kbn/test'; import expect from '@kbn/expect'; import { REPO_ROOT } from '@kbn/repo-info'; import { resolve } from 'path'; +import getopts from 'getopts'; +import type { ServerlessProjectType } from '@kbn/es'; import type { FtrProviderContext } from '../ftr_provider_context'; import { getAuthProvider } from './get_auth_provider'; import type { InternalRequestHeader } from './default_request_headers'; @@ -53,6 +55,11 @@ export function SamlAuthProvider({ getService }: FtrProviderContext) { const customRolesFileName = process.env.ROLES_FILENAME_OVERRIDE; const cloudUsersFilePath = resolve(REPO_ROOT, '.ftr', customRolesFileName ?? 'role_users.json'); + const kbnServerOptions = getopts(config.get('kbnTestServer.serverArgs'), { + boolean: ['xpack.security.uiam.enabled'], + string: ['serverless', 'xpack.cloud.organization_id'], + }); + // Sharing the instance within FTR config run means cookies are persistent for each role between tests. const sessionManager = new SamlSessionManager({ hostOptions: { @@ -69,6 +76,13 @@ export function SamlAuthProvider({ getService }: FtrProviderContext) { sourcePath: authRoleProvider.getRolesDefinitionPath(), }, cloudUsersFilePath, + serverless: !!kbnServerOptions.serverless + ? { + uiam: kbnServerOptions['xpack.security.uiam.enabled'] ?? false, + projectType: kbnServerOptions.serverless as ServerlessProjectType, + organizationId: kbnServerOptions['xpack.cloud.organization_id']!, + } + : undefined, }); const DEFAULT_ROLE = authRoleProvider.getDefaultRole(); diff --git a/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/automatic_import_v2/serverless/security_complete.serverless.config.ts b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/automatic_import_v2/serverless/security_complete.serverless.config.ts index fde85a7f4b568..9104458098fe3 100644 --- a/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/automatic_import_v2/serverless/security_complete.serverless.config.ts +++ b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/automatic_import_v2/serverless/security_complete.serverless.config.ts @@ -12,7 +12,6 @@ import type { ScoutServerConfig } from '../../../../../types'; export const servers: ScoutServerConfig = { ...defaultConfig, - esServerlessOptions: { uiam: true }, kbnTestServer: { ...defaultConfig.kbnTestServer, serverArgs: [ diff --git a/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/default/serverless/serverless.base.config.ts b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/default/serverless/serverless.base.config.ts index 0798c06da6ffb..810a20bbf4343 100644 --- a/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/default/serverless/serverless.base.config.ts +++ b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/default/serverless/serverless.base.config.ts @@ -11,7 +11,7 @@ import { resolve, join } from 'path'; import { format as formatUrl } from 'url'; import Fs from 'fs'; -import { CA_CERT_PATH, kibanaDevServiceAccount } from '@kbn/dev-utils'; +import { CA_CERT_PATH, KBN_CERT_PATH, KBN_KEY_PATH, kibanaDevServiceAccount } from '@kbn/dev-utils'; import { fleetPackageRegistryDockerImage, defineDockerServersConfig, @@ -21,6 +21,8 @@ import { MOCK_IDP_REALM_NAME, MOCK_IDP_UIAM_ORGANIZATION_ID, MOCK_IDP_UIAM_PROJECT_ID, + MOCK_IDP_UIAM_SERVICE_URL, + MOCK_IDP_UIAM_SHARED_SECRET, } from '@kbn/mock-idp-utils'; import { REPO_ROOT } from '@kbn/repo-info'; import type { ScoutServerConfig } from '../../../../../types'; @@ -37,6 +39,9 @@ const dockerArgs: string[] = ['-v', `${packageRegistryConfig}:/package-registry/ */ const dockerRegistryPort: string | undefined = process.env.FLEET_PACKAGE_REGISTRY_PORT; +// Indicates whether the config is used on CI or locally. +const isRunOnCI = process.env.CI; + const servers = { elasticsearch: { protocol: 'https', @@ -94,6 +99,7 @@ export const defaultConfig: ScoutServerConfig = { ], ssl: true, // SSL is required for SAML realm }, + esServerlessOptions: { uiam: true }, kbnTestServer: { buildArgs: [], env: { @@ -150,7 +156,14 @@ export const defaultConfig: ScoutServerConfig = { '--xpack.cloud.base_url=https://fake-cloud.elastic.co', '--xpack.cloud.billing_url=/billing/overview/', '--xpack.cloud.deployments_url=/deployments', - '--xpack.cloud.id=ftr_fake_cloud_id', + // cloud.id is decoded by the security plugin to obtain the ES endpoint for UIAM API key conversion. + // CI: decodes to https://es01:9220 (ES listens on port 9220 inside the Docker network) + // Local: decodes to https://host.docker.internal:9220 (ES is on the host, reached via Docker bridge) + `--xpack.cloud.id=${ + isRunOnCI + ? 'ci:ZXMwMTo5MjIwJDo5MjIwJGtpYmFuYTo5MjIw' + : 'local-dev:ZG9ja2VyLmludGVybmFsOjkyMjAkaG9zdDo5MjIwJGtpYmFuYTo5MjIw' + }`, `--xpack.cloud.organization_id=${MOCK_IDP_UIAM_ORGANIZATION_ID}`, '--xpack.cloud.organization_url=/account/', '--xpack.cloud.profile_url=/user/settings/', @@ -194,6 +207,13 @@ export const defaultConfig: ScoutServerConfig = { ], }, ])}`, + ...(isRunOnCI ? [] : ['--mockIdpPlugin.uiam.enabled=true']), + `--xpack.security.uiam.enabled=true`, + `--xpack.security.uiam.url=${MOCK_IDP_UIAM_SERVICE_URL}`, + `--xpack.security.uiam.sharedSecret=${MOCK_IDP_UIAM_SHARED_SECRET}`, + `--xpack.security.uiam.ssl.certificate=${KBN_CERT_PATH}`, + `--xpack.security.uiam.ssl.key=${KBN_KEY_PATH}`, + '--xpack.security.uiam.ssl.verificationMode=none', ], }, }; diff --git a/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/observability_complete.serverless.config.ts b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/observability_complete.serverless.config.ts index 0cfc9f3754c77..2230fcfdb9424 100644 --- a/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/observability_complete.serverless.config.ts +++ b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/observability_complete.serverless.config.ts @@ -7,28 +7,23 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { MOCK_IDP_UIAM_SERVICE_URL, MOCK_IDP_UIAM_SHARED_SECRET } from '@kbn/mock-idp-utils'; -import { KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils'; -import { servers as defaultConfig } from '../../default/serverless/security_complete.serverless.config'; +import { resolve } from 'path'; +import { REPO_ROOT } from '@kbn/repo-info'; +import { servers as defaultConfig } from '../../default/serverless/observability_complete.serverless.config'; import type { ScoutServerConfig } from '../../../../../types'; -// Indicates whether the config is used on CI or locally. -const isRunOnCI = process.env.CI; +// We need to test certain APIs that are only exposed by the plugin contract and not through +// any HTTP endpoint, so this test plugin exposes these APIs through test HTTP endpoints that +// we can call in our tests. +const pluginPath = `--plugin-path=${resolve( + REPO_ROOT, + 'x-pack/platform/test/security_functional/plugins/test_endpoints' +)}`; export const servers: ScoutServerConfig = { ...defaultConfig, - esServerlessOptions: { uiam: true }, kbnTestServer: { ...defaultConfig.kbnTestServer, - serverArgs: [ - ...defaultConfig.kbnTestServer.serverArgs, - ...(isRunOnCI ? [] : ['--mockIdpPlugin.uiam.enabled=true']), - `--xpack.security.uiam.enabled=true`, - `--xpack.security.uiam.url=${MOCK_IDP_UIAM_SERVICE_URL}`, - `--xpack.security.uiam.sharedSecret=${MOCK_IDP_UIAM_SHARED_SECRET}`, - `--xpack.security.uiam.ssl.certificate=${KBN_CERT_PATH}`, - `--xpack.security.uiam.ssl.key=${KBN_KEY_PATH}`, - '--xpack.security.uiam.ssl.verificationMode=none', - ], + serverArgs: [...defaultConfig.kbnTestServer.serverArgs, pluginPath], }, }; diff --git a/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/observability_logs_essentials.serverless.config.ts b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/observability_logs_essentials.serverless.config.ts new file mode 100644 index 0000000000000..392ba8464b60d --- /dev/null +++ b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/observability_logs_essentials.serverless.config.ts @@ -0,0 +1,29 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { resolve } from 'path'; +import { REPO_ROOT } from '@kbn/repo-info'; +import { servers as defaultConfig } from '../../default/serverless/observability_logs_essentials.serverless.config'; +import type { ScoutServerConfig } from '../../../../../types'; + +// We need to test certain APIs that are only exposed by the plugin contract and not through +// any HTTP endpoint, so this test plugin exposes these APIs through test HTTP endpoints that +// we can call in our tests. +const pluginPath = `--plugin-path=${resolve( + REPO_ROOT, + 'x-pack/platform/test/security_functional/plugins/test_endpoints' +)}`; + +export const servers: ScoutServerConfig = { + ...defaultConfig, + kbnTestServer: { + ...defaultConfig.kbnTestServer, + serverArgs: [...defaultConfig.kbnTestServer.serverArgs, pluginPath], + }, +}; diff --git a/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/search.serverless.config.ts b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/search.serverless.config.ts new file mode 100644 index 0000000000000..729c0517d1d69 --- /dev/null +++ b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/search.serverless.config.ts @@ -0,0 +1,29 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { resolve } from 'path'; +import { REPO_ROOT } from '@kbn/repo-info'; +import { servers as defaultConfig } from '../../default/serverless/search.serverless.config'; +import type { ScoutServerConfig } from '../../../../../types'; + +// We need to test certain APIs that are only exposed by the plugin contract and not through +// any HTTP endpoint, so this test plugin exposes these APIs through test HTTP endpoints that +// we can call in our tests. +const pluginPath = `--plugin-path=${resolve( + REPO_ROOT, + 'x-pack/platform/test/security_functional/plugins/test_endpoints' +)}`; + +export const servers: ScoutServerConfig = { + ...defaultConfig, + kbnTestServer: { + ...defaultConfig.kbnTestServer, + serverArgs: [...defaultConfig.kbnTestServer.serverArgs, pluginPath], + }, +}; diff --git a/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/security_complete.serverless.config.ts b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/security_complete.serverless.config.ts index c2282c3c9886f..3262faa5c973d 100644 --- a/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/security_complete.serverless.config.ts +++ b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/security_complete.serverless.config.ts @@ -7,45 +7,23 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { MOCK_IDP_UIAM_SERVICE_URL, MOCK_IDP_UIAM_SHARED_SECRET } from '@kbn/mock-idp-utils'; import { resolve } from 'path'; import { REPO_ROOT } from '@kbn/repo-info'; -import { KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils'; import { servers as defaultConfig } from '../../default/serverless/security_complete.serverless.config'; import type { ScoutServerConfig } from '../../../../../types'; -// Indicates whether the config is used on CI or locally. -const isRunOnCI = process.env.CI; +// We need to test certain APIs that are only exposed by the plugin contract and not through +// any HTTP endpoint, so this test plugin exposes these APIs through test HTTP endpoints that +// we can call in our tests. +const pluginPath = `--plugin-path=${resolve( + REPO_ROOT, + 'x-pack/platform/test/security_functional/plugins/test_endpoints' +)}`; export const servers: ScoutServerConfig = { ...defaultConfig, - esServerlessOptions: { uiam: true }, kbnTestServer: { ...defaultConfig.kbnTestServer, - serverArgs: [ - ...defaultConfig.kbnTestServer.serverArgs, - ...(isRunOnCI ? [] : ['--mockIdpPlugin.uiam.enabled=true']), - // We need to test certain APIs that are only exposed by the plugin contract and not through - // any HTTP endpoint, so this test plugin exposes these APIs through test HTTP endpoints that - // we can call in our tests. - `--plugin-path=${resolve( - REPO_ROOT, - 'x-pack/platform/test/security_functional/plugins/test_endpoints' - )}`, - `--xpack.security.uiam.enabled=true`, - `--xpack.security.uiam.url=${MOCK_IDP_UIAM_SERVICE_URL}`, - `--xpack.security.uiam.sharedSecret=${MOCK_IDP_UIAM_SHARED_SECRET}`, - `--xpack.security.uiam.ssl.certificate=${KBN_CERT_PATH}`, - `--xpack.security.uiam.ssl.key=${KBN_KEY_PATH}`, - '--xpack.security.uiam.ssl.verificationMode=none', - // cloud.id is decoded by the security plugin to obtain the ES endpoint for UIAM API key conversion. - // CI: decodes to https://es01:9220 (ES listens on port 9220 inside the Docker network) - // Local: decodes to https://host.docker.internal:9220 (ES is on the host, reached via Docker bridge) - `--xpack.cloud.id=${ - isRunOnCI - ? 'ci:ZXMwMTo5MjIwJDo5MjIwJGtpYmFuYTo5MjIw' - : 'local-dev:ZG9ja2VyLmludGVybmFsOjkyMjAkaG9zdDo5MjIwJGtpYmFuYTo5MjIw' - }`, - ], + serverArgs: [...defaultConfig.kbnTestServer.serverArgs, pluginPath], }, }; diff --git a/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/security_ease.serverless.config.ts b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/security_ease.serverless.config.ts new file mode 100644 index 0000000000000..4cf593fc7bea6 --- /dev/null +++ b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/security_ease.serverless.config.ts @@ -0,0 +1,29 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { resolve } from 'path'; +import { REPO_ROOT } from '@kbn/repo-info'; +import { servers as defaultConfig } from '../../default/serverless/security_ease.serverless.config'; +import type { ScoutServerConfig } from '../../../../../types'; + +// We need to test certain APIs that are only exposed by the plugin contract and not through +// any HTTP endpoint, so this test plugin exposes these APIs through test HTTP endpoints that +// we can call in our tests. +const pluginPath = `--plugin-path=${resolve( + REPO_ROOT, + 'x-pack/platform/test/security_functional/plugins/test_endpoints' +)}`; + +export const servers: ScoutServerConfig = { + ...defaultConfig, + kbnTestServer: { + ...defaultConfig.kbnTestServer, + serverArgs: [...defaultConfig.kbnTestServer.serverArgs, pluginPath], + }, +}; diff --git a/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/security_essentials.serverless.config.ts b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/security_essentials.serverless.config.ts new file mode 100644 index 0000000000000..2928dd31ba1ff --- /dev/null +++ b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/security_essentials.serverless.config.ts @@ -0,0 +1,29 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { resolve } from 'path'; +import { REPO_ROOT } from '@kbn/repo-info'; +import { servers as defaultConfig } from '../../default/serverless/security_essentials.serverless.config'; +import type { ScoutServerConfig } from '../../../../../types'; + +// We need to test certain APIs that are only exposed by the plugin contract and not through +// any HTTP endpoint, so this test plugin exposes these APIs through test HTTP endpoints that +// we can call in our tests. +const pluginPath = `--plugin-path=${resolve( + REPO_ROOT, + 'x-pack/platform/test/security_functional/plugins/test_endpoints' +)}`; + +export const servers: ScoutServerConfig = { + ...defaultConfig, + kbnTestServer: { + ...defaultConfig.kbnTestServer, + serverArgs: [...defaultConfig.kbnTestServer.serverArgs, pluginPath], + }, +}; diff --git a/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/workplaceai.serverless.config.ts b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/workplaceai.serverless.config.ts new file mode 100644 index 0000000000000..1958a1aae07ff --- /dev/null +++ b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/uiam_local/serverless/workplaceai.serverless.config.ts @@ -0,0 +1,29 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { resolve } from 'path'; +import { REPO_ROOT } from '@kbn/repo-info'; +import { servers as defaultConfig } from '../../default/serverless/workplaceai.serverless.config'; +import type { ScoutServerConfig } from '../../../../../types'; + +// We need to test certain APIs that are only exposed by the plugin contract and not through +// any HTTP endpoint, so this test plugin exposes these APIs through test HTTP endpoints that +// we can call in our tests. +const pluginPath = `--plugin-path=${resolve( + REPO_ROOT, + 'x-pack/platform/test/security_functional/plugins/test_endpoints' +)}`; + +export const servers: ScoutServerConfig = { + ...defaultConfig, + kbnTestServer: { + ...defaultConfig.kbnTestServer, + serverArgs: [...defaultConfig.kbnTestServer.serverArgs, pluginPath], + }, +}; diff --git a/src/platform/packages/shared/kbn-test-saml-auth/src/session_manager.ts b/src/platform/packages/shared/kbn-test-saml-auth/src/session_manager.ts index 3decb2b7d22c3..b9ebf4cad37c5 100644 --- a/src/platform/packages/shared/kbn-test-saml-auth/src/session_manager.ts +++ b/src/platform/packages/shared/kbn-test-saml-auth/src/session_manager.ts @@ -198,7 +198,10 @@ Set env variable 'TEST_CLOUD=1' to run FTR against your Cloud deployment` const username = this.serverless?.uiam ? createHash('sha256').update(`elastic_${role}`).digest().readUInt32BE(0).toString() : `elastic_${role}`; - this.log.debug(`Creating new local SAML session for a user '${username}' with role '${role}'`); + this.log.debug( + `Creating new local SAML session for a user '${username}' with role '${role}' (serverless: ${!!this + .serverless}, uiam: ${!!this.serverless?.uiam})` + ); return await createLocalSAMLSession({ username, email: `elastic_${role}@elastic.co`, diff --git a/x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/.meta/api/parallel.json b/x-pack/platform/plugins/shared/alerting/test/scout/.meta/api/parallel.json similarity index 90% rename from x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/.meta/api/parallel.json rename to x-pack/platform/plugins/shared/alerting/test/scout/.meta/api/parallel.json index 24f4f9a68df61..c350a6d86ab6c 100644 --- a/x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/.meta/api/parallel.json +++ b/x-pack/platform/plugins/shared/alerting/test/scout/.meta/api/parallel.json @@ -10,7 +10,7 @@ "@cloud-serverless-observability_complete" ], "location": { - "file": "x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/api/parallel_tests/rules.spec.ts", + "file": "x-pack/platform/plugins/shared/alerting/test/scout/api/parallel_tests/rules.spec.ts", "line": 66, "column": 10 } @@ -24,7 +24,7 @@ "@cloud-serverless-observability_complete" ], "location": { - "file": "x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/api/parallel_tests/rules.spec.ts", + "file": "x-pack/platform/plugins/shared/alerting/test/scout/api/parallel_tests/rules.spec.ts", "line": 81, "column": 10 } @@ -38,7 +38,7 @@ "@cloud-serverless-observability_complete" ], "location": { - "file": "x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/api/parallel_tests/rules.spec.ts", + "file": "x-pack/platform/plugins/shared/alerting/test/scout/api/parallel_tests/rules.spec.ts", "line": 91, "column": 10 } @@ -52,10 +52,10 @@ "@cloud-serverless-observability_complete" ], "location": { - "file": "x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/api/parallel_tests/rules.spec.ts", + "file": "x-pack/platform/plugins/shared/alerting/test/scout/api/parallel_tests/rules.spec.ts", "line": 126, "column": 10 } } ] -} \ No newline at end of file +} diff --git a/x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/api/fixtures/constants.ts b/x-pack/platform/plugins/shared/alerting/test/scout/api/fixtures/constants.ts similarity index 100% rename from x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/api/fixtures/constants.ts rename to x-pack/platform/plugins/shared/alerting/test/scout/api/fixtures/constants.ts diff --git a/x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/api/fixtures/index.ts b/x-pack/platform/plugins/shared/alerting/test/scout/api/fixtures/index.ts similarity index 100% rename from x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/api/fixtures/index.ts rename to x-pack/platform/plugins/shared/alerting/test/scout/api/fixtures/index.ts diff --git a/x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/api/lib/wait_for_successful_event_log.ts b/x-pack/platform/plugins/shared/alerting/test/scout/api/lib/wait_for_successful_event_log.ts similarity index 100% rename from x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/api/lib/wait_for_successful_event_log.ts rename to x-pack/platform/plugins/shared/alerting/test/scout/api/lib/wait_for_successful_event_log.ts diff --git a/x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/api/parallel.playwright.config.ts b/x-pack/platform/plugins/shared/alerting/test/scout/api/parallel.playwright.config.ts similarity index 100% rename from x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/api/parallel.playwright.config.ts rename to x-pack/platform/plugins/shared/alerting/test/scout/api/parallel.playwright.config.ts diff --git a/x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/api/parallel_tests/rules.spec.ts b/x-pack/platform/plugins/shared/alerting/test/scout/api/parallel_tests/rules.spec.ts similarity index 100% rename from x-pack/platform/plugins/shared/alerting/test/scout_uiam_local/api/parallel_tests/rules.spec.ts rename to x-pack/platform/plugins/shared/alerting/test/scout/api/parallel_tests/rules.spec.ts diff --git a/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_access_token_errors.spec.ts b/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_access_token_errors.spec.ts index 85c83a2f85a6c..6052409fbb4d9 100644 --- a/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_access_token_errors.spec.ts +++ b/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_access_token_errors.spec.ts @@ -18,10 +18,12 @@ import { expect } from '@kbn/scout/api'; import { ES_CLIENT_AUTHENTICATION_HEADER } from '../../../../common/constants'; -apiTest.describe('UIAM access token errors', { tag: tags.serverless.security.complete }, () => { +// These tests cannot be run on MKI because we cannot obtain the raw UIAM tokens and spin up Mock IdP plugin. +apiTest.describe('[NON-MKI] UIAM access token errors', { tag: tags.serverless.all }, () => { let sessionTokensFactory: (params?: { lifetime: { accessToken: number }; }) => Promise<{ accessToken: string }>; + apiTest.beforeAll(async ({ config: { organizationId, projectType } }) => { sessionTokensFactory = async (params) => { return await createUiamSessionTokens({ diff --git a/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_api_keys.spec.ts b/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_api_keys.spec.ts index b36c082ccc3ea..0b40ed72893b5 100644 --- a/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_api_keys.spec.ts +++ b/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_api_keys.spec.ts @@ -16,7 +16,7 @@ import { COMMON_UNSAFE_HEADERS, extractAttributeValue } from '../fixtures'; // These tests cannot be run on MKI because we cannot obtain the raw UIAM tokens and spin up Mock IdP plugin. apiTest.describe( '[NON-MKI] UIAM API Keys grant and invalidate functions', - { tag: [...tags.serverless.security.complete] }, + { tag: tags.serverless.all }, () => { let userSessionCookieFactory: () => Promise<[string, { accessToken: string }]>; diff --git a/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_convert_api_keys.spec.ts b/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_convert_api_keys.spec.ts index fc73b71df9b5d..914674b6c58c9 100644 --- a/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_convert_api_keys.spec.ts +++ b/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_convert_api_keys.spec.ts @@ -12,109 +12,105 @@ import { expect } from '@kbn/scout/api'; import { COMMON_UNSAFE_HEADERS } from '../fixtures'; // These tests cannot be run on MKI because we cannot spin up Mock IdP plugin. -apiTest.describe( - '[NON-MKI] UIAM API Keys convert function', - { tag: [...tags.serverless.security.complete] }, - () => { - const grantNativeEsApiKey = async ( - samlAuth: SamlAuth, - apiClient: { post: (url: string, options?: any) => Promise } - ) => { - const { cookieHeader } = await samlAuth.asInteractiveUser('admin'); - const grantResponse = await apiClient.post('test_endpoints/api_keys/_grant', { - headers: { ...COMMON_UNSAFE_HEADERS, ...cookieHeader }, +apiTest.describe('[NON-MKI] UIAM API Keys convert function', { tag: tags.serverless.all }, () => { + const grantNativeEsApiKey = async ( + samlAuth: SamlAuth, + apiClient: { post: (url: string, options?: any) => Promise } + ) => { + const { cookieHeader } = await samlAuth.asInteractiveUser('admin'); + const grantResponse = await apiClient.post('test_endpoints/api_keys/_grant', { + headers: { ...COMMON_UNSAFE_HEADERS, ...cookieHeader }, + responseType: 'json', + body: {}, + }); + expect(grantResponse).toHaveStatusCode(200); + return grantResponse.body; + }; + + apiTest( + 'should successfully convert a valid Elasticsearch API key into a UIAM API key', + async ({ apiClient, samlAuth }) => { + const esApiKey = await grantNativeEsApiKey(samlAuth, apiClient); + + const convertResponse = await apiClient.post('test_endpoints/uiam/api_keys/_convert', { + headers: { ...COMMON_UNSAFE_HEADERS }, responseType: 'json', - body: {}, + body: { keys: [esApiKey.encoded] }, }); - expect(grantResponse).toHaveStatusCode(200); - return grantResponse.body; - }; - apiTest( - 'should successfully convert a valid Elasticsearch API key into a UIAM API key', - async ({ apiClient, samlAuth }) => { - const esApiKey = await grantNativeEsApiKey(samlAuth, apiClient); - - const convertResponse = await apiClient.post('test_endpoints/uiam/api_keys/_convert', { - headers: { ...COMMON_UNSAFE_HEADERS }, - responseType: 'json', - body: { keys: [esApiKey.encoded] }, - }); + expect(convertResponse).toHaveStatusCode(200); + expect(convertResponse.body.results).toHaveLength(1); + + const result = convertResponse.body.results[0]; + expect(result.status).toBe('success'); + expect(result.id).toBeDefined(); + expect(typeof result.id).toBe('string'); + expect(result.key).toBeDefined(); + expect(typeof result.key).toBe('string'); + } + ); + + apiTest( + 'should successfully convert multiple Elasticsearch API keys in a single request', + async ({ apiClient, samlAuth }) => { + const esApiKey1 = await grantNativeEsApiKey(samlAuth, apiClient); + const esApiKey2 = await grantNativeEsApiKey(samlAuth, apiClient); + + const convertResponse = await apiClient.post('test_endpoints/uiam/api_keys/_convert', { + headers: { ...COMMON_UNSAFE_HEADERS }, + responseType: 'json', + body: { + keys: [esApiKey1.encoded, esApiKey2.encoded], + }, + }); - expect(convertResponse).toHaveStatusCode(200); - expect(convertResponse.body.results).toHaveLength(1); + expect(convertResponse).toHaveStatusCode(200); + expect(convertResponse.body.results).toHaveLength(2); - const result = convertResponse.body.results[0]; + for (const result of convertResponse.body.results) { expect(result.status).toBe('success'); expect(result.id).toBeDefined(); - expect(typeof result.id).toBe('string'); expect(result.key).toBeDefined(); - expect(typeof result.key).toBe('string'); - } - ); - - apiTest( - 'should successfully convert multiple Elasticsearch API keys in a single request', - async ({ apiClient, samlAuth }) => { - const esApiKey1 = await grantNativeEsApiKey(samlAuth, apiClient); - const esApiKey2 = await grantNativeEsApiKey(samlAuth, apiClient); - - const convertResponse = await apiClient.post('test_endpoints/uiam/api_keys/_convert', { - headers: { ...COMMON_UNSAFE_HEADERS }, - responseType: 'json', - body: { - keys: [esApiKey1.encoded, esApiKey2.encoded], - }, - }); - - expect(convertResponse).toHaveStatusCode(200); - expect(convertResponse.body.results).toHaveLength(2); - - for (const result of convertResponse.body.results) { - expect(result.status).toBe('success'); - expect(result.id).toBeDefined(); - expect(result.key).toBeDefined(); - } } - ); - - apiTest( - 'should return a failed result for an invalid Elasticsearch API key', - async ({ apiClient }) => { - const convertResponse = await apiClient.post('test_endpoints/uiam/api_keys/_convert', { - headers: { ...COMMON_UNSAFE_HEADERS }, - responseType: 'json', - body: { keys: ['dGhpcy1pcy1ub3QtYS12YWxpZC1rZXk='] }, - }); - - expect(convertResponse).toHaveStatusCode(200); - expect(convertResponse.body.results).toHaveLength(1); - expect(convertResponse.body.results[0].status).toBe('failed'); - } - ); - - apiTest( - 'should handle a mix of valid and invalid keys in a single request', - async ({ apiClient, samlAuth }) => { - const esApiKey = await grantNativeEsApiKey(samlAuth, apiClient); - - const convertResponse = await apiClient.post('test_endpoints/uiam/api_keys/_convert', { - headers: { ...COMMON_UNSAFE_HEADERS }, - responseType: 'json', - body: { - keys: [esApiKey.encoded, 'dGhpcy1pcy1ub3QtYS12YWxpZC1rZXk='], - }, - }); - - expect(convertResponse).toHaveStatusCode(200); - expect(convertResponse.body.results).toHaveLength(2); - - const [validResult, invalidResult] = convertResponse.body.results; - expect(validResult.status).toBe('success'); - expect(validResult.id).toBeDefined(); - expect(validResult.key).toBeDefined(); - expect(invalidResult.status).toBe('failed'); - } - ); - } -); + } + ); + + apiTest( + 'should return a failed result for an invalid Elasticsearch API key', + async ({ apiClient }) => { + const convertResponse = await apiClient.post('test_endpoints/uiam/api_keys/_convert', { + headers: { ...COMMON_UNSAFE_HEADERS }, + responseType: 'json', + body: { keys: ['dGhpcy1pcy1ub3QtYS12YWxpZC1rZXk='] }, + }); + + expect(convertResponse).toHaveStatusCode(200); + expect(convertResponse.body.results).toHaveLength(1); + expect(convertResponse.body.results[0].status).toBe('failed'); + } + ); + + apiTest( + 'should handle a mix of valid and invalid keys in a single request', + async ({ apiClient, samlAuth }) => { + const esApiKey = await grantNativeEsApiKey(samlAuth, apiClient); + + const convertResponse = await apiClient.post('test_endpoints/uiam/api_keys/_convert', { + headers: { ...COMMON_UNSAFE_HEADERS }, + responseType: 'json', + body: { + keys: [esApiKey.encoded, 'dGhpcy1pcy1ub3QtYS12YWxpZC1rZXk='], + }, + }); + + expect(convertResponse).toHaveStatusCode(200); + expect(convertResponse.body.results).toHaveLength(2); + + const [validResult, invalidResult] = convertResponse.body.results; + expect(validResult.status).toBe('success'); + expect(validResult.id).toBeDefined(); + expect(validResult.key).toBeDefined(); + expect(invalidResult.status).toBe('failed'); + } + ); +}); diff --git a/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_create_session.spec.ts b/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_create_session.spec.ts index d62138350346c..c41e048c8e9ec 100644 --- a/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_create_session.spec.ts +++ b/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_create_session.spec.ts @@ -10,7 +10,7 @@ import { expect } from '@kbn/scout/api'; import { COMMON_HEADERS } from '../fixtures'; -apiTest.describe('Create new UIAM session', { tag: tags.serverless.security.complete }, () => { +apiTest.describe('Create new UIAM session', { tag: tags.serverless.all }, () => { apiTest('should be able to authenticate as UIAM user', async ({ apiClient, samlAuth }) => { const { cookieHeader } = await samlAuth.asInteractiveUser('viewer'); const response = await apiClient.get('internal/security/me', { diff --git a/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_invalidate_session.spec.ts b/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_invalidate_session.spec.ts index fe7991b4d5533..980e66b01b9f6 100644 --- a/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_invalidate_session.spec.ts +++ b/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_invalidate_session.spec.ts @@ -23,87 +23,84 @@ import { ES_CLIENT_AUTHENTICATION_HEADER } from '../../../../common/constants'; import { COMMON_HEADERS, extractAttributeValue } from '../fixtures'; // These tests cannot be run on MKI because we cannot obtain the raw UIAM tokens required to verify their invalidation. -apiTest.describe( - '[NON-MKI] Invalidate UIAM session', - { tag: [...tags.serverless.security.complete] }, - () => { - let userSessionCookieFactory: () => Promise< - [string, { accessToken: string; refreshToken: string }] - >; - apiTest.beforeAll(async ({ apiClient, kbnUrl, config: { organizationId, projectType } }) => { - userSessionCookieFactory = async () => { - const samlResponse = await createSAMLResponse({ - kibanaUrl: kbnUrl.get('/api/security/saml/callback'), - username: '1234567890', - email: 'elastic_viewer@elastic.co', - roles: ['viewer'], - serverless: { - uiamEnabled: true, - organizationId: organizationId!, - projectType: projectType!, - }, - }); +apiTest.describe('[NON-MKI] Invalidate UIAM session', { tag: tags.serverless.all }, () => { + let userSessionCookieFactory: () => Promise< + [string, { accessToken: string; refreshToken: string }] + >; - const decodedSamlResponse = Buffer.from(samlResponse, 'base64').toString('utf-8'); - return [ - parseCookie( - ( - await apiClient.post('api/security/saml/callback', { - body: `SAMLResponse=${encodeURIComponent(samlResponse)}`, - }) - ).headers['set-cookie'][0] - )!.cookieString(), - { - accessToken: extractAttributeValue( - decodedSamlResponse, - MOCK_IDP_ATTRIBUTE_UIAM_ACCESS_TOKEN - ), - refreshToken: extractAttributeValue( - decodedSamlResponse, - MOCK_IDP_ATTRIBUTE_UIAM_REFRESH_TOKEN - ), - }, - ]; - }; - }); + apiTest.beforeAll(async ({ apiClient, kbnUrl, config: { organizationId, projectType } }) => { + userSessionCookieFactory = async () => { + const samlResponse = await createSAMLResponse({ + kibanaUrl: kbnUrl.get('/api/security/saml/callback'), + username: '1234567890', + email: 'elastic_viewer@elastic.co', + roles: ['viewer'], + serverless: { + uiamEnabled: true, + organizationId: organizationId!, + projectType: projectType!, + }, + }); - apiTest( - 'UIAM session tokens should not be usable after logging out', - async ({ apiClient, log }) => { - // 1. Check that session is valid and UIAM accepts the tokens. - const [userSessionCookie, { accessToken, refreshToken }] = await userSessionCookieFactory(); - let response = await apiClient.get('internal/security/me', { - headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, - responseType: 'json', - }); - expect(response).toHaveStatusCode(200); - expect(response.body).toStrictEqual(expect.objectContaining({ username: '1234567890' })); - // Check only access token here, refresh token will be checked after logout. - expect((await checkUiamAccessToken(accessToken)).status).toBe(200); + const decodedSamlResponse = Buffer.from(samlResponse, 'base64').toString('utf-8'); + return [ + parseCookie( + ( + await apiClient.post('api/security/saml/callback', { + body: `SAMLResponse=${encodeURIComponent(samlResponse)}`, + }) + ).headers['set-cookie'][0] + )!.cookieString(), + { + accessToken: extractAttributeValue( + decodedSamlResponse, + MOCK_IDP_ATTRIBUTE_UIAM_ACCESS_TOKEN + ), + refreshToken: extractAttributeValue( + decodedSamlResponse, + MOCK_IDP_ATTRIBUTE_UIAM_REFRESH_TOKEN + ), + }, + ]; + }; + }); + + apiTest( + 'UIAM session tokens should not be usable after logging out', + async ({ apiClient, log }) => { + // 1. Check that session is valid and UIAM accepts the tokens. + const [userSessionCookie, { accessToken, refreshToken }] = await userSessionCookieFactory(); + let response = await apiClient.get('internal/security/me', { + headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, + responseType: 'json', + }); + expect(response).toHaveStatusCode(200); + expect(response.body).toStrictEqual(expect.objectContaining({ username: '1234567890' })); + // Check only access token here, refresh token will be checked after logout. + expect((await checkUiamAccessToken(accessToken)).status).toBe(200); - // 2. Logout to invalidate the session and tokens. - response = await apiClient.get('api/security/logout', { - headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, - responseType: 'json', - }); - expect(response).toHaveStatusCode(302); + // 2. Logout to invalidate the session and tokens. + response = await apiClient.get('api/security/logout', { + headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, + responseType: 'json', + }); + expect(response).toHaveStatusCode(302); - log.info('Waiting for the UIAM refresh 3s grace period to lapse (+5s)…'); - await setTimeoutAsync(5000); - log.info('UIAM refresh grace period wait time is over, making the request again.'); + log.info('Waiting for the UIAM refresh 3s grace period to lapse (+5s)…'); + await setTimeoutAsync(5000); + log.info('UIAM refresh grace period wait time is over, making the request again.'); - // 3. Check that session is invalidated and UIAM no longer accepts the tokens. - response = await apiClient.get('internal/security/me', { - headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, - responseType: 'json', - }); - expect(response).toHaveStatusCode(401); - expect((await checkUiamAccessToken(accessToken)).status).toBe(401); - expect((await checkUiamRefreshToken(refreshToken)).status).toBe(401); - } - ); - } -); + // 3. Check that session is invalidated and UIAM no longer accepts the tokens. + response = await apiClient.get('internal/security/me', { + headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, + responseType: 'json', + }); + expect(response).toHaveStatusCode(401); + expect((await checkUiamAccessToken(accessToken)).status).toBe(401); + expect((await checkUiamRefreshToken(refreshToken)).status).toBe(401); + } + ); +}); const checkUiamAccessToken = async (accessToken: string) => await fetch(`${MOCK_IDP_UIAM_SERVICE_URL}/uiam/api/v1/authentication/_authenticate`, { diff --git a/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_refresh_session.spec.ts b/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_refresh_session.spec.ts index 3ea98965d1e80..e12a4108f0292 100644 --- a/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_refresh_session.spec.ts +++ b/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_refresh_session.spec.ts @@ -15,115 +15,112 @@ import { expect } from '@kbn/scout/api'; import { COMMON_HEADERS } from '../fixtures'; // These tests cannot be run on MKI because we cannot control the UIAM session lifetime to reduce it (it's 1h by default). -apiTest.describe( - '[NON-MKI] Refresh UIAM session', - { tag: [...tags.serverless.security.complete] }, - () => { - let userSessionCookieFactory: (params: { - lifetime: { accessToken: number; refreshToken?: number }; - }) => Promise; - apiTest.beforeAll(async ({ apiClient, kbnUrl, config: { organizationId, projectType } }) => { - userSessionCookieFactory = async ({ lifetime: { accessToken, refreshToken } }) => - parseCookie( - ( - await apiClient.post('api/security/saml/callback', { - body: `SAMLResponse=${encodeURIComponent( - await createSAMLResponse({ - kibanaUrl: kbnUrl.get('/api/security/saml/callback'), - username: '1234567890', - email: 'elastic_viewer@elastic.co', - roles: ['viewer'], - serverless: { - uiamEnabled: true, - organizationId: organizationId!, - projectType: projectType!, - accessTokenLifetimeSec: accessToken, - refreshTokenLifetimeSec: refreshToken, - }, - }) - )}`, - }) - ).headers['set-cookie'][0] - )!.cookieString(); - }); +apiTest.describe('[NON-MKI] Refresh UIAM session', { tag: tags.serverless.all }, () => { + let userSessionCookieFactory: (params: { + lifetime: { accessToken: number; refreshToken?: number }; + }) => Promise; - apiTest( - 'should be able to authenticate as UIAM user even if the access token has expired', - async ({ apiClient, log }) => { - const userSessionCookie = await userSessionCookieFactory({ lifetime: { accessToken: 2 } }); - let response = await apiClient.get('internal/security/me', { - headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, - responseType: 'json', - }); - expect(response).toHaveStatusCode(200); - expect(response.body).toStrictEqual(expect.objectContaining({ username: '1234567890' })); + apiTest.beforeAll(async ({ apiClient, kbnUrl, config: { organizationId, projectType } }) => { + userSessionCookieFactory = async ({ lifetime: { accessToken, refreshToken } }) => + parseCookie( + ( + await apiClient.post('api/security/saml/callback', { + body: `SAMLResponse=${encodeURIComponent( + await createSAMLResponse({ + kibanaUrl: kbnUrl.get('/api/security/saml/callback'), + username: '1234567890', + email: 'elastic_viewer@elastic.co', + roles: ['viewer'], + serverless: { + uiamEnabled: true, + organizationId: organizationId!, + projectType: projectType!, + accessTokenLifetimeSec: accessToken, + refreshTokenLifetimeSec: refreshToken, + }, + }) + )}`, + }) + ).headers['set-cookie'][0] + )!.cookieString(); + }); - log.info('Waiting for the UIAM session to expire (+5s)…'); - await setTimeoutAsync(5000); - log.info('Session expiration wait time is over, making the request again.'); + apiTest( + 'should be able to authenticate as UIAM user even if the access token has expired', + async ({ apiClient, log }) => { + const userSessionCookie = await userSessionCookieFactory({ lifetime: { accessToken: 2 } }); + let response = await apiClient.get('internal/security/me', { + headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, + responseType: 'json', + }); + expect(response).toHaveStatusCode(200); + expect(response.body).toStrictEqual(expect.objectContaining({ username: '1234567890' })); - response = await apiClient.get('internal/security/me', { - headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, - responseType: 'json', - }); - expect(response).toHaveStatusCode(200); - expect(response.body).toStrictEqual(expect.objectContaining({ username: '1234567890' })); - } - ); + log.info('Waiting for the UIAM session to expire (+5s)…'); + await setTimeoutAsync(5000); + log.info('Session expiration wait time is over, making the request again.'); + + response = await apiClient.get('internal/security/me', { + headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, + responseType: 'json', + }); + expect(response).toHaveStatusCode(200); + expect(response.body).toStrictEqual(expect.objectContaining({ username: '1234567890' })); + } + ); - apiTest( - 'should be able to authenticate as UIAM user when the tokens are refreshed concurrently', - async ({ apiClient, log }) => { - const userSessionCookie = await userSessionCookieFactory({ lifetime: { accessToken: 2 } }); - const response = await apiClient.get('internal/security/me', { - headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, - responseType: 'json', - }); - expect(response).toHaveStatusCode(200); - expect(response.body).toStrictEqual(expect.objectContaining({ username: '1234567890' })); + apiTest( + 'should be able to authenticate as UIAM user when the tokens are refreshed concurrently', + async ({ apiClient, log }) => { + const userSessionCookie = await userSessionCookieFactory({ lifetime: { accessToken: 2 } }); + const response = await apiClient.get('internal/security/me', { + headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, + responseType: 'json', + }); + expect(response).toHaveStatusCode(200); + expect(response.body).toStrictEqual(expect.objectContaining({ username: '1234567890' })); - log.info('Waiting for the UIAM session to expire (+5s)…'); - await setTimeoutAsync(5000); - log.info('Session expiration wait time is over, making the request again.'); + log.info('Waiting for the UIAM session to expire (+5s)…'); + await setTimeoutAsync(5000); + log.info('Session expiration wait time is over, making the request again.'); - const responses = await Promise.all( - Array.from({ length: 10 }, () => - apiClient.get('internal/security/me', { - headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, - responseType: 'json', - }) - ) - ); - for (const res of responses) { - expect(res).toHaveStatusCode(200); - expect(res.body).toStrictEqual(expect.objectContaining({ username: '1234567890' })); - } + const responses = await Promise.all( + Array.from({ length: 10 }, () => + apiClient.get('internal/security/me', { + headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, + responseType: 'json', + }) + ) + ); + for (const res of responses) { + expect(res).toHaveStatusCode(200); + expect(res.body).toStrictEqual(expect.objectContaining({ username: '1234567890' })); } - ); + } + ); - apiTest( - 'should fail if both access and refresh tokens have expired', - async ({ apiClient, log }) => { - const userSessionCookie = await userSessionCookieFactory({ - lifetime: { accessToken: 2, refreshToken: 2 }, - }); - let response = await apiClient.get('internal/security/me', { - headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, - responseType: 'json', - }); - expect(response).toHaveStatusCode(200); - expect(response.body).toStrictEqual(expect.objectContaining({ username: '1234567890' })); + apiTest( + 'should fail if both access and refresh tokens have expired', + async ({ apiClient, log }) => { + const userSessionCookie = await userSessionCookieFactory({ + lifetime: { accessToken: 2, refreshToken: 2 }, + }); + let response = await apiClient.get('internal/security/me', { + headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, + responseType: 'json', + }); + expect(response).toHaveStatusCode(200); + expect(response.body).toStrictEqual(expect.objectContaining({ username: '1234567890' })); - log.info('Waiting for the UIAM session to expire (+5s)…'); - await setTimeoutAsync(5000); - log.info('Session expiration wait time is over, making the request again.'); + log.info('Waiting for the UIAM session to expire (+5s)…'); + await setTimeoutAsync(5000); + log.info('Session expiration wait time is over, making the request again.'); - response = await apiClient.get('internal/security/me', { - headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, - responseType: 'json', - }); - expect(response).toHaveStatusCode(401); - } - ); - } -); + response = await apiClient.get('internal/security/me', { + headers: { ...COMMON_HEADERS, Cookie: userSessionCookie }, + responseType: 'json', + }); + expect(response).toHaveStatusCode(401); + } + ); +}); diff --git a/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_use_uiam_credentials.spec.ts b/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_use_uiam_credentials.spec.ts index 4d940f3371577..13b3508e60905 100644 --- a/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_use_uiam_credentials.spec.ts +++ b/x-pack/platform/plugins/shared/security/test/scout_uiam_local/api/parallel_tests/uiam_use_uiam_credentials.spec.ts @@ -24,9 +24,10 @@ import { COMMON_HEADERS, COMMON_UNSAFE_HEADERS, extractAttributeValue } from '.. // These tests cannot be run on MKI because we cannot obtain the raw UIAM tokens and spin up Mock IdP plugin. apiTest.describe( '[NON-MKI] Use UIAM credentials for various purposes in real and fake requests', - { tag: [...tags.serverless.security.complete] }, + { tag: tags.serverless.all }, () => { let userSessionCookieFactory: () => Promise<[string, { accessToken: string }]>; + apiTest.beforeAll(async ({ apiClient, kbnUrl, config: { organizationId, projectType } }) => { userSessionCookieFactory = async () => { const samlResponse = await createSAMLResponse({ diff --git a/x-pack/platform/test/serverless/api_integration/config.base.ts b/x-pack/platform/test/serverless/api_integration/config.base.ts index 9573bb651bf13..3309e595177d8 100644 --- a/x-pack/platform/test/serverless/api_integration/config.base.ts +++ b/x-pack/platform/test/serverless/api_integration/config.base.ts @@ -7,7 +7,6 @@ import { dockerRegistryPort, type FtrConfigProviderContext } from '@kbn/test'; import { ScoutTestRunConfigCategory } from '@kbn/scout-info'; -import { MOCK_IDP_UIAM_SERVICE_URL, MOCK_IDP_UIAM_SHARED_SECRET } from '@kbn/mock-idp-utils'; import { services } from './services'; import type { CreateTestConfigOptions } from '../shared/types'; @@ -47,14 +46,6 @@ export function createTestConfig(options: CreateTestConfigOptions) { ...svlSharedConfig.get('kbnTestServer.serverArgs'), `--serverless=${options.serverlessProject}`, ...(options.kbnServerArgs || []), - ...(options.esServerlessOptions?.uiam - ? [ - '--mockIdpPlugin.uiam.enabled=true', - `--xpack.security.uiam.enabled=true`, - `--xpack.security.uiam.url=${MOCK_IDP_UIAM_SERVICE_URL}`, - `--xpack.security.uiam.sharedSecret=${MOCK_IDP_UIAM_SHARED_SECRET}`, - ] - : []), ...(enableFleetDockerRegistry && dockerRegistryPort ? [`--xpack.fleet.registryUrl=http://localhost:${dockerRegistryPort}`] : []), diff --git a/x-pack/platform/test/serverless/api_integration/configs/security/config.group1.ts b/x-pack/platform/test/serverless/api_integration/configs/security/config.group1.ts index d395085eca98c..20973b3fd5e73 100644 --- a/x-pack/platform/test/serverless/api_integration/configs/security/config.group1.ts +++ b/x-pack/platform/test/serverless/api_integration/configs/security/config.group1.ts @@ -47,7 +47,7 @@ export default createTestConfig({ // useful for testing (also enabled in MKI QA) '--coreApp.allowDynamicConfigOverrides=true', `--xpack.securitySolutionServerless.cloudSecurityUsageReportingTaskInterval=5s`, - `--xpack.securitySolutionServerless.usageApi.url=http://localhost:8081`, + `--xpack.securitySolutionServerless.usageApi.url=http://localhost:8089`, '--xpack.dataUsage.enabled=true', '--xpack.dataUsage.enableExperimental=[]', // dataUsage.autoops* config is set in kibana controller diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_usage/mock_api.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_usage/mock_api.ts index e4973ba603192..c0d5bd9ed2d0e 100644 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_usage/mock_api.ts +++ b/x-pack/platform/test/serverless/api_integration/test_suites/data_usage/mock_api.ts @@ -9,6 +9,7 @@ import { createServer } from '@mswjs/http-middleware'; import type { UsageMetricsAutoOpsResponseSchemaBody } from '@kbn/data-usage-plugin/server/services/autoops_api'; import type { StrictResponse } from 'msw'; import { http, HttpResponse } from 'msw'; +import { MOCK_IDP_UIAM_PROJECT_ID } from '@kbn/mock-idp-utils'; import { mockAutoOpsResponse } from './mock_data'; export const setupMockServer = () => { @@ -17,7 +18,7 @@ export const setupMockServer = () => { }; const autoOpsHandler = http.post( - '/monitoring/serverless/v1/projects/fakeprojectid/metrics', + `/monitoring/serverless/v1/projects/${MOCK_IDP_UIAM_PROJECT_ID}/metrics`, async ({ request }): Promise> => { return HttpResponse.json(mockAutoOpsResponse); } diff --git a/x-pack/platform/test/serverless/shared/config.base.ts b/x-pack/platform/test/serverless/shared/config.base.ts index e9c0e5542a91b..39f75759906e1 100644 --- a/x-pack/platform/test/serverless/shared/config.base.ts +++ b/x-pack/platform/test/serverless/shared/config.base.ts @@ -16,12 +16,21 @@ import { kibanaTestSuperuserServerless, getDockerFileMountPath, } from '@kbn/test'; -import { CA_CERT_PATH, kibanaDevServiceAccount } from '@kbn/dev-utils'; -import { MOCK_IDP_REALM_NAME } from '@kbn/mock-idp-utils'; +import { CA_CERT_PATH, KBN_CERT_PATH, KBN_KEY_PATH, kibanaDevServiceAccount } from '@kbn/dev-utils'; +import { + MOCK_IDP_REALM_NAME, + MOCK_IDP_UIAM_ORGANIZATION_ID, + MOCK_IDP_UIAM_PROJECT_ID, + MOCK_IDP_UIAM_SERVICE_URL, + MOCK_IDP_UIAM_SHARED_SECRET, +} from '@kbn/mock-idp-utils'; import path from 'path'; import { fleetPackageRegistryDockerImage, defineDockerServersConfig } from '@kbn/test'; import { services as svlServices } from './services'; +// Indicates whether the config is used on CI or locally. +const isRunOnCI = process.env.CI; + export default async () => { const packageRegistryConfig = path.join(__dirname, './common/package_registry_config.yml'); const dockerArgs: string[] = ['-v', `${packageRegistryConfig}:/package-registry/config.yml`]; @@ -97,7 +106,7 @@ export default async () => { ], ssl: true, // SSL is required for SAML realm }, - + esServerlessOptions: { uiam: true }, kbnTestServer: { buildArgs: [], env: { @@ -166,9 +175,16 @@ export default async () => { // configure security reponse header report-to settings to mimic MKI configuration `--csp.report_to=${JSON.stringify(['violations-endpoint'])}`, `--permissionsPolicy.report_to=${JSON.stringify(['violations-endpoint'])}`, - // normally below is injected by control plane - '--xpack.cloud.id=ftr_fake_cloud_id', - `--xpack.cloud.serverless.project_id=fakeprojectid`, + // cloud.id is decoded by the security plugin to obtain the ES endpoint for UIAM API key conversion. + // CI: decodes to https://es01:9220 (ES listens on port 9220 inside the Docker network) + // Local: decodes to https://host.docker.internal:9220 (ES is on the host, reached via Docker bridge) + `--xpack.cloud.id=${ + isRunOnCI + ? 'ci:ZXMwMTo5MjIwJDo5MjIwJGtpYmFuYTo5MjIw' + : 'local-dev:ZG9ja2VyLmludGVybmFsOjkyMjAkaG9zdDo5MjIwJGtpYmFuYTo5MjIw' + }`, + `--xpack.cloud.organization_id=${MOCK_IDP_UIAM_ORGANIZATION_ID}`, + `--xpack.cloud.serverless.project_id=${MOCK_IDP_UIAM_PROJECT_ID}`, `--xpack.cloud.base_url=https://fake-cloud.elastic.co`, `--xpack.cloud.projects_url=/projects/`, `--xpack.cloud.profile_url=/user/settings/`, @@ -176,6 +192,13 @@ export default async () => { `--xpack.cloud.deployments_url=/deployments`, `--xpack.cloud.organization_url=/account/`, `--xpack.cloud.users_and_roles_url=/account/members/`, + ...(isRunOnCI ? [] : ['--mockIdpPlugin.uiam.enabled=true']), + `--xpack.security.uiam.enabled=true`, + `--xpack.security.uiam.url=${MOCK_IDP_UIAM_SERVICE_URL}`, + `--xpack.security.uiam.sharedSecret=${MOCK_IDP_UIAM_SHARED_SECRET}`, + `--xpack.security.uiam.ssl.certificate=${KBN_CERT_PATH}`, + `--xpack.security.uiam.ssl.key=${KBN_KEY_PATH}`, + '--xpack.security.uiam.ssl.verificationMode=none', ], }, diff --git a/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/ai_assistant/complete/functions/summarize.spec.ts b/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/ai_assistant/complete/functions/summarize.spec.ts index 30c3a47a051ec..18bcf8b1f0acc 100644 --- a/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/ai_assistant/complete/functions/summarize.spec.ts +++ b/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/ai_assistant/complete/functions/summarize.spec.ts @@ -18,10 +18,13 @@ import { import { clearKnowledgeBase, getKnowledgeBaseEntriesFromApi } from '../../utils/knowledge_base'; export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const config = getService('config'); const log = getService('log'); const es = getService('es'); const observabilityAIAssistantAPIClient = getService('observabilityAIAssistantApi'); + const isServerless = config.get('serverless'); + describe('tool: summarize', function () { // LLM Proxy is not yet support in MKI: https://github.com/elastic/obs-ai-assistant-team/issues/199 this.tags(['skipCloud']); @@ -75,7 +78,8 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon expect(isPublic).to.eql(false); expect(text).to.eql('Hello world'); expect(type).to.eql('contextual'); - expect(user?.name).to.eql('elastic_editor'); + // In serverless, usernames of Cloud users are purely numeric. + expect(user?.name).to.eql(isServerless ? '2180895557' : 'elastic_editor'); expect(title).to.eql('My Title'); expect(res.body.entries).to.have.length(1); }); diff --git a/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/synthetics/inspect_monitor.ts b/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/synthetics/inspect_monitor.ts index c3f24f354190c..f4dd83fd688d1 100644 --- a/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/synthetics/inspect_monitor.ts +++ b/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/synthetics/inspect_monitor.ts @@ -79,7 +79,7 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { result: { publicConfigs: [ rawExpect.objectContaining({ - cloud_id: 'ftr_fake_cloud_id', + cloud_id: rawExpect.any(String), license_level: rawExpect.any(String), monitors: [ { @@ -123,7 +123,7 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { ], }, ], - output: { hosts: [] }, + output: { hosts: rawExpect.any(Array) }, }), ], privateConfig: null, @@ -196,8 +196,8 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { }, ], license_level: rawExpect.any(String), - cloud_id: 'ftr_fake_cloud_id', - output: { hosts: [] }, + cloud_id: rawExpect.any(String), + output: { hosts: rawExpect.any(Array) }, }), ], privateConfig: null, diff --git a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/edr_workflows/artifacts/trial_license_complete_tier/endpoint_exceptions_per_policy_opt_in.ts b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/edr_workflows/artifacts/trial_license_complete_tier/endpoint_exceptions_per_policy_opt_in.ts index 2835342ce6d60..3f2c68f793711 100644 --- a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/edr_workflows/artifacts/trial_license_complete_tier/endpoint_exceptions_per_policy_opt_in.ts +++ b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/edr_workflows/artifacts/trial_license_complete_tier/endpoint_exceptions_per_policy_opt_in.ts @@ -25,7 +25,8 @@ export default function endpointExceptionsPerPolicyOptInTests({ getService }: Ft const endpointArtifactTestResources = getService('endpointArtifactTestResources'); const isServerless = config.get('serverless'); - const username = isServerless ? 'elastic_admin' : 'elastic'; + // In serverless, usernames of Cloud users are purely numeric. + const username = isServerless ? '1954128031' : 'elastic'; const superuserRole = isServerless ? 'admin' : 'elastic'; const IS_ENDPOINT_EXCEPTION_MOVE_FF_ENABLED = ( diff --git a/x-pack/solutions/security/test/serverless/api_integration/configs/config.graph.ts b/x-pack/solutions/security/test/serverless/api_integration/configs/config.graph.ts index fe7b92832f135..8f03fb8b028f9 100644 --- a/x-pack/solutions/security/test/serverless/api_integration/configs/config.graph.ts +++ b/x-pack/solutions/security/test/serverless/api_integration/configs/config.graph.ts @@ -19,7 +19,7 @@ export default createTestConfig({ `--xpack.task_manager.unsafe.exclude_task_types=${JSON.stringify(['Fleet-Metrics-Task'])}`, '--coreApp.allowDynamicConfigOverrides=true', `--xpack.securitySolutionServerless.cloudSecurityUsageReportingTaskInterval=5s`, - `--xpack.securitySolutionServerless.usageApi.url=http://localhost:8081`, + `--xpack.securitySolutionServerless.usageApi.url=http://localhost:8089`, '--xpack.dataUsage.enabled=true', '--xpack.dataUsage.enableExperimental=[]', '--xpack.dataUsage.autoops.enabled=true', diff --git a/x-pack/solutions/security/test/serverless/api_integration/configs/config.ts b/x-pack/solutions/security/test/serverless/api_integration/configs/config.ts index f437df3b5c48e..a0c64c0e7acf7 100644 --- a/x-pack/solutions/security/test/serverless/api_integration/configs/config.ts +++ b/x-pack/solutions/security/test/serverless/api_integration/configs/config.ts @@ -25,7 +25,7 @@ export default createTestConfig({ // useful for testing (also enabled in MKI QA) '--coreApp.allowDynamicConfigOverrides=true', `--xpack.securitySolutionServerless.cloudSecurityUsageReportingTaskInterval=5s`, - `--xpack.securitySolutionServerless.usageApi.url=http://localhost:8081`, + `--xpack.securitySolutionServerless.usageApi.url=http://localhost:8089`, '--xpack.dataUsage.enabled=true', '--xpack.dataUsage.enableExperimental=[]', // dataUsage.autoops* config is set in kibana controller diff --git a/x-pack/solutions/security/test/serverless/api_integration/test_suites/cloud_security_posture/serverless_metering/cloud_security_metering.ts b/x-pack/solutions/security/test/serverless/api_integration/test_suites/cloud_security_posture/serverless_metering/cloud_security_metering.ts index 8a3b455e1cf6a..61f995bc26151 100644 --- a/x-pack/solutions/security/test/serverless/api_integration/test_suites/cloud_security_posture/serverless_metering/cloud_security_metering.ts +++ b/x-pack/solutions/security/test/serverless/api_integration/test_suites/cloud_security_posture/serverless_metering/cloud_security_metering.ts @@ -51,7 +51,7 @@ export default function (providerContext: FtrProviderContext) { let roleAuthc: RoleCredentials; let internalRequestHeader: { 'x-elastic-internal-origin': string; 'kbn-xsrf': string }; before(async () => { - mockUsageApiServer = mockUsageApiApp.listen(8081); // Start the usage api mock server on port 8081 + mockUsageApiServer = mockUsageApiApp.listen(8089); // Start the usage api mock server on port 8089 }); beforeEach(async () => {