diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 26e8d3855bf7b..8435771ead0d5 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -461,8 +461,5 @@ enabled: - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/ess.config.ts - - - - - + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/configs/ess.config.ts diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts index 2f4cb317d5615..7bdf4530c2ab8 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts @@ -146,6 +146,9 @@ export const schema = Joi.object() slow: Joi.number().default(30000), timeout: Joi.number().default(INSPECTING ? 360000 * 100 : 360000), ui: Joi.string().default('bdd'), + rootHooks: Joi.object().keys({ + beforeAll: Joi.func(), + }), }) .default(), diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.ts b/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.ts index 9bc399ea1215d..95e6c320ac843 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.ts @@ -20,6 +20,7 @@ import { Lifecycle } from '../lifecycle'; import { Config } from '../config'; import { ProviderCollection } from '../providers'; import { EsVersion } from '../es_version'; +import { GenericFtrProviderContext } from '../../public_types'; import { MochaReporterProvider } from './reporter'; import { validateCiGroupTags } from './validate_ci_group_tags'; @@ -47,9 +48,24 @@ export async function setupMocha({ reporter, reporterOptions, }: Options) { + const mochaRootHooks = config.get('mochaOpts.rootHooks'); + const rootHookCtx: GenericFtrProviderContext = { + loadTestFile: () => { + throw new Error('loadTestFile is unsupported in root hooks'); + }, + getService: providers.getService as any, + hasService: providers.hasService as any, + getPageObject: providers.getPageObject as any, + getPageObjects: providers.getPageObjects as any, + updateBaselines: config.get('updateBaselines'), + }; + // configure mocha const mocha = new Mocha({ ...config.get('mochaOpts'), + rootHooks: { + beforeAll: () => mochaRootHooks?.beforeAll?.(rootHookCtx), + }, reporter: reporter || (await providers.loadExternalService('mocha reporter', MochaReporterProvider)), reporterOptions, diff --git a/packages/kbn-test/src/functional_tests/run_tests/run_tests.ts b/packages/kbn-test/src/functional_tests/run_tests/run_tests.ts index 81c0039001197..a70ea66cd5c66 100644 --- a/packages/kbn-test/src/functional_tests/run_tests/run_tests.ts +++ b/packages/kbn-test/src/functional_tests/run_tests/run_tests.ts @@ -7,7 +7,7 @@ */ import Path from 'path'; -import { setTimeout } from 'timers/promises'; +import { setTimeout, scheduler } from 'timers/promises'; import { REPO_ROOT } from '@kbn/repo-info'; import { ToolingLog } from '@kbn/tooling-log'; @@ -68,6 +68,7 @@ export async function runTests(log: ToolingLog, options: RunTestsOptions) { } const config = await readConfigFile(log, options.esVersion, path, settingOverrides); + const serverless = config.get('serverless') as boolean; const hasTests = await checkForEnabledTestsInFtrConfig({ config, @@ -113,6 +114,12 @@ export async function runTests(log: ToolingLog, options: RunTestsOptions) { return; } + if (serverless) { + // A dirty temporary solution to wait for ES to process Kibana privileges. + // Stateless ES takes 30-35 seconds to process privileges request sent by Kibana + await scheduler.wait(35000); + } + await runFtr({ log, config, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/index.ts index 94ba07b31bddf..d959179e1e97a 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/index.ts @@ -15,7 +15,6 @@ export default ({ loadTestFile }: FtrProviderContext): void => { // existence being near 0. loadTestFile(require.resolve('./aliases')); - loadTestFile(require.resolve('./check_privileges')); loadTestFile(require.resolve('./create_index')); loadTestFile(require.resolve('./create_rules_bulk')); loadTestFile(require.resolve('./delete_rules')); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/index.ts index 7822d11698c95..b05464606867a 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/index.ts @@ -25,7 +25,6 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./perform_bulk_action')); loadTestFile(require.resolve('./perform_bulk_action_dry_run')); loadTestFile(require.resolve('./patch_rules')); - loadTestFile(require.resolve('./read_privileges')); loadTestFile(require.resolve('./open_close_signals')); loadTestFile(require.resolve('./get_signals_migration_status')); loadTestFile(require.resolve('./create_signals_migrations')); diff --git a/x-pack/test/security_solution_api_integration/config/ess/config.base.ts b/x-pack/test/security_solution_api_integration/config/ess/config.base.ts index b4fbdba6de4c4..d493d76b712fc 100644 --- a/x-pack/test/security_solution_api_integration/config/ess/config.base.ts +++ b/x-pack/test/security_solution_api_integration/config/ess/config.base.ts @@ -8,6 +8,8 @@ import { CA_CERT_PATH } from '@kbn/dev-utils'; import { FtrConfigProviderContext, kbnTestConfig, kibanaTestUser } from '@kbn/test'; import { services } from '../../../api_integration/services'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { setupUsers } from './setup_users'; interface CreateTestConfigOptions { license: string; @@ -107,6 +109,10 @@ export function createTestConfig(options: CreateTestConfigOptions, testFiles?: s }, mochaOpts: { grep: '/^(?!.*@brokenInEss).*@ess.*/', + rootHooks: { + beforeAll: async ({ getService }: FtrProviderContext) => + setupUsers(getService('security')), + }, }, }; }; diff --git a/x-pack/test/security_solution_api_integration/config/ess/setup_users.ts b/x-pack/test/security_solution_api_integration/config/ess/setup_users.ts new file mode 100644 index 0000000000000..d31b44a527918 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/config/ess/setup_users.ts @@ -0,0 +1,30 @@ +/* + * 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 { + KNOWN_ESS_ROLE_DEFINITIONS, + KNOWN_SERVERLESS_ROLE_DEFINITIONS, +} from '@kbn/security-solution-plugin/common/test'; +import { SecurityService } from '../../../../../test/common/services/security/security'; + +export async function setupUsers(securityService: SecurityService): Promise { + const KNOWN_ROLE_DEFINITIONS = [ + ...Object.values(KNOWN_SERVERLESS_ROLE_DEFINITIONS), + ...Object.values(KNOWN_ESS_ROLE_DEFINITIONS), + ]; + + for (const roleDefinition of KNOWN_ROLE_DEFINITIONS) { + await securityService.role.create(roleDefinition.name, roleDefinition); + + await securityService.user.create(roleDefinition.name, { + password: 'changeme', + roles: [roleDefinition.name], + full_name: roleDefinition.name, + email: 'detections-reader@example.com', + }); + } +} diff --git a/x-pack/test/security_solution_api_integration/package.json b/x-pack/test/security_solution_api_integration/package.json index d431f6c0ebed6..272d9c339146b 100644 --- a/x-pack/test/security_solution_api_integration/package.json +++ b/x-pack/test/security_solution_api_integration/package.json @@ -36,6 +36,10 @@ "actions:runner:serverless": "npm run run-tests:dr:default actions serverless serverlessEnv", "actions:qa:serverless": "npm run run-tests:dr:default actions serverless qaEnv", "actions:server:ess": "npm run initialize-server:dr:default actions ess", - "actions:runner:ess": "npm run run-tests:dr:default actions ess essEnv" + "actions:runner:ess": "npm run run-tests:dr:default actions ess essEnv", + "privileges:server:serverless": "npm run initialize-server:dr:default privileges serverless", + "privileges:runner:serverless": "npm run run-tests:dr:default privileges serverless serverlessEnv", + "privileges:server:ess": "npm run initialize-server:dr:default privileges ess", + "privileges:runner:ess": "npm run run-tests:dr:default privileges ess essEnv" } } diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/configs/ess.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/configs/ess.config.ts new file mode 100644 index 0000000000000..a3205ac0927c1 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/configs/ess.config.ts @@ -0,0 +1,22 @@ +/* + * 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile( + require.resolve('../../../../../config/ess/config.base.trial') + ); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('..')], + junit: { + reportName: 'Detection Engine ESS/ Privileges API Integration Tests', + }, + }; +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/configs/serverless.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/configs/serverless.config.ts new file mode 100644 index 0000000000000..65e31e7f0a9b0 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/configs/serverless.config.ts @@ -0,0 +1,15 @@ +/* + * 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 '../../../../../config/serverless/config.base'; + +export default createTestConfig({ + testFiles: [require.resolve('..')], + junit: { + reportName: 'Detection Engine Serverless/ Privileges API Integration Tests', + }, +}); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/ess_only_privileges.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/ess_only_privileges.ts new file mode 100644 index 0000000000000..54b5f1d81d76a --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/ess_only_privileges.ts @@ -0,0 +1,266 @@ +/* + * 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 expect from '@kbn/expect'; +import { DETECTION_ENGINE_PRIVILEGES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { ROLES } from '@kbn/security-solution-plugin/common/test'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('@ess Privileges', () => { + it('should return expected privileges for elastic admin', async () => { + const { body } = await supertest.get(DETECTION_ENGINE_PRIVILEGES_URL).send().expect(200); + expect(body).to.eql({ + username: 'elastic', + has_all_requested: true, + cluster: { + monitor_ml: true, + manage_ccr: true, + manage_index_templates: true, + monitor_watcher: true, + monitor_transform: true, + read_ilm: true, + manage_api_key: true, + manage_security: true, + manage_own_api_key: true, + manage_saml: true, + all: true, + manage_ilm: true, + manage_ingest_pipelines: true, + read_ccr: true, + manage_rollup: true, + monitor: true, + manage_watcher: true, + manage: true, + manage_transform: true, + manage_token: true, + manage_ml: true, + manage_pipeline: true, + monitor_rollup: true, + transport_client: true, + create_snapshot: true, + }, + index: { + '.alerts-security.alerts-default': { + all: true, + manage_ilm: true, + read: true, + create_index: true, + read_cross_cluster: true, + index: true, + monitor: true, + delete: true, + manage: true, + delete_index: true, + create_doc: true, + view_index_metadata: true, + create: true, + manage_follow_index: true, + manage_leader_index: true, + maintenance: true, + write: true, + }, + }, + application: {}, + is_authenticated: true, + has_encryption_key: true, + }); + }); + + it('should return expected privileges for a "detections_admin" user', async () => { + const { body } = await supertestWithoutAuth + .get(DETECTION_ENGINE_PRIVILEGES_URL) + .auth(ROLES.detections_admin, 'changeme') + .send() + .expect(200); + expect(body).to.eql({ + username: 'detections_admin', + has_all_requested: false, + cluster: { + monitor_ml: true, + manage_ccr: false, + manage_index_templates: true, + monitor_watcher: true, + monitor_transform: true, + read_ilm: true, + manage_api_key: false, + manage_security: false, + manage_own_api_key: false, + manage_saml: false, + all: false, + manage_ilm: true, + manage_ingest_pipelines: true, + read_ccr: false, + manage_rollup: true, + monitor: true, + manage_watcher: true, + manage: true, + manage_transform: true, + manage_token: false, + manage_ml: true, + manage_pipeline: true, + monitor_rollup: true, + transport_client: true, + create_snapshot: true, + }, + index: { + '.alerts-security.alerts-default': { + all: false, + manage_ilm: true, + read: true, + create_index: true, + read_cross_cluster: false, + index: true, + monitor: true, + delete: true, + manage: true, + delete_index: true, + create_doc: true, + view_index_metadata: true, + create: true, + manage_follow_index: true, + manage_leader_index: true, + maintenance: true, + write: true, + }, + }, + application: {}, + is_authenticated: true, + has_encryption_key: true, + }); + }); + + it('should return expected privileges for a "reader" user', async () => { + const { body } = await supertestWithoutAuth + .get(DETECTION_ENGINE_PRIVILEGES_URL) + .auth(ROLES.reader, 'changeme') + .send() + .expect(200); + expect(body).to.eql({ + username: 'reader', + has_all_requested: false, + cluster: { + monitor_ml: false, + manage_ccr: false, + manage_index_templates: false, + monitor_watcher: false, + monitor_transform: false, + read_ilm: false, + manage_api_key: false, + manage_security: false, + manage_own_api_key: false, + manage_saml: false, + all: false, + manage_ilm: false, + manage_ingest_pipelines: false, + read_ccr: false, + manage_rollup: false, + monitor: false, + manage_watcher: false, + manage: false, + manage_transform: false, + manage_token: false, + manage_ml: false, + manage_pipeline: false, + monitor_rollup: false, + transport_client: false, + create_snapshot: false, + }, + index: { + '.alerts-security.alerts-default': { + all: false, + manage_ilm: false, + read: true, + create_index: false, + read_cross_cluster: false, + index: false, + monitor: false, + delete: false, + manage: false, + delete_index: false, + create_doc: false, + view_index_metadata: true, + create: false, + manage_follow_index: false, + manage_leader_index: false, + maintenance: true, + write: false, + }, + }, + application: {}, + is_authenticated: true, + has_encryption_key: true, + }); + }); + + it('should return expected privileges for a "hunter" user', async () => { + const { body } = await supertestWithoutAuth + .get(DETECTION_ENGINE_PRIVILEGES_URL) + .auth(ROLES.hunter, 'changeme') + .send() + .expect(200); + expect(body).to.eql({ + username: 'hunter', + has_all_requested: false, + cluster: { + monitor_ml: false, + manage_ccr: false, + manage_index_templates: false, + monitor_watcher: false, + monitor_transform: false, + read_ilm: false, + manage_api_key: false, + manage_security: false, + manage_own_api_key: false, + manage_saml: false, + all: false, + manage_ilm: false, + manage_ingest_pipelines: false, + read_ccr: false, + manage_rollup: false, + monitor: false, + manage_watcher: false, + manage: false, + manage_transform: false, + manage_token: false, + manage_ml: false, + manage_pipeline: false, + monitor_rollup: false, + transport_client: false, + create_snapshot: false, + }, + index: { + '.alerts-security.alerts-default': { + all: false, + manage_ilm: false, + read: true, + create_index: false, + read_cross_cluster: false, + index: true, + monitor: false, + delete: true, + manage: false, + delete_index: false, + create_doc: true, + view_index_metadata: false, + create: true, + manage_follow_index: false, + manage_leader_index: false, + maintenance: false, + write: true, + }, + }, + application: {}, + is_authenticated: true, + has_encryption_key: true, + }); + }); + }); +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/index.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/index.ts new file mode 100644 index 0000000000000..231337ef55342 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/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 { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Privileges', function () { + loadTestFile(require.resolve('./rule_privileges')); + loadTestFile(require.resolve('./privileges')); + loadTestFile(require.resolve('./serverless_only_privileges')); + loadTestFile(require.resolve('./ess_only_privileges')); + }); +} diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_privileges.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/privileges.ts similarity index 59% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_privileges.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/privileges.ts index b95c6771367f4..9f3f665386ee0 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_privileges.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/privileges.ts @@ -7,78 +7,14 @@ import expect from '@kbn/expect'; import { DETECTION_ENGINE_PRIVILEGES_URL } from '@kbn/security-solution-plugin/common/constants'; - import { ROLES } from '@kbn/security-solution-plugin/common/test'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { createUserAndRole, deleteUserAndRole } from '../../../common/services/security_solution'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { - const supertest = getService('supertest'); const supertestWithoutAuth = getService('supertestWithoutAuth'); - describe('read_privileges', () => { - it('should return expected privileges for elastic admin', async () => { - const { body } = await supertest.get(DETECTION_ENGINE_PRIVILEGES_URL).send().expect(200); - expect(body).to.eql({ - username: 'elastic', - has_all_requested: true, - cluster: { - monitor_ml: true, - manage_ccr: true, - manage_index_templates: true, - monitor_watcher: true, - monitor_transform: true, - read_ilm: true, - manage_api_key: true, - manage_security: true, - manage_own_api_key: true, - manage_saml: true, - all: true, - manage_ilm: true, - manage_ingest_pipelines: true, - read_ccr: true, - manage_rollup: true, - monitor: true, - manage_watcher: true, - manage: true, - manage_transform: true, - manage_token: true, - manage_ml: true, - manage_pipeline: true, - monitor_rollup: true, - transport_client: true, - create_snapshot: true, - }, - index: { - '.alerts-security.alerts-default': { - all: true, - manage_ilm: true, - read: true, - create_index: true, - read_cross_cluster: true, - index: true, - monitor: true, - delete: true, - manage: true, - delete_index: true, - create_doc: true, - view_index_metadata: true, - create: true, - manage_follow_index: true, - manage_leader_index: true, - maintenance: true, - write: true, - }, - }, - application: {}, - is_authenticated: true, - has_encryption_key: true, - }); - }); - + describe('@ess @serverless Privileges', () => { it('should return expected privileges for a "t1_analyst" user', async () => { - await createUserAndRole(getService, ROLES.t1_analyst); const { body } = await supertestWithoutAuth .get(DETECTION_ENGINE_PRIVILEGES_URL) .auth(ROLES.t1_analyst, 'changeme') @@ -139,11 +75,9 @@ export default ({ getService }: FtrProviderContext) => { is_authenticated: true, has_encryption_key: true, }); - await deleteUserAndRole(getService, ROLES.t1_analyst); }); it('should return expected privileges for a "t2_analyst" user', async () => { - await createUserAndRole(getService, ROLES.t2_analyst); const { body } = await supertestWithoutAuth .get(DETECTION_ENGINE_PRIVILEGES_URL) .auth(ROLES.t2_analyst, 'changeme') @@ -204,76 +138,9 @@ export default ({ getService }: FtrProviderContext) => { is_authenticated: true, has_encryption_key: true, }); - await deleteUserAndRole(getService, ROLES.t2_analyst); - }); - - it('should return expected privileges for a "hunter" user', async () => { - await createUserAndRole(getService, ROLES.hunter); - const { body } = await supertestWithoutAuth - .get(DETECTION_ENGINE_PRIVILEGES_URL) - .auth(ROLES.hunter, 'changeme') - .send() - .expect(200); - expect(body).to.eql({ - username: 'hunter', - has_all_requested: false, - cluster: { - monitor_ml: false, - manage_ccr: false, - manage_index_templates: false, - monitor_watcher: false, - monitor_transform: false, - read_ilm: false, - manage_api_key: false, - manage_security: false, - manage_own_api_key: false, - manage_saml: false, - all: false, - manage_ilm: false, - manage_ingest_pipelines: false, - read_ccr: false, - manage_rollup: false, - monitor: false, - manage_watcher: false, - manage: false, - manage_transform: false, - manage_token: false, - manage_ml: false, - manage_pipeline: false, - monitor_rollup: false, - transport_client: false, - create_snapshot: false, - }, - index: { - '.alerts-security.alerts-default': { - all: false, - manage_ilm: false, - read: true, - create_index: false, - read_cross_cluster: false, - index: true, - monitor: false, - delete: true, - manage: false, - delete_index: false, - create_doc: true, - view_index_metadata: false, - create: true, - manage_follow_index: false, - manage_leader_index: false, - maintenance: false, - write: true, - }, - }, - application: {}, - is_authenticated: true, - has_encryption_key: true, - }); - await deleteUserAndRole(getService, ROLES.hunter); }); it('should return expected privileges for a "rule_author" user', async () => { - await createUserAndRole(getService, ROLES.rule_author); const { body } = await supertestWithoutAuth .get(DETECTION_ENGINE_PRIVILEGES_URL) .auth(ROLES.rule_author, 'changeme') @@ -334,11 +201,9 @@ export default ({ getService }: FtrProviderContext) => { is_authenticated: true, has_encryption_key: true, }); - await deleteUserAndRole(getService, ROLES.rule_author); }); it('should return expected privileges for a "soc_manager" user', async () => { - await createUserAndRole(getService, ROLES.soc_manager); const { body } = await supertestWithoutAuth .get(DETECTION_ENGINE_PRIVILEGES_URL) .auth(ROLES.soc_manager, 'changeme') @@ -399,11 +264,9 @@ export default ({ getService }: FtrProviderContext) => { is_authenticated: true, has_encryption_key: true, }); - await deleteUserAndRole(getService, ROLES.soc_manager); }); it('should return expected privileges for a "platform_engineer" user', async () => { - await createUserAndRole(getService, ROLES.platform_engineer); const { body } = await supertestWithoutAuth .get(DETECTION_ENGINE_PRIVILEGES_URL) .auth(ROLES.platform_engineer, 'changeme') @@ -464,72 +327,6 @@ export default ({ getService }: FtrProviderContext) => { is_authenticated: true, has_encryption_key: true, }); - await deleteUserAndRole(getService, ROLES.platform_engineer); - }); - - it('should return expected privileges for a "detections_admin" user', async () => { - await createUserAndRole(getService, ROLES.detections_admin); - const { body } = await supertestWithoutAuth - .get(DETECTION_ENGINE_PRIVILEGES_URL) - .auth(ROLES.detections_admin, 'changeme') - .send() - .expect(200); - expect(body).to.eql({ - username: 'detections_admin', - has_all_requested: false, - cluster: { - monitor_ml: true, - manage_ccr: false, - manage_index_templates: true, - monitor_watcher: true, - monitor_transform: true, - read_ilm: true, - manage_api_key: false, - manage_security: false, - manage_own_api_key: false, - manage_saml: false, - all: false, - manage_ilm: true, - manage_ingest_pipelines: true, - read_ccr: false, - manage_rollup: true, - monitor: true, - manage_watcher: true, - manage: true, - manage_transform: true, - manage_token: false, - manage_ml: true, - manage_pipeline: true, - monitor_rollup: true, - transport_client: true, - create_snapshot: true, - }, - index: { - '.alerts-security.alerts-default': { - all: false, - manage_ilm: true, - read: true, - create_index: true, - read_cross_cluster: false, - index: true, - monitor: true, - delete: true, - manage: true, - delete_index: true, - create_doc: true, - view_index_metadata: true, - create: true, - manage_follow_index: true, - manage_leader_index: true, - maintenance: true, - write: true, - }, - }, - application: {}, - is_authenticated: true, - has_encryption_key: true, - }); - await deleteUserAndRole(getService, ROLES.detections_admin); }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/check_privileges.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/rule_privileges.ts similarity index 55% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/check_privileges.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/rule_privileges.ts index 3a016fe68618d..57de1072985fd 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/check_privileges.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/rule_privileges.ts @@ -8,71 +8,76 @@ import expect from '@kbn/expect'; import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; import { ROLES } from '@kbn/security-solution-plugin/common/test'; -import { ThresholdRuleCreateProps } from '@kbn/security-solution-plugin/common/api/detection_engine'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; import { - createSignalsIndex, + createDetectionEngineIndices, deleteAllRules, waitForRulePartialFailure, - getRuleForSignalTesting, createRuleWithAuth, - getThresholdRuleForSignalTesting, deleteAllAlerts, + getCustomQueryRule, + getThresholdRule, } from '../../utils'; -import { createUserAndRole, deleteUserAndRole } from '../../../common/services/security_solution'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const log = getService('log'); const es = getService('es'); - describe('check_privileges', () => { - before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); - await esArchiver.load('x-pack/test/functional/es_archives/security_solution/alias'); - await createSignalsIndex(supertest, log); - }); + const INDEX_WITH_ACCESS = 'logs-test'; + const INDEX_WITH_ACCESS_PATTERN = 'logs-*'; + const INDEX_WITHOUT_ACCESS = 'unknown'; + const INDEX_WITHOUT_ACCESS_PATTERN = 'unknown'; - after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts'); - await esArchiver.unload('x-pack/test/functional/es_archives/security_solution/alias'); + describe('@ess @serverless Rule privileges', () => { + before(async () => { + await createDetectionEngineIndices(supertest, log); await deleteAllAlerts(supertest, log, es); + await es.indices.delete({ index: INDEX_WITH_ACCESS, ignore_unavailable: true }); + await es.indices.delete({ index: INDEX_WITHOUT_ACCESS, ignore_unavailable: true }); + // @timestamp mapping is required for the Threshold rule testing + await es.indices.create({ + index: 'logs-test', + mappings: { + properties: { + '@timestamp': { + type: 'date', + }, + }, + }, + }); }); beforeEach(async () => { await deleteAllRules(supertest, log); }); - afterEach(async () => { - await deleteAllRules(supertest, log); - }); - describe('should set status to partial failure when user has no access', () => { - const indexTestCases = [ - ['host_alias'], - ['host_alias', 'auditbeat-8.0.0'], - ['host_alias*'], - ['host_alias*', 'auditbeat-*'], - ]; - indexTestCases.forEach((index) => { - it(`for KQL rule with index param: ${index}`, async () => { - const rule = { - ...getRuleForSignalTesting(index), + [ + [INDEX_WITHOUT_ACCESS], + [INDEX_WITH_ACCESS, INDEX_WITHOUT_ACCESS], + [INDEX_WITHOUT_ACCESS_PATTERN], + [INDEX_WITH_ACCESS_PATTERN, INDEX_WITHOUT_ACCESS_PATTERN], + ].forEach((indices) => { + it(`for KQL rule with index param: ${indices}`, async () => { + const rule = getCustomQueryRule({ + index: indices, query: 'process.executable: "/usr/bin/sudo"', - }; - await createUserAndRole(getService, ROLES.detections_admin); + from: 'now-1h', + enabled: true, + }); const { id } = await createRuleWithAuth(supertestWithoutAuth, rule, { user: ROLES.detections_admin, pass: 'changeme', }); + await waitForRulePartialFailure({ supertest, log, id, }); + const { body } = await supertest .get(DETECTION_ENGINE_RULES_URL) .set('kbn-xsrf', 'true') @@ -82,36 +87,36 @@ export default ({ getService }: FtrProviderContext) => { // TODO: https://github.com/elastic/kibana/pull/121644 clean up, make type-safe expect(body?.execution_summary?.last_execution.message).to.eql( - `This rule may not have the required read privileges to the following index patterns: ["${index[0]}"]` + `This rule may not have the required read privileges to the following index patterns: ["${INDEX_WITHOUT_ACCESS}"]` ); - - await deleteUserAndRole(getService, ROLES.detections_admin); }); }); - const thresholdIndexTestCases = [ - ['host_alias', 'auditbeat-8.0.0'], - ['host_alias*', 'auditbeat-*'], - ]; - thresholdIndexTestCases.forEach((index) => { - it(`for threshold rule with index param: ${index}`, async () => { - const rule: ThresholdRuleCreateProps = { - ...getThresholdRuleForSignalTesting(index), + [ + [INDEX_WITH_ACCESS, INDEX_WITHOUT_ACCESS], + [INDEX_WITH_ACCESS_PATTERN, INDEX_WITHOUT_ACCESS_PATTERN], + ].forEach((indices) => { + it(`for threshold rule with index param: ${indices}`, async () => { + const rule = getThresholdRule({ + index: indices, threshold: { field: [], - value: 700, + value: 1, }, - }; - await createUserAndRole(getService, ROLES.detections_admin); + from: 'now-1h', + enabled: true, + }); const { id } = await createRuleWithAuth(supertestWithoutAuth, rule, { user: ROLES.detections_admin, pass: 'changeme', }); + await waitForRulePartialFailure({ supertest, log, id, }); + const { body } = await supertest .get(DETECTION_ENGINE_RULES_URL) .set('kbn-xsrf', 'true') @@ -121,10 +126,8 @@ export default ({ getService }: FtrProviderContext) => { // TODO: https://github.com/elastic/kibana/pull/121644 clean up, make type-safe expect(body?.execution_summary?.last_execution.message).to.eql( - `This rule may not have the required read privileges to the following index patterns: ["${index[0]}"]` + `This rule may not have the required read privileges to the following index patterns: ["${INDEX_WITHOUT_ACCESS}"]` ); - - await deleteUserAndRole(getService, ROLES.detections_admin); }); }); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/serverless_only_privileges.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/serverless_only_privileges.ts new file mode 100644 index 0000000000000..6da2bda62d4fa --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/privileges/serverless_only_privileges.ts @@ -0,0 +1,140 @@ +/* + * 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 expect from '@kbn/expect'; +import { DETECTION_ENGINE_PRIVILEGES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { ROLES } from '@kbn/security-solution-plugin/common/test'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('@serverless Privileges', () => { + it('should return expected privileges for elastic admin', async () => { + const { body } = await supertest.get(DETECTION_ENGINE_PRIVILEGES_URL).send().expect(200); + expect(body).to.eql({ + username: 'elastic_serverless', + has_all_requested: true, + cluster: { + monitor_ml: true, + manage_ccr: true, + manage_index_templates: true, + monitor_watcher: true, + monitor_transform: true, + read_ilm: true, + manage_api_key: true, + manage_security: true, + manage_own_api_key: true, + manage_saml: true, + all: true, + manage_ilm: true, + manage_ingest_pipelines: true, + read_ccr: true, + manage_rollup: true, + monitor: true, + manage_watcher: true, + manage: true, + manage_transform: true, + manage_token: true, + manage_ml: true, + manage_pipeline: true, + monitor_rollup: true, + transport_client: true, + create_snapshot: true, + }, + index: { + '.alerts-security.alerts-default': { + all: true, + manage_ilm: true, + read: true, + create_index: true, + read_cross_cluster: true, + index: true, + monitor: true, + delete: true, + manage: true, + delete_index: true, + create_doc: true, + view_index_metadata: true, + create: true, + manage_follow_index: true, + manage_leader_index: true, + maintenance: true, + write: true, + }, + }, + application: {}, + is_authenticated: true, + has_encryption_key: true, + }); + }); + + it('should return expected privileges for a "detections_admin" user', async () => { + const { body } = await supertestWithoutAuth + .get(DETECTION_ENGINE_PRIVILEGES_URL) + .auth(ROLES.detections_admin, 'changeme') + .send() + .expect(200); + expect(body).to.eql({ + username: 'detections_admin', + has_all_requested: false, + cluster: { + monitor_ml: false, + manage_ccr: false, + manage_index_templates: true, + monitor_watcher: false, + monitor_transform: true, + read_ilm: false, + manage_api_key: false, + manage_security: false, + manage_own_api_key: false, + manage_saml: false, + all: false, + manage_ilm: false, + manage_ingest_pipelines: false, + read_ccr: false, + manage_rollup: false, + monitor: false, + manage_watcher: false, + manage: false, + manage_transform: true, + manage_token: false, + manage_ml: false, + manage_pipeline: false, + monitor_rollup: false, + transport_client: false, + create_snapshot: false, + }, + index: { + '.alerts-security.alerts-default': { + all: false, + manage_ilm: true, + read: true, + create_index: true, + read_cross_cluster: false, + index: true, + monitor: true, + delete: true, + manage: true, + delete_index: true, + create_doc: true, + view_index_metadata: true, + create: true, + manage_follow_index: true, + manage_leader_index: true, + maintenance: true, + write: true, + }, + }, + application: {}, + is_authenticated: true, + has_encryption_key: true, + }); + }); + }); +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/create_detection_engine_indices.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/create_detection_engine_indices.ts new file mode 100644 index 0000000000000..fac8d70f91100 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/create_detection_engine_indices.ts @@ -0,0 +1,37 @@ +/* + * 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 { ToolingLog } from '@kbn/tooling-log'; + +import { DETECTION_ENGINE_INDEX_URL } from '@kbn/security-solution-plugin/common/constants'; +import { countDownTest } from './count_down_test'; + +/** + * Creates the detection engine indices for use inside of beforeEach blocks of tests + * This will retry 50 times before giving up and hopefully still not interfere with other tests + * @param supertest The supertest client library + */ +export const createDetectionEngineIndices = async ( + supertest: SuperTest.SuperTest, + log: ToolingLog +): Promise => { + await countDownTest( + async () => { + await supertest + .post(DETECTION_ENGINE_INDEX_URL) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31') + .send(); + return { + passed: true, + }; + }, + 'createDetectionEngineIndices', + log + ); +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/generate_event.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/generate_event.ts new file mode 100644 index 0000000000000..ea7a61f222eec --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/generate_event.ts @@ -0,0 +1,17 @@ +/* + * 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 { SecurityEvent } from './types'; + +export function generateEvent(extra: Record = {}): SecurityEvent { + return { + '@timestamp': Date.now(), + ecs: { version: '1.4.0' }, + event: { kind: 'event', category: 'process', type: 'start' }, + ...extra, + }; +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/index.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/index.ts index 3289ea7d8f7ab..89549354bf401 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/index.ts @@ -16,3 +16,5 @@ export * from './count_down_test'; export * from './count_down_es'; export * from './update_username'; export * from './refresh_index'; +export * from './create_detection_engine_indices'; +export * from './generate_event'; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_auth.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_auth.ts new file mode 100644 index 0000000000000..f465dcf598895 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_auth.ts @@ -0,0 +1,35 @@ +/* + * 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 { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import type { + RuleCreateProps, + RuleResponse, +} from '@kbn/security-solution-plugin/common/api/detection_engine'; + +/** + * Helper to cut down on the noise in some of the tests. + * @param supertest The supertest deps + * @param rule The rule to create + */ +export const createRuleWithAuth = async ( + supertest: SuperTest.SuperTest, + rule: RuleCreateProps, + auth: { user: string; pass: string } +): Promise => { + const { body } = await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31') + .auth(auth.user, auth.pass) + .send(rule) + .expect(200); + + return body; +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_custom_query_rule.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_custom_query_rule.ts new file mode 100644 index 0000000000000..6971e71fc8eda --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_custom_query_rule.ts @@ -0,0 +1,33 @@ +/* + * 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 { QueryRuleCreateProps } from '@kbn/security-solution-plugin/common/api/detection_engine'; +import { CreateRulePropsRewrites } from './types'; + +/** + * Returns custom query rule params that is easy for most basic testing of output of alerts. + * It starts out in an disabled. The 'from' is set very far back to test the basics of signal + * creation and testing by getting all the signals at once. + * + * @param rewrites rule params rewrites, see QueryRuleCreateProps for possible fields + */ +export const getCustomQueryRule = ( + rewrites?: CreateRulePropsRewrites +): QueryRuleCreateProps => ({ + type: 'query', + query: '*:*', + name: 'Custom query rule', + description: 'Custom query rule description', + risk_score: 1, + rule_id: 'rule-1', + severity: 'high', + index: ['auditbeat-*'], + interval: '100m', + from: 'now-6m', + enabled: false, + ...rewrites, +}); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_threshold_rule.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_threshold_rule.ts new file mode 100644 index 0000000000000..2c7ebcb65c2f5 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_threshold_rule.ts @@ -0,0 +1,41 @@ +/* + * 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 { ThresholdRuleCreateProps } from '@kbn/security-solution-plugin/common/api/detection_engine'; +import { CreateRulePropsRewrites } from './types'; + +/** + * This is a typical signal testing rule that is easy for most basic testing of output of Threshold signals. + * It starts out in an enabled true state. The 'from' is set very far back to test the basics of signal + * creation for Threshold and testing by getting all the signals at once. + * @param ruleId The optional ruleId which is threshold-rule by default. + * @param enabled Enables the rule on creation or not. Defaulted to true. + */ +export const getThresholdRule = ( + rewrites?: CreateRulePropsRewrites +): ThresholdRuleCreateProps => ({ + type: 'threshold', + query: '*:*', + index: ['auditbeat-*'], + name: 'Threshold Rule', + description: 'The new rule description.', + severity: 'high', + risk_score: 17, + tags: ['test', 'newRule'], + references: ['http://example.com/', 'https://example.com/'], + false_positives: ['False1', 'False2'], + note: '# test markdown', + threshold: { + field: 'process.name', + value: 21, + }, + interval: '100m', + from: 'now-6m', + max_signals: 100, + enabled: false, + ...rewrites, +}); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/index.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/index.ts index 26862dc62d038..c307b8d9ef0b0 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/index.ts @@ -7,8 +7,11 @@ export * from './get_rule'; export * from './get_simple_rule'; export * from './create_rule'; +export * from './create_rule_with_auth'; export * from './delete_all_rules'; export * from './delete_rule'; +export * from './get_custom_query_rule'; +export * from './get_threshold_rule'; export * from './get_simple_rule_output'; export * from './get_simple_rule_output_without_rule_id'; export * from './get_simple_rule_without_rule_id'; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/types.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/types.ts new file mode 100644 index 0000000000000..f8acb18aa9930 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/types.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export type CreateRulePropsRewrites = Partial>; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/types.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/types.ts new file mode 100644 index 0000000000000..3052ee26e259b --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/types.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. + */ + +export interface SecurityEvent { + [field: string]: unknown; + '@timestamp': number; + ecs: { + version: string; + }; + event: { + kind: 'event'; + category: string; + type: string; + }; +}