"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateRulesFromRaw = generateRulesFromRaw; var _i18n = require("@kbn/i18n"); var _all_rule = require("./all_rule"); var _any_rule = require("./any_rule"); var _except_all_rule = require("./except_all_rule"); var _except_any_rule = require("./except_any_rule"); var _field_rule = require("./field_rule"); var _rule_builder_error = require("./rule_builder_error"); /* * 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. */ /** * Given a set of raw rules, this constructs a class based tree for consumption by the Role Management UI. * This also performs validation on the raw rule set, as it is possible to enter raw JSON in the JSONRuleEditor, * so we have no guarantees that the rule set is valid ahead of time. * * @param rawRules the raw rules to translate. */ function generateRulesFromRaw(rawRules = {}) { return parseRawRules(rawRules, null, [], 0); } function parseRawRules(rawRules, parentRuleType, ruleTrace, depth) { const entries = Object.entries(rawRules); if (!entries.length) { return { rules: null, maxDepth: 0 }; } if (entries.length > 1) { throw new _rule_builder_error.RuleBuilderError(_i18n.i18n.translate('xpack.security.management.editRoleMapping.ruleBuilder.expectSingleRule', { defaultMessage: `Expected a single rule definition, but found {numberOfRules}.`, values: { numberOfRules: entries.length } }), ruleTrace); } const rule = entries[0]; const [ruleType, ruleDefinition] = rule; return createRuleForType(ruleType, ruleDefinition, parentRuleType, ruleTrace, depth + 1); } function createRuleForType(ruleType, ruleDefinition, parentRuleType, ruleTrace = [], depth) { const isRuleNegated = parentRuleType === 'except'; const currentRuleTrace = [...ruleTrace, ruleType]; switch (ruleType) { case 'field': { assertIsObject(ruleDefinition, currentRuleTrace); const entries = Object.entries(ruleDefinition); if (entries.length !== 1) { throw new _rule_builder_error.RuleBuilderError(_i18n.i18n.translate('xpack.security.management.editRoleMapping.ruleBuilder.expectedSingleFieldRule', { defaultMessage: `Expected a single field, but found {count}.`, values: { count: entries.length } }), currentRuleTrace); } const [field, value] = entries[0]; const values = Array.isArray(value) ? value : [value]; values.forEach(fieldValue => { const valueType = typeof fieldValue; if (fieldValue !== null && !['string', 'number', 'boolean'].includes(valueType)) { throw new _rule_builder_error.RuleBuilderError(_i18n.i18n.translate('xpack.security.management.editRoleMapping.ruleBuilder.invalidFieldValueType', { defaultMessage: `Invalid value type for field. Expected one of null, string, number, or boolean, but found {valueType} ({value}).`, values: { valueType, value: JSON.stringify(value) } }), currentRuleTrace); } }); const fieldRule = new _field_rule.FieldRule(field, value); return { rules: isRuleNegated ? new _except_all_rule.ExceptAllRule([fieldRule]) : fieldRule, maxDepth: depth }; } case 'any': // intentional fall-through to 'all', as validation logic is identical case 'all': { if (ruleDefinition != null && !Array.isArray(ruleDefinition)) { throw new _rule_builder_error.RuleBuilderError(_i18n.i18n.translate('xpack.security.management.editRoleMapping.ruleBuilder.expectedArrayForGroupRule', { defaultMessage: `Expected an array of rules, but found {type}.`, values: { type: typeof ruleDefinition } }), currentRuleTrace); } const subRulesResults = (ruleDefinition || []).map((definition, index) => parseRawRules(definition, ruleType, [...currentRuleTrace, `[${index}]`], depth)); const { subRules, maxDepth } = subRulesResults.reduce((acc, result) => { return { subRules: [...acc.subRules, result.rules], maxDepth: Math.max(acc.maxDepth, result.maxDepth) }; }, { subRules: [], maxDepth: 0 }); if (ruleType === 'all') { return { rules: isRuleNegated ? new _except_all_rule.ExceptAllRule(subRules) : new _all_rule.AllRule(subRules), maxDepth }; } else { return { rules: isRuleNegated ? new _except_any_rule.ExceptAnyRule(subRules) : new _any_rule.AnyRule(subRules), maxDepth }; } } case 'except': { assertIsObject(ruleDefinition, currentRuleTrace); if (parentRuleType !== 'all') { throw new _rule_builder_error.RuleBuilderError(_i18n.i18n.translate('xpack.security.management.editRoleMapping.ruleBuilder.exceptOnlyInAllRule', { defaultMessage: `"except" rule can only exist within an "all" rule.` }), currentRuleTrace); } // subtracting 1 from depth because we don't currently count the "except" level itself as part of the depth calculation // for the purpose of determining if the rule set is "too complex" for the visual rule editor. // The "except" rule MUST be nested within an "all" rule type (see validation above), so the depth itself will always be a non-negative number. return parseRawRules(ruleDefinition || {}, ruleType, currentRuleTrace, depth - 1); } default: throw new _rule_builder_error.RuleBuilderError(_i18n.i18n.translate('xpack.security.management.editRoleMapping.ruleBuilder.unknownRuleType', { defaultMessage: `Unknown rule type: {ruleType}.`, values: { ruleType } }), currentRuleTrace); } } function assertIsObject(ruleDefinition, ruleTrace) { let fieldType = typeof ruleDefinition; if (Array.isArray(ruleDefinition)) { fieldType = 'array'; } if (ruleDefinition && fieldType !== 'object') { throw new _rule_builder_error.RuleBuilderError(_i18n.i18n.translate('xpack.security.management.editRoleMapping.ruleBuilder.expectedObjectError', { defaultMessage: `Expected an object, but found {type}.`, values: { type: fieldType } }), ruleTrace); } }