diff --git a/packages/kbn-rule-data-utils/index.ts b/packages/kbn-rule-data-utils/index.ts index ea0028b972ed9..3e9787891be05 100644 --- a/packages/kbn-rule-data-utils/index.ts +++ b/packages/kbn-rule-data-utils/index.ts @@ -14,3 +14,4 @@ export * from './src/alerts_as_data_severity'; export * from './src/alerts_as_data_status'; export * from './src/alerts_as_data_cases'; export * from './src/routes/stack_rule_paths'; +export * from './src/rule_types'; diff --git a/packages/kbn-rule-data-utils/src/rule_types/index.ts b/packages/kbn-rule-data-utils/src/rule_types/index.ts new file mode 100644 index 0000000000000..b2f860b063c9b --- /dev/null +++ b/packages/kbn-rule-data-utils/src/rule_types/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 or the Server + * Side Public License, v 1. + */ + +export * from './stack_rules'; +export * from './o11y_rules'; diff --git a/packages/kbn-rule-data-utils/src/rule_types/o11y_rules.ts b/packages/kbn-rule-data-utils/src/rule_types/o11y_rules.ts new file mode 100644 index 0000000000000..b004d1758d4c7 --- /dev/null +++ b/packages/kbn-rule-data-utils/src/rule_types/o11y_rules.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 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 or the Server + * Side Public License, v 1. + */ + +export const OBSERVABILITY_THRESHOLD_RULE_TYPE_ID = 'observability.rules.threshold'; diff --git a/x-pack/plugins/stack_alerts/common/constants.ts b/packages/kbn-rule-data-utils/src/rule_types/stack_rules.ts similarity index 57% rename from x-pack/plugins/stack_alerts/common/constants.ts rename to packages/kbn-rule-data-utils/src/rule_types/stack_rules.ts index 38b0a2c6e73e3..ff426a9069537 100644 --- a/x-pack/plugins/stack_alerts/common/constants.ts +++ b/packages/kbn-rule-data-utils/src/rule_types/stack_rules.ts @@ -1,8 +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. + * 2.0 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 or the Server + * Side Public License, v 1. */ export const STACK_ALERTS_FEATURE_ID = 'stackAlerts'; diff --git a/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.tsx b/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.tsx index 75202710945dd..0a81a70ecd519 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.tsx @@ -13,6 +13,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { DataView } from '@kbn/data-plugin/common'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { STACK_ALERTS_FEATURE_ID } from '@kbn/rule-data-utils'; import { DiscoverStateContainer } from '../../services/discover_state'; import { DiscoverServices } from '../../../../build_services'; @@ -84,7 +85,7 @@ export function AlertsPopover({ return triggersActionsUi?.getAddRuleFlyout({ metadata: discoverMetadata, - consumer: 'discover', + consumer: STACK_ALERTS_FEATURE_ID, onClose: (_, metadata) => { onFinishFlyoutInteraction(metadata as EsQueryAlertMetaData); onClose(); diff --git a/src/plugins/discover/tsconfig.json b/src/plugins/discover/tsconfig.json index 3328073a026e7..acd0edd107100 100644 --- a/src/plugins/discover/tsconfig.json +++ b/src/plugins/discover/tsconfig.json @@ -71,7 +71,8 @@ "@kbn/react-kibana-mount", "@kbn/react-kibana-context-render", "@kbn/unified-data-table", - "@kbn/no-data-page-plugin" + "@kbn/no-data-page-plugin", + "@kbn/rule-data-utils" ], "exclude": [ "target/**/*" diff --git a/x-pack/plugins/alerting/common/rule.ts b/x-pack/plugins/alerting/common/rule.ts index 57ef90ed99620..22f7ae2dfc279 100644 --- a/x-pack/plugins/alerting/common/rule.ts +++ b/x-pack/plugins/alerting/common/rule.ts @@ -126,6 +126,7 @@ export interface AggregateOptions { id: string; }; filter?: string | KueryNode; + filterConsumers?: string[]; page?: number; perPage?: number; } diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts index 78b2e41431c22..f09e1a10d019f 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts @@ -62,6 +62,7 @@ const ruleType: jest.Mocked = { mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, shouldWrite: true, }, + validLegacyConsumers: [], }; const mockLegacyAlertsClient = legacyAlertsClientMock.create(); diff --git a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts index f8c341e132e51..6a884159f6f7c 100644 --- a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts @@ -99,6 +99,7 @@ const ruleType: jest.Mocked = { validate: { params: schema.any(), }, + validLegacyConsumers: [], }; const testAlert1 = { diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.test.ts b/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.test.ts index cb90b75d2c16d..6e8d28de01753 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/format_rule.test.ts @@ -29,6 +29,7 @@ const ruleType: jest.Mocked = { mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, shouldWrite: true, }, + validLegacyConsumers: [], }; describe('formatRule', () => { diff --git a/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts b/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts index 90552e1d5b0ac..954a03d697a61 100644 --- a/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts +++ b/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts @@ -202,6 +202,7 @@ const ruleType: jest.Mocked = { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], }; const ruleTypeWithAlertDefinition: jest.Mocked = { diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.test.ts index 5e672bcc83c68..f92197e76bcca 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.test.ts @@ -231,6 +231,7 @@ describe('bulkEdit()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], }); (migrateLegacyActions as jest.Mock).mockResolvedValue(migrateLegacyActionsMock); @@ -733,6 +734,7 @@ describe('bulkEdit()', () => { mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, shouldWrite: true, }, + validLegacyConsumers: [], }); const existingAction = { frequency: { @@ -2339,6 +2341,7 @@ describe('bulkEdit()', () => { return { state: {} }; }, producer: 'alerts', + validLegacyConsumers: [], }); const result = await rulesClient.bulkEdit({ @@ -2383,6 +2386,7 @@ describe('bulkEdit()', () => { return { state: {} }; }, producer: 'alerts', + validLegacyConsumers: [], }); const result = await rulesClient.bulkEdit({ diff --git a/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.test.ts index 496cfe317586f..26d343f1480c6 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.test.ts @@ -1540,6 +1540,7 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); const data = getMockData({ params: ruleParams, @@ -1727,6 +1728,7 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); const data = getMockData({ params: ruleParams, @@ -2546,6 +2548,7 @@ describe('create()', () => { return { state: {} }; }, producer: 'alerts', + validLegacyConsumers: [], }); await expect(rulesClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( `"params invalid: [param1]: expected value of type [string] but got [undefined]"` @@ -3020,6 +3023,7 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); const createdAttributes = { ...data, @@ -3092,6 +3096,7 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); const data = getMockData({ schedule: { interval: '1s' } }); @@ -3129,6 +3134,7 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); const data = getMockData({ @@ -3221,6 +3227,7 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); const data = getMockData({ @@ -3270,6 +3277,7 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); const data = getMockData({ @@ -3332,6 +3340,7 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); const data = getMockData({ @@ -3412,6 +3421,7 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); const data = getMockData({ @@ -3616,6 +3626,7 @@ describe('create()', () => { mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, shouldWrite: true, }, + validLegacyConsumers: [], })); const data = getMockData({ @@ -3668,6 +3679,7 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); const data = getMockData({ diff --git a/x-pack/plugins/alerting/server/authorization/alerting_authorization.test.ts b/x-pack/plugins/alerting/server/authorization/alerting_authorization.test.ts index 12970b7e91cfb..c68532082f18a 100644 --- a/x-pack/plugins/alerting/server/authorization/alerting_authorization.test.ts +++ b/x-pack/plugins/alerting/server/authorization/alerting_authorization.test.ts @@ -22,7 +22,7 @@ import { } from './alerting_authorization'; import { v4 as uuidv4 } from 'uuid'; import { RecoveredActionGroup } from '../../common'; -import { RegistryRuleType } from '../rule_type_registry'; +import { NormalizedRuleType, RegistryRuleType } from '../rule_type_registry'; import { AlertingAuthorizationFilterType } from './alerting_authorization_kuery'; import { schema } from '@kbn/config-schema'; @@ -201,6 +201,7 @@ beforeEach(() => { validate: { params: schema.any(), }, + validLegacyConsumers: [], })); features.getKibanaFeatures.mockReturnValue([ myAppFeature, @@ -764,6 +765,7 @@ describe('AlertingAuthorization', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }; const myAppAlertType: RegistryRuleType = { actionGroups: [], @@ -778,6 +780,7 @@ describe('AlertingAuthorization', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }; const mySecondAppAlertType: RegistryRuleType = { actionGroups: [], @@ -792,6 +795,7 @@ describe('AlertingAuthorization', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }; const setOfAlertTypes = new Set([myAppAlertType, myOtherAppAlertType, mySecondAppAlertType]); test('omits filter when there is no authorization api', async () => { @@ -1165,6 +1169,7 @@ describe('AlertingAuthorization', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }; const myAppAlertType: RegistryRuleType = { actionGroups: [], @@ -1179,6 +1184,7 @@ describe('AlertingAuthorization', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }; const setOfAlertTypes = new Set([myAppAlertType, myOtherAppAlertType]); beforeEach(() => { @@ -1245,6 +1251,7 @@ describe('AlertingAuthorization', () => { "id": "recovered", "name": "Recovered", }, + "validLegacyConsumers": Array [], }, Object { "actionGroups": Array [], @@ -1280,6 +1287,7 @@ describe('AlertingAuthorization', () => { "id": "recovered", "name": "Recovered", }, + "validLegacyConsumers": Array [], }, } `); @@ -1361,15 +1369,12 @@ describe('AlertingAuthorization', () => { "id": "recovered", "name": "Recovered", }, + "validLegacyConsumers": Array [], }, Object { "actionGroups": Array [], "actionVariables": undefined, "authorizedConsumers": Object { - "alerts": Object { - "all": true, - "read": true, - }, "myApp": Object { "all": true, "read": true, @@ -1392,6 +1397,7 @@ describe('AlertingAuthorization', () => { "id": "recovered", "name": "Recovered", }, + "validLegacyConsumers": Array [], }, } `); @@ -1442,10 +1448,6 @@ describe('AlertingAuthorization', () => { "actionGroups": Array [], "actionVariables": undefined, "authorizedConsumers": Object { - "alerts": Object { - "all": true, - "read": true, - }, "myApp": Object { "all": true, "read": true, @@ -1464,6 +1466,7 @@ describe('AlertingAuthorization', () => { "id": "recovered", "name": "Recovered", }, + "validLegacyConsumers": Array [], }, } `); @@ -1548,10 +1551,6 @@ describe('AlertingAuthorization', () => { "actionGroups": Array [], "actionVariables": undefined, "authorizedConsumers": Object { - "alerts": Object { - "all": false, - "read": true, - }, "myApp": Object { "all": true, "read": true, @@ -1574,15 +1573,12 @@ describe('AlertingAuthorization', () => { "id": "recovered", "name": "Recovered", }, + "validLegacyConsumers": Array [], }, Object { "actionGroups": Array [], "actionVariables": undefined, "authorizedConsumers": Object { - "alerts": Object { - "all": false, - "read": true, - }, "myApp": Object { "all": false, "read": true, @@ -1605,6 +1601,7 @@ describe('AlertingAuthorization', () => { "id": "recovered", "name": "Recovered", }, + "validLegacyConsumers": Array [], }, } `); @@ -1668,10 +1665,6 @@ describe('AlertingAuthorization', () => { "actionGroups": Array [], "actionVariables": undefined, "authorizedConsumers": Object { - "alerts": Object { - "all": true, - "read": true, - }, "myApp": Object { "all": true, "read": true, @@ -1694,6 +1687,7 @@ describe('AlertingAuthorization', () => { "id": "recovered", "name": "Recovered", }, + "validLegacyConsumers": Array [], }, } `); @@ -1714,6 +1708,7 @@ describe('AlertingAuthorization', () => { isExportable: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }; const myAppAlertType: RegistryRuleType = { actionGroups: [], @@ -1728,6 +1723,7 @@ describe('AlertingAuthorization', () => { isExportable: true, hasAlertsMappings: true, hasFieldsForAAD: true, + validLegacyConsumers: [], }; const mySecondAppAlertType: RegistryRuleType = { actionGroups: [], @@ -1742,6 +1738,7 @@ describe('AlertingAuthorization', () => { isExportable: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }; const setOfAlertTypes = new Set([myAppAlertType, myOtherAppAlertType, mySecondAppAlertType]); beforeEach(() => { @@ -1806,6 +1803,7 @@ describe('AlertingAuthorization', () => { "id": "recovered", "name": "Recovered", }, + "validLegacyConsumers": Array [], }, }, "hasAllRequested": false, @@ -1881,6 +1879,7 @@ describe('AlertingAuthorization', () => { "id": "recovered", "name": "Recovered", }, + "validLegacyConsumers": Array [], }, }, "hasAllRequested": false, @@ -1889,4 +1888,626 @@ describe('AlertingAuthorization', () => { `); }); }); + + describe('8.11+', () => { + let alertAuthorization: AlertingAuthorization; + + const setOfRuleTypes: RegistryRuleType[] = [ + { + actionGroups: [], + actionVariables: undefined, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + recoveryActionGroup: RecoveredActionGroup, + id: '.esQuery', + name: 'ES Query', + producer: 'stackAlerts', + enabledInLicense: true, + hasAlertsMappings: false, + hasFieldsForAAD: false, + validLegacyConsumers: ['discover', 'alerts'], + }, + { + actionGroups: [], + actionVariables: undefined, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + recoveryActionGroup: RecoveredActionGroup, + id: '.threshold-rule-o11y', + name: 'New threshold 011y', + producer: 'observability', + enabledInLicense: true, + hasAlertsMappings: false, + hasFieldsForAAD: false, + validLegacyConsumers: [], + }, + { + actionGroups: [], + actionVariables: undefined, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + recoveryActionGroup: RecoveredActionGroup, + id: '.infrastructure-threshold-o11y', + name: 'Metrics o11y', + producer: 'infrastructure', + enabledInLicense: true, + hasAlertsMappings: false, + hasFieldsForAAD: false, + validLegacyConsumers: ['alerts'], + }, + { + actionGroups: [], + actionVariables: undefined, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + recoveryActionGroup: RecoveredActionGroup, + id: '.logs-threshold-o11y', + name: 'Logs o11y', + producer: 'logs', + enabledInLicense: true, + hasAlertsMappings: false, + hasFieldsForAAD: false, + validLegacyConsumers: ['alerts'], + }, + ]; + + const onlyStackAlertsKibanaPrivileges = [ + { + privilege: mockAuthorizationAction('.esQuery', 'stackAlerts', 'rule', 'create'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('.esQuery', 'stackAlerts', 'rule', 'find'), + authorized: true, + }, + ]; + const only011yKibanaPrivileges = [ + { + privilege: mockAuthorizationAction( + '.infrastructure-threshold-o11y', + 'infrastructure', + 'rule', + 'create' + ), + authorized: true, + }, + { + privilege: mockAuthorizationAction( + '.infrastructure-threshold-o11y', + 'infrastructure', + 'rule', + 'find' + ), + authorized: true, + }, + { + privilege: mockAuthorizationAction( + '.threshold-rule-o11y', + 'infrastructure', + 'rule', + 'create' + ), + authorized: true, + }, + { + privilege: mockAuthorizationAction( + '.threshold-rule-o11y', + 'infrastructure', + 'rule', + 'find' + ), + authorized: true, + }, + { + privilege: mockAuthorizationAction('.logs-threshold-o11y', 'logs', 'rule', 'create'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('.logs-threshold-o11y', 'logs', 'rule', 'find'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('.threshold-rule-o11y', 'logs', 'rule', 'create'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('.threshold-rule-o11y', 'logs', 'rule', 'find'), + authorized: true, + }, + ]; + const onlyLogsAndStackAlertsKibanaPrivileges = [ + { + privilege: mockAuthorizationAction('.esQuery', 'stackAlerts', 'rule', 'create'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('.esQuery', 'stackAlerts', 'rule', 'find'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('.logs-threshold-o11y', 'logs', 'rule', 'create'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('.logs-threshold-o11y', 'logs', 'rule', 'find'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('.threshold-rule-o11y', 'logs', 'rule', 'create'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('.threshold-rule-o11y', 'logs', 'rule', 'find'), + authorized: true, + }, + ]; + + beforeEach(async () => { + ruleTypeRegistry.list.mockReturnValue(new Set(setOfRuleTypes)); + ruleTypeRegistry.get.mockImplementation((id: string) => { + if (setOfRuleTypes.some((rt) => rt.id === id)) { + const ruleType = setOfRuleTypes.find((rt) => rt.id === id); + return (ruleType ?? {}) as NormalizedRuleType<{}, {}, {}, {}, {}, '', '', {}>; + } + return {} as NormalizedRuleType<{}, {}, {}, {}, {}, '', '', {}>; + }); + }); + + describe('user only access to stack alerts + discover', () => { + beforeEach(() => { + const { authorization } = mockSecurity(); + const checkPrivileges: jest.MockedFunction< + ReturnType + > = jest.fn(); + authorization.mode.useRbacForRequest.mockReturnValue(true); + + features.getKibanaFeatures.mockReset(); + features.getKibanaFeatures.mockReturnValue([ + mockFeature('stackAlerts', ['.esQuery']), + mockFeature('discover', []), + ]); + checkPrivileges.mockReset(); + checkPrivileges.mockResolvedValue({ + username: 'onlyStack', + hasAllRequested: true, + privileges: { + kibana: onlyStackAlertsKibanaPrivileges, + }, + }); + authorization.checkPrivilegesDynamicallyWithRequest.mockReset(); + authorization.checkPrivilegesDynamicallyWithRequest.mockReturnValue(checkPrivileges); + alertAuthorization = new AlertingAuthorization({ + request, + authorization, + ruleTypeRegistry, + features, + getSpace, + getSpaceId, + }); + }); + + describe('ensureAuthorized', () => { + test('should allow to create .esquery rule type with stackAlerts consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.esQuery', + consumer: 'stackAlerts', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).resolves.toEqual(undefined); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should allow to create .esquery rule type with discover consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.esQuery', + consumer: 'discover', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).resolves.toEqual(undefined); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should allow to create .esquery rule type with alerts consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.esQuery', + consumer: 'alerts', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).resolves.toEqual(undefined); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should throw an error to create .esquery rule type with logs consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.esQuery', + consumer: 'logs', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unauthorized by \\"logs\\" to create \\".esQuery\\" rule"` + ); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should throw an error to create .esquery rule type with infrastructure consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.esQuery', + consumer: 'infrastructure', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unauthorized by \\"infrastructure\\" to create \\".esQuery\\" rule"` + ); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should throw an error to create .threshold-rule-o11y rule type with alerts consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.threshold-rule-o11y', + consumer: 'alerts', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unauthorized by \\"alerts\\" to create \\".threshold-rule-o11y\\" rule"` + ); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should throw an error to create .logs-threshold-o11y rule type with alerts infrastructure', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.logs-threshold-o11y', + consumer: 'alerts', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unauthorized by \\"alerts\\" to create \\".logs-threshold-o11y\\" rule"` + ); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + }); + test('creates a filter based on the privileged types', async () => { + expect( + ( + await alertAuthorization.getFindAuthorizationFilter(AlertingAuthorizationEntity.Rule, { + type: AlertingAuthorizationFilterType.KQL, + fieldNames: { + ruleTypeId: 'path.to.rule_type_id', + consumer: 'consumer-field', + }, + }) + ).filter + ).toEqual( + fromKueryExpression( + `path.to.rule_type_id:.esQuery and consumer-field:(alerts or stackAlerts or discover)` + ) + ); + }); + }); + + describe('user only access to o11y', () => { + beforeEach(() => { + const { authorization } = mockSecurity(); + const checkPrivileges: jest.MockedFunction< + ReturnType + > = jest.fn(); + authorization.mode.useRbacForRequest.mockReturnValue(true); + + features.getKibanaFeatures.mockReset(); + features.getKibanaFeatures.mockReturnValue([ + mockFeature('infrastructure', [ + '.infrastructure-threshold-o11y', + '.threshold-rule-o11y', + '.esQuery', + ]), + mockFeature('logs', ['.threshold-rule-o11y', '.esQuery', '.logs-threshold-o11y']), + ]); + checkPrivileges.mockReset(); + checkPrivileges.mockResolvedValue({ + username: 'onlyO11y', + hasAllRequested: true, + privileges: { + kibana: only011yKibanaPrivileges, + }, + }); + authorization.checkPrivilegesDynamicallyWithRequest.mockReset(); + authorization.checkPrivilegesDynamicallyWithRequest.mockReturnValue(checkPrivileges); + alertAuthorization = new AlertingAuthorization({ + request, + authorization, + ruleTypeRegistry, + features, + getSpace, + getSpaceId, + }); + }); + + describe('ensureAuthorized', () => { + test('should throw an error to create .esquery rule type with stackAlerts consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.esQuery', + consumer: 'stackAlerts', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unauthorized by \\"stackAlerts\\" to create \\".esQuery\\" rule"` + ); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should throw an error to create .esquery rule type with discover consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.esQuery', + consumer: 'discover', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unauthorized by \\"discover\\" to create \\".esQuery\\" rule"` + ); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should throw an error to create .threshold-rule-o11y rule type with alerts consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.threshold-rule-o11y', + consumer: 'alerts', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unauthorized by \\"alerts\\" to create \\".threshold-rule-o11y\\" rule"` + ); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should allow to create .esquery rule type with logs consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.esQuery', + consumer: 'logs', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).resolves.toEqual(undefined); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should allow to create .esquery rule type with logs infrastructure', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.esQuery', + consumer: 'infrastructure', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).resolves.toEqual(undefined); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should allow to create .logs-threshold-o11y rule type with alerts consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.logs-threshold-o11y', + consumer: 'alerts', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).resolves.toEqual(undefined); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should throw an error to create .threshold-rule-o11y rule type with logs consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.threshold-rule-o11y', + consumer: 'logs', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).resolves.toEqual(undefined); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + }); + test('creates a filter based on the privileged types', async () => { + expect( + ( + await alertAuthorization.getFindAuthorizationFilter( + AlertingAuthorizationEntity.Rule, + { + type: AlertingAuthorizationFilterType.KQL, + fieldNames: { + ruleTypeId: 'path.to.rule_type_id', + consumer: 'consumer-field', + }, + }, + new Set(['infrastructure', 'logs']) + ) + ).filter + ).toEqual( + fromKueryExpression( + `(path.to.rule_type_id:.infrastructure-threshold-o11y and consumer-field:(infrastructure or alerts)) or (path.to.rule_type_id:.threshold-rule-o11y and consumer-field:(infrastructure or logs)) or (path.to.rule_type_id:.logs-threshold-o11y and consumer-field:(logs or alerts))` + ) + ); + }); + }); + + describe('user only access to logs and stackAlerts', () => { + beforeEach(() => { + const { authorization } = mockSecurity(); + const checkPrivileges: jest.MockedFunction< + ReturnType + > = jest.fn(); + authorization.mode.useRbacForRequest.mockReturnValue(true); + + features.getKibanaFeatures.mockClear(); + features.getKibanaFeatures.mockReturnValue([ + mockFeature('stackAlerts', ['.esQuery']), + mockFeature('logs', ['.logs-threshold-o11y', '.threshold-rule-o11y', '.esQuery']), + ]); + checkPrivileges.mockClear(); + checkPrivileges.mockResolvedValue({ + username: 'stackAndLogs', + hasAllRequested: true, + privileges: { + kibana: onlyLogsAndStackAlertsKibanaPrivileges, + }, + }); + authorization.checkPrivilegesDynamicallyWithRequest.mockReturnValue(checkPrivileges); + alertAuthorization = new AlertingAuthorization({ + request, + authorization, + ruleTypeRegistry, + features, + getSpace, + getSpaceId, + }); + }); + + describe('ensureAuthorized', () => { + test('should allow to create .esquery rule type with stackAlerts consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.esQuery', + consumer: 'stackAlerts', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).resolves.toEqual(undefined); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should allow to create .esquery rule type with discover consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.esQuery', + consumer: 'discover', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).resolves.toEqual(undefined); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should allow to create .esquery rule type with logs consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.esQuery', + consumer: 'logs', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).resolves.toEqual(undefined); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should allow to create .logs-threshold-o11y rule type with alerts consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.logs-threshold-o11y', + consumer: 'alerts', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).resolves.toEqual(undefined); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should throw an error to create .threshold-rule-o11y rule type with logs consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.threshold-rule-o11y', + consumer: 'logs', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).resolves.toEqual(undefined); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should throw an error to create .esquery rule type with logs infrastructure', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.esQuery', + consumer: 'infrastructure', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unauthorized by \\"infrastructure\\" to create \\".esQuery\\" rule"` + ); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should throw an error to create .threshold-rule-o11y rule type with alerts consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.threshold-rule-o11y', + consumer: 'alerts', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unauthorized by \\"alerts\\" to create \\".threshold-rule-o11y\\" rule"` + ); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + test('should throw an error to create .esquery rule type with infrastructure consumer', async () => { + await expect( + alertAuthorization.ensureAuthorized({ + ruleTypeId: '.esQuery', + consumer: 'infrastructure', + operation: WriteOperations.Create, + entity: AlertingAuthorizationEntity.Rule, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unauthorized by \\"infrastructure\\" to create \\".esQuery\\" rule"` + ); + + expect(ruleTypeRegistry.get).toHaveBeenCalledTimes(1); + }); + }); + test('creates a filter based on the privileged types', async () => { + expect( + ( + await alertAuthorization.getFindAuthorizationFilter(AlertingAuthorizationEntity.Rule, { + type: AlertingAuthorizationFilterType.KQL, + fieldNames: { + ruleTypeId: 'path.to.rule_type_id', + consumer: 'consumer-field', + }, + }) + ).filter + ).toEqual( + fromKueryExpression( + `(path.to.rule_type_id:.esQuery and consumer-field:(alerts or stackAlerts or logs or discover)) or (path.to.rule_type_id:.logs-threshold-o11y and consumer-field:(alerts or stackAlerts or logs or discover)) or (path.to.rule_type_id:.threshold-rule-o11y and consumer-field:(alerts or stackAlerts or logs or discover))` + ) + ); + }); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts b/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts index 01f035070d63f..90f4b189b1197 100644 --- a/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts +++ b/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts @@ -6,7 +6,7 @@ */ import Boom from '@hapi/boom'; -import { fromPairs, has } from 'lodash'; +import { has, isEmpty } from 'lodash'; import { KibanaRequest } from '@kbn/core/server'; import { JsonObject } from '@kbn/utility-types'; import { KueryNode } from '@kbn/es-query'; @@ -175,9 +175,11 @@ export class AlertingAuthorization { }: EnsureAuthorizedOpts) { const { authorization } = this; const ruleType = this.ruleTypeRegistry.get(ruleTypeId); - // We have some rules with consumer of "alerts" which indirectly means the same as - // a consumer of the rule type producer. Let's simplify the code and set it accordingly - const consumer = legacyConsumer === ALERTS_FEATURE_ID ? ruleType.producer : legacyConsumer; + const consumer = getValidConsumer({ + validLegacyConsumers: ruleType.validLegacyConsumers, + legacyConsumer, + producer: ruleType.producer, + }); const isAvailableConsumer = has(await this.allPossibleConsumers, consumer); if (authorization && this.shouldCheckAuthorization()) { @@ -208,12 +210,18 @@ export class AlertingAuthorization { public async getFindAuthorizationFilter( authorizationEntity: AlertingAuthorizationEntity, - filterOpts: AlertingAuthorizationFilterOpts + filterOpts: AlertingAuthorizationFilterOpts, + featuresIds?: Set ): Promise<{ filter?: KueryNode | JsonObject; ensureRuleTypeIsAuthorized: (ruleTypeId: string, consumer: string, auth: string) => void; }> { - return this.getAuthorizationFilter(authorizationEntity, filterOpts, ReadOperations.Find); + return this.getAuthorizationFilter( + authorizationEntity, + filterOpts, + ReadOperations.Find, + featuresIds + ); } public async getAuthorizedRuleTypes( @@ -232,7 +240,8 @@ export class AlertingAuthorization { public async getAuthorizationFilter( authorizationEntity: AlertingAuthorizationEntity, filterOpts: AlertingAuthorizationFilterOpts, - operation: WriteOperations | ReadOperations + operation: WriteOperations | ReadOperations, + featuresIds?: Set ): Promise<{ filter?: KueryNode | JsonObject; ensureRuleTypeIsAuthorized: (ruleTypeId: string, consumer: string, auth: string) => void; @@ -241,7 +250,8 @@ export class AlertingAuthorization { const { authorizedRuleTypes } = await this.augmentRuleTypesWithAuthorization( this.ruleTypeRegistry.list(), [operation], - authorizationEntity + authorizationEntity, + featuresIds ); if (!authorizedRuleTypes.size) { @@ -326,6 +336,9 @@ export class AlertingAuthorization { string, [RegistryAlertTypeWithAuth, string, HasPrivileges, IsAuthorizedAtProducerLevel] >(); + const allPossibleConsumers = await this.allPossibleConsumers; + const addLegacyConsumerPrivileges = (legacyConsumer: string) => + legacyConsumer === ALERTS_FEATURE_ID || isEmpty(featuresIds); for (const feature of fIds) { const featureDef = this.features .getKibanaFeatures() @@ -351,6 +364,31 @@ export class AlertingAuthorization { ruleTypeAuth.producer === feature, ] ); + // FUTURE ENGINEER + // We are just trying to add back the legacy consumers associated + // to the rule type to get back the privileges that was given at one point + if (!isEmpty(ruleTypeAuth.validLegacyConsumers)) { + ruleTypeAuth.validLegacyConsumers.forEach((legacyConsumer) => { + if (addLegacyConsumerPrivileges(legacyConsumer)) { + if (!allPossibleConsumers[legacyConsumer]) { + allPossibleConsumers[legacyConsumer] = { + read: true, + all: true, + }; + } + + privilegeToRuleType.set( + this.authorization!.actions.alerting.get( + ruleTypeId, + legacyConsumer, + authorizationEntity, + operation + ), + [ruleTypeAuth, legacyConsumer, hasPrivilegeByOperation(operation), false] + ); + } + }); + } } } } @@ -368,7 +406,7 @@ export class AlertingAuthorization { ? // has access to all features this.augmentWithAuthorizedConsumers( new Set(ruleTypesAuthorized.values()), - await this.allPossibleConsumers + allPossibleConsumers ) : // only has some of the required privileges privileges.kibana.reduce((authorizedRuleTypes, { authorized, privilege }) => { @@ -383,10 +421,14 @@ export class AlertingAuthorization { if (isAuthorizedAtProducerLevel) { // granting privileges under the producer automatically authorized the Rules Management UI as well - ruleType.authorizedConsumers[ALERTS_FEATURE_ID] = mergeHasPrivileges( - hasPrivileges, - ruleType.authorizedConsumers[ALERTS_FEATURE_ID] - ); + ruleType.validLegacyConsumers.forEach((legacyConsumer) => { + if (addLegacyConsumerPrivileges(legacyConsumer)) { + ruleType.authorizedConsumers[legacyConsumer] = mergeHasPrivileges( + hasPrivileges, + ruleType.authorizedConsumers[legacyConsumer] + ); + } + }); } authorizedRuleTypes.add(ruleType); } @@ -438,7 +480,10 @@ function asAuthorizedConsumers( consumers: string[], hasPrivileges: HasPrivileges ): AuthorizedConsumers { - return fromPairs(consumers.map((feature) => [feature, hasPrivileges])); + return consumers.reduce((acc, feature) => { + acc[feature] = hasPrivileges; + return acc; + }, {}); } function getUnauthorizedMessage( @@ -449,3 +494,16 @@ function getUnauthorizedMessage( ): string { return `Unauthorized by "${scope}" to ${operation} "${alertTypeId}" ${entity}`; } + +export const getValidConsumer = ({ + validLegacyConsumers, + legacyConsumer, + producer, +}: { + validLegacyConsumers: string[]; + legacyConsumer: string; + producer: string; +}): string => + legacyConsumer === ALERTS_FEATURE_ID || validLegacyConsumers.includes(legacyConsumer) + ? producer + : legacyConsumer; diff --git a/x-pack/plugins/alerting/server/authorization/alerting_authorization_kuery.test.ts b/x-pack/plugins/alerting/server/authorization/alerting_authorization_kuery.test.ts index c9e129085c3c0..bfa36ccfd46c5 100644 --- a/x-pack/plugins/alerting/server/authorization/alerting_authorization_kuery.test.ts +++ b/x-pack/plugins/alerting/server/authorization/alerting_authorization_kuery.test.ts @@ -34,6 +34,7 @@ describe('asKqlFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]), { @@ -71,6 +72,7 @@ describe('asKqlFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]), { @@ -111,6 +113,7 @@ describe('asKqlFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, { actionGroups: [], @@ -130,6 +133,7 @@ describe('asKqlFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, { actionGroups: [], @@ -149,6 +153,7 @@ describe('asKqlFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]), { @@ -189,6 +194,7 @@ describe('asKqlFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, { actionGroups: [], @@ -208,6 +214,7 @@ describe('asKqlFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]), { @@ -249,6 +256,7 @@ describe('asKqlFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, { actionGroups: [], @@ -268,6 +276,7 @@ describe('asKqlFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]), { @@ -303,6 +312,7 @@ describe('asKqlFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]), { @@ -339,6 +349,7 @@ describe('asEsDslFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]), { @@ -403,6 +414,7 @@ describe('asEsDslFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]), { @@ -475,6 +487,7 @@ describe('asEsDslFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, { actionGroups: [], @@ -494,6 +507,7 @@ describe('asEsDslFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, { actionGroups: [], @@ -513,6 +527,7 @@ describe('asEsDslFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]), { @@ -678,6 +693,7 @@ describe('asEsDslFiltersByRuleTypeAndConsumer', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]), { diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts index 007cd4481bd7e..02fa23fbb8cdb 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts @@ -46,6 +46,7 @@ const ruleType: jest.Mocked = { validate: { params: schema.any(), }, + validLegacyConsumers: [], }; const context: RuleContextOpts = { diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts index 6b18d1aac93dd..5aec760a1eadf 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts @@ -26,6 +26,7 @@ describe('createAlertEventLogRecordObject', () => { validate: { params: schema.any(), }, + validLegacyConsumers: [], }; test('created alert event "execute-start"', async () => { diff --git a/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts b/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts index c9bfbdca4aa0b..24e73f9620582 100644 --- a/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts +++ b/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts @@ -237,6 +237,7 @@ describe('aggregateRulesRoute', () => { }, "options": Object { "defaultSearchOperator": "AND", + "filterConsumers": undefined, }, }, ] diff --git a/x-pack/plugins/alerting/server/routes/aggregate_rules.ts b/x-pack/plugins/alerting/server/routes/aggregate_rules.ts index ea3bb22bd0d17..2e10bb1ba31a1 100644 --- a/x-pack/plugins/alerting/server/routes/aggregate_rules.ts +++ b/x-pack/plugins/alerting/server/routes/aggregate_rules.ts @@ -38,16 +38,19 @@ const querySchema = schema.object({ ) ), filter: schema.maybe(schema.string()), + filter_consumers: schema.maybe(schema.arrayOf(schema.string())), }); const rewriteQueryReq: RewriteRequestCase = ({ default_search_operator: defaultSearchOperator, has_reference: hasReference, search_fields: searchFields, + filter_consumers: filterConsumers, ...rest }) => ({ ...rest, defaultSearchOperator, + filterConsumers, ...(hasReference ? { hasReference } : {}), ...(searchFields ? { searchFields } : {}), }); diff --git a/x-pack/plugins/alerting/server/routes/find_rules.test.ts b/x-pack/plugins/alerting/server/routes/find_rules.test.ts index 6e8f4e5474dbf..afd621c6e7abd 100644 --- a/x-pack/plugins/alerting/server/routes/find_rules.test.ts +++ b/x-pack/plugins/alerting/server/routes/find_rules.test.ts @@ -79,6 +79,7 @@ describe('findRulesRoute', () => { "includeSnoozeData": true, "options": Object { "defaultSearchOperator": "OR", + "filterConsumers": undefined, "page": 1, "perPage": 1, }, diff --git a/x-pack/plugins/alerting/server/routes/find_rules.ts b/x-pack/plugins/alerting/server/routes/find_rules.ts index 04b18da1a1b0c..1eab92db82383 100644 --- a/x-pack/plugins/alerting/server/routes/find_rules.ts +++ b/x-pack/plugins/alerting/server/routes/find_rules.ts @@ -47,6 +47,7 @@ const querySchema = schema.object({ ), fields: schema.maybe(schema.arrayOf(schema.string())), filter: schema.maybe(schema.string()), + filter_consumers: schema.maybe(schema.arrayOf(schema.string())), }); const rewriteQueryReq: RewriteRequestCase = ({ @@ -56,11 +57,13 @@ const rewriteQueryReq: RewriteRequestCase = ({ per_page: perPage, sort_field: sortField, sort_order: sortOrder, + filter_consumers: filterConsumers, ...rest }) => ({ ...rest, defaultSearchOperator, perPage, + filterConsumers, ...(sortField ? { sortField } : {}), ...(sortOrder ? { sortOrder } : {}), ...(hasReference ? { hasReference } : {}), diff --git a/x-pack/plugins/alerting/server/routes/health.test.ts b/x-pack/plugins/alerting/server/routes/health.test.ts index b1c562ada8717..bb42109ef8502 100644 --- a/x-pack/plugins/alerting/server/routes/health.test.ts +++ b/x-pack/plugins/alerting/server/routes/health.test.ts @@ -51,6 +51,7 @@ const ruleTypes = [ defaultScheduleInterval: '10m', hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], } as RegistryAlertTypeWithAuth, ]; diff --git a/x-pack/plugins/alerting/server/routes/legacy/health.test.ts b/x-pack/plugins/alerting/server/routes/legacy/health.test.ts index 9946f659a0744..e25485229dc0c 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/health.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/health.test.ts @@ -55,6 +55,7 @@ const ruleTypes = [ defaultScheduleInterval: '10m', hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], } as RegistryAlertTypeWithAuth, ]; diff --git a/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.test.ts b/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.test.ts index 7262f42319c1f..ae094df73d093 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.test.ts @@ -64,6 +64,7 @@ describe('listAlertTypesRoute', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], } as RegistryAlertTypeWithAuth, ]; rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(listTypes)); @@ -98,6 +99,7 @@ describe('listAlertTypesRoute', () => { "id": "recovered", "name": "Recovered", }, + "validLegacyConsumers": Array [], }, ], } @@ -143,6 +145,7 @@ describe('listAlertTypesRoute', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], } as RegistryAlertTypeWithAuth, ]; @@ -198,6 +201,7 @@ describe('listAlertTypesRoute', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], } as RegistryAlertTypeWithAuth, ]; diff --git a/x-pack/plugins/alerting/server/routes/rule_types.test.ts b/x-pack/plugins/alerting/server/routes/rule_types.test.ts index 2dab9284bb5ac..c52ce30e41dcd 100644 --- a/x-pack/plugins/alerting/server/routes/rule_types.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule_types.test.ts @@ -62,9 +62,12 @@ describe('ruleTypesRoute', () => { doesSetRecoveryContext: false, hasAlertsMappings: true, hasFieldsForAAD: false, + validLegacyConsumers: [], } as RegistryAlertTypeWithAuth, ]; - const expectedResult: Array> = [ + const expectedResult: Array< + AsApiContract> + > = [ { id: '1', name: 'name', @@ -172,6 +175,7 @@ describe('ruleTypesRoute', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], } as RegistryAlertTypeWithAuth, ]; @@ -227,6 +231,7 @@ describe('ruleTypesRoute', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], } as RegistryAlertTypeWithAuth, ]; diff --git a/x-pack/plugins/alerting/server/routes/rule_types.ts b/x-pack/plugins/alerting/server/routes/rule_types.ts index 4c4a812d75684..4521bdfd167a2 100644 --- a/x-pack/plugins/alerting/server/routes/rule_types.ts +++ b/x-pack/plugins/alerting/server/routes/rule_types.ts @@ -8,10 +8,10 @@ import { IRouter } from '@kbn/core/server'; import { ILicenseState } from '../lib'; import { RegistryAlertTypeWithAuth } from '../authorization'; -import { RewriteResponseCase, verifyAccessAndContext } from './lib'; +import { verifyAccessAndContext } from './lib'; import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; -const rewriteBodyRes: RewriteResponseCase = (results) => { +const rewriteBodyRes = (results: RegistryAlertTypeWithAuth[]) => { return results.map( ({ enabledInLicense, @@ -27,8 +27,9 @@ const rewriteBodyRes: RewriteResponseCase = (result doesSetRecoveryContext, hasAlertsMappings, hasFieldsForAAD, + validLegacyConsumers, ...rest - }) => ({ + }: RegistryAlertTypeWithAuth) => ({ ...rest, enabled_in_license: enabledInLicense, recovery_action_group: recoveryActionGroup, diff --git a/x-pack/plugins/alerting/server/rule_type_registry.test.ts b/x-pack/plugins/alerting/server/rule_type_registry.test.ts index 689741c7479ac..05aa84292ed8d 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.test.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.test.ts @@ -590,40 +590,41 @@ describe('Create Lifecycle', () => { }); const ruleType = registry.get('test'); expect(ruleType).toMatchInlineSnapshot(` - Object { - "actionGroups": Array [ - Object { - "id": "default", - "name": "Default", - }, - Object { - "id": "recovered", - "name": "Recovered", - }, - ], - "actionVariables": Object { - "context": Array [], - "params": Array [], - "state": Array [], - }, - "defaultActionGroupId": "default", - "executor": [MockFunction], - "id": "test", - "isExportable": true, - "minimumLicenseRequired": "basic", - "name": "Test", - "producer": "alerts", - "recoveryActionGroup": Object { - "id": "recovered", - "name": "Recovered", - }, - "validate": Object { - "params": Object { - "validate": [Function], - }, - }, - } - `); + Object { + "actionGroups": Array [ + Object { + "id": "default", + "name": "Default", + }, + Object { + "id": "recovered", + "name": "Recovered", + }, + ], + "actionVariables": Object { + "context": Array [], + "params": Array [], + "state": Array [], + }, + "defaultActionGroupId": "default", + "executor": [MockFunction], + "id": "test", + "isExportable": true, + "minimumLicenseRequired": "basic", + "name": "Test", + "producer": "alerts", + "recoveryActionGroup": Object { + "id": "recovered", + "name": "Recovered", + }, + "validLegacyConsumers": Array [], + "validate": Object { + "params": Object { + "validate": [Function], + }, + }, + } + `); }); test(`should throw an error if type isn't registered`, () => { @@ -713,6 +714,7 @@ describe('Create Lifecycle', () => { "name": "Recovered", }, "ruleTaskTimeout": "20m", + "validLegacyConsumers": Array [], }, } `); diff --git a/x-pack/plugins/alerting/server/rule_type_registry.ts b/x-pack/plugins/alerting/server/rule_type_registry.ts index c0339275bcf35..68e3e1d3d590b 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.ts @@ -38,6 +38,7 @@ import { getRuleTypeFeatureUsageName } from './lib/get_rule_type_feature_usage_n import { InMemoryMetrics } from './monitoring'; import { AlertingRulesConfig } from '.'; import { AlertsService } from './alerts_service/alerts_service'; +import { getRuleTypeIdValidLegacyConsumers } from './rule_type_registry_deprecated_consumers'; export interface ConstructorOptions { logger: Logger; @@ -70,6 +71,7 @@ export interface RegistryRuleType enabledInLicense: boolean; hasFieldsForAAD: boolean; hasAlertsMappings: boolean; + validLegacyConsumers: string[]; } /** @@ -102,6 +104,7 @@ export type NormalizedRuleType< RecoveryActionGroupId extends string, AlertData extends RuleAlertData > = { + validLegacyConsumers: string[]; actionGroups: Array>; } & Omit< RuleType< @@ -386,6 +389,7 @@ export class RuleTypeRegistry { doesSetRecoveryContext, alerts, fieldsForAAD, + validLegacyConsumers, }, ]) => { // KEEP the type here to be safe if not the map is ignoring it for some reason @@ -409,6 +413,7 @@ export class RuleTypeRegistry { ).isValid, hasFieldsForAAD: Boolean(fieldsForAAD), hasAlertsMappings: !!alerts, + validLegacyConsumers, ...(alerts ? { alerts } : {}), }; return ruleType; @@ -499,5 +504,6 @@ function augmentActionGroupsWithReserved< ...ruleType, actionGroups: [...actionGroups, ...reservedActionGroups], recoveryActionGroup: recoveryActionGroup ?? RecoveredActionGroup, + validLegacyConsumers: getRuleTypeIdValidLegacyConsumers(id), }; } diff --git a/x-pack/plugins/alerting/server/rule_type_registry_deprecated_consumers.test.ts b/x-pack/plugins/alerting/server/rule_type_registry_deprecated_consumers.test.ts new file mode 100644 index 0000000000000..e1a7828d85042 --- /dev/null +++ b/x-pack/plugins/alerting/server/rule_type_registry_deprecated_consumers.test.ts @@ -0,0 +1,103 @@ +/* + * 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 { + getRuleTypeIdValidLegacyConsumers, + ruleTypeIdWithValidLegacyConsumers, +} from './rule_type_registry_deprecated_consumers'; + +describe('rule_type_registry_deprecated_consumers', () => { + describe('ruleTypeIdWithValidLegacyConsumers', () => { + test('Only these rule type ids should be in the list', () => { + expect(Object.keys(ruleTypeIdWithValidLegacyConsumers)).toMatchInlineSnapshot(` + Array [ + "example.always-firing", + "transform_health", + ".index-threshold", + ".geo-containment", + ".es-query", + "xpack.ml.anomaly_detection_alert", + "xpack.ml.anomaly_detection_jobs_health", + "xpack.synthetics.alerts.monitorStatus", + "xpack.synthetics.alerts.tls", + "xpack.uptime.alerts.monitorStatus", + "xpack.uptime.alerts.tlsCertificate", + "xpack.uptime.alerts.durationAnomaly", + "xpack.uptime.alerts.tls", + "siem.eqlRule", + "siem.savedQueryRule", + "siem.indicatorRule", + "siem.mlRule", + "siem.queryRule", + "siem.thresholdRule", + "siem.newTermsRule", + "siem.notifications", + "slo.rules.burnRate", + "metrics.alert.anomaly", + "logs.alert.document.count", + "metrics.alert.inventory.threshold", + "metrics.alert.threshold", + "monitoring_alert_cluster_health", + "monitoring_alert_license_expiration", + "monitoring_alert_cpu_usage", + "monitoring_alert_missing_monitoring_data", + "monitoring_alert_disk_usage", + "monitoring_alert_thread_pool_search_rejections", + "monitoring_alert_thread_pool_write_rejections", + "monitoring_alert_jvm_memory_usage", + "monitoring_alert_nodes_changed", + "monitoring_alert_logstash_version_mismatch", + "monitoring_alert_kibana_version_mismatch", + "monitoring_alert_elasticsearch_version_mismatch", + "monitoring_ccr_read_exceptions", + "monitoring_shard_size", + "apm.transaction_duration", + "apm.anomaly", + "apm.error_rate", + "apm.transaction_error_rate", + "test.always-firing", + "test.always-firing-alert-as-data", + "test.authorization", + "test.cancellableRule", + "test.cumulative-firing", + "test.exceedsAlertLimit", + "test.failing", + "test.gold.noop", + "test.longRunning", + "test.multipleSearches", + "test.never-firing", + "test.noop", + "test.onlyContextVariables", + "test.onlyStateVariables", + "test.patternFiring", + "test.patternFiringAad", + "test.patternFiringAutoRecoverFalse", + "test.patternLongRunning", + "test.patternLongRunning.cancelAlertsOnRuleTimeout", + "test.patternSuccessOrFailure", + "test.restricted-noop", + "test.throw", + "test.unrestricted-noop", + "test.validation", + ] + `); + }); + }); + describe('getRuleTypeIdValidLegacyConsumers', () => { + test('".es-query" should have "alerts" & "discover" as legacy consumers', () => { + expect(getRuleTypeIdValidLegacyConsumers('.es-query')).toEqual(['alerts', 'discover']); + }); + + test('All other rule types except ".es-query" should have "alerts" as legacy consumer', () => { + for (const ruleTypeId of Object.keys(ruleTypeIdWithValidLegacyConsumers).filter( + (rt) => rt !== '.es-query' + )) { + expect(getRuleTypeIdValidLegacyConsumers(ruleTypeId)).toEqual(['alerts']); + } + }); + }); +}); diff --git a/x-pack/plugins/alerting/server/rule_type_registry_deprecated_consumers.ts b/x-pack/plugins/alerting/server/rule_type_registry_deprecated_consumers.ts new file mode 100644 index 0000000000000..d6a238c414243 --- /dev/null +++ b/x-pack/plugins/alerting/server/rule_type_registry_deprecated_consumers.ts @@ -0,0 +1,88 @@ +/* + * 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 { ALERTS_FEATURE_ID } from './types'; + +export const ruleTypeIdWithValidLegacyConsumers: Record = { + 'example.always-firing': [ALERTS_FEATURE_ID], + transform_health: [ALERTS_FEATURE_ID], + '.index-threshold': [ALERTS_FEATURE_ID], + '.geo-containment': [ALERTS_FEATURE_ID], + '.es-query': [ALERTS_FEATURE_ID, 'discover'], + 'xpack.ml.anomaly_detection_alert': [ALERTS_FEATURE_ID], + 'xpack.ml.anomaly_detection_jobs_health': [ALERTS_FEATURE_ID], + 'xpack.synthetics.alerts.monitorStatus': [ALERTS_FEATURE_ID], + 'xpack.synthetics.alerts.tls': [ALERTS_FEATURE_ID], + 'xpack.uptime.alerts.monitorStatus': [ALERTS_FEATURE_ID], + 'xpack.uptime.alerts.tlsCertificate': [ALERTS_FEATURE_ID], + 'xpack.uptime.alerts.durationAnomaly': [ALERTS_FEATURE_ID], + 'xpack.uptime.alerts.tls': [ALERTS_FEATURE_ID], + 'siem.eqlRule': [ALERTS_FEATURE_ID], + 'siem.savedQueryRule': [ALERTS_FEATURE_ID], + 'siem.indicatorRule': [ALERTS_FEATURE_ID], + 'siem.mlRule': [ALERTS_FEATURE_ID], + 'siem.queryRule': [ALERTS_FEATURE_ID], + 'siem.thresholdRule': [ALERTS_FEATURE_ID], + 'siem.newTermsRule': [ALERTS_FEATURE_ID], + 'siem.notifications': [ALERTS_FEATURE_ID], + 'slo.rules.burnRate': [ALERTS_FEATURE_ID], + 'metrics.alert.anomaly': [ALERTS_FEATURE_ID], + 'logs.alert.document.count': [ALERTS_FEATURE_ID], + 'metrics.alert.inventory.threshold': [ALERTS_FEATURE_ID], + 'metrics.alert.threshold': [ALERTS_FEATURE_ID], + monitoring_alert_cluster_health: [ALERTS_FEATURE_ID], + monitoring_alert_license_expiration: [ALERTS_FEATURE_ID], + monitoring_alert_cpu_usage: [ALERTS_FEATURE_ID], + monitoring_alert_missing_monitoring_data: [ALERTS_FEATURE_ID], + monitoring_alert_disk_usage: [ALERTS_FEATURE_ID], + monitoring_alert_thread_pool_search_rejections: [ALERTS_FEATURE_ID], + monitoring_alert_thread_pool_write_rejections: [ALERTS_FEATURE_ID], + monitoring_alert_jvm_memory_usage: [ALERTS_FEATURE_ID], + monitoring_alert_nodes_changed: [ALERTS_FEATURE_ID], + monitoring_alert_logstash_version_mismatch: [ALERTS_FEATURE_ID], + monitoring_alert_kibana_version_mismatch: [ALERTS_FEATURE_ID], + monitoring_alert_elasticsearch_version_mismatch: [ALERTS_FEATURE_ID], + monitoring_ccr_read_exceptions: [ALERTS_FEATURE_ID], + monitoring_shard_size: [ALERTS_FEATURE_ID], + 'apm.transaction_duration': [ALERTS_FEATURE_ID], + 'apm.anomaly': [ALERTS_FEATURE_ID], + 'apm.error_rate': [ALERTS_FEATURE_ID], + 'apm.transaction_error_rate': [ALERTS_FEATURE_ID], + 'test.always-firing': [ALERTS_FEATURE_ID], + 'test.always-firing-alert-as-data': [ALERTS_FEATURE_ID], + 'test.authorization': [ALERTS_FEATURE_ID], + 'test.cancellableRule': [ALERTS_FEATURE_ID], + 'test.cumulative-firing': [ALERTS_FEATURE_ID], + 'test.exceedsAlertLimit': [ALERTS_FEATURE_ID], + 'test.failing': [ALERTS_FEATURE_ID], + 'test.gold.noop': [ALERTS_FEATURE_ID], + 'test.longRunning': [ALERTS_FEATURE_ID], + 'test.multipleSearches': [ALERTS_FEATURE_ID], + 'test.never-firing': [ALERTS_FEATURE_ID], + 'test.noop': [ALERTS_FEATURE_ID], + 'test.onlyContextVariables': [ALERTS_FEATURE_ID], + 'test.onlyStateVariables': [ALERTS_FEATURE_ID], + 'test.patternFiring': [ALERTS_FEATURE_ID], + 'test.patternFiringAad': [ALERTS_FEATURE_ID], + 'test.patternFiringAutoRecoverFalse': [ALERTS_FEATURE_ID], + 'test.patternLongRunning': [ALERTS_FEATURE_ID], + 'test.patternLongRunning.cancelAlertsOnRuleTimeout': [ALERTS_FEATURE_ID], + 'test.patternSuccessOrFailure': [ALERTS_FEATURE_ID], + 'test.restricted-noop': [ALERTS_FEATURE_ID], + 'test.throw': [ALERTS_FEATURE_ID], + 'test.unrestricted-noop': [ALERTS_FEATURE_ID], + 'test.validation': [ALERTS_FEATURE_ID], +}; + +const getRuleTypeIdValidLegacyConsumers = (ruleTypeId: string): string[] => { + if (ruleTypeIdWithValidLegacyConsumers[ruleTypeId]) { + return ruleTypeIdWithValidLegacyConsumers[ruleTypeId]; + } + return []; +}; + +export { getRuleTypeIdValidLegacyConsumers }; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.test.ts index e0bbdb0d770ec..5e6e958f3bbc0 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.test.ts @@ -46,6 +46,7 @@ const ruleType: jest.Mocked = { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], }; const context = { diff --git a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts index 84f0a89e72c01..d01d45abc9759 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts @@ -32,6 +32,7 @@ describe('validateActions', () => { context: 'context', mappings: { fieldMap: { field: { type: 'fieldType', required: false } } }, }, + validLegacyConsumers: [], }; const data = { diff --git a/x-pack/plugins/alerting/server/rules_client/methods/aggregate.ts b/x-pack/plugins/alerting/server/rules_client/methods/aggregate.ts index 6dd26c1a7e197..69ad4edaa42f3 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/aggregate.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/aggregate.ts @@ -7,6 +7,7 @@ import { KueryNode, nodeBuilder } from '@kbn/es-query'; import type { AggregationsAggregationContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { isEmpty } from 'lodash'; import { AlertingAuthorizationEntity } from '../../authorization'; import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { buildKueryNodeFilter } from '../common'; @@ -25,13 +26,14 @@ export async function aggregate>( params: AggregateParams ): Promise { const { options = {}, aggs } = params; - const { filter, page = 1, perPage = 0, ...restOptions } = options; + const { filter, page = 1, perPage = 0, filterConsumers, ...restOptions } = options; let authorizationTuple; try { authorizationTuple = await context.authorization.getFindAuthorizationFilter( AlertingAuthorizationEntity.Rule, - alertingAuthorizationFilterOpts + alertingAuthorizationFilterOpts, + isEmpty(filterConsumers) ? undefined : new Set(filterConsumers) ); validateRuleAggregationFields(aggs); } catch (error) { diff --git a/x-pack/plugins/alerting/server/rules_client/methods/find.ts b/x-pack/plugins/alerting/server/rules_client/methods/find.ts index 6493174a61b6a..537dfef55aff0 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/find.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/find.ts @@ -7,7 +7,7 @@ import Boom from '@hapi/boom'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { pick } from 'lodash'; +import { isEmpty, pick } from 'lodash'; import { KueryNode, nodeBuilder } from '@kbn/es-query'; import { AlertConsumers } from '@kbn/rule-data-utils'; import { RawRule, RuleTypeParams, SanitizedRule, Rule } from '../../types'; @@ -34,6 +34,7 @@ export interface FindParams { options?: FindOptions; excludeFromPublicApi?: boolean; includeSnoozeData?: boolean; + featuresIds?: string[]; } export interface FindOptions extends IndexType { @@ -50,6 +51,7 @@ export interface FindOptions extends IndexType { }; fields?: string[]; filter?: string | KueryNode; + filterConsumers?: string[]; } export interface FindResult { @@ -62,7 +64,7 @@ export interface FindResult { export async function find( context: RulesClientContext, { - options: { fields, ...options } = {}, + options: { fields, filterConsumers, ...options } = {}, excludeFromPublicApi = false, includeSnoozeData = false, }: FindParams = {} @@ -71,7 +73,8 @@ export async function find( try { authorizationTuple = await context.authorization.getFindAuthorizationFilter( AlertingAuthorizationEntity.Rule, - alertingAuthorizationFilterOpts + alertingAuthorizationFilterOpts, + isEmpty(filterConsumers) ? undefined : new Set(filterConsumers) ); } catch (error) { context.auditLogger?.log( @@ -84,7 +87,6 @@ export async function find( } const { filter: authorizationFilter, ensureRuleTypeIsAuthorized } = authorizationTuple; - const filterKueryNode = buildKueryNodeFilter(options.filter); let sortField = mapSortField(options.sortField); if (excludeFromPublicApi) { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/aggregate.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/aggregate.test.ts index c45e74da45999..ab5b1d7bcc16d 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/aggregate.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/aggregate.test.ts @@ -76,6 +76,7 @@ describe('aggregate()', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]); beforeEach(() => { @@ -160,6 +161,7 @@ describe('aggregate()', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]) ); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/bulk_delete.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/bulk_delete.test.ts index dbe04ec420000..70911dcde76d2 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/bulk_delete.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/bulk_delete.test.ts @@ -178,6 +178,7 @@ describe('bulkDelete', () => { validate: { params: schema.any(), }, + validLegacyConsumers: [], }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts index 4b1a6fc2eba8c..03e4cfb50d514 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts @@ -84,6 +84,7 @@ describe('find()', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]); beforeEach(() => { @@ -146,6 +147,7 @@ describe('find()', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]) ); @@ -459,6 +461,7 @@ describe('find()', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]) ); @@ -477,6 +480,7 @@ describe('find()', () => { validate: { params: schema.any(), }, + validLegacyConsumers: [], })); ruleTypeRegistry.get.mockImplementationOnce(() => ({ id: '123', @@ -497,6 +501,7 @@ describe('find()', () => { validate: { params: schema.any(), }, + validLegacyConsumers: [], })); unsecuredSavedObjectsClient.find.mockResolvedValue({ total: 2, @@ -667,6 +672,7 @@ describe('find()', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]) ); @@ -685,6 +691,7 @@ describe('find()', () => { validate: { params: schema.any(), }, + validLegacyConsumers: [], })); ruleTypeRegistry.get.mockImplementationOnce(() => ({ id: '123', @@ -705,6 +712,7 @@ describe('find()', () => { validate: { params: schema.any(), }, + validLegacyConsumers: [], })); unsecuredSavedObjectsClient.find.mockResolvedValue({ total: 2, diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts index 20af52838652a..a2210f56af291 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts @@ -312,6 +312,7 @@ describe('get()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ @@ -438,6 +439,7 @@ describe('get()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_tags.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_tags.test.ts index 1f0c4f405f2c2..31eb7bd5bafad 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_tags.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_tags.test.ts @@ -63,6 +63,7 @@ const listedTypes = new Set([ enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]); @@ -116,6 +117,7 @@ describe('getTags()', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]) ); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/lib.ts b/x-pack/plugins/alerting/server/rules_client/tests/lib.ts index 25afdedf54d3c..ee84a789454ef 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/lib.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/lib.ts @@ -122,6 +122,7 @@ export function getBeforeSetup( validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); rulesClientParams.getEventLogClient.mockResolvedValue( eventLogClient ?? eventLogClientMock.create() diff --git a/x-pack/plugins/alerting/server/rules_client/tests/list_rule_types.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/list_rule_types.test.ts index 9c8c78f2753f4..c8b69f632fd08 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/list_rule_types.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/list_rule_types.test.ts @@ -69,6 +69,7 @@ describe('listRuleTypes', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }; const myAppAlertType: RegistryRuleType = { actionGroups: [], @@ -83,6 +84,7 @@ describe('listRuleTypes', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }; const setOfAlertTypes = new Set([myAppAlertType, alertingAlertType]); @@ -127,6 +129,7 @@ describe('listRuleTypes', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, { id: 'myOtherType', @@ -140,6 +143,7 @@ describe('listRuleTypes', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]); beforeEach(() => { @@ -163,6 +167,7 @@ describe('listRuleTypes', () => { enabledInLicense: true, hasAlertsMappings: false, hasFieldsForAAD: false, + validLegacyConsumers: [], }, ]); authorization.filterByRuleTypeAuthorization.mockResolvedValue(authorizedTypes); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts index 37372752d53a3..1ebdabaf4cf6a 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts @@ -287,6 +287,7 @@ describe('resolve()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ @@ -423,6 +424,7 @@ describe('resolve()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ diff --git a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts index 26350664b8445..7dadc787f7710 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts @@ -175,6 +175,7 @@ describe('update()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], }); (migrateLegacyActions as jest.Mock).mockResolvedValue({ hasLegacyActions: false, @@ -997,6 +998,7 @@ describe('update()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', @@ -1512,6 +1514,7 @@ describe('update()', () => { return { state: {} }; }, producer: 'alerts', + validLegacyConsumers: [], }); await expect( rulesClient.update({ @@ -1896,6 +1899,7 @@ describe('update()', () => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], }); encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({ id: alertId, diff --git a/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts b/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts index fcde2f6e4f444..a996598655f91 100644 --- a/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts +++ b/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts @@ -362,6 +362,7 @@ beforeEach(() => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], })); ruleTypeRegistry.get.mockReturnValue({ @@ -379,6 +380,7 @@ beforeEach(() => { validate: { params: { validate: (params) => params }, }, + validLegacyConsumers: [], }); rulesClient = new RulesClient(rulesClientParams); diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts index ce86dd4756093..de77ebd536549 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts @@ -80,6 +80,7 @@ const ruleType: NormalizedRuleType< mappings: { fieldMap: { field: { type: 'fieldType', required: false } } }, }, autoRecoverAlerts: false, + validLegacyConsumers: [], }; const rule = { id: '1', diff --git a/x-pack/plugins/alerting/server/task_runner/fixtures.ts b/x-pack/plugins/alerting/server/task_runner/fixtures.ts index 467d7460afc2b..40a8e65a1f009 100644 --- a/x-pack/plugins/alerting/server/task_runner/fixtures.ts +++ b/x-pack/plugins/alerting/server/task_runner/fixtures.ts @@ -152,6 +152,7 @@ export const ruleType: jest.Mocked = { context: 'test', mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, }, + validLegacyConsumers: [], }; export const mockRunNowResponse = { diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts index 3cfa7dde78341..786a0fe4a338f 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts @@ -63,6 +63,7 @@ const ruleType: UntypedNormalizedRuleType = { validate: { params: schema.any(), }, + validLegacyConsumers: [], }; let fakeTimer: sinon.SinonFakeTimers; diff --git a/x-pack/plugins/apm/server/feature.ts b/x-pack/plugins/apm/server/feature.ts index 8311fc43fcb57..fe22261fd57f7 100644 --- a/x-pack/plugins/apm/server/feature.ts +++ b/x-pack/plugins/apm/server/feature.ts @@ -12,8 +12,11 @@ import { LicensingPluginSetup, LicensingApiRequestHandlerContext, } from '@kbn/licensing-plugin/server'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; -import { ES_QUERY_ID } from '@kbn/stack-alerts-plugin/common'; + +import { + ES_QUERY_ID, + OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, +} from '@kbn/rule-data-utils'; import { APM_INDEX_SETTINGS_SAVED_OBJECT_TYPE } from '@kbn/apm-data-access-plugin/server/saved_objects/apm_indices'; import { ApmRuleType, diff --git a/x-pack/plugins/apm/tsconfig.json b/x-pack/plugins/apm/tsconfig.json index adbcf96345dfe..1b06fa44a8dd5 100644 --- a/x-pack/plugins/apm/tsconfig.json +++ b/x-pack/plugins/apm/tsconfig.json @@ -96,7 +96,6 @@ "@kbn/unified-field-list", "@kbn/discover-plugin", "@kbn/observability-ai-assistant-plugin", - "@kbn/stack-alerts-plugin", "@kbn/apm-data-access-plugin" ], "exclude": ["target/**/*"] diff --git a/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap b/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap index 5ab73d95c15de..3edffdda86868 100644 --- a/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap +++ b/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap @@ -680,15 +680,11 @@ Array [ "privilege": Object { "alerting": Object { "alert": Object { - "all": Array [ - ".es-query", - ], + "all": Array [], "read": Array [], }, "rule": Object { - "all": Array [ - ".es-query", - ], + "all": Array [], "read": Array [], }, }, @@ -739,18 +735,6 @@ Array [ }, Object { "privilege": Object { - "alerting": Object { - "alert": Object { - "all": Array [ - ".es-query", - ], - }, - "rule": Object { - "all": Array [ - ".es-query", - ], - }, - }, "app": Array [ "discover", "kibana", @@ -1284,15 +1268,11 @@ Array [ "privilege": Object { "alerting": Object { "alert": Object { - "all": Array [ - ".es-query", - ], + "all": Array [], "read": Array [], }, "rule": Object { - "all": Array [ - ".es-query", - ], + "all": Array [], "read": Array [], }, }, @@ -1343,18 +1323,6 @@ Array [ }, Object { "privilege": Object { - "alerting": Object { - "alert": Object { - "all": Array [ - ".es-query", - ], - }, - "rule": Object { - "all": Array [ - ".es-query", - ], - }, - }, "app": Array [ "discover", "kibana", diff --git a/x-pack/plugins/features/server/oss_features.ts b/x-pack/plugins/features/server/oss_features.ts index 076ef4941c530..4097780cdfe10 100644 --- a/x-pack/plugins/features/server/oss_features.ts +++ b/x-pack/plugins/features/server/oss_features.ts @@ -32,7 +32,6 @@ export const buildOSSFeatures = ({ category: DEFAULT_APP_CATEGORIES.kibana, app: ['discover', 'kibana'], catalogue: ['discover'], - alerting: ['.es-query'], privileges: { all: { app: ['discover', 'kibana'], @@ -43,14 +42,6 @@ export const buildOSSFeatures = ({ read: ['index-pattern'], }, ui: ['show', 'save', 'saveQuery'], - alerting: { - rule: { - all: ['.es-query'], - }, - alert: { - all: ['.es-query'], - }, - }, }, read: { app: ['discover', 'kibana'], @@ -60,14 +51,6 @@ export const buildOSSFeatures = ({ read: ['index-pattern', 'search', 'query'], }, ui: ['show'], - alerting: { - rule: { - all: ['.es-query'], - }, - alert: { - all: ['.es-query'], - }, - }, }, }, subFeatures: [ diff --git a/x-pack/plugins/infra/server/features.ts b/x-pack/plugins/infra/server/features.ts index 3c95fb71324c0..49d1c1dff7544 100644 --- a/x-pack/plugins/infra/server/features.ts +++ b/x-pack/plugins/infra/server/features.ts @@ -8,8 +8,8 @@ import { i18n } from '@kbn/i18n'; import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server'; import { logViewSavedObjectName } from '@kbn/logs-shared-plugin/server'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; -import { ES_QUERY_ID } from '@kbn/stack-alerts-plugin/common'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { ES_QUERY_ID } from '@kbn/rule-data-utils'; import { LOG_DOCUMENT_COUNT_RULE_TYPE_ID } from '../common/alerting/logs/log_threshold/types'; import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, diff --git a/x-pack/plugins/infra/tsconfig.json b/x-pack/plugins/infra/tsconfig.json index 31643d4a1235b..0ace444a00e99 100644 --- a/x-pack/plugins/infra/tsconfig.json +++ b/x-pack/plugins/infra/tsconfig.json @@ -70,7 +70,6 @@ "@kbn/licensing-plugin", "@kbn/aiops-utils", "@kbn/lens-embeddable-utils", - "@kbn/stack-alerts-plugin" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability/common/constants.ts b/x-pack/plugins/observability/common/constants.ts index 20e121595489e..ef3bdea149ba7 100644 --- a/x-pack/plugins/observability/common/constants.ts +++ b/x-pack/plugins/observability/common/constants.ts @@ -9,10 +9,8 @@ import { i18n } from '@kbn/i18n'; import { AlertConsumers } from '@kbn/rule-data-utils'; import type { ValidFeatureId } from '@kbn/rule-data-utils'; import type { RuleCreationValidConsumer } from '@kbn/triggers-actions-ui-plugin/public'; -import { STACK_ALERTS_FEATURE_ID } from '@kbn/stack-alerts-plugin/common'; export const SLO_BURN_RATE_RULE_TYPE_ID = 'slo.rules.burnRate'; -export const OBSERVABILITY_THRESHOLD_RULE_TYPE_ID = 'observability.rules.threshold'; export const INVALID_EQUATION_REGEX = /[^A-Z|+|\-|\s|\d+|\.|\(|\)|\/|\*|>|<|=|\?|\:|&|\!|\|]+/g; export const ALERT_STATUS_ALL = 'all'; @@ -65,5 +63,4 @@ export const observabilityRuleCreationValidConsumers: RuleCreationValidConsumer[ AlertConsumers.LOGS, AlertConsumers.UPTIME, AlertConsumers.SLO, - STACK_ALERTS_FEATURE_ID, ]; diff --git a/x-pack/plugins/observability/public/components/threshold/components/alert_flyout.tsx b/x-pack/plugins/observability/public/components/threshold/components/alert_flyout.tsx index e0d24d58eb0db..e270ce8b8b143 100644 --- a/x-pack/plugins/observability/public/components/threshold/components/alert_flyout.tsx +++ b/x-pack/plugins/observability/public/components/threshold/components/alert_flyout.tsx @@ -6,12 +6,14 @@ */ import React, { useCallback, useContext, useMemo } from 'react'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '../../../../common/constants'; + +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { MetricsExplorerSeries } from '../../../../common/threshold_rule/metrics_explorer'; import { TriggerActionsContext } from './triggers_actions_context'; import { useAlertPrefillContext } from '../helpers/use_alert_prefill'; import { MetricsExplorerOptions } from '../hooks/use_metrics_explorer_options'; +import { observabilityRuleCreationValidConsumers } from '../../../../common/constants'; interface Props { visible?: boolean; @@ -28,7 +30,7 @@ export function AlertFlyout(props: Props) { () => triggersActionsUI && triggersActionsUI.getAddRuleFlyout({ - consumer: 'alerts', + consumer: 'logs', onClose: onCloseFlyout, canChangeTrigger: false, ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, @@ -36,6 +38,7 @@ export function AlertFlyout(props: Props) { currentOptions: props.options, series: props.series, }, + validConsumers: observabilityRuleCreationValidConsumers, }), // eslint-disable-next-line react-hooks/exhaustive-deps [triggersActionsUI, onCloseFlyout] diff --git a/x-pack/plugins/observability/public/hooks/use_get_filtered_rule_types.ts b/x-pack/plugins/observability/public/hooks/use_get_filtered_rule_types.ts index 9ed239f507ea2..834cc31f39d2e 100644 --- a/x-pack/plugins/observability/public/hooks/use_get_filtered_rule_types.ts +++ b/x-pack/plugins/observability/public/hooks/use_get_filtered_rule_types.ts @@ -6,7 +6,7 @@ */ import { useMemo } from 'react'; -import { ES_QUERY_ID } from '@kbn/stack-alerts-plugin/common'; +import { ES_QUERY_ID } from '@kbn/rule-data-utils'; import { usePluginContext } from './use_plugin_context'; export function useGetFilteredRuleTypes() { diff --git a/x-pack/plugins/observability/public/pages/alerts/alerts.tsx b/x-pack/plugins/observability/public/pages/alerts/alerts.tsx index c09cc504e62db..a95cdce7f9948 100644 --- a/x-pack/plugins/observability/public/pages/alerts/alerts.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/alerts.tsx @@ -31,10 +31,12 @@ import { } from '../../components/alert_search_bar/containers'; import { calculateTimeRangeBucketSize } from '../overview/helpers/calculate_bucket_size'; import { getAlertSummaryTimeRange } from '../../utils/alert_summary_widget'; -import { observabilityAlertFeatureIds } from '../../../common/constants'; +import { + observabilityAlertFeatureIds, + observabilityRuleCreationValidConsumers, +} from '../../../common/constants'; import { ALERTS_URL_STORAGE_KEY } from '../../../common/constants'; import { HeaderMenu } from '../overview/components/header_menu/header_menu'; -import { useGetFilteredRuleTypes } from '../../hooks/use_get_filtered_rule_types'; const ALERTS_SEARCH_BAR_ID = 'alerts-search-bar-o11y'; const ALERTS_PER_PAGE = 50; @@ -131,6 +133,7 @@ function InternalAlertsPage() { const response = await loadRuleAggregations({ http, typesFilter: filteredRuleTypes, + filterConsumers: observabilityRuleCreationValidConsumers, }); const { ruleExecutionStatus, ruleMutedStatus, ruleEnabledStatus, ruleSnoozedStatus } = response; diff --git a/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx b/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx index 14afd4994b2be..3b883febf3add 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx @@ -20,8 +20,7 @@ import { CaseAttachmentsWithoutOwner } from '@kbn/cases-plugin/public'; import { AttachmentType } from '@kbn/cases-plugin/common'; import { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; import { TimelineNonEcsData } from '@kbn/timelines-plugin/common'; - -import { ALERT_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { ALERT_RULE_TYPE_ID, OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { useKibana } from '../../../utils/kibana_react'; import { useGetUserCasesPermissions } from '../../../hooks/use_get_user_cases_permissions'; @@ -32,7 +31,6 @@ import { RULE_DETAILS_PAGE_ID } from '../../rule_details/constants'; import type { ObservabilityRuleTypeRegistry } from '../../..'; import type { ConfigSchema } from '../../../plugin'; import type { TopAlert } from '../../../typings/alerts'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '../../../../common/constants'; const ALERT_DETAILS_PAGE_ID = 'alert-details-o11y'; diff --git a/x-pack/plugins/observability/public/pages/rules/rules.tsx b/x-pack/plugins/observability/public/pages/rules/rules.tsx index 2c2ef3463b0ea..700f348842757 100644 --- a/x-pack/plugins/observability/public/pages/rules/rules.tsx +++ b/x-pack/plugins/observability/public/pages/rules/rules.tsx @@ -150,6 +150,7 @@ export function RulesPage({ activeTab = RULES_TAB_NAME }: RulesPageProps) { setRefresh(new Date()); return Promise.resolve(); }} + useRuleProducer={true} /> )} diff --git a/x-pack/plugins/observability/public/pages/rules/rules_tab.tsx b/x-pack/plugins/observability/public/pages/rules/rules_tab.tsx index 317091e81fb29..22b321262943c 100644 --- a/x-pack/plugins/observability/public/pages/rules/rules_tab.tsx +++ b/x-pack/plugins/observability/public/pages/rules/rules_tab.tsx @@ -11,6 +11,7 @@ import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import { RuleStatus } from '@kbn/triggers-actions-ui-plugin/public'; import { useKibana } from '../../utils/kibana_react'; import { useGetFilteredRuleTypes } from '../../hooks/use_get_filtered_rule_types'; +import { observabilityRuleCreationValidConsumers } from '../../../common/constants'; interface RulesTabProps { setRefresh: React.Dispatch>; @@ -73,6 +74,7 @@ export function RulesTab({ setRefresh, stateRefresh }: RulesTabProps) { return ( { @@ -137,6 +137,7 @@ interface SingleSearchAfterAndAudit { operation: WriteOperations.Update | ReadOperations.Find | ReadOperations.Get; sort?: estypes.SortOptions[] | undefined; lastSortIds?: Array | undefined; + featureIds?: string[]; } /** @@ -152,6 +153,7 @@ export class AlertsClient { private readonly spaceId: string | undefined; private readonly ruleDataService: IRuleDataService; private readonly getRuleType: RuleTypeRegistry['get']; + private getAlertIndicesAlias!: AlertingStart['getAlertIndicesAlias']; constructor(options: ConstructorOptions) { this.logger = options.logger; @@ -163,6 +165,7 @@ export class AlertsClient { this.spaceId = this.authorization.getSpaceId(); this.ruleDataService = options.ruleDataService; this.getRuleType = options.getRuleType; + this.getAlertIndicesAlias = options.getAlertIndicesAlias; } private getOutcome( @@ -281,6 +284,7 @@ export class AlertsClient { operation, sort, lastSortIds = [], + featureIds, }: SingleSearchAfterAndAudit) { try { const alertSpaceId = this.spaceId; @@ -294,7 +298,14 @@ export class AlertsClient { let queryBody: estypes.SearchRequest['body'] = { fields: [ALERT_RULE_TYPE_ID, ALERT_RULE_CONSUMER, ALERT_WORKFLOW_STATUS, SPACE_IDS], - query: await this.buildEsQueryWithAuthz(query, id, alertSpaceId, operation, config), + query: await this.buildEsQueryWithAuthz( + query, + id, + alertSpaceId, + operation, + config, + featureIds ? new Set(featureIds) : undefined + ), aggs, _source, track_total_hits: trackTotalHits, @@ -433,10 +444,15 @@ export class AlertsClient { id: string | null | undefined, alertSpaceId: string, operation: WriteOperations.Update | ReadOperations.Get | ReadOperations.Find, - config: EsQueryConfig + config: EsQueryConfig, + featuresIds?: Set ) { try { - const authzFilter = (await getAuthzFilter(this.authorization, operation)) as Filter; + const authzFilter = (await getAuthzFilter( + this.authorization, + operation, + featuresIds + )) as Filter; const spacesFilter = getSpacesFilter(alertSpaceId) as unknown as Filter; let esQuery; if (id != null) { @@ -681,6 +697,7 @@ export class AlertsClient { }, }, size: 0, + featureIds, }); let activeAlertCount = 0; @@ -1006,35 +1023,16 @@ export class AlertsClient { public async getAuthorizedAlertsIndices(featureIds: string[]): Promise { try { - // ATTENTION FUTURE DEVELOPER when you are a super user the augmentedRuleTypes.authorizedRuleTypes will - // return all of the features that you can access and does not care about your featureIds - const augmentedRuleTypes = await this.authorization.getAugmentedRuleTypesWithAuthorization( - featureIds, - [ReadOperations.Find, ReadOperations.Get, WriteOperations.Update], - AlertingAuthorizationEntity.Alert + const authorizedRuleTypes = await this.authorization.getAuthorizedRuleTypes( + AlertingAuthorizationEntity.Alert, + new Set(featureIds) ); - // As long as the user can read a minimum of one type of rule type produced by the provided feature, - // the user should be provided that features' alerts index. - // Limiting which alerts that user can read on that index will be done via the findAuthorizationFilter - const authorizedFeatures = new Set(); - for (const ruleType of augmentedRuleTypes.authorizedRuleTypes) { - authorizedFeatures.add(ruleType.producer); - } - const validAuthorizedFeatures = Array.from(authorizedFeatures).filter( - (feature): feature is ValidFeatureId => - featureIds.includes(feature) && isValidFeatureId(feature) + const indices = this.getAlertIndicesAlias( + authorizedRuleTypes.map((art: { id: any }) => art.id), + this.spaceId ); - const toReturn = validAuthorizedFeatures.map((feature) => { - const index = this.ruleDataService.findIndexByFeature(feature, Dataset.alerts); - if (index == null) { - throw new Error(`This feature id ${feature} should be associated to an alert index`); - } - return ( - index?.getPrimaryAlias(feature === AlertConsumers.SIEM ? this.spaceId ?? '*' : '*') ?? '' - ); - }); - return toReturn; + return indices; } catch (exc) { const errMessage = `getAuthorizedAlertsIndices failed to get authorized rule types: ${exc}`; this.logger.error(errMessage); diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.test.ts index bbb33244e2975..43966d1207004 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.test.ts @@ -26,6 +26,7 @@ const alertsClientFactoryParams: AlertsClientFactoryProps = { esClient: {} as ElasticsearchClient, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getAlertIndicesAlias: jest.fn(), }; const auditLogger = auditLoggerMock.create(); @@ -53,6 +54,7 @@ describe('AlertsClientFactory', () => { esClient: {}, ruleDataService: alertsClientFactoryParams.ruleDataService, getRuleType: alertsClientFactoryParams.getRuleType, + getAlertIndicesAlias: alertsClientFactoryParams.getAlertIndicesAlias, }); }); diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.ts b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.ts index 9a171514b588a..de0afb5a0b226 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.ts @@ -8,7 +8,10 @@ import { PublicMethodsOf } from '@kbn/utility-types'; import { ElasticsearchClient, KibanaRequest, Logger } from '@kbn/core/server'; import type { RuleTypeRegistry } from '@kbn/alerting-plugin/server/types'; -import { AlertingAuthorization } from '@kbn/alerting-plugin/server'; +import { + AlertingAuthorization, + PluginStartContract as AlertingStart, +} from '@kbn/alerting-plugin/server'; import { SecurityPluginSetup } from '@kbn/security-plugin/server'; import { IRuleDataService } from '../rule_data_plugin_service'; import { AlertsClient } from './alerts_client'; @@ -20,6 +23,7 @@ export interface AlertsClientFactoryProps { securityPluginSetup: SecurityPluginSetup | undefined; ruleDataService: IRuleDataService | null; getRuleType: RuleTypeRegistry['get']; + getAlertIndicesAlias: AlertingStart['getAlertIndicesAlias']; } export class AlertsClientFactory { @@ -32,6 +36,7 @@ export class AlertsClientFactory { private securityPluginSetup!: SecurityPluginSetup | undefined; private ruleDataService!: IRuleDataService | null; private getRuleType!: RuleTypeRegistry['get']; + private getAlertIndicesAlias!: AlertingStart['getAlertIndicesAlias']; public initialize(options: AlertsClientFactoryProps) { /** @@ -48,6 +53,7 @@ export class AlertsClientFactory { this.securityPluginSetup = options.securityPluginSetup; this.ruleDataService = options.ruleDataService; this.getRuleType = options.getRuleType; + this.getAlertIndicesAlias = options.getAlertIndicesAlias; } public async create(request: KibanaRequest): Promise { @@ -60,6 +66,7 @@ export class AlertsClientFactory { esClient: this.esClient, ruleDataService: this.ruleDataService!, getRuleType: this.getRuleType, + getAlertIndicesAlias: this.getAlertIndicesAlias, }); } } diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update.test.ts index fb3acbd5e26fd..4229ae23793fc 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update.test.ts @@ -31,6 +31,7 @@ const alertsClientParams: jest.Mocked = { auditLogger, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getAlertIndicesAlias: jest.fn(), }; const DEFAULT_SPACE = 'test_default_space_id'; @@ -334,10 +335,10 @@ describe('bulkUpdate()', () => { status: 'closed', }) ).rejects.toThrowErrorMatchingInlineSnapshot(` - "queryAndAuditAllAlerts threw an error: Unable to retrieve alerts with query \\"kibana.alert.status: active\\" and operation update - Error: Unable to retrieve alert details for alert with id of \\"null\\" or with query \\"kibana.alert.status: active\\" and operation update - Error: Error: Unauthorized for fake.rule and apm" - `); + "queryAndAuditAllAlerts threw an error: Unable to retrieve alerts with query \\"kibana.alert.status: active\\" and operation update + Error: Unable to retrieve alert details for alert with id of \\"null\\" or with query \\"kibana.alert.status: active\\" and operation update + Error: Error: Unauthorized for fake.rule and apm" + `); expect(auditLogger.log).toHaveBeenNthCalledWith(1, { message: `Failed attempt to update alert [id=${fakeAlertId}]`, @@ -401,10 +402,10 @@ describe('bulkUpdate()', () => { status: 'closed', }) ).rejects.toThrowErrorMatchingInlineSnapshot(` - "queryAndAuditAllAlerts threw an error: Unable to retrieve alerts with query \\"kibana.alert.status: active\\" and operation update - Error: Unable to retrieve alert details for alert with id of \\"null\\" or with query \\"kibana.alert.status: active\\" and operation update - Error: Error: Unauthorized for fake.rule and apm" - `); + "queryAndAuditAllAlerts threw an error: Unable to retrieve alerts with query \\"kibana.alert.status: active\\" and operation update + Error: Unable to retrieve alert details for alert with id of \\"null\\" or with query \\"kibana.alert.status: active\\" and operation update + Error: Error: Unauthorized for fake.rule and apm" + `); expect(auditLogger.log).toHaveBeenCalledTimes(2); expect(auditLogger.log).toHaveBeenNthCalledWith(1, { diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update_cases.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update_cases.test.ts index 20db7747fc898..4047a3ecadd27 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update_cases.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update_cases.test.ts @@ -37,6 +37,7 @@ describe('bulkUpdateCases', () => { auditLogger, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getAlertIndicesAlias: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/find_alerts.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/find_alerts.test.ts index b287ba863e5fa..37ad46a523a70 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/find_alerts.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/find_alerts.test.ts @@ -30,6 +30,7 @@ const alertsClientParams: jest.Mocked = { auditLogger, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getAlertIndicesAlias: jest.fn(), }; const DEFAULT_SPACE = 'test_default_space_id'; @@ -420,9 +421,9 @@ describe('find()', () => { index: '.alerts-observability.apm.alerts', }) ).rejects.toThrowErrorMatchingInlineSnapshot(` - "Unable to retrieve alert details for alert with id of \\"undefined\\" or with query \\"[object Object]\\" and operation find - Error: Error: Unauthorized for fake.rule and apm" - `); + "Unable to retrieve alert details for alert with id of \\"undefined\\" or with query \\"[object Object]\\" and operation find + Error: Error: Unauthorized for fake.rule and apm" + `); expect(auditLogger.log).toHaveBeenNthCalledWith(1, { message: `Failed attempt to access alert [id=${fakeAlertId}]`, @@ -450,9 +451,9 @@ describe('find()', () => { index: '.alerts-observability.apm.alerts', }) ).rejects.toThrowErrorMatchingInlineSnapshot(` - "Unable to retrieve alert details for alert with id of \\"undefined\\" or with query \\"[object Object]\\" and operation find - Error: Error: something went wrong" - `); + "Unable to retrieve alert details for alert with id of \\"undefined\\" or with query \\"[object Object]\\" and operation find + Error: Error: something went wrong" + `); }); describe('authorization', () => { diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/get.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/get.test.ts index c7b342f0fa548..fb1e0eef432ef 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/get.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/get.test.ts @@ -31,6 +31,7 @@ const alertsClientParams: jest.Mocked = { auditLogger, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getAlertIndicesAlias: jest.fn(), }; const DEFAULT_SPACE = 'test_default_space_id'; @@ -266,9 +267,9 @@ describe('get()', () => { await expect(alertsClient.get({ id: fakeAlertId, index: '.alerts-observability.apm.alerts' })) .rejects.toThrowErrorMatchingInlineSnapshot(` - "Unable to retrieve alert details for alert with id of \\"myfakeid1\\" or with query \\"undefined\\" and operation get - Error: Error: Unauthorized for fake.rule and apm" - `); + "Unable to retrieve alert details for alert with id of \\"myfakeid1\\" or with query \\"undefined\\" and operation get + Error: Error: Unauthorized for fake.rule and apm" + `); expect(auditLogger.log).toHaveBeenNthCalledWith(1, { message: `Failed attempt to access alert [id=${fakeAlertId}]`, @@ -293,9 +294,9 @@ describe('get()', () => { await expect( alertsClient.get({ id: 'NoxgpHkBqbdrfX07MqXV', index: '.alerts-observability.apm.alerts' }) ).rejects.toThrowErrorMatchingInlineSnapshot(` - "Unable to retrieve alert details for alert with id of \\"NoxgpHkBqbdrfX07MqXV\\" or with query \\"undefined\\" and operation get - Error: Error: something went wrong" - `); + "Unable to retrieve alert details for alert with id of \\"NoxgpHkBqbdrfX07MqXV\\" or with query \\"undefined\\" and operation get + Error: Error: something went wrong" + `); }); describe('authorization', () => { diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/remove_cases_from_alerts.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/remove_cases_from_alerts.test.ts index 08f0c3c21ea37..2611200afd85f 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/remove_cases_from_alerts.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/remove_cases_from_alerts.test.ts @@ -32,6 +32,7 @@ describe('remove cases from alerts', () => { auditLogger, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getAlertIndicesAlias: jest.fn(), }; beforeEach(() => { @@ -89,6 +90,7 @@ describe('remove cases from alerts', () => { auditLogger, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getAlertIndicesAlias: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/update.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/update.test.ts index 4db890d93b326..bca5e7d967f3a 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/update.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/update.test.ts @@ -30,6 +30,7 @@ const alertsClientParams: jest.Mocked = { auditLogger, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getAlertIndicesAlias: jest.fn(), }; const DEFAULT_SPACE = 'test_default_space_id'; @@ -257,9 +258,9 @@ describe('update()', () => { index: '.alerts-observability.apm.alerts', }) ).rejects.toThrowErrorMatchingInlineSnapshot(` - "Unable to retrieve alert details for alert with id of \\"myfakeid1\\" or with query \\"undefined\\" and operation update - Error: Error: Unauthorized for fake.rule and apm" - `); + "Unable to retrieve alert details for alert with id of \\"myfakeid1\\" or with query \\"undefined\\" and operation update + Error: Error: Unauthorized for fake.rule and apm" + `); expect(auditLogger.log).toHaveBeenNthCalledWith(1, { message: `Failed attempt to update alert [id=${fakeAlertId}]`, @@ -289,9 +290,9 @@ describe('update()', () => { index: '.alerts-observability.apm.alerts', }) ).rejects.toThrowErrorMatchingInlineSnapshot(` - "Unable to retrieve alert details for alert with id of \\"NoxgpHkBqbdrfX07MqXV\\" or with query \\"undefined\\" and operation update - Error: Error: something went wrong on update" - `); + "Unable to retrieve alert details for alert with id of \\"NoxgpHkBqbdrfX07MqXV\\" or with query \\"undefined\\" and operation update + Error: Error: something went wrong on update" + `); }); test(`throws an error if ES client update fails`, async () => { diff --git a/x-pack/plugins/rule_registry/server/lib/get_authz_filter.ts b/x-pack/plugins/rule_registry/server/lib/get_authz_filter.ts index 35bab55c7c4e3..e1524b99f88d9 100644 --- a/x-pack/plugins/rule_registry/server/lib/get_authz_filter.ts +++ b/x-pack/plugins/rule_registry/server/lib/get_authz_filter.ts @@ -19,7 +19,8 @@ import { export async function getAuthzFilter( authorization: PublicMethodsOf, - operation: WriteOperations.Update | ReadOperations.Get | ReadOperations.Find + operation: WriteOperations.Update | ReadOperations.Get | ReadOperations.Find, + featuresIds?: Set ) { const { filter } = await authorization.getAuthorizationFilter( AlertingAuthorizationEntity.Alert, @@ -27,7 +28,8 @@ export async function getAuthzFilter( type: AlertingAuthorizationFilterType.ESDSL, fieldNames: { consumer: ALERT_RULE_CONSUMER, ruleTypeId: ALERT_RULE_TYPE_ID }, }, - operation + operation, + featuresIds ); return filter; } diff --git a/x-pack/plugins/rule_registry/server/plugin.ts b/x-pack/plugins/rule_registry/server/plugin.ts index 0b17e237057e6..6fba837a10c1a 100644 --- a/x-pack/plugins/rule_registry/server/plugin.ts +++ b/x-pack/plugins/rule_registry/server/plugin.ts @@ -166,6 +166,7 @@ export class RuleRegistryPlugin securityPluginSetup: security, ruleDataService, getRuleType: plugins.alerting.getType, + getAlertIndicesAlias: plugins.alerting.getAlertIndicesAlias, }); const getRacClientWithRequest = (request: KibanaRequest) => { diff --git a/x-pack/plugins/rule_registry/server/routes/get_browser_fields_by_feature_id.ts b/x-pack/plugins/rule_registry/server/routes/get_browser_fields_by_feature_id.ts index 3441a8571e81f..259ca03478745 100644 --- a/x-pack/plugins/rule_registry/server/routes/get_browser_fields_by_feature_id.ts +++ b/x-pack/plugins/rule_registry/server/routes/get_browser_fields_by_feature_id.ts @@ -35,11 +35,13 @@ export const getBrowserFieldsByFeatureId = (router: IRouter fId !== 'siem' ); const o11yIndices = - indices?.filter((index) => index.startsWith('.alerts-observability')) ?? []; + (onlyO11yFeatureIds + ? await alertsClient.getAuthorizedAlertsIndices(onlyO11yFeatureIds) + : []) ?? []; if (o11yIndices.length === 0) { return response.notFound({ body: { diff --git a/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts b/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts index 76c1168d50d0b..f8da02ad0666d 100644 --- a/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts +++ b/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts @@ -72,20 +72,18 @@ export const ruleRegistrySearchStrategyProvider = ( alerting.getAlertingAuthorizationWithRequest(deps.request), ]); let authzFilter; - - if (!siemRequest) { + const fIds = new Set(featureIds); + if (!siemRequest && featureIds.length > 0) { authzFilter = (await getAuthzFilter( authorization, - ReadOperations.Find + ReadOperations.Find, + fIds )) as estypes.QueryDslQueryContainer; } const authorizedRuleTypes = featureIds.length > 0 - ? await authorization.getAuthorizedRuleTypes( - AlertingAuthorizationEntity.Alert, - new Set(featureIds) - ) + ? await authorization.getAuthorizedRuleTypes(AlertingAuthorizationEntity.Alert, fIds) : []; return { space, authzFilter, authorizedRuleTypes }; }; diff --git a/x-pack/plugins/session_view/server/routes/alerts_client_mock.test.ts b/x-pack/plugins/session_view/server/routes/alerts_client_mock.test.ts index 282d4297d3670..10f1c48977c34 100644 --- a/x-pack/plugins/session_view/server/routes/alerts_client_mock.test.ts +++ b/x-pack/plugins/session_view/server/routes/alerts_client_mock.test.ts @@ -57,6 +57,7 @@ const getResponse = async () => { }; const esClientMock = elasticsearchServiceMock.createElasticsearchClient(getResponse()); +const getAlertIndicesAliasMock = jest.fn(); const alertsClientParams: jest.Mocked = { logger: loggingSystemMock.create().get(), authorization: alertingAuthMock, @@ -64,6 +65,7 @@ const alertsClientParams: jest.Mocked = { ruleDataService: ruleDataServiceMock.create(), esClient: esClientMock, getRuleType: jest.fn(), + getAlertIndicesAlias: getAlertIndicesAliasMock, }; export function getAlertsClientMockInstance(esClient?: ElasticsearchClient) { @@ -86,6 +88,20 @@ export function resetAlertingAuthMock() { authorizedRuleTypes.add({ producer: 'apm' }); return Promise.resolve({ authorizedRuleTypes }); }); + // @ts-expect-error + alertingAuthMock.getAuthorizedRuleTypes.mockImplementation(async () => { + const authorizedRuleTypes = [ + { + producer: 'apm', + id: 'apm.error_rate', + alerts: { + context: 'observability.apm', + }, + }, + ]; + return Promise.resolve(authorizedRuleTypes); + }); + getAlertIndicesAliasMock.mockReturnValue(['.alerts-observability.apm-default']); alertingAuthMock.ensureAuthorized.mockImplementation( // @ts-expect-error diff --git a/x-pack/plugins/stack_alerts/common/index.ts b/x-pack/plugins/stack_alerts/common/index.ts index 48e5a8b8d65fd..60537366f26cb 100644 --- a/x-pack/plugins/stack_alerts/common/index.ts +++ b/x-pack/plugins/stack_alerts/common/index.ts @@ -11,7 +11,6 @@ export { ComparatorFnNames, getHumanReadableComparator, } from './comparator'; -export { STACK_ALERTS_FEATURE_ID, ES_QUERY_ID } from './constants'; export type { EsqlTable } from './esql_query_utils'; export { rowToDocument, transformDatatableToEsqlTable, toEsQueryHits } from './esql_query_utils'; diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/search_source_expression_form.tsx b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/search_source_expression_form.tsx index 1cc45801ffe65..fee5e7149db02 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/search_source_expression_form.tsx +++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/search_source_expression_form.tsx @@ -28,9 +28,9 @@ import { isGroupAggregation, parseAggregationResults, } from '@kbn/triggers-actions-ui-plugin/public/common'; +import { STACK_ALERTS_FEATURE_ID } from '@kbn/rule-data-utils'; import { getComparatorScript } from '../../../../common'; import { Comparator } from '../../../../common/comparator_types'; -import { STACK_ALERTS_FEATURE_ID } from '../../../../common'; import { CommonRuleParams, EsQueryRuleMetaData, EsQueryRuleParams, SearchType } from '../types'; import { DEFAULT_VALUES } from '../constants'; import { DataViewSelectPopover } from '../../components/data_view_select_popover'; diff --git a/x-pack/plugins/stack_alerts/public/rule_types/geo_containment/query_builder/index.tsx b/x-pack/plugins/stack_alerts/public/rule_types/geo_containment/query_builder/index.tsx index d49ee6c15b83a..80360f113d9d4 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/geo_containment/query_builder/index.tsx +++ b/x-pack/plugins/stack_alerts/public/rule_types/geo_containment/query_builder/index.tsx @@ -21,7 +21,7 @@ import type { IUiSettingsClient } from '@kbn/core-ui-settings-server'; import type { CoreStart } from '@kbn/core/public'; import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; -import { STACK_ALERTS_FEATURE_ID } from '../../../../common/constants'; +import { STACK_ALERTS_FEATURE_ID } from '@kbn/rule-data-utils'; import { BoundaryIndexExpression } from './expressions/boundary_index_expression'; import { EntityByExpression } from './expressions/entity_by_expression'; import { EntityIndexExpression } from './expressions/entity_index_expression'; diff --git a/x-pack/plugins/stack_alerts/server/feature.ts b/x-pack/plugins/stack_alerts/server/feature.ts index 7392ee2e5eb9e..1a54134d2cdc1 100644 --- a/x-pack/plugins/stack_alerts/server/feature.ts +++ b/x-pack/plugins/stack_alerts/server/feature.ts @@ -9,10 +9,10 @@ import { i18n } from '@kbn/i18n'; import { KibanaFeatureConfig } from '@kbn/features-plugin/common'; import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server'; import { TRANSFORM_RULE_TYPE } from '@kbn/transform-plugin/common'; +import { STACK_ALERTS_FEATURE_ID } from '@kbn/rule-data-utils'; +import { ES_QUERY_ID as ElasticsearchQuery } from '@kbn/rule-data-utils'; import { ID as IndexThreshold } from './rule_types/index_threshold/rule_type'; import { GEO_CONTAINMENT_ID as GeoContainment } from './rule_types/geo_containment'; -import { ES_QUERY_ID as ElasticsearchQuery } from './rule_types/es_query/constants'; -import { STACK_ALERTS_FEATURE_ID } from '../common'; const TransformHealth = TRANSFORM_RULE_TYPE.TRANSFORM_HEALTH; diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/constants.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/constants.ts index 9627249c2e6e1..598fe60f58c84 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/constants.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/constants.ts @@ -7,4 +7,3 @@ export const ActionGroupId = 'query matched'; export const ConditionMetAlertInstanceId = 'query matched'; -export { ES_QUERY_ID } from '../../../common'; diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_es_query.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_es_query.ts index 8c44c6e673ad2..f44ad3f470106 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_es_query.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_es_query.ts @@ -12,10 +12,10 @@ import { parseAggregationResults, } from '@kbn/triggers-actions-ui-plugin/common'; import { isGroupAggregation } from '@kbn/triggers-actions-ui-plugin/common'; +import { ES_QUERY_ID } from '@kbn/rule-data-utils'; import { getComparatorScript } from '../../../../common'; import { OnlyEsQueryRuleParams } from '../types'; import { buildSortedEventsQuery } from '../../../../common/build_sorted_events_query'; -import { ES_QUERY_ID } from '../constants'; import { getSearchParams } from './get_search_params'; export interface FetchEsQueryOpts { diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.ts index eabe7bf346669..14cd64c36dd0c 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup } from '@kbn/core/server'; import { extractReferences, injectReferences } from '@kbn/data-plugin/common'; import { IRuleTypeAlerts } from '@kbn/alerting-plugin/server'; -import { ALERT_EVALUATION_VALUE } from '@kbn/rule-data-utils'; +import { ALERT_EVALUATION_VALUE, ES_QUERY_ID, STACK_ALERTS_FEATURE_ID } from '@kbn/rule-data-utils'; import { StackAlert } from '@kbn/alerts-as-data-utils'; import { STACK_AAD_INDEX_NAME } from '..'; import { ALERT_TITLE, ALERT_EVALUATION_CONDITIONS } from './fields'; @@ -21,9 +21,8 @@ import { EsQueryRuleParamsSchema, EsQueryRuleState, } from './rule_type_params'; -import { STACK_ALERTS_FEATURE_ID } from '../../../common'; import { ExecutorOptions } from './types'; -import { ActionGroupId, ES_QUERY_ID } from './constants'; +import { ActionGroupId } from './constants'; import { executor } from './executor'; import { isSearchSourceRule } from './util'; diff --git a/x-pack/plugins/stack_alerts/server/rule_types/geo_containment/rule_type.ts b/x-pack/plugins/stack_alerts/server/rule_types/geo_containment/rule_type.ts index 938edad8cb394..f6d1668151843 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/geo_containment/rule_type.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/geo_containment/rule_type.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; import { SavedObjectReference } from '@kbn/core/server'; import { RuleParamsAndRefs } from '@kbn/alerting-plugin/server'; -import { STACK_ALERTS_FEATURE_ID } from '../../../common'; +import { STACK_ALERTS_FEATURE_ID } from '@kbn/rule-data-utils'; import type { GeoContainmentRuleType, GeoContainmentExtractedRuleParams, diff --git a/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.ts b/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.ts index 4b9a7e69ac31c..ab56c5775fda3 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.ts @@ -11,15 +11,11 @@ import { TIME_SERIES_BUCKET_SELECTOR_FIELD, } from '@kbn/triggers-actions-ui-plugin/server'; import { isGroupAggregation } from '@kbn/triggers-actions-ui-plugin/common'; +import { STACK_ALERTS_FEATURE_ID } from '@kbn/rule-data-utils'; import { RuleType, RuleExecutorOptions, StackAlertsStartDeps } from '../../types'; import { Params, ParamsSchema } from './rule_type_params'; import { ActionContext, BaseActionContext, addMessages } from './action_context'; -import { - ComparatorFns, - getComparatorScript, - getHumanReadableComparator, - STACK_ALERTS_FEATURE_ID, -} from '../../../common'; +import { ComparatorFns, getComparatorScript, getHumanReadableComparator } from '../../../common'; export const ID = '.index-threshold'; export const ActionGroupId = 'threshold met'; diff --git a/x-pack/plugins/synthetics/server/feature.ts b/x-pack/plugins/synthetics/server/feature.ts index 576b48b4e87ec..d8e82ea031707 100644 --- a/x-pack/plugins/synthetics/server/feature.ts +++ b/x-pack/plugins/synthetics/server/feature.ts @@ -6,8 +6,8 @@ */ import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server'; -import { ES_QUERY_ID } from '@kbn/stack-alerts-plugin/common'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; +import { ES_QUERY_ID } from '@kbn/rule-data-utils'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { syntheticsMonitorType, syntheticsParamType } from '../common/types/saved_objects'; import { SYNTHETICS_RULE_TYPES } from '../common/constants/synthetics_alerts'; import { privateLocationsSavedObjectName } from '../common/saved_objects/private_locations'; diff --git a/x-pack/plugins/synthetics/tsconfig.json b/x-pack/plugins/synthetics/tsconfig.json index c140e4053e4a9..ff288ec3cee97 100644 --- a/x-pack/plugins/synthetics/tsconfig.json +++ b/x-pack/plugins/synthetics/tsconfig.json @@ -77,7 +77,6 @@ "@kbn/core-saved-objects-server-mocks", "@kbn/shared-ux-page-kibana-template", "@kbn/observability-ai-assistant-plugin", - "@kbn/stack-alerts-plugin", "@kbn/unified-doc-viewer-plugin", "@kbn/discover-utils", ], diff --git a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts index 98882b52b43ad..bf4cafcf63da9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts @@ -6,6 +6,7 @@ */ import { i18n } from '@kbn/i18n'; +import { ES_QUERY_ID, OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; export { BASE_ALERTING_API_PATH, INTERNAL_BASE_ALERTING_API_PATH, @@ -14,9 +15,6 @@ export { BASE_ACTION_API_PATH, INTERNAL_BASE_ACTION_API_PATH } from '@kbn/action export type Section = 'connectors' | 'rules' | 'alerts' | 'logs'; -export const OBSERVABILITY_THRESHOLD_RULE_TYPE_ID = 'observability.rules.threshold'; -export const ES_QUERY_RULE_TYPE_ID = '.es-query'; - export const routeToHome = `/`; export const routeToConnectors = `/connectors`; export const routeToRules = `/rules`; @@ -122,7 +120,4 @@ export const GLOBAL_CONNECTOR_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS = [ ...CONNECTOR_LOCKED_COLUMNS, ]; -export const MULTI_CONSUMER_RULE_TYPE_IDS = [ - OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, - ES_QUERY_RULE_TYPE_ID, -]; +export const MULTI_CONSUMER_RULE_TYPE_IDS = [OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, ES_QUERY_ID]; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_aggregations_query.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_aggregations_query.ts index a581fea1832b7..b7f5be0325729 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_aggregations_query.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_aggregations_query.ts @@ -25,11 +25,12 @@ const initializeAggregationResult = (values: readonly string[]) => { interface UseLoadRuleAggregationsQueryProps { filters: RulesListFilters; enabled: boolean; + filterConsumers?: string[]; refresh?: Date; } export const useLoadRuleAggregationsQuery = (props: UseLoadRuleAggregationsQueryProps) => { - const { filters, enabled, refresh } = props; + const { filters, enabled, refresh, filterConsumers } = props; const { http, @@ -46,6 +47,7 @@ export const useLoadRuleAggregationsQuery = (props: UseLoadRuleAggregationsQuery ruleLastRunOutcomesFilter: filters.ruleLastRunOutcomes, ruleStatusesFilter: filters.ruleStatuses, tagsFilter: filters.tags, + filterConsumers, }); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules_query.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules_query.ts index d4e045ae038af..90bfea7719b07 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules_query.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules_query.ts @@ -20,10 +20,11 @@ type UseLoadRulesQueryProps = Omit & { sort: LoadRulesProps['sort']; enabled: boolean; refresh?: Date; + filterConsumers?: string[]; }; export const useLoadRulesQuery = (props: UseLoadRulesQueryProps) => { - const { filters, page, sort, onPage, enabled, refresh } = props; + const { filterConsumers, filters, page, sort, onPage, enabled, refresh } = props; const { http, notifications: { toasts }, @@ -51,6 +52,7 @@ export const useLoadRulesQuery = (props: UseLoadRulesQueryProps) => { { refresh: refresh?.toISOString(), }, + filterConsumers, ], queryFn: () => { return loadRulesWithKueryFilter({ @@ -66,6 +68,7 @@ export const useLoadRulesQuery = (props: UseLoadRulesQueryProps) => { tagsFilter: filters.tags, kueryNode: filters.kueryNode, sort, + filterConsumers, }); }, onSuccess: (response) => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.ts index 6e2465e58c2fd..45a0437717470 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.ts @@ -43,6 +43,7 @@ export async function loadRuleAggregations({ ruleExecutionStatusesFilter, ruleStatusesFilter, tagsFilter, + filterConsumers, }: LoadRuleAggregationsProps): Promise { const filters = mapFiltersToKql({ typesFilter, @@ -59,6 +60,7 @@ export async function loadRuleAggregations({ search: searchText, filter: filters.length ? filters.join(' and ') : undefined, default_search_operator: 'AND', + filter_consumers: filterConsumers, }), } ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_helpers.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_helpers.ts index 61fff1e95a451..f2e56e8aec71b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_helpers.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_helpers.ts @@ -52,6 +52,7 @@ export interface LoadRuleAggregationsProps { ruleLastRunOutcomesFilter?: string[]; ruleStatusesFilter?: RuleStatus[]; tagsFilter?: string[]; + filterConsumers?: string[]; } export interface LoadRuleTagsProps { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_kuery_filter.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_kuery_filter.ts index 20d1fc9281b48..8348af72d1d95 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_kuery_filter.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_kuery_filter.ts @@ -18,6 +18,7 @@ export async function loadRuleAggregationsWithKueryFilter({ ruleExecutionStatusesFilter, ruleStatusesFilter, tagsFilter, + filterConsumers, }: LoadRuleAggregationsProps): Promise { const filtersKueryNode = mapFiltersToKueryNode({ typesFilter, @@ -33,6 +34,7 @@ export async function loadRuleAggregationsWithKueryFilter({ { body: JSON.stringify({ ...(filtersKueryNode ? { filter: JSON.stringify(filtersKueryNode) } : {}), + filter_consumers: filterConsumers, default_search_operator: 'AND', }), } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_helpers.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_helpers.ts index ebab20adfb4a1..5effb3cd3c305 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_helpers.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_helpers.ts @@ -24,6 +24,7 @@ export interface LoadRulesProps { ruleStatusesFilter?: RuleStatus[]; sort?: Sorting; kueryNode?: KueryNode; + filterConsumers?: string[]; } export const rewriteRulesResponseRes = (results: Array>): Rule[] => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_kuery_filter.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_kuery_filter.ts index ee3a798e50738..9d38716d35028 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_kuery_filter.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_kuery_filter.ts @@ -24,6 +24,7 @@ export async function loadRulesWithKueryFilter({ tagsFilter, sort = { field: 'name', direction: 'asc' }, kueryNode, + filterConsumers, }: LoadRulesProps): Promise<{ page: number; perPage: number; @@ -56,6 +57,7 @@ export async function loadRulesWithKueryFilter({ ...(filtersKueryNode ? { filter: JSON.stringify(filtersKueryNode) } : {}), sort_field: sort.field, sort_order: sort.direction, + filter_consumers: filterConsumers, }), }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx index 7534b968dc981..88b2aa583d031 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx @@ -53,6 +53,7 @@ const RuleAdd = ({ metadata: initialMetadata, filteredRuleTypes, validConsumers, + useRuleProducer, ...props }: RuleAddProps) => { const onSaveHandler = onSave ?? reloadRules; @@ -199,7 +200,6 @@ const RuleAdd = ({ }; const ruleType = rule.ruleTypeId ? ruleTypeRegistry.get(rule.ruleTypeId) : null; - const { ruleBaseErrors, ruleErrors, ruleParamsErrors } = useMemo( () => getRuleErrors(rule as Rule, ruleType, config), [rule, ruleType, config] @@ -279,6 +279,7 @@ const RuleAdd = ({ hideInterval={hideInterval} onChangeMetaData={onChangeMetaData} setConsumer={setSelectedConsumer} + useRuleProducer={useRuleProducer} /> > { connectorFeatureId?: string; validConsumers?: RuleCreationValidConsumer[]; onChangeMetaData: (metadata: MetaData) => void; + useRuleProducer?: boolean; } export const RuleForm = ({ @@ -162,6 +163,7 @@ export const RuleForm = ({ connectorFeatureId = AlertingConnectorFeatureId, validConsumers, onChangeMetaData, + useRuleProducer, }: RuleFormProps) => { const { notifications: { toasts }, @@ -522,6 +524,13 @@ export const RuleForm = ({ if (ruleTypeIndex && ruleTypeIndex.has(item.id)) { setDefaultActionGroupId(ruleTypeIndex.get(item.id)!.defaultActionGroupId); } + + if ( + useRuleProducer && + validConsumers?.includes(solution as RuleCreationValidConsumer) + ) { + setConsumer(solution as RuleCreationValidConsumer); + } }} /> ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx index 197077daceaf3..83285163aeee3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx @@ -107,6 +107,7 @@ const RuleAdd = lazy(() => import('../../rule_form/rule_add')); const RuleEdit = lazy(() => import('../../rule_form/rule_edit')); export interface RulesListProps { + filterConsumers?: string[]; filteredRuleTypes?: string[]; lastResponseFilter?: string[]; lastRunOutcomeFilter?: string[]; @@ -146,6 +147,7 @@ const initialPercentileOptions = Object.values(Percentiles).map((percentile) => const EMPTY_ARRAY: string[] = []; export const RulesList = ({ + filterConsumers, filteredRuleTypes = EMPTY_ARRAY, lastResponseFilter, lastRunOutcomeFilter, @@ -263,6 +265,7 @@ export const RulesList = ({ // Fetch rules const { rulesState, loadRules, hasData, lastUpdate } = useLoadRulesQuery({ + filterConsumers, filters: computedFilter, hasDefaultRuleTypesFiltersOn, page, @@ -275,6 +278,7 @@ export const RulesList = ({ // Fetch status aggregation const { loadRuleAggregations, rulesStatusesTotal, rulesLastRunOutcomesTotal } = useLoadRuleAggregationsQuery({ + filterConsumers, filters: computedFilter, enabled: canLoadRules, refresh, diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 2072765072ccf..48f99f765df49 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -26,7 +26,7 @@ import type { EuiSuperSelectOption, EuiDataGridOnColumnResizeHandler, } from '@elastic/eui'; -import type { AlertConsumers } from '@kbn/rule-data-utils'; +import type { AlertConsumers, STACK_ALERTS_FEATURE_ID } from '@kbn/rule-data-utils'; import { EuiDataGridColumn, EuiDataGridControlColumn, EuiDataGridSorting } from '@elastic/eui'; import { HttpSetup } from '@kbn/core/public'; import { KueryNode } from '@kbn/es-query'; @@ -459,6 +459,7 @@ export interface RuleAddProps> { ruleTypeIndex?: RuleTypeIndex; filteredRuleTypes?: string[]; validConsumers?: RuleCreationValidConsumer[]; + useRuleProducer?: boolean; } export interface RuleDefinitionProps { rule: Rule; @@ -828,4 +829,4 @@ export type RuleCreationValidConsumer = | typeof AlertConsumers.APM | typeof AlertConsumers.UPTIME | typeof AlertConsumers.SLO - | 'stackAlerts'; + | typeof STACK_ALERTS_FEATURE_ID; diff --git a/x-pack/test/alerting_api_integration/observability/threshold_rule/avg_pct_fired.ts b/x-pack/test/alerting_api_integration/observability/threshold_rule/avg_pct_fired.ts index e636388fb269a..11da73c6cb3f3 100644 --- a/x-pack/test/alerting_api_integration/observability/threshold_rule/avg_pct_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/threshold_rule/avg_pct_fired.ts @@ -9,7 +9,7 @@ import { cleanup, generate } from '@kbn/infra-forge'; import { Aggregators, Comparator } from '@kbn/observability-plugin/common/threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/threshold/threshold_executor'; import expect from '@kbn/expect'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { createDataView, deleteDataView } from '../helpers/data_view'; import { waitForAlertInIndex, waitForRuleStatus } from '../helpers/alerting_wait_for_helpers'; diff --git a/x-pack/test/alerting_api_integration/observability/threshold_rule/avg_pct_no_data.ts b/x-pack/test/alerting_api_integration/observability/threshold_rule/avg_pct_no_data.ts index 8bea4a377ea03..1bcbd5bbff89e 100644 --- a/x-pack/test/alerting_api_integration/observability/threshold_rule/avg_pct_no_data.ts +++ b/x-pack/test/alerting_api_integration/observability/threshold_rule/avg_pct_no_data.ts @@ -8,7 +8,7 @@ import { Aggregators, Comparator } from '@kbn/observability-plugin/common/threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/threshold/threshold_executor'; import expect from '@kbn/expect'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { createDataView, deleteDataView } from '../helpers/data_view'; diff --git a/x-pack/test/alerting_api_integration/observability/threshold_rule/avg_us_fired.ts b/x-pack/test/alerting_api_integration/observability/threshold_rule/avg_us_fired.ts index 9ff58b68db98b..133ff70a3c32a 100644 --- a/x-pack/test/alerting_api_integration/observability/threshold_rule/avg_us_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/threshold_rule/avg_us_fired.ts @@ -11,7 +11,7 @@ import { format } from 'url'; import { Aggregators, Comparator } from '@kbn/observability-plugin/common/threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/threshold/threshold_executor'; import expect from '@kbn/expect'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { createDataView, deleteDataView } from '../helpers/data_view'; diff --git a/x-pack/test/alerting_api_integration/observability/threshold_rule/custom_eq_avg_bytes_fired.ts b/x-pack/test/alerting_api_integration/observability/threshold_rule/custom_eq_avg_bytes_fired.ts index 0d9c1970bd8c4..afc125e85f632 100644 --- a/x-pack/test/alerting_api_integration/observability/threshold_rule/custom_eq_avg_bytes_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/threshold_rule/custom_eq_avg_bytes_fired.ts @@ -15,7 +15,7 @@ import { cleanup, generate } from '@kbn/infra-forge'; import { Aggregators, Comparator } from '@kbn/observability-plugin/common/threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/threshold/threshold_executor'; import expect from '@kbn/expect'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { createDataView, deleteDataView } from '../helpers/data_view'; import { waitForAlertInIndex, waitForRuleStatus } from '../helpers/alerting_wait_for_helpers'; diff --git a/x-pack/test/alerting_api_integration/observability/threshold_rule/documents_count_fired.ts b/x-pack/test/alerting_api_integration/observability/threshold_rule/documents_count_fired.ts index 72e1866f1d953..911ee13bd60af 100644 --- a/x-pack/test/alerting_api_integration/observability/threshold_rule/documents_count_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/threshold_rule/documents_count_fired.ts @@ -9,7 +9,7 @@ import { cleanup, generate } from '@kbn/infra-forge'; import { Aggregators, Comparator } from '@kbn/observability-plugin/common/threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/threshold/threshold_executor'; import expect from '@kbn/expect'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { createDataView, deleteDataView } from '../helpers/data_view'; import { waitForAlertInIndex, waitForRuleStatus } from '../helpers/alerting_wait_for_helpers'; diff --git a/x-pack/test/alerting_api_integration/observability/threshold_rule/group_by_fired.ts b/x-pack/test/alerting_api_integration/observability/threshold_rule/group_by_fired.ts index 91ae5d43bdb09..0d7e44c780699 100644 --- a/x-pack/test/alerting_api_integration/observability/threshold_rule/group_by_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/threshold_rule/group_by_fired.ts @@ -16,7 +16,7 @@ import { cleanup, generate } from '@kbn/infra-forge'; import { Aggregators, Comparator } from '@kbn/observability-plugin/common/threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/threshold/threshold_executor'; import expect from '@kbn/expect'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { createDataView, deleteDataView } from '../helpers/data_view'; import { diff --git a/x-pack/test/alerting_api_integration/observability/threshold_rule_data_view.ts b/x-pack/test/alerting_api_integration/observability/threshold_rule_data_view.ts index 71b0570d21c90..6b77905c5adcb 100644 --- a/x-pack/test/alerting_api_integration/observability/threshold_rule_data_view.ts +++ b/x-pack/test/alerting_api_integration/observability/threshold_rule_data_view.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { Aggregators, Comparator } from '@kbn/observability-plugin/common/threshold_rule/types'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { FtrProviderContext } from '../common/ftr_provider_context'; import { getUrlPrefix, ObjectRemover } from '../common/lib'; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/rbac_legacy.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/rbac_legacy.ts index ce30fbc283034..8cca92320cfa7 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/rbac_legacy.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/rbac_legacy.ts @@ -150,7 +150,7 @@ export default function alertTests({ getService }: FtrProviderContext) { expect(failedUpdateKeyDueToAlertsPrivilegesResponse.body).to.eql({ error: 'Forbidden', message: - 'Unauthorized to updateApiKey a "test.always-firing" rule for "alertsFixture"', + 'Unauthorized by "alertsFixture" to updateApiKey "test.always-firing" rule', statusCode: 403, }); break; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_enable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_enable.ts index 471faeda4c25b..0366eca2ad24d 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_enable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_enable.ts @@ -221,7 +221,6 @@ export default ({ getService }: FtrProviderContext) => { expect(response.statusCode).to.eql(400); break; case 'superuser at space1': - expect(response.body).to.eql(defaultSuccessfulResponse); expect(response.statusCode).to.eql(200); break; default: diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/rule_types.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/rule_types.ts index ae2e2d61267c4..8f9b7566b7640 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/rule_types.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/rule_types.ts @@ -137,6 +137,7 @@ export default function listRuleTypes({ getService }: FtrProviderContext) { hasFieldsForAAD: false, hasAlertsMappings: false, ruleTaskTimeout: '5m', + validLegacyConsumers: ['alerts'], }); expect(Object.keys(authorizedConsumers)).to.contain('alertsFixture'); }); diff --git a/x-pack/test/functional/es_archives/rule_registry/o11y_alerts/data.json b/x-pack/test/functional/es_archives/rule_registry/o11y_alerts/data.json new file mode 100644 index 0000000000000..c3a19cfbcf81a --- /dev/null +++ b/x-pack/test/functional/es_archives/rule_registry/o11y_alerts/data.json @@ -0,0 +1,104 @@ +{ + "type": "doc", + "value": { + "index": ".alerts-observability.apm.alerts-default-000001", + "id": "NoxgpHkBqbdrfX07MqXV", + "source": { + "event.kind" : "signal", + "@timestamp": "2020-12-16T15:16:18.570Z", + "kibana.alert.rule.rule_type_id": "apm.error_rate", + "message": "hello world 1", + "kibana.alert.rule.consumer": "apm", + "kibana.alert.workflow_status": "open", + "kibana.alert.time_range": { + "gte": "2020-12-16T15:16:18.570Z" + }, + "kibana.alert.status": "active", + "kibana.space_ids": ["space1", "space2"] + } + } +} + +{ + "type": "doc", + "value": { + "index": ".alerts-observability.apm.alerts-default-000001", + "id": "space1alert", + "source": { + "event.kind" : "signal", + "@timestamp": "2020-12-16T15:16:18.570Z", + "kibana.alert.rule.rule_type_id": "apm.error_rate", + "message": "hello world 1", + "kibana.alert.rule.consumer": "apm", + "kibana.alert.workflow_status": "recovered", + "kibana.alert.time_range": { + "gte": "2020-12-16T15:16:18.570Z", + "lte": "2020-12-16T15:16:19.570Z" + }, + "kibana.alert.status": "recovered", + "kibana.space_ids": ["space1"] + } + } +} + +{ + "type": "doc", + "value": { + "index": ".alerts-observability.apm.alerts-default-000001", + "id": "space2alert", + "source": { + "event.kind" : "signal", + "@timestamp": "2020-12-16T15:16:19.570Z", + "kibana.alert.rule.rule_type_id": "apm.error_rate", + "message": "hello world 1", + "kibana.alert.rule.consumer": "apm", + "kibana.alert.workflow_status": "open", + "kibana.alert.status": "active", + "kibana.space_ids": ["space2"] + } + } +} + +{ + "type": "doc", + "value": { + "index": ".alerts-observability.logs.alerts-default-000001", + "id": "123456789XYZ", + "source": { + "event.kind": "signal", + "@timestamp": "2020-12-16T15:16:18.570Z", + "kibana.alert.rule.rule_type_id": "logs.alert.document.count", + "message": "hello world 1", + "kibana.alert.rule.consumer": "logs", + "kibana.alert.workflow_status": "open", + "kibana.alert.time_range": { + "gte": "2020-12-16T15:16:18.570Z" + }, + "kibana.alert.status": "active", + "kibana.space_ids": ["space1", "space2"] + } + } +} + +{ + "type": "doc", + "value": { + "index": ".alerts-observability.logs.alerts-default-000001", + "id": "space1alertLogs", + "source": { + "event.kind": "signal", + "@timestamp": "2020-12-16T15:16:18.570Z", + "kibana.alert.rule.rule_type_id": "logs.alert.document.count", + "message": "hello world 1", + "kibana.alert.rule.consumer": "logs", + "kibana.alert.workflow_status": "recovered", + "kibana.alert.time_range": { + "gte": "2020-12-16T15:16:18.570Z", + "lte": "2020-12-16T15:27:19.570Z" + }, + "kibana.alert.end": "2020-12-16T15:27:19.570Z", + "kibana.alert.status": "recovered", + "kibana.space_ids": ["space1"] + } + } +} diff --git a/x-pack/test/functional/es_archives/rule_registry/o11y_alerts/mappings.json b/x-pack/test/functional/es_archives/rule_registry/o11y_alerts/mappings.json new file mode 100644 index 0000000000000..0faf5daf3df76 --- /dev/null +++ b/x-pack/test/functional/es_archives/rule_registry/o11y_alerts/mappings.json @@ -0,0 +1,66 @@ +{ + "type": "index", + "value": { + "index": ".alerts-observability.apm.alerts-default-000001", + "mappings": { + "properties": { + "message": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, + "kibana.alert.rule.consumer": { + "type": "keyword", + "ignore_above": 256 + }, + "kibana.alert.time_range": { + "type": "date_range", + "format": "epoch_millis||strict_date_optional_time" + } + } + } + } +} + + +{ + "type": "index", + "value": { + "index": ".alerts-observability.logs.alerts-default-000001", + "aliases": { + ".alerts-observability.logs.alerts-default": {} + }, + "mappings": { + "properties": { + "message": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, + "kibana.alert.rule.consumer": { + "type": "keyword", + "ignore_above": 256 + }, + "kibana.alert.status": { + "type": "keyword", + "ignore_above": 256 + }, + "kibana.alert.time_range": { + "type": "date_range", + "format": "epoch_millis||strict_date_optional_time" + }, + "kibana.alert.end": { + "type": "date" + } + } + } + } +} diff --git a/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_alert_summary.ts b/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_alert_summary.ts index b22fc830cb73d..5da72c7f6a76a 100644 --- a/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_alert_summary.ts +++ b/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_alert_summary.ts @@ -24,11 +24,11 @@ export default ({ getService }: FtrProviderContext) => { describe('Alerts - GET - _alert_summary', () => { before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/rule_registry/alerts'); + await esArchiver.load('x-pack/test/functional/es_archives/rule_registry/o11y_alerts'); }); after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/rule_registry/alerts'); + await esArchiver.unload('x-pack/test/functional/es_archives/rule_registry/o11y_alerts'); }); it('Alert summary for all LOGS alerts with features', async () => { diff --git a/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_alerts_index.ts b/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_alerts_index.ts index 2ac420a8beb6b..1146fa925908c 100644 --- a/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_alerts_index.ts +++ b/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_alerts_index.ts @@ -20,7 +20,7 @@ export default ({ getService }: FtrProviderContext) => { const TEST_URL = '/internal/rac/alerts'; const ALERTS_INDEX_URL = `${TEST_URL}/index`; const SPACE1 = 'space1'; - const APM_ALERT_INDEX = '.alerts-observability.apm.alerts'; + const APM_ALERT_INDEX = '.alerts-observability.apm.alerts-default'; const SECURITY_SOLUTION_ALERT_INDEX = '.alerts-security.alerts'; const getAPMIndexName = async (user: User, space: string, expectedStatusCode: number = 200) => { @@ -53,12 +53,12 @@ export default ({ getService }: FtrProviderContext) => { describe('Users:', () => { it(`${obsOnlySpacesAll.username} should be able to access the APM alert in ${SPACE1}`, async () => { const indexNames = await getAPMIndexName(obsOnlySpacesAll, SPACE1); - expect(indexNames.includes(`${APM_ALERT_INDEX}-*`)).to.eql(true); // assert this here so we can use constants in the dynamically-defined test cases below + expect(indexNames.includes(APM_ALERT_INDEX)).to.eql(true); // assert this here so we can use constants in the dynamically-defined test cases below }); it(`${superUser.username} should be able to access the APM alert in ${SPACE1}`, async () => { const indexNames = await getAPMIndexName(superUser, SPACE1); - expect(indexNames.includes(`${APM_ALERT_INDEX}-*`)).to.eql(true); // assert this here so we can use constants in the dynamically-defined test cases below + expect(indexNames.includes(APM_ALERT_INDEX)).to.eql(true); // assert this here so we can use constants in the dynamically-defined test cases below }); it(`${secOnlyRead.username} should NOT be able to access the APM alert in ${SPACE1}`, async () => { diff --git a/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_browser_fields_by_feature_id.ts b/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_browser_fields_by_feature_id.ts index feb84ab625fdb..94cbdbce77491 100644 --- a/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_browser_fields_by_feature_id.ts +++ b/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_browser_fields_by_feature_id.ts @@ -46,7 +46,7 @@ export default ({ getService }: FtrProviderContext) => { 'uptime', ]); expect(Object.keys(resp.browserFields)).toEqual( - expect.arrayContaining(['base', 'event', 'kibana', 'message']) + expect.arrayContaining(['base', 'event', 'kibana']) ); }); @@ -66,7 +66,6 @@ export default ({ getService }: FtrProviderContext) => { 'error', 'event', 'kibana', - 'message', 'monitor', 'observer', 'tls', diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/avg_pct_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/avg_pct_fired.ts index 27c8471c4eff0..4b13bf57e4f45 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/avg_pct_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/avg_pct_fired.ts @@ -9,7 +9,7 @@ import { cleanup, generate } from '@kbn/infra-forge'; import { Aggregators, Comparator } from '@kbn/observability-plugin/common/threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/threshold/threshold_executor'; import expect from '@kbn/expect'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/avg_pct_no_data.ts b/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/avg_pct_no_data.ts index 8050f0a672cb7..413f810610cac 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/avg_pct_no_data.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/avg_pct_no_data.ts @@ -8,7 +8,7 @@ import { Aggregators, Comparator } from '@kbn/observability-plugin/common/threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/threshold/threshold_executor'; import expect from '@kbn/expect'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/custom_eq_avg_bytes_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/custom_eq_avg_bytes_fired.ts index 2fab6c3a9ebe2..c2503c10e3488 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/custom_eq_avg_bytes_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/custom_eq_avg_bytes_fired.ts @@ -15,7 +15,7 @@ import { cleanup, generate } from '@kbn/infra-forge'; import { Aggregators, Comparator } from '@kbn/observability-plugin/common/threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/threshold/threshold_executor'; import expect from '@kbn/expect'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/documents_count_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/documents_count_fired.ts index 1d79f3aca06a7..f311fd4b902fc 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/documents_count_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/documents_count_fired.ts @@ -9,7 +9,7 @@ import { cleanup, generate } from '@kbn/infra-forge'; import { Aggregators, Comparator } from '@kbn/observability-plugin/common/threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/threshold/threshold_executor'; import expect from '@kbn/expect'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/group_by_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/group_by_fired.ts index b8b0418a2124e..e17209c044656 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/group_by_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/threshold_rule/group_by_fired.ts @@ -17,7 +17,7 @@ import { cleanup, generate } from '@kbn/infra-forge'; import { Aggregators, Comparator } from '@kbn/observability-plugin/common/threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/threshold/threshold_executor'; import expect from '@kbn/expect'; -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { diff --git a/x-pack/test_serverless/tsconfig.json b/x-pack/test_serverless/tsconfig.json index de711146da7ec..323c9271d1e9e 100644 --- a/x-pack/test_serverless/tsconfig.json +++ b/x-pack/test_serverless/tsconfig.json @@ -53,6 +53,7 @@ "@kbn/data-plugin", "@kbn/dev-utils", "@kbn/bfetch-plugin", + "@kbn/rule-data-utils", "@kbn/rison", ] }