"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.validateActions = validateActions; var _boom = _interopRequireDefault(require("@hapi/boom")); var _lodash = require("lodash"); var _i18n = require("@kbn/i18n"); var _validate_hours = require("../../routes/lib/validate_hours"); var _types = require("../../types"); var _lib = require("../../lib"); /* * 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. */ async function validateActions(context, ruleType, data, allowMissingConnectorSecrets) { const { actions, notifyWhen, throttle } = data; const hasRuleLevelNotifyWhen = typeof notifyWhen !== 'undefined'; const hasRuleLevelThrottle = Boolean(throttle); if (actions.length === 0) { return; } const errors = []; const uniqueActions = new Set(actions.map(action => action.uuid)); if (uniqueActions.size < actions.length) { errors.push(_i18n.i18n.translate('xpack.alerting.rulesClient.validateActions.hasDuplicatedUuid', { defaultMessage: 'Actions have duplicated UUIDs' })); } // check for actions using connectors with missing secrets const actionsClient = await context.getActionsClient(); const actionIds = [...new Set(actions.map(action => action.id))]; const actionResults = (await actionsClient.getBulk({ ids: actionIds, throwIfSystemAction: false })) || []; const actionsUsingConnectorsWithMissingSecrets = actionResults.filter(result => result.isMissingSecrets); if (actionsUsingConnectorsWithMissingSecrets.length) { if (allowMissingConnectorSecrets) { context.logger.error(`Invalid connectors with "allowMissingConnectorSecrets": ${actionsUsingConnectorsWithMissingSecrets.map(connector => connector.name).join(', ')}`); } else { errors.push(_i18n.i18n.translate('xpack.alerting.rulesClient.validateActions.misconfiguredConnector', { defaultMessage: 'Invalid connectors: {groups}', values: { groups: actionsUsingConnectorsWithMissingSecrets.map(connector => connector.name).join(', ') } })); } } // check for actions with invalid action groups const { actionGroups: alertTypeActionGroups } = ruleType; const usedAlertActionGroups = actions.map(action => action.group); const availableAlertTypeActionGroups = new Set((0, _lodash.map)(alertTypeActionGroups, 'id')); const invalidActionGroups = usedAlertActionGroups.filter(group => !availableAlertTypeActionGroups.has(group)); if (invalidActionGroups.length) { errors.push(_i18n.i18n.translate('xpack.alerting.rulesClient.validateActions.invalidGroups', { defaultMessage: 'Invalid action groups: {groups}', values: { groups: invalidActionGroups.join(', ') } })); } // check for actions using frequency params if the rule has rule-level frequency params defined if (hasRuleLevelNotifyWhen || hasRuleLevelThrottle) { const actionsWithFrequency = actions.filter(action => Boolean(action.frequency)); if (actionsWithFrequency.length) { errors.push(_i18n.i18n.translate('xpack.alerting.rulesClient.validateActions.mixAndMatchFreqParams', { defaultMessage: 'Cannot specify per-action frequency params when notify_when or throttle are defined at the rule level: {groups}', values: { groups: actionsWithFrequency.map(a => a.group).join(', ') } })); } } else { const actionsWithoutFrequency = actions.filter(action => !action.frequency); if (actionsWithoutFrequency.length) { errors.push(_i18n.i18n.translate('xpack.alerting.rulesClient.validateActions.notAllActionsWithFreq', { defaultMessage: 'Actions missing frequency parameters: {groups}', values: { groups: actionsWithoutFrequency.map(a => a.group).join(', ') } })); } } const scheduleInterval = (0, _lib.parseDuration)(data.schedule.interval); const actionsWithInvalidThrottles = []; const actionWithoutQueryAndTimeframe = []; const actionWithInvalidTimeframe = []; const actionsWithInvalidTimeRange = []; const actionsWithInvalidDays = []; const actionsWithAlertsFilterWithoutAlertsMapping = []; for (const action of actions) { var _action$frequency; const { alertsFilter } = action; // check for actions throttled shorter than the rule schedule if (((_action$frequency = action.frequency) === null || _action$frequency === void 0 ? void 0 : _action$frequency.notifyWhen) === _types.RuleNotifyWhen.THROTTLE && (0, _lib.parseDuration)(action.frequency.throttle) < scheduleInterval) { actionsWithInvalidThrottles.push(action); } if (alertsFilter) { // Action has alertsFilter but the ruleType does not support AAD if (!ruleType.alerts) { actionsWithAlertsFilterWithoutAlertsMapping.push(action); } // alertsFilter must have at least one of query and timeframe if (!alertsFilter.query && !alertsFilter.timeframe) { actionWithoutQueryAndTimeframe.push(action); } if (alertsFilter.timeframe) { // hours, days and timezone fields are required if (!alertsFilter.timeframe.hours || !alertsFilter.timeframe.days || !alertsFilter.timeframe.timezone) { actionWithInvalidTimeframe.push(action); } if (alertsFilter.timeframe.hours) { if ((0, _validate_hours.validateHours)(alertsFilter.timeframe.hours.start) || (0, _validate_hours.validateHours)(alertsFilter.timeframe.hours.end)) { actionsWithInvalidTimeRange.push(action); } } if (alertsFilter.timeframe.days) { if (alertsFilter.timeframe.days.some(day => ![1, 2, 3, 4, 5, 6, 7].includes(day))) { actionsWithInvalidDays.push(action); } } } } } if (actionsWithInvalidThrottles.length > 0) { errors.push(_i18n.i18n.translate('xpack.alerting.rulesClient.validateActions.actionsWithInvalidThrottles', { defaultMessage: 'Action frequency cannot be shorter than the schedule interval of {scheduleIntervalText}: {groups}', values: { scheduleIntervalText: data.schedule.interval, groups: actionsWithInvalidThrottles.map(a => { var _a$frequency; return `${a.group} (${(_a$frequency = a.frequency) === null || _a$frequency === void 0 ? void 0 : _a$frequency.throttle})`; }).join(', ') } })); } if (actionWithoutQueryAndTimeframe.length > 0) { errors.push(_i18n.i18n.translate('xpack.alerting.rulesClient.validateActions.actionsWithInvalidAlertsFilter', { defaultMessage: `Action's alertsFilter must have either "query" or "timeframe" : {uuids}`, values: { uuids: actionWithoutQueryAndTimeframe.map(a => `${a.uuid}`).join(', ') } })); } if (actionWithInvalidTimeframe.length > 0) { errors.push(_i18n.i18n.translate('xpack.alerting.rulesClient.validateActions.actionWithInvalidTimeframe', { defaultMessage: `Action's alertsFilter timeframe has missing fields: days, hours or timezone: {uuids}`, values: { uuids: actionWithInvalidTimeframe.map(a => a.uuid).join(', ') } })); } if (actionsWithInvalidDays.length > 0) { errors.push(_i18n.i18n.translate('xpack.alerting.rulesClient.validateActions.actionsWithInvalidDays', { defaultMessage: `Action's alertsFilter days has invalid values: {uuidAndDays}`, values: { uuidAndDays: actionsWithInvalidDays.map(a => `(${a.uuid}:[${a.alertsFilter.timeframe.days}]) `).join(', ') } })); } if (actionsWithInvalidTimeRange.length > 0) { errors.push(_i18n.i18n.translate('xpack.alerting.rulesClient.validateActions.actionsWithInvalidTimeRange', { defaultMessage: `Action's alertsFilter time range has an invalid value: {hours}`, values: { hours: actionsWithInvalidTimeRange.map(a => `${a.alertsFilter.timeframe.hours.start}-${a.alertsFilter.timeframe.hours.end}`).join(', ') } })); } if (actionsWithAlertsFilterWithoutAlertsMapping.length > 0) { errors.push(_i18n.i18n.translate('xpack.alerting.rulesClient.validateActions.actionsWithAlertsFilterWithoutSummaryGetter', { defaultMessage: `This ruleType ({ruleType}) can't have an action with Alerts Filter. Actions: [{uuids}]`, values: { uuids: actionsWithAlertsFilterWithoutAlertsMapping.map(a => a.uuid).join(', '), ruleType: ruleType.name } })); } // Finalize and throw any errors present if (errors.length) { throw _boom.default.badRequest(_i18n.i18n.translate('xpack.alerting.rulesClient.validateActions.errorSummary', { defaultMessage: 'Failed to validate actions due to the following {errorNum, plural, one {error:} other {# errors:\n-}} {errorList}', values: { errorNum: errors.length, errorList: errors.join('\n- ') } })); } }