UNPKG

@backstage-community/plugin-rbac-backend

Version:
194 lines (188 loc) 6.72 kB
'use strict'; var yaml = require('js-yaml'); var lodash = require('lodash'); var fs = require('fs'); var auditor = require('../auditor/auditor.cjs.js'); var helper = require('../helper.cjs.js'); var conditionValidation = require('../validation/condition-validation.cjs.js'); var fileWatcher = require('./file-watcher.cjs.js'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; } var yaml__default = /*#__PURE__*/_interopDefaultCompat(yaml); var fs__default = /*#__PURE__*/_interopDefaultCompat(fs); class YamlConditinalPoliciesFileWatcher extends fileWatcher.AbstractFileWatcher { constructor(filePath, allowReload, logger, conditionalStorage, auditor, auth, pluginMetadataCollector, roleMetadataStorage, roleEventEmitter) { super(filePath, allowReload, logger); this.conditionalStorage = conditionalStorage; this.auditor = auditor; this.auth = auth; this.pluginMetadataCollector = pluginMetadataCollector; this.roleMetadataStorage = roleMetadataStorage; this.roleEventEmitter = roleEventEmitter; this.conditionsDiff = { addedConditions: [], removedConditions: [] }; } conditionsDiff; async initialize() { if (!this.filePath) { return; } const fileExists = fs__default.default.existsSync(this.filePath); if (!fileExists) { const auditorEvent = await this.auditor.createEvent({ eventId: auditor.ConditionEvents.CONDITIONAL_POLICIES_FILE_NOT_FOUND, severityLevel: "medium" }); await auditorEvent.fail({ error: new Error(`File '${this.filePath}' was not found`) }); return; } this.roleEventEmitter.on("roleAdded", this.onChange.bind(this)); await this.onChange(); if (this.allowReload) { this.watchFile(); } } async onChange() { try { const newConds = this.parse().filter((c) => c); const addedConds = []; const removedConds = []; const csvFileRoles = await this.roleMetadataStorage.filterRoleMetadata("csv-file"); const existedFileConds = (await this.conditionalStorage.filterConditions( csvFileRoles.map((role) => role.roleEntityRef) )).map((condition) => { return { ...condition, permissionMapping: condition.permissionMapping.map((pm) => pm.action) }; }); for (const condition of newConds) { const roleMetadata = csvFileRoles.find( (role) => condition.roleEntityRef === role.roleEntityRef ); if (!roleMetadata) { this.logger.warn( `skip to add condition for role '${condition.roleEntityRef}'. The role either does not exist or was not created from a CSV file.` ); continue; } if (roleMetadata.source !== "csv-file") { this.logger.warn( `skip to add condition for role '${condition.roleEntityRef}'. Role is not from csv-file` ); continue; } const existingCondition = existedFileConds.find( (c) => helper.deepSortEqual(lodash.omit(c, ["id"]), lodash.omit(condition, ["id"])) ); if (!existingCondition) { addedConds.push(condition); } } for (const condition of existedFileConds) { if (!newConds.find( (c) => helper.deepSortEqual(lodash.omit(c, ["id"]), lodash.omit(condition, ["id"])) )) { removedConds.push(condition); } } this.conditionsDiff = { addedConditions: addedConds, removedConditions: removedConds }; await this.handleFileChanges(); } catch (error) { const auditorEvent = await this.auditor.createEvent({ eventId: auditor.ConditionEvents.CONDITIONAL_POLICIES_FILE_CHANGE, severityLevel: "medium" }); await auditorEvent.fail({ error }); } } /** * Reads the current contents of the file and parses it. * @returns parsed data. */ parse() { const fileContents = this.getCurrentContents(); const data = yaml__default.default.loadAll( fileContents ); for (const condition of data) { conditionValidation.validateRoleCondition(condition); } return data; } async handleFileChanges() { await this.removeConditions(); await this.addConditions(); } async addConditions() { for (const condition of this.conditionsDiff.addedConditions) { const auditorEvent = await this.auditor.createEvent({ eventId: auditor.ConditionEvents.CONDITION_WRITE, severityLevel: "medium", meta: { actionType: auditor.ActionType.CREATE } }); try { const conditionToCreate = await helper.processConditionMapping( condition, this.pluginMetadataCollector, this.auth ); await this.conditionalStorage.createCondition(conditionToCreate); await auditorEvent.success({ meta: { condition } }); } catch (error) { await auditorEvent.fail({ error, meta: { condition } }); } } this.conditionsDiff.addedConditions = []; } async removeConditions() { for (const condition of this.conditionsDiff.removedConditions) { const auditorEvent = await this.auditor.createEvent({ eventId: auditor.ConditionEvents.CONDITION_WRITE, severityLevel: "medium", meta: { actionType: auditor.ActionType.DELETE } }); try { const conditionToDelete = (await this.conditionalStorage.filterConditions( condition.roleEntityRef, condition.pluginId, condition.resourceType, condition.permissionMapping ))[0]; await this.conditionalStorage.deleteCondition(conditionToDelete.id); await auditorEvent.success({ meta: { condition } }); } catch (error) { await auditorEvent.fail({ error, meta: { condition } }); } } this.conditionsDiff.removedConditions = []; } async cleanUpConditionalPolicies() { const csvFileRoles = await this.roleMetadataStorage.filterRoleMetadata("csv-file"); const existedFileConds = (await this.conditionalStorage.filterConditions( csvFileRoles.map((role) => role.roleEntityRef) )).map((condition) => { return { ...condition, permissionMapping: condition.permissionMapping.map((pm) => pm.action) }; }); this.conditionsDiff.removedConditions = existedFileConds; await this.removeConditions(); } } exports.YamlConditinalPoliciesFileWatcher = YamlConditinalPoliciesFileWatcher; //# sourceMappingURL=yaml-conditional-file-watcher.cjs.js.map