diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/delete/index.ts b/x-pack/plugins/alerting/common/routes/rule/apis/delete/index.ts new file mode 100644 index 00000000000000..d1440870677560 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/delete/index.ts @@ -0,0 +1,12 @@ +/* + * 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 { deleteRuleRequestParamsSchema } from './schemas/latest'; +export type { DeleteRuleRequestParams } from './types/latest'; + +export { deleteRuleRequestParamsSchema as deleteRuleRequestParamsSchemaV1 } from './schemas/v1'; +export type { DeleteRuleRequestParams as DeleteRuleRequestParamsV1 } from './types/v1'; diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/delete/schemas/latest.ts b/x-pack/plugins/alerting/common/routes/rule/apis/delete/schemas/latest.ts new file mode 100644 index 00000000000000..25300c97a6d2e1 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/delete/schemas/latest.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 * from './v1'; diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/delete/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/delete/schemas/v1.ts new file mode 100644 index 00000000000000..bd79d1b7f395ee --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/delete/schemas/v1.ts @@ -0,0 +1,12 @@ +/* + * 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 { schema } from '@kbn/config-schema'; + +export const deleteRuleRequestParamsSchema = schema.object({ + id: schema.string(), +}); diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/delete/types/latest.ts b/x-pack/plugins/alerting/common/routes/rule/apis/delete/types/latest.ts new file mode 100644 index 00000000000000..25300c97a6d2e1 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/delete/types/latest.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 * from './v1'; diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/delete/types/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/delete/types/v1.ts new file mode 100644 index 00000000000000..8e019949e8139d --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/delete/types/v1.ts @@ -0,0 +1,11 @@ +/* + * 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 { TypeOf } from '@kbn/config-schema'; +import { deleteRuleRequestParamsSchemaV1 } from '..'; + +export type DeleteRuleRequestParams = TypeOf; diff --git a/x-pack/plugins/alerting/server/rules_client/tests/delete.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/delete/delete_rule.test.ts similarity index 89% rename from x-pack/plugins/alerting/server/rules_client/tests/delete.test.ts rename to x-pack/plugins/alerting/server/application/rule/methods/delete/delete_rule.test.ts index ca62ca9f5ad7f0..8d96334ca8713f 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/delete.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/delete/delete_rule.test.ts @@ -7,7 +7,7 @@ import { AlertConsumers } from '@kbn/rule-data-utils'; -import { RulesClient, ConstructorOptions } from '../rules_client'; +import { RulesClient, ConstructorOptions } from '../../../../rules_client/rules_client'; import { savedObjectsClientMock, loggingSystemMock, @@ -15,21 +15,21 @@ import { uiSettingsServiceMock, } from '@kbn/core/server/mocks'; import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; -import { ruleTypeRegistryMock } from '../../rule_type_registry.mock'; -import { alertingAuthorizationMock } from '../../authorization/alerting_authorization.mock'; +import { ruleTypeRegistryMock } from '../../../../rule_type_registry.mock'; +import { alertingAuthorizationMock } from '../../../../authorization/alerting_authorization.mock'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; import { actionsAuthorizationMock } from '@kbn/actions-plugin/server/mocks'; -import { AlertingAuthorization } from '../../authorization/alerting_authorization'; +import { AlertingAuthorization } from '../../../../authorization/alerting_authorization'; import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; -import { getBeforeSetup } from './lib'; -import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; -import { migrateLegacyActions } from '../lib'; -import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; -import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; -import { backfillClientMock } from '../../backfill_client/backfill_client.mock'; - -jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { +import { getBeforeSetup } from '../../../../rules_client/tests/lib'; +import { bulkMarkApiKeysForInvalidation } from '../../../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; +import { migrateLegacyActions } from '../../../../rules_client/lib'; +import { ConnectorAdapterRegistry } from '../../../../connector_adapters/connector_adapter_registry'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; +import { backfillClientMock } from '../../../../backfill_client/backfill_client.mock'; + +jest.mock('../../../../rules_client/lib/siem_legacy_actions/migrate_legacy_actions', () => { return { migrateLegacyActions: jest.fn(), }; @@ -40,7 +40,7 @@ jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { resultedReferences: [], }); -jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ +jest.mock('../../../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ bulkMarkApiKeysForInvalidation: jest.fn(), })); @@ -140,7 +140,11 @@ describe('delete()', () => { test('successfully removes an alert', async () => { const result = await rulesClient.delete({ id: '1' }); expect(result).toEqual({ success: true }); - expect(unsecuredSavedObjectsClient.delete).toHaveBeenCalledWith(RULE_SAVED_OBJECT_TYPE, '1'); + expect(unsecuredSavedObjectsClient.delete).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + undefined + ); expect(taskManager.removeIfExists).toHaveBeenCalledWith('task-123'); expect(bulkMarkApiKeysForInvalidation).toHaveBeenCalledTimes(1); expect(bulkMarkApiKeysForInvalidation).toHaveBeenCalledWith( @@ -163,10 +167,18 @@ describe('delete()', () => { const result = await rulesClient.delete({ id: '1' }); expect(result).toEqual({ success: true }); - expect(unsecuredSavedObjectsClient.delete).toHaveBeenCalledWith(RULE_SAVED_OBJECT_TYPE, '1'); + expect(unsecuredSavedObjectsClient.delete).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + undefined + ); expect(taskManager.removeIfExists).toHaveBeenCalledWith('task-123'); expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith(RULE_SAVED_OBJECT_TYPE, '1'); + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + undefined + ); expect(rulesClientParams.logger.error).toHaveBeenCalledWith( 'delete(): Failed to load API key to invalidate on alert 1: Fail' ); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/delete/delete_rule.ts b/x-pack/plugins/alerting/server/application/rule/methods/delete/delete_rule.ts new file mode 100644 index 00000000000000..75163b8dff5cb4 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/delete/delete_rule.ts @@ -0,0 +1,127 @@ +/* + * 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 Boom from '@hapi/boom'; +import { AlertConsumers } from '@kbn/rule-data-utils'; +import { RawRule } from '../../../../types'; +import { WriteOperations, AlertingAuthorizationEntity } from '../../../../authorization'; +import { retryIfConflicts } from '../../../../lib/retry_if_conflicts'; +import { bulkMarkApiKeysForInvalidation } from '../../../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; +import { ruleAuditEvent, RuleAuditAction } from '../../../../rules_client/common/audit_events'; +import { RulesClientContext } from '../../../../rules_client/types'; +import { untrackRuleAlerts, migrateLegacyActions } from '../../../../rules_client/lib'; +import { RuleAttributes } from '../../../../data/rule/types'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; +import { DeleteRuleParams } from './types'; +import { deleteRuleParamsSchema } from './schemas'; +import { deleteRuleSo, getDecryptedRuleSo, getRuleSo } from '../../../../data/rule'; + +export async function deleteRule(context: RulesClientContext, params: DeleteRuleParams) { + try { + deleteRuleParamsSchema.validate(params); + } catch (error) { + throw Boom.badRequest(`Error validating delete params - ${error.message}`); + } + + const { id } = params; + + return await retryIfConflicts( + context.logger, + `rulesClient.delete('${id}')`, + async () => await deleteRuleWithOCC(context, { id }) + ); +} + +async function deleteRuleWithOCC(context: RulesClientContext, { id }: { id: string }) { + let taskIdToRemove: string | undefined | null; + let apiKeyToInvalidate: string | null = null; + let apiKeyCreatedByUser: boolean | undefined | null = false; + let attributes: RuleAttributes; + + try { + const decryptedRule = await getDecryptedRuleSo({ + encryptedSavedObjectsClient: context.encryptedSavedObjectsClient, + id, + savedObjectsGetOptions: { + namespace: context.namespace, + }, + }); + apiKeyToInvalidate = decryptedRule.attributes.apiKey; + apiKeyCreatedByUser = decryptedRule.attributes.apiKeyCreatedByUser; + taskIdToRemove = decryptedRule.attributes.scheduledTaskId; + attributes = decryptedRule.attributes; + } catch (e) { + // We'll skip invalidating the API key since we failed to load the decrypted saved object + context.logger.error( + `delete(): Failed to load API key to invalidate on alert ${id}: ${e.message}` + ); + + // Still attempt to load the scheduledTaskId using SOC + const rule = await getRuleSo({ + savedObjectsClient: context.unsecuredSavedObjectsClient, + id, + }); + taskIdToRemove = rule.attributes.scheduledTaskId; + attributes = rule.attributes; + } + + try { + await context.authorization.ensureAuthorized({ + ruleTypeId: attributes.alertTypeId, + consumer: attributes.consumer, + operation: WriteOperations.Delete, + entity: AlertingAuthorizationEntity.Rule, + }); + } catch (error) { + context.auditLogger?.log( + ruleAuditEvent({ + action: RuleAuditAction.DELETE, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, + error, + }) + ); + throw error; + } + + await untrackRuleAlerts(context, id, attributes); + + // migrate legacy actions only for SIEM rules + // TODO (http-versioning): Remove this cast, this enables us to move forward + // without fixing all of other solution types + if (attributes.consumer === AlertConsumers.SIEM) { + await migrateLegacyActions(context, { + ruleId: id, + attributes: attributes as RawRule, + skipActionsValidation: true, + }); + } + + context.auditLogger?.log( + ruleAuditEvent({ + action: RuleAuditAction.DELETE, + outcome: 'unknown', + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, + }) + ); + const removeResult = await deleteRuleSo({ + savedObjectsClient: context.unsecuredSavedObjectsClient, + id, + }); + + await Promise.all([ + taskIdToRemove ? context.taskManager.removeIfExists(taskIdToRemove) : null, + apiKeyToInvalidate && !apiKeyCreatedByUser + ? bulkMarkApiKeysForInvalidation( + { apiKeys: [apiKeyToInvalidate] }, + context.logger, + context.unsecuredSavedObjectsClient + ) + : null, + ]); + + return removeResult; +} diff --git a/x-pack/plugins/alerting/server/application/rule/methods/delete/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/delete/index.ts new file mode 100644 index 00000000000000..0785c2af23f5c1 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/delete/index.ts @@ -0,0 +1,9 @@ +/* + * 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 { DeleteRuleParams } from './types'; +export { deleteRule } from './delete_rule'; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/delete/schemas/delete_rule_params_schema.ts b/x-pack/plugins/alerting/server/application/rule/methods/delete/schemas/delete_rule_params_schema.ts new file mode 100644 index 00000000000000..720d2db4c24f6e --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/delete/schemas/delete_rule_params_schema.ts @@ -0,0 +1,12 @@ +/* + * 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 { schema } from '@kbn/config-schema'; + +export const deleteRuleParamsSchema = schema.object({ + id: schema.string(), +}); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/delete/schemas/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/delete/schemas/index.ts new file mode 100644 index 00000000000000..717d448c4fad2e --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/delete/schemas/index.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 * from './delete_rule_params_schema'; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/delete/types/delete_rule_params.ts b/x-pack/plugins/alerting/server/application/rule/methods/delete/types/delete_rule_params.ts new file mode 100644 index 00000000000000..69d54b9387f726 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/delete/types/delete_rule_params.ts @@ -0,0 +1,11 @@ +/* + * 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 { TypeOf } from '@kbn/config-schema'; +import { deleteRuleParamsSchema } from '../schemas'; + +export type DeleteRuleParams = TypeOf; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/delete/types/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/delete/types/index.ts new file mode 100644 index 00000000000000..f67d751e9ad37f --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/delete/types/index.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 * from './delete_rule_params'; diff --git a/x-pack/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts index 527b4ba48c0823..c695a0420c55b4 100644 --- a/x-pack/plugins/alerting/server/routes/index.ts +++ b/x-pack/plugins/alerting/server/routes/index.ts @@ -16,7 +16,7 @@ import { AlertingRequestHandlerContext } from '../types'; import { createRuleRoute } from './rule/apis/create'; import { getRuleRoute, getInternalRuleRoute } from './rule/apis/get/get_rule_route'; import { updateRuleRoute } from './rule/apis/update/update_rule_route'; -import { deleteRuleRoute } from './delete_rule'; +import { deleteRuleRoute } from './rule/apis/delete/delete_rule_route'; import { aggregateRulesRoute } from './rule/apis/aggregate/aggregate_rules_route'; import { disableRuleRoute } from './disable_rule'; import { enableRuleRoute } from './enable_rule'; diff --git a/x-pack/plugins/alerting/server/routes/delete_rule.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/delete/delete_rule_route.test.ts similarity index 87% rename from x-pack/plugins/alerting/server/routes/delete_rule.test.ts rename to x-pack/plugins/alerting/server/routes/rule/apis/delete/delete_rule_route.test.ts index 71222d859d3c85..488caab4281ac6 100644 --- a/x-pack/plugins/alerting/server/routes/delete_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/delete/delete_rule_route.test.ts @@ -5,16 +5,16 @@ * 2.0. */ -import { deleteRuleRoute } from './delete_rule'; +import { deleteRuleRoute } from './delete_rule_route'; import { httpServiceMock } from '@kbn/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { rulesClientMock } from '../rules_client.mock'; +import { licenseStateMock } from '../../../../lib/license_state.mock'; +import { verifyApiAccess } from '../../../../lib/license_api_access'; +import { mockHandlerArguments } from '../../../_mock_handler_arguments'; +import { rulesClientMock } from '../../../../rules_client.mock'; const rulesClient = rulesClientMock.create(); -jest.mock('../lib/license_api_access', () => ({ +jest.mock('../../../../lib/license_api_access', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/delete_rule.ts b/x-pack/plugins/alerting/server/routes/rule/apis/delete/delete_rule_route.ts similarity index 66% rename from x-pack/plugins/alerting/server/routes/delete_rule.ts rename to x-pack/plugins/alerting/server/routes/rule/apis/delete/delete_rule_route.ts index e4ccd491046d1a..bdd264ee21bf93 100644 --- a/x-pack/plugins/alerting/server/routes/delete_rule.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/delete/delete_rule_route.ts @@ -6,14 +6,13 @@ */ import { IRouter } from '@kbn/core/server'; -import { schema } from '@kbn/config-schema'; -import { ILicenseState } from '../lib'; -import { verifyAccessAndContext } from './lib'; -import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; - -const paramSchema = schema.object({ - id: schema.string(), -}); +import { ILicenseState } from '../../../../lib'; +import { verifyAccessAndContext } from '../../../lib'; +import { + deleteRuleRequestParamsSchemaV1, + DeleteRuleRequestParamsV1, +} from '../../../../../common/routes/rule/apis/delete'; +import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../../../../types'; export const deleteRuleRoute = ( router: IRouter, @@ -23,14 +22,16 @@ export const deleteRuleRoute = ( { path: `${BASE_ALERTING_API_PATH}/rule/{id}`, validate: { - params: paramSchema, + params: deleteRuleRequestParamsSchemaV1, }, }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { const rulesClient = (await context.alerting).getRulesClient(); - const { id } = req.params; - await rulesClient.delete({ id }); + + const params: DeleteRuleRequestParamsV1 = req.params; + + await rulesClient.delete({ id: params.id }); return res.noContent(); }) ) diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.test.ts index 7b0d4352cbb532..e7922b069f8f10 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.test.ts @@ -18,7 +18,7 @@ import { import { retrieveMigratedLegacyActions } from './retrieve_migrated_legacy_actions'; import { findRules } from '../../../application/rule/methods/find/find_rules'; -import { deleteRule } from '../../methods/delete'; +import { deleteRule } from '../../../application/rule/methods/delete/delete_rule'; jest.mock('../../../application/rule/methods/find/find_rules', () => { return { @@ -26,7 +26,7 @@ jest.mock('../../../application/rule/methods/find/find_rules', () => { }; }); -jest.mock('../../methods/delete', () => { +jest.mock('../../../application/rule/methods/delete/delete_rule', () => { return { deleteRule: jest.fn(), }; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.ts index a9ab748cfa3d90..d11e679fd1f564 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.ts @@ -10,7 +10,7 @@ import { RULE_SAVED_OBJECT_TYPE } from '../../../saved_objects'; import type { RulesClientContext } from '../..'; import { RawRuleAction } from '../../../types'; import { findRules } from '../../../application/rule/methods/find/find_rules'; -import { deleteRule } from '../../methods/delete'; +import { deleteRule } from '../../../application/rule/methods/delete/delete_rule'; import { LegacyIRuleActionsAttributes, legacyRuleActionsSavedObjectType } from './types'; import { transformFromLegacyActions } from './transform_legacy_actions'; diff --git a/x-pack/plugins/alerting/server/rules_client/methods/delete.ts b/x-pack/plugins/alerting/server/rules_client/methods/delete.ts deleted file mode 100644 index 53baa548d783c4..00000000000000 --- a/x-pack/plugins/alerting/server/rules_client/methods/delete.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { AlertConsumers } from '@kbn/rule-data-utils'; -import { RawRule } from '../../types'; -import { WriteOperations, AlertingAuthorizationEntity } from '../../authorization'; -import { retryIfConflicts } from '../../lib/retry_if_conflicts'; -import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; -import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; -import { RulesClientContext } from '../types'; -import { untrackRuleAlerts, migrateLegacyActions } from '../lib'; -import { RuleAttributes } from '../../data/rule/types'; -import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; - -export async function deleteRule(context: RulesClientContext, { id }: { id: string }) { - return await retryIfConflicts( - context.logger, - `rulesClient.delete('${id}')`, - async () => await deleteWithOCC(context, { id }) - ); -} - -async function deleteWithOCC(context: RulesClientContext, { id }: { id: string }) { - let taskIdToRemove: string | undefined | null; - let apiKeyToInvalidate: string | null = null; - let apiKeyCreatedByUser: boolean | undefined | null = false; - let attributes: RawRule; - - try { - const decryptedAlert = - await context.encryptedSavedObjectsClient.getDecryptedAsInternalUser( - RULE_SAVED_OBJECT_TYPE, - id, - { - namespace: context.namespace, - } - ); - apiKeyToInvalidate = decryptedAlert.attributes.apiKey; - apiKeyCreatedByUser = decryptedAlert.attributes.apiKeyCreatedByUser; - taskIdToRemove = decryptedAlert.attributes.scheduledTaskId; - attributes = decryptedAlert.attributes; - } catch (e) { - // We'll skip invalidating the API key since we failed to load the decrypted saved object - context.logger.error( - `delete(): Failed to load API key to invalidate on alert ${id}: ${e.message}` - ); - // Still attempt to load the scheduledTaskId using SOC - const alert = await context.unsecuredSavedObjectsClient.get( - RULE_SAVED_OBJECT_TYPE, - id - ); - taskIdToRemove = alert.attributes.scheduledTaskId; - attributes = alert.attributes; - } - - try { - await context.authorization.ensureAuthorized({ - ruleTypeId: attributes.alertTypeId, - consumer: attributes.consumer, - operation: WriteOperations.Delete, - entity: AlertingAuthorizationEntity.Rule, - }); - } catch (error) { - context.auditLogger?.log( - ruleAuditEvent({ - action: RuleAuditAction.DELETE, - savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, - error, - }) - ); - throw error; - } - - await untrackRuleAlerts(context, id, attributes as RuleAttributes); - - // migrate legacy actions only for SIEM rules - if (attributes.consumer === AlertConsumers.SIEM) { - await migrateLegacyActions(context, { ruleId: id, attributes, skipActionsValidation: true }); - } - - context.auditLogger?.log( - ruleAuditEvent({ - action: RuleAuditAction.DELETE, - outcome: 'unknown', - savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, - }) - ); - const removeResult = await context.unsecuredSavedObjectsClient.delete(RULE_SAVED_OBJECT_TYPE, id); - - await Promise.all([ - taskIdToRemove ? context.taskManager.removeIfExists(taskIdToRemove) : null, - apiKeyToInvalidate && !apiKeyCreatedByUser - ? bulkMarkApiKeysForInvalidation( - { apiKeys: [apiKeyToInvalidate] }, - context.logger, - context.unsecuredSavedObjectsClient - ) - : null, - ]); - - return removeResult; -} diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index 86be428ed40788..bc5918875c1933 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -39,7 +39,7 @@ import { import { findRules, FindRulesParams } from '../application/rule/methods/find'; import { AggregateParams } from '../application/rule/methods/aggregate/types'; import { aggregateRules } from '../application/rule/methods/aggregate'; -import { deleteRule } from './methods/delete'; +import { deleteRule, DeleteRuleParams } from '../application/rule/methods/delete'; import { bulkDeleteRules, BulkDeleteRulesRequestBody, @@ -131,7 +131,7 @@ export class RulesClient { cloneRule(this.context, params); public create = (params: CreateRuleParams) => createRule(this.context, params); - public delete = (params: { id: string }) => deleteRule(this.context, params); + public delete = (params: DeleteRuleParams) => deleteRule(this.context, params); public find = (params?: FindRulesParams) => findRules(this.context, params); public get = (params: GetRuleParams) =>