"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.bulkDisableRules = void 0;
var _esQuery = require("@kbn/es-query");
var _boom = _interopRequireDefault(require("@hapi/boom"));
var _apmUtils = require("@kbn/apm-utils");
var _pMap = _interopRequireDefault(require("p-map"));
var _lib = require("../../../../lib");
var _audit_events = require("../../../../rules_client/common/audit_events");
var _common = require("../../../../rules_client/common");
var _lib2 = require("../../../../rules_client/lib");
var _transforms = require("../../transforms");
var _validation = require("./validation");
var _schemas = require("../../schemas");
var _rule = require("../../../../data/rule");
/*
 * 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.
 */

const bulkDisableRules = async (context, options) => {
  try {
    (0, _validation.validateBulkDisableRulesBody)(options);
  } catch (error) {
    throw _boom.default.badRequest(`Error validating bulk disable data - ${error.message}`);
  }
  const {
    ids,
    filter
  } = options;
  const kueryNodeFilter = ids ? (0, _lib.convertRuleIdsToKueryNode)(ids) : (0, _common.buildKueryNodeFilter)(filter);
  const authorizationFilter = await (0, _lib2.getAuthorizationFilter)(context, {
    action: 'DISABLE'
  });
  const kueryNodeFilterWithAuth = authorizationFilter && kueryNodeFilter ? _esQuery.nodeBuilder.and([kueryNodeFilter, authorizationFilter]) : kueryNodeFilter;
  const {
    total
  } = await (0, _lib2.checkAuthorizationAndGetTotal)(context, {
    filter: kueryNodeFilterWithAuth,
    action: 'DISABLE'
  });
  const {
    errors,
    rules,
    accListSpecificForBulkOperation
  } = await (0, _apmUtils.withSpan)({
    name: 'retryIfBulkOperationConflicts',
    type: 'rules'
  }, () => (0, _common.retryIfBulkOperationConflicts)({
    action: 'DISABLE',
    logger: context.logger,
    bulkOperation: filterKueryNode => bulkDisableRulesWithOCC(context, {
      filter: filterKueryNode
    }),
    filter: kueryNodeFilterWithAuth
  }));
  const [taskIdsToDisable, taskIdsToDelete, taskIdsToClearState] = accListSpecificForBulkOperation;
  await Promise.allSettled([tryToDisableTasks({
    taskIdsToDisable,
    taskIdsToClearState,
    logger: context.logger,
    taskManager: context.taskManager
  }), (0, _common.tryToRemoveTasks)({
    taskIdsToDelete,
    logger: context.logger,
    taskManager: context.taskManager
  })]);
  const disabledRules = rules.map(({
    id,
    attributes,
    references
  }) => {
    // TODO (http-versioning): alertTypeId should never be null, but we need to
    // fix the type cast from SavedObjectsBulkUpdateObject to SavedObjectsBulkUpdateObject
    // when we are doing the bulk disable and this should fix itself
    const ruleType = context.ruleTypeRegistry.get(attributes.alertTypeId);
    const ruleDomain = (0, _transforms.transformRuleAttributesToRuleDomain)(attributes, {
      id,
      logger: context.logger,
      ruleType,
      references,
      omitGeneratedValues: false
    });
    try {
      _schemas.ruleDomainSchema.validate(ruleDomain);
    } catch (e) {
      context.logger.warn(`Error validating bulk disabled rule domain object for id: ${id}, ${e}`);
    }
    return ruleDomain;
  });

  // TODO (http-versioning): This should be of type Rule, change this when all rule types are fixed
  const disabledPublicRules = disabledRules.map(rule => {
    return (0, _transforms.transformRuleDomainToRule)(rule);
  });
  return {
    errors,
    rules: disabledPublicRules,
    total
  };
};
exports.bulkDisableRules = bulkDisableRules;
const bulkDisableRulesWithOCC = async (context, {
  filter
}) => {
  const additionalFilter = _esQuery.nodeBuilder.is('alert.attributes.enabled', 'true');
  const rulesFinder = await (0, _apmUtils.withSpan)({
    name: 'encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser',
    type: 'rules'
  }, () => context.encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser({
    filter: filter ? _esQuery.nodeBuilder.and([filter, additionalFilter]) : additionalFilter,
    type: 'alert',
    perPage: 100,
    ...(context.namespace ? {
      namespaces: [context.namespace]
    } : undefined)
  }));
  const rulesToDisable = [];
  const errors = [];
  const ruleNameToRuleIdMapping = {};
  const username = await context.getUserName();
  await (0, _apmUtils.withSpan)({
    name: 'Get rules, collect them and their attributes',
    type: 'rules'
  }, async () => {
    for await (const response of rulesFinder.find()) {
      await (0, _pMap.default)(response.saved_objects, async rule => {
        try {
          var _context$auditLogger;
          await (0, _lib2.untrackRuleAlerts)(context, rule.id, rule.attributes);
          if (rule.attributes.name) {
            ruleNameToRuleIdMapping[rule.id] = rule.attributes.name;
          }

          // migrate legacy actions only for SIEM rules
          // TODO (http-versioning) Remove RawRuleAction and RawRule casts
          const migratedActions = await (0, _lib2.migrateLegacyActions)(context, {
            ruleId: rule.id,
            actions: rule.attributes.actions,
            references: rule.references,
            attributes: rule.attributes
          });

          // TODO (http-versioning) Remove casts when updateMeta has been converted
          const castedAttributes = rule.attributes;
          const updatedAttributes = (0, _lib2.updateMeta)(context, {
            ...castedAttributes,
            ...(migratedActions.hasLegacyActions ? {
              actions: migratedActions.resultedActions,
              throttle: undefined,
              notifyWhen: undefined
            } : {}),
            enabled: false,
            scheduledTaskId: rule.attributes.scheduledTaskId === rule.id ? rule.attributes.scheduledTaskId : null,
            updatedBy: username,
            updatedAt: new Date().toISOString()
          });
          rulesToDisable.push({
            ...rule,
            // TODO (http-versioning) Remove casts when updateMeta has been converted
            attributes: {
              ...updatedAttributes
            },
            ...(migratedActions.hasLegacyActions ? {
              references: migratedActions.resultedReferences
            } : {})
          });
          (_context$auditLogger = context.auditLogger) === null || _context$auditLogger === void 0 ? void 0 : _context$auditLogger.log((0, _audit_events.ruleAuditEvent)({
            action: _audit_events.RuleAuditAction.DISABLE,
            outcome: 'unknown',
            savedObject: {
              type: 'alert',
              id: rule.id
            }
          }));
        } catch (error) {
          var _rule$attributes, _context$auditLogger2;
          errors.push({
            message: error.message,
            rule: {
              id: rule.id,
              name: (_rule$attributes = rule.attributes) === null || _rule$attributes === void 0 ? void 0 : _rule$attributes.name
            }
          });
          (_context$auditLogger2 = context.auditLogger) === null || _context$auditLogger2 === void 0 ? void 0 : _context$auditLogger2.log((0, _audit_events.ruleAuditEvent)({
            action: _audit_events.RuleAuditAction.DISABLE,
            error
          }));
        }
      });
    }
    await rulesFinder.close();
  });

  // TODO (http-versioning): for whatever reasoning we are using SavedObjectsBulkUpdateObject
  // everywhere when it should be SavedObjectsBulkCreateObject. We need to fix it in
  // bulk_disable, bulk_enable, etc. to fix this cast

  const result = await (0, _apmUtils.withSpan)({
    name: 'unsecuredSavedObjectsClient.bulkCreate',
    type: 'rules'
  }, () => (0, _rule.bulkDisableRulesSo)({
    savedObjectsClient: context.unsecuredSavedObjectsClient,
    bulkDisableRuleAttributes: rulesToDisable,
    savedObjectsBulkCreateOptions: {
      overwrite: true
    }
  }));
  const taskIdsToDisable = [];
  const taskIdsToDelete = [];
  const taskIdsToClearState = [];
  const disabledRules = [];
  result.saved_objects.forEach(rule => {
    if (rule.error === undefined) {
      if (rule.attributes.scheduledTaskId) {
        if (rule.attributes.scheduledTaskId !== rule.id) {
          taskIdsToDelete.push(rule.attributes.scheduledTaskId);
        } else {
          taskIdsToDisable.push(rule.attributes.scheduledTaskId);
          if (rule.attributes.alertTypeId) {
            const {
              autoRecoverAlerts: isLifecycleAlert
            } = context.ruleTypeRegistry.get(rule.attributes.alertTypeId);
            if (isLifecycleAlert) taskIdsToClearState.push(rule.attributes.scheduledTaskId);
          }
        }
      }
      disabledRules.push(rule);
    } else {
      var _rule$error$message, _ruleNameToRuleIdMapp;
      errors.push({
        message: (_rule$error$message = rule.error.message) !== null && _rule$error$message !== void 0 ? _rule$error$message : 'n/a',
        status: rule.error.statusCode,
        rule: {
          id: rule.id,
          name: (_ruleNameToRuleIdMapp = ruleNameToRuleIdMapping[rule.id]) !== null && _ruleNameToRuleIdMapp !== void 0 ? _ruleNameToRuleIdMapp : 'n/a'
        }
      });
    }
  });
  return {
    errors,
    // TODO: delete the casting when we do versioning of bulk disable api
    rules: disabledRules,
    accListSpecificForBulkOperation: [taskIdsToDisable, taskIdsToDelete, taskIdsToClearState]
  };
};
const tryToDisableTasks = async ({
  taskIdsToDisable,
  taskIdsToClearState,
  logger,
  taskManager
}) => {
  return await (0, _apmUtils.withSpan)({
    name: 'taskManager.bulkDisable',
    type: 'rules'
  }, async () => {
    if (taskIdsToDisable.length > 0) {
      try {
        const resultFromDisablingTasks = await taskManager.bulkDisable(taskIdsToDisable, taskIdsToClearState);
        if (resultFromDisablingTasks.tasks.length) {
          logger.debug(`Successfully disabled schedules for underlying tasks: ${resultFromDisablingTasks.tasks.map(task => task.id).join(', ')}`);
        }
        if (resultFromDisablingTasks.errors.length) {
          logger.error(`Failure to disable schedules for underlying tasks: ${resultFromDisablingTasks.errors.map(error => error.id).join(', ')}`);
        }
      } catch (error) {
        logger.error(`Failure to disable schedules for underlying tasks: ${taskIdsToDisable.join(', ')}. TaskManager bulkDisable failed with Error: ${error.message}`);
      }
    }
  });
};