More plugin hooks / development of base
This commit is contained in:
parent
36931698ad
commit
d143990f31
|
@ -757,17 +757,6 @@ function createMeshCore(agent)
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
// Unknown action, ignore it.
|
// Unknown action, ignore it.
|
||||||
break;
|
break;
|
||||||
|
@ -841,6 +830,19 @@ function createMeshCore(agent)
|
||||||
}
|
}
|
||||||
case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; }
|
case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; }
|
||||||
case '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:
|
default:
|
||||||
// Unknown action, ignore it.
|
// Unknown action, ignore it.
|
||||||
break;
|
break;
|
||||||
|
@ -2353,9 +2355,12 @@ function createMeshCore(agent)
|
||||||
case 'plugin': {
|
case 'plugin': {
|
||||||
if (typeof args['_'][0] == 'string') {
|
if (typeof args['_'][0] == 'string') {
|
||||||
try {
|
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) {
|
} catch(e) {
|
||||||
response = 'There was an error in the plugin';
|
response = 'There was an error in the plugin (' + e + ')';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
response = 'Proper usage: plugin [pluginName] [args].';
|
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 (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); }
|
if ((meshCoreObj.av == null) || (JSON.stringify(meshCoreObj.av) != JSON.stringify(av))) { meshCoreObj.av = av; mesh.SendCommand(meshCoreObj); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: add plugin hook here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
12
meshagent.js
12
meshagent.js
|
@ -1345,6 +1345,18 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
||||||
});
|
});
|
||||||
break;
|
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: {
|
default: {
|
||||||
parent.agentStats.unknownAgentActionCount++;
|
parent.agentStats.unknownAgentActionCount++;
|
||||||
console.log('Unknown agent action (' + obj.remoteaddrport + '): ' + command.action + '.');
|
console.log('Unknown agent action (' + obj.remoteaddrport + '): ' + command.action + '.');
|
||||||
|
|
|
@ -865,6 +865,11 @@ function CreateMeshCentralServer(config, args) {
|
||||||
// Dispatch an event that the server is now running
|
// Dispatch an event that the server is now running
|
||||||
obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'started', msg: 'Server started' });
|
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
|
// Load the login cookie encryption key from the database if allowed
|
||||||
if ((obj.config) && (obj.config.settings) && (obj.config.settings.allowlogintoken == true)) {
|
if ((obj.config) && (obj.config.settings) && (obj.config.settings.allowlogintoken == true)) {
|
||||||
obj.db.Get('LoginCookieEncryptionKey', function (err, docs) {
|
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
|
// Merge the cores and compute the hashes
|
||||||
for (var i in modulesAdd) {
|
for (var i in modulesAdd) {
|
||||||
if ((i == 'windows-recovery') || (i == 'linux-recovery')) {
|
if ((i == 'windows-recovery') || (i == 'linux-recovery')) {
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
|
@ -122,6 +122,7 @@
|
||||||
<td tabindex=0 id=MainDevInfo class="topbar_td style3x" onclick=go(17,event) onkeypress="if (event.key == 'Enter') go(17)">Details</td>
|
<td tabindex=0 id=MainDevInfo class="topbar_td style3x" onclick=go(17,event) onkeypress="if (event.key == 'Enter') go(17)">Details</td>
|
||||||
<td tabindex=0 id=MainDevAmt class="topbar_td style3x" onclick=go(14,event) onkeypress="if (event.key == 'Enter') go(14)">Intel® AMT</td>
|
<td tabindex=0 id=MainDevAmt class="topbar_td style3x" onclick=go(14,event) onkeypress="if (event.key == 'Enter') go(14)">Intel® AMT</td>
|
||||||
<td tabindex=0 id=MainDevConsole class="topbar_td style3x" onclick=go(15,event) onkeypress="if (event.key == 'Enter') go(15)">Console</td>
|
<td tabindex=0 id=MainDevConsole class="topbar_td style3x" onclick=go(15,event) onkeypress="if (event.key == 'Enter') go(15)">Console</td>
|
||||||
|
<td tabindex=0 id=MainDevPlugins class="topbar_td style3x" onclick=go(19,event) onkeypress="if (event.key == 'Enter') go(19)">Plugins</td>
|
||||||
<td class="topbar_td_end style3"> </td>
|
<td class="topbar_td_end style3"> </td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
@ -841,6 +842,12 @@
|
||||||
</div>
|
</div>
|
||||||
<div id=p41events style=""></div>
|
<div id=p41events style=""></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id=p19 style="display:none">
|
||||||
|
<h1>Plugins - <span id=p19deviceName></span></h1>
|
||||||
|
<div class="p19headers">
|
||||||
|
</div>
|
||||||
|
<div id=p19pages style=""></div>
|
||||||
|
</div>
|
||||||
<br id="column_l_bottomgap" />
|
<br id="column_l_bottomgap" />
|
||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
|
@ -1008,6 +1015,7 @@
|
||||||
var nightMode = (getstore('_nightMode', '0') == '1');
|
var nightMode = (getstore('_nightMode', '0') == '1');
|
||||||
var sessionActivity = Date.now();
|
var sessionActivity = Date.now();
|
||||||
var updateSessionTimer = null;
|
var updateSessionTimer = null;
|
||||||
|
var pluginHandler = {{{pluginHandler}}};
|
||||||
|
|
||||||
// Console Message Display Timers
|
// Console Message Display Timers
|
||||||
var p11DeskConsoleMsgTimer = null;
|
var p11DeskConsoleMsgTimer = null;
|
||||||
|
@ -2298,6 +2306,17 @@
|
||||||
QH('p0span', message.msg);
|
QH('p0span', message.msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'plugin': {
|
||||||
|
if (typeof message.plugin == 'string') {
|
||||||
|
try {
|
||||||
|
var ph = pluginHandler();
|
||||||
|
ph[message.plugin][message.method](server, message);
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Error loading plugin handler ('+ e + ')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
//console.log('Unknown message.action', message.action);
|
//console.log('Unknown message.action', message.action);
|
||||||
break;
|
break;
|
||||||
|
@ -9090,7 +9109,7 @@
|
||||||
QV('MeshSubMenuSpan', x >= 20 && x < 30);
|
QV('MeshSubMenuSpan', x >= 20 && x < 30);
|
||||||
QV('UserSubMenuSpan', x >= 30 && x < 40);
|
QV('UserSubMenuSpan', x >= 30 && x < 40);
|
||||||
QV('ServerSubMenuSpan', x == 6 || x == 115 || x == 40 || x == 41);
|
QV('ServerSubMenuSpan', x == 6 || x == 115 || x == 40 || x == 41);
|
||||||
var panels = { 10: 'MainDev', 11: 'MainDevDesktop', 12: 'MainDevTerminal', 13: 'MainDevFiles', 14: 'MainDevAmt', 15: 'MainDevConsole', 16: 'MainDevEvents', 17: 'MainDevInfo', 20: 'MeshGeneral', 30: 'UserGeneral', 31: 'UserEvents', 6: 'ServerGeneral', 40: 'ServerStats', 41: 'ServerTrace', 115: 'ServerConsole' };
|
var panels = { 10: 'MainDev', 11: 'MainDevDesktop', 12: 'MainDevTerminal', 13: 'MainDevFiles', 14: 'MainDevAmt', 15: 'MainDevConsole', 16: 'MainDevEvents', 17: 'MainDevInfo', 20: 'MeshGeneral', 30: 'UserGeneral', 31: 'UserEvents', 6: 'ServerGeneral', 40: 'ServerStats', 41: 'ServerTrace', 19: 'Plugins', 115: 'ServerConsole' };
|
||||||
for (var i in panels) {
|
for (var i in panels) {
|
||||||
QC(panels[i]).remove('style3x');
|
QC(panels[i]).remove('style3x');
|
||||||
QC(panels[i]).remove('style3sel');
|
QC(panels[i]).remove('style3sel');
|
||||||
|
|
|
@ -1517,11 +1517,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
// Clean up the U2F challenge if needed
|
// Clean up the U2F challenge if needed
|
||||||
if (req.session.u2fchallenge) { delete req.session.u2fchallenge; };
|
if (req.session.u2fchallenge) { delete req.session.u2fchallenge; };
|
||||||
|
|
||||||
|
var pluginHandler = require('./pluginHandler.js').pluginHandler(parent);
|
||||||
|
|
||||||
// Fetch the web state
|
// Fetch the web state
|
||||||
parent.debug('web', 'handleRootRequestEx: success.');
|
parent.debug('web', 'handleRootRequestEx: success.');
|
||||||
obj.db.Get('ws' + user._id, function (err, states) {
|
obj.db.Get('ws' + user._id, function (err, states) {
|
||||||
var webstate = (states.length == 1) ? states[0].state : '';
|
var webstate = (states.length == 1) ? states[0].state : '';
|
||||||
res.render(getRenderPage('default', req), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer, webstate: encodeURIComponent(webstate) });
|
res.render(getRenderPage('default', req), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer, webstate: encodeURIComponent(webstate), pluginHandler: pluginHandler.prepExports() });
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Send back the login application
|
// Send back the login application
|
||||||
|
|
Loading…
Reference in New Issue