From be87880dc5d4d82ffe6f1e75a1f8d7386a19f12d Mon Sep 17 00:00:00 2001 From: Ryan Blenis Date: Tue, 8 Oct 2019 04:18:40 -0400 Subject: [PATCH] More plugin hooks / development of base --- agents/meshcore.min.js | 33 ++++++---- meshagent.js | 12 ++++ meshcentral.js | 8 ++- pluginHandler.js | 137 +++++++++++++++++++++++++++++++++++++++ views/default.handlebars | 21 +++++- webserver.js | 6 +- 6 files changed, 200 insertions(+), 17 deletions(-) create mode 100644 pluginHandler.js diff --git a/agents/meshcore.min.js b/agents/meshcore.min.js index bc6ad446..b43ccd66 100644 --- a/agents/meshcore.min.js +++ b/agents/meshcore.min.js @@ -757,17 +757,6 @@ function createMeshCore(agent) } break; } - case 'plugin': { - if (typeof data.pluginaction == 'string') { - try { - MeshServerLog('Plugin called', data); - require(data.plugin.name).serveraction(data); - } catch(e) { - MeshServerLog('Error calling plugin', data); - } - } - break; - } default: // Unknown action, ignore it. break; @@ -841,6 +830,19 @@ function createMeshCore(agent) } case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; } case 'pong': { break; } + case 'plugin': { + if (typeof data.pluginaction == 'string') { + try { + + MeshServerLog('Plugin called', data); + /* Not yet implmented + require(data.plugin.name).serveraction(data);*/ + } catch(e) { + MeshServerLog('Error calling plugin', data); + } + } + break; + } default: // Unknown action, ignore it. break; @@ -2353,9 +2355,12 @@ function createMeshCore(agent) case 'plugin': { if (typeof args['_'][0] == 'string') { try { - response = require(args['_'][0]).consoleaction(args, rights, sessionid); + // pass off the action to the plugin + // for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js + // to control the output / actions here. + response = require(args['_'][0]).consoleaction(args, rights, sessionid, mesh); } catch(e) { - response = 'There was an error in the plugin'; + response = 'There was an error in the plugin (' + e + ')'; } } else { response = 'Proper usage: plugin [pluginName] [args].'; @@ -2475,6 +2480,8 @@ function createMeshCore(agent) //if (process.platform == 'win32') { try { pr = require('win-info').pendingReboot(); } catch (ex) { pr = null; } } // Pending reboot if ((meshCoreObj.av == null) || (JSON.stringify(meshCoreObj.av) != JSON.stringify(av))) { meshCoreObj.av = av; mesh.SendCommand(meshCoreObj); } } + + // TODO: add plugin hook here } diff --git a/meshagent.js b/meshagent.js index d7e58fa7..0873962b 100644 --- a/meshagent.js +++ b/meshagent.js @@ -1345,6 +1345,18 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { }); break; } + case 'plugin': { + if (typeof command.plugin == 'string') { + try { + var pluginHandler = require('./pluginHandler.js').pluginHandler(parent.parent); + pluginHandler.plugins[command.plugin].serveraction(command, obj, parent); + } catch (e) { + + console.log('Error loading plugin handler ('+ e + ')'); + } + } + break; + } default: { parent.agentStats.unknownAgentActionCount++; console.log('Unknown agent action (' + obj.remoteaddrport + '): ' + command.action + '.'); diff --git a/meshcentral.js b/meshcentral.js index 919ca9b0..a1583df1 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -865,6 +865,11 @@ function CreateMeshCentralServer(config, args) { // Dispatch an event that the server is now running obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'started', msg: 'Server started' }); + obj.pluginHandler = require("./pluginHandler.js").pluginHandler(obj); + + // Plugin hook. Need to run something at server startup? This is the place. + obj.pluginHandler.callHook("server_startup"); + // Load the login cookie encryption key from the database if allowed if ((obj.config) && (obj.config.settings) && (obj.config.settings.allowlogintoken == true)) { obj.db.Get('LoginCookieEncryptionKey', function (err, docs) { @@ -1347,7 +1352,8 @@ function CreateMeshCentralServer(config, args) { } } } - + obj.pluginHandler = require("./pluginHandler.js").pluginHandler(obj); + obj.pluginHandler.addMeshCoreModules(modulesAdd); // Merge the cores and compute the hashes for (var i in modulesAdd) { if ((i == 'windows-recovery') || (i == 'linux-recovery')) { diff --git a/pluginHandler.js b/pluginHandler.js new file mode 100644 index 00000000..6688e485 --- /dev/null +++ b/pluginHandler.js @@ -0,0 +1,137 @@ +/** +* @description MeshCentral plugin module +* @author Ryan Blenis +* @copyright +* @license Apache-2.0 +* @version v0.0.1 +*/ + +/*xjslint node: true */ +/*xjslint plusplus: true */ +/*xjslint maxlen: 256 */ +/*jshint node: true */ +/*jshint strict: false */ +/*jshint esversion: 6 */ +"use strict"; + +module.exports.pluginHandler = function (parent) { + var obj = {}; + + obj.fs = require('fs'); + obj.path = require('path'); + obj.parent = parent; + obj.pluginPath = obj.parent.path.join(obj.parent.datapath, 'plugins'); + obj.enabled = obj.parent.config.settings.plugins.enabled; + obj.loadList = obj.parent.config.settings.plugins.list; + obj.plugins = {}; + obj.exports = {}; + + if (obj.enabled) { + obj.loadList.forEach(function(plugin, index) { + if (obj.fs.existsSync(obj.pluginPath + '/' + plugin)) { + try { + obj.plugins[plugin] = require(obj.pluginPath + '/' + plugin + '/' + plugin + '.js')[plugin](obj); + obj.exports[plugin] = obj.plugins[plugin].exports; + } catch (e) { + console.log("Error loading plugin: " + plugin + " (" + e + "). It has been disabled"); + } + } + }); + } + + obj.prepExports = function() { + var str = 'function() {\r\n'; + str += ' var obj = {};\r\n'; + + for (const p of Object.keys(obj.plugins)) { + str += ' obj.'+ p +' = {};\r\n'; + for (const l of Object.values(obj.exports[p])) { + str += ' obj.'+ p +'.'+ l + ' = '+ obj.plugins[p][l].toString()+'\r\n'; + } + } + str += 'return obj; };\r\n'; + return str; + } + + obj.callHook = function(hookName, ...args) { + for (var p in obj.plugins) { + if (typeof obj.plugins[p][hookName] == 'function') { + try { + obj.plugins[p][hookName](args); + } catch (e) { + console.log('Error ocurred while running plugin hook' + p + ':' + hookName + ' (' + e + ')'); + } + } + } + }; + + obj.addMeshCoreModules = function(modulesAdd) { + if (obj.enabled !== true) return; + for (var plugin in obj.plugins) { + var moduleDirPath = null; + var modulesDir = null; + //if (obj.args.minifycore !== false) { try { moduleDirPath = obj.path.join(obj.pluginPath, 'modules_meshcore_min'); modulesDir = obj.fs.readdirSync(moduleDirPath); } catch (e) { } } // Favor minified modules if present. + if (modulesDir == null) { try { moduleDirPath = obj.path.join(obj.pluginPath, plugin + '/modules_meshcore'); modulesDir = obj.fs.readdirSync(moduleDirPath); } catch (e) { } } // Use non-minified mofules. + if (modulesDir != null) { + for (var i in modulesDir) { + if (modulesDir[i].toLowerCase().endsWith('.js')) { + var moduleName = modulesDir[i].substring(0, modulesDir[i].length - 3); + if (moduleName.endsWith('.min')) { moduleName = moduleName.substring(0, moduleName.length - 4); } // Remove the ".min" for ".min.js" files. + var moduleData = [ 'try { addModule("', moduleName, '", "', obj.parent.escapeCodeString(obj.fs.readFileSync(obj.path.join(moduleDirPath, modulesDir[i])).toString('binary')), '"); addedModules.push("', moduleName, '"); } catch (e) { }\r\n' ]; + + // Merge this module + // NOTE: "smbios" module makes some non-AI Linux segfault, only include for IA platforms. + if (moduleName.startsWith('amt-') || (moduleName == 'smbios')) { + // Add to IA / Intel AMT cores only + modulesAdd['windows-amt'].push(...moduleData); + modulesAdd['linux-amt'].push(...moduleData); + } else if (moduleName.startsWith('win-')) { + // Add to Windows cores only + modulesAdd['windows-amt'].push(...moduleData); + } else if (moduleName.startsWith('linux-')) { + // Add to Linux cores only + modulesAdd['linux-amt'].push(...moduleData); + modulesAdd['linux-noamt'].push(...moduleData); + } else { + // Add to all cores + modulesAdd['windows-amt'].push(...moduleData); + modulesAdd['linux-amt'].push(...moduleData); + modulesAdd['linux-noamt'].push(...moduleData); + } + + // Merge this module to recovery modules if needed + if (modulesAdd['windows-recovery'] != null) { + if ((moduleName == 'win-console') || (moduleName == 'win-message-pump') || (moduleName == 'win-terminal')) { + modulesAdd['windows-recovery'].push(...moduleData); + } + } + + // Merge this module to agent recovery modules if needed + if (modulesAdd['windows-agentrecovery'] != null) { + if ((moduleName == 'win-console') || (moduleName == 'win-message-pump') || (moduleName == 'win-terminal')) { + modulesAdd['windows-agentrecovery'].push(...moduleData); + } + } + } + } + } + } + }; + + obj.deviceViewPanel = function() { + var panel = {}; + for (var p in obj.plugins) { + if (typeof obj.plugins[p][hookName] == 'function') { + try { + panel[p].header = obj.plugins[p].on_device_header(); + panel[p].content = obj.plugins[p].on_device_page(); + } catch (e) { + console.log('Error ocurred while getting plugin views ' + p + ':' + ' (' + e + ')'); + } + } + } + return panel; + } + + return obj; +}; \ No newline at end of file diff --git a/views/default.handlebars b/views/default.handlebars index 096cafee..bea503a1 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -122,6 +122,7 @@ Details Intel® AMT Console + Plugins   @@ -841,6 +842,12 @@
+