UNPKG

@mdf.js/openc2-core

Version:

MMS - API Core - OpenC2

185 lines 6.8 kB
"use strict"; /** * Copyright 2024 Mytra Control S.L. All rights reserved. * * Use of this source code is governed by an MIT-style license that can be found in the LICENSE file * or at https://opensource.org/licenses/MIT. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ConsumerMap = void 0; const tslib_1 = require("tslib"); const core_1 = require("@mdf.js/core"); const events_1 = tslib_1.__importDefault(require("events")); const lodash_1 = require("lodash"); const uuid_1 = require("uuid"); const helpers_1 = require("../../helpers"); class ConsumerMap extends events_1.default { /** * Create a new instance of the consumer map * @param name - name of the producer * @param agingInterval - agingInterval to update the consumer map * @param maxAge - Max allowed age in in milliseconds for a table entry */ constructor(name, agingInterval, maxAge) { super(); this.name = name; this.agingInterval = agingInterval; this.maxAge = maxAge; /** Component identification */ this.componentId = (0, uuid_1.v4)(); /** Perform the aging of the consumer map */ this.aging = () => { var _a; const emitAsAged = []; const checkTime = new Date().getTime(); for (const [consumerId, consumer] of this.map.entries()) { const age = checkTime - new Date((_a = consumer.time) !== null && _a !== void 0 ? _a : 0).getTime(); if (age > this.maxAge) { emitAsAged.push(consumerId); this.map.delete(consumerId); } } if (emitAsAged.length > 0) { this.emit('aged', emitAsAged); this.emit('updated', emitAsAged); } if (this.map.size === 0) { clearInterval(this.agingTimer); this.agingTimer = undefined; } }; this.map = new Map(); } /** Get the actual nodes */ get nodes() { return Array.from(this.map.values()) .map(node => node.observedValue) .filter(node => node !== undefined); } /** * Returns the grouped features of all the consumer in the map * @returns */ getGroupedFeatures() { const nodes = this.nodes; const pairs = {}; const arrayOfPairs = nodes.map(node => { var _a; return ((_a = node.results) === null || _a === void 0 ? void 0 : _a.pairs) || {}; }); for (const nodePairs of arrayOfPairs) { for (const [action, targets] of Object.entries(nodePairs)) { if (pairs[action]) { pairs[action] = (0, lodash_1.uniq)(pairs[action].concat(targets)); } else { pairs[action] = targets; } } } const profiles = (0, lodash_1.uniq)(nodes.map(node => { var _a, _b; return (_b = (_a = node.results) === null || _a === void 0 ? void 0 : _a.profiles) !== null && _b !== void 0 ? _b : []; }).flat()); return { pairs, profiles }; } /** * Return the consumers identifiers that has the indicated action/target pair * @param action - action to search * @param target - target requested */ getConsumersWithPair(action, target) { const consumers = []; for (const [consumerId, consumer] of this.map.entries()) { const targets = (0, lodash_1.get)(consumer, `observedValue.results.pairs.${action}`, []); if (targets.includes(target)) { consumers.push(consumerId); } } return consumers; } /** * Perform the update of the health map * @param responses - responses to update the consumer map */ update(responses) { const emitAsNewEntry = []; const emitAsChanged = []; for (const response of responses) { const actualRegistry = this.map.get(response.from); if (!actualRegistry) { emitAsNewEntry.push(response.from); } else if (!(0, lodash_1.isEqual)(actualRegistry.observedValue, response.content)) { emitAsChanged.push(response.from); } this.updateEntry(response); } if (emitAsNewEntry.length > 0) { this.emit('new', emitAsNewEntry); } if (emitAsChanged.length > 0) { this.emit('update', emitAsChanged); } if (emitAsChanged.length > 0 || emitAsNewEntry.length > 0) { this.emit('updated', emitAsChanged.concat(emitAsNewEntry)); } this.emitStatus(); if (!this.agingTimer && this.map.size > 0) { this.agingTimer = setInterval(this.aging, this.agingInterval); } } /** * Get one node from the map * @param consumerId - consumer id to get the status * @returns */ getNode(consumerId) { return this.map.get(consumerId); } /** Return the state of all the underlying consumers */ get checks() { return { [`${this.name}:consumers`]: Array.from(this.map.values()) }; } /** * Update the consumer map with a new response * @param response - response to update the consumer map */ updateEntry(response) { this.map.set(response.from, { componentId: response.from, componentType: 'OpenC2 Consumer', time: new Date(response.created).toISOString(), status: helpers_1.Accessors.getStatusFromResponseMessage(response), observedValue: response.content, observedUnit: 'features', }); } /** Overall component status */ get status() { return core_1.Health.overallStatus(this.checks); } /** Emit the status if it's different from the last emitted status */ emitStatus() { if (this.lastStatusEmitted !== this.status) { this.lastStatusEmitted = this.status; this.emit('status', this.status); } } /** Clean the nodes map */ clear() { this.map.clear(); if (this.agingTimer) { clearInterval(this.agingTimer); this.agingTimer = undefined; } } /** Fake start method used to implement the Resource interface */ async start() { return Promise.resolve(); } /** Fake stop method used to implement the Resource interface */ async stop() { return Promise.resolve(); } /** Fake close method used to implement the Resource interface */ async close() { return Promise.resolve(); } } exports.ConsumerMap = ConsumerMap; //# sourceMappingURL=ConsumerMap.js.map