Added batch command execution.

This commit is contained in:
Ylian Saint-Hilaire 2020-07-10 23:12:43 -07:00
parent 99345f7259
commit cf8f58cd48
5 changed files with 1611 additions and 1440 deletions

View File

@ -891,6 +891,39 @@ function createMeshCore(agent) {
for (var i in data.macs) { sendWakeOnLan(data.macs[i]); } for (var i in data.macs) { sendWakeOnLan(data.macs[i]); }
break; break;
} }
case 'runcommands': {
if (mesh.cmdchild != null) { sendConsoleText("Run commands can't execute, already busy."); break; }
MeshServerLog("Running commands", data);
sendConsoleText("Run commands: " + data.cmds);
if (process.platform == 'win32') {
if (data.type == 1) {
// Windows command shell
mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd']);
mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
} else if (data.type == 2) {
// Windows Powershell
mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-']);
mesh.cmdchild.descriptorMetadata = 'UserCommandsPowerShell';
mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
}
} else if (data.type == 3) {
// Linux shell
mesh.cmdchild = require('child_process').execFile('/bin/sh', ['sh']);
mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
mesh.cmdchild.stdin.write(data.cmds.split('\r').join('') + '\nexit\n');
mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
}
break;
}
case 'uninstallagent': case 'uninstallagent':
// Uninstall this agent // Uninstall this agent
var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'; var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';

View File

@ -3338,18 +3338,18 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
db.Get('if' + node._id, function (err, nodeifs) { db.Get('if' + node._id, function (err, nodeifs) {
if ((nodeifs != null) && (nodeifs.length == 1)) { if ((nodeifs != null) && (nodeifs.length == 1)) {
var macs = [], nodeif = nodeifs[0]; var macs = [], nodeif = nodeifs[0];
for (var i in nodeif.netif) { if (nodeif.netif[i].mac) { macs.push(nodeif.netif[i].mac); } } for (var j in nodeif.netif) { if (nodeif.netif[j].mac) { macs.push(nodeif.netif[j].mac); } }
// Have the server send a wake-on-lan packet (Will not work in WAN-only) // Have the server send a wake-on-lan packet (Will not work in WAN-only)
if (parent.parent.meshScanner != null) { parent.parent.meshScanner.wakeOnLan(macs); } if (parent.parent.meshScanner != null) { parent.parent.meshScanner.wakeOnLan(macs); }
// Get the list of mesh this user as access to // Get the list of mesh this user as access to
var targetMeshes = []; var targetMeshes = [];
for (i in user.links) { targetMeshes.push(i); } // TODO: Include used security groups!! for (j in user.links) { targetMeshes.push(j); } // TODO: Include used security groups!!
// Go thru all the connected agents and send wake-on-lan on all the ones in the target mesh list // Go thru all the connected agents and send wake-on-lan on all the ones in the target mesh list
for (i in parent.wsagents) { for (j in parent.wsagents) {
var agent = parent.wsagents[i]; var agent = parent.wsagents[j];
if ((targetMeshes.indexOf(agent.dbMeshKey) >= 0) && (agent.authenticated == 2)) { if ((targetMeshes.indexOf(agent.dbMeshKey) >= 0) && (agent.authenticated == 2)) {
//console.log('Asking agent ' + agent.dbNodeKey + ' to wake ' + macs.join(',')); //console.log('Asking agent ' + agent.dbNodeKey + ' to wake ' + macs.join(','));
try { agent.send(JSON.stringify({ action: 'wakeonlan', macs: macs })); } catch (ex) { } try { agent.send(JSON.stringify({ action: 'wakeonlan', macs: macs })); } catch (ex) { }
@ -3363,6 +3363,39 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
} }
break; break;
} }
case 'runcommands':
{
if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
if (typeof command.type != 'number') break; // Check command type
if (typeof command.cmds != 'string') break; // Check commands
for (i in command.nodeids) {
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
// Check we have the rights to run commands on this device
if ((rights & MESHRIGHT_REMOTECONTROL) == 0) return;
// Get the agent and run the commands
var agent = parent.wsagents[node._id];
if ((agent != null) && (agent.authenticated == 2) && (agent.agentInfo != null)) {
// Check if this agent is correct for this command type
// command.type 1 = Windows Command, 2 = Windows PowerShell, 3 = Linux/BSD/macOS
var commandsOk = false;
if ((agent.agentInfo.agentId > 0) && (agent.agentInfo.agentId < 5)) {
// Windows Agent
if ((command.type == 1) || (command.type == 2)) { commandsOk = true; }
} else {
// Non-Windows Agent
if (command.type == 3) { commandsOk = true; }
}
if (commandsOk == true) {
try { agent.send(JSON.stringify({ action: 'runcommands', type: command.type, cmds: command.cmds })); } catch (ex) { }
}
}
});
}
break;
}
case 'uninstallagent': case 'uninstallagent':
{ {
if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's

View File

@ -30,6 +30,8 @@
"sample-config-advanced.json" "sample-config-advanced.json"
], ],
"dependencies": { "dependencies": {
"archiver": "^4.0.1",
"archiver-zip-encrypted": "^1.0.8",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"cbor": "^4.1.5", "cbor": "^4.1.5",
"compression": "^1.7.4", "compression": "^1.7.4",
@ -39,16 +41,32 @@
"express-handlebars": "^3.1.0", "express-handlebars": "^3.1.0",
"express-ws": "^4.0.0", "express-ws": "^4.0.0",
"html-minifier": "^4.0.0", "html-minifier": "^4.0.0",
"image-size": "^0.8.3",
"ipcheck": "^0.1.0", "ipcheck": "^0.1.0",
"jsdom": "^16.3.0", "jsdom": "^16.3.0",
"jwt-simple": "^0.5.6",
"minify-js": "0.0.4", "minify-js": "0.0.4",
"minimist": "^1.2.0", "minimist": "^1.2.0",
"mongodb": "^3.5.9",
"multiparty": "^4.2.1", "multiparty": "^4.2.1",
"nedb": "^1.8.0", "nedb": "^1.8.0",
"node-forge": "^0.8.4", "node-forge": "^0.8.4",
"node-rdpjs-2": "^0.3.5",
"node-windows": "^1.0.0-beta.1",
"otplib": "^10.2.3",
"passport": "^0.4.1",
"passport-azure-oauth2": "^0.1.0",
"passport-github2": "^0.1.12",
"passport-google-oauth20": "^2.0.0",
"passport-reddit": "^0.2.4",
"passport-twitter": "^1.0.4",
"promise": "^8.1.0",
"saslprep": "^1.0.3",
"twilio": "^3.48.0",
"ws": "^6.2.1", "ws": "^6.2.1",
"xmldom": "^0.1.27", "xmldom": "^0.1.27",
"yauzl": "^2.10.0" "yauzl": "^2.10.0",
"yubikeyotp": "^0.2.0"
}, },
"devDependencies": {}, "devDependencies": {},
"repository": { "repository": {

File diff suppressed because it is too large Load Diff

View File

@ -4233,7 +4233,7 @@
} }
var x = "Select an operation to perform on all selected devices. Actions will be performed only with proper rights." + '<br /><br />'; var x = "Select an operation to perform on all selected devices. Actions will be performed only with proper rights." + '<br /><br />';
x += addHtmlValue("Operation", '<select id=d2groupop><option value=100>' + "Wake-up devices" + '</option><option value=4>' + "Sleep devices" + '</option><option value=3>' + "Reset devices" + '</option><option value=2>' + "Power off devices" + '</option><option value=105>' + "Export device information" + '</option><option value=102>' + "Move to device group" + '</option>' + addedOptions + '<option value=101>' + "Delete devices" + '</option></select>'); x += addHtmlValue("Operation", '<select id=d2groupop><option value=100>' + "Wake-up devices" + '</option><option value=106>' + "Run commands" + '</option><option value=4>' + "Sleep devices" + '</option><option value=3>' + "Reset devices" + '</option><option value=2>' + "Power off devices" + '</option><option value=105>' + "Export device information" + '</option><option value=102>' + "Move to device group" + '</option>' + addedOptions + '<option value=101>' + "Delete devices" + '</option></select>');
setDialogMode(2, "Group Action", 3, groupActionFunctionEx, x); setDialogMode(2, "Group Action", 3, groupActionFunctionEx, x);
} }
@ -4273,6 +4273,26 @@
} else if (op == 105) { } else if (op == 105) {
// Export device information // Export device information
p2downloadDeviceInfo(); p2downloadDeviceInfo();
} else if (op == 106) {
// Run commands
var wintype = false, linuxtype = false, chkNodeIds = getCheckedDevices();
for (var i in chkNodeIds) {
var n = getNodeFromId(chkNodeIds[i]);
if (n.agent) { if ((n.agent.id > 0) && (n.agent.id < 5)) { wintype = true; } else { linuxtype = true; } }
}
if ((wintype == true) || (linuxtype == true)) {
var x = "Run commands on selected devices." + '<br /><br />';
if (wintype == true) {
x += '<select id=d2cmdtype style=width:100%>';
x += '<option value=1>' + "Windows Command Prompt" + '</option><option value=2>' + "Windows PowerShell" + '</option>';
if (linuxtype == true) { x += '<option value=3>' + "Linux/BSD/macOS Command Shell" + '</option>'; }
x += '</select>';
}
x += '<textarea id=d2runcmd style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll></textarea>';
setDialogMode(2, "Run Commands", 3, d2groupActionFunctionRunCommands, x);
Q('d2runcmd').focus();
//QE('idx_dlgOkButton', true);
}
} else { } else {
// Power operation // Power operation
meshserver.send({ action: 'poweraction', nodeids: getCheckedDevices(), actiontype: parseInt(op) }); meshserver.send({ action: 'poweraction', nodeids: getCheckedDevices(), actiontype: parseInt(op) });
@ -4309,6 +4329,11 @@
function d2groupActionFunctionDelCheck() { QE('idx_dlgOkButton', Q('d2check').checked); } function d2groupActionFunctionDelCheck() { QE('idx_dlgOkButton', Q('d2check').checked); }
function d2groupActionFunctionDelExec() { meshserver.send({ action: 'removedevices', nodeids: getCheckedDevices() }); uncheckAllDevices(); } function d2groupActionFunctionDelExec() { meshserver.send({ action: 'removedevices', nodeids: getCheckedDevices() }); uncheckAllDevices(); }
function d2groupActionFunctionRunCommands() {
var type = 3;
try { type = parseInt(Q('d2cmdtype').value); } catch (ex) { }
meshserver.send({ action: 'runcommands', nodeids: getCheckedDevices(), type: type, cmds: Q('d2runcmd').value }); uncheckAllDevices();
}
function onSortSelectChange(skipsave) { function onSortSelectChange(skipsave) {
sort = document.getElementById('sortselect').selectedIndex; sort = document.getElementById('sortselect').selectedIndex;
@ -5763,7 +5788,7 @@
var x = "Select an operation to perform on this device." + '<br /><br />'; var x = "Select an operation to perform on this device." + '<br /><br />';
var y = '<select id=d2deviceop style=float:right;width:250px>'; var y = '<select id=d2deviceop style=float:right;width:250px>';
if ((rights & 64) != 0) { y += '<option value=100>' + "Wake-up" + '</option>'; } // Wake-up permission if ((rights & 64) != 0) { y += '<option value=100>' + "Wake-up" + '</option>'; } // Wake-up permission
if ((rights & 8) != 0) { y += '<option value=4>' + "Sleep" + '</option><option value=3>' + "Reset" + '</option><option value=2>' + "Power off" + '</option>'; } // Remote control permission if ((rights & 8) != 0) { y += '<option value=106>' + "Run Commands" + '</option><option value=4>' + "Sleep" + '</option><option value=3>' + "Reset" + '</option><option value=2>' + "Power off" + '</option>'; } // Remote control permission
if ((currentNode.conn & 16) != 0) { y += '<option value=103>' + "Send MQTT Message" + '</option>'; } if ((currentNode.conn & 16) != 0) { y += '<option value=103>' + "Send MQTT Message" + '</option>'; }
if (((currentNode.conn & 1) != 0) && ((rights & 32768) != 0)) { y += '<option value=104>' + "Uninstall Agent" + '</option>'; } if (((currentNode.conn & 1) != 0) && ((rights & 32768) != 0)) { y += '<option value=104>' + "Uninstall Agent" + '</option>'; }
y += '</select>'; y += '</select>';
@ -5782,12 +5807,29 @@
} else if (op == 104) { } else if (op == 104) {
// Uninstall agent // Uninstall agent
p10showSendUninstallAgentDialog([currentNode._id]); p10showSendUninstallAgentDialog([currentNode._id]);
} else if (op == 106) {
// Run commands
var wintype = false, linuxtype = false;
if (currentNode.agent) { if ((currentNode.agent.id > 0) && (currentNode.agent.id < 5)) { wintype = true; } else { linuxtype = true; } }
if ((wintype == true) || (linuxtype == true)) {
var x = "Run commands on this device." + '<br /><br />';
if (wintype == true) { x += '<select id=d2cmdtype style=width:100%><option value=1>' + "Windows Command Prompt" + '</option><option value=2>' + "Windows PowerShell" + '</option></select>'; }
x += '<textarea id=d2runcmd style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll></textarea>';
setDialogMode(2, "Run Commands", 3, deviceRunCmdsFunctionEx, x);
Q('d2runcmd').focus();
}
} else { } else {
// Power operation // Power operation
meshserver.send({ action: 'poweraction', nodeids: [ currentNode._id ], actiontype: parseInt(op) }); meshserver.send({ action: 'poweraction', nodeids: [ currentNode._id ], actiontype: parseInt(op) });
} }
} }
function deviceRunCmdsFunctionEx() {
var cmdType = 3;
if ((currentNode.agent.id > 0) && (currentNode.agent.id < 5)) { cmdType = Q('d2cmdtype').value; }
meshserver.send({ action: 'runcommands', nodeids: [ currentNode._id ], type: parseInt(cmdType), cmds: Q('d2runcmd').value });
}
// Called when MeshCommander needs new credentials or updated credentials. // Called when MeshCommander needs new credentials or updated credentials.
function updateAmtCredentials(forceDialog) { function updateAmtCredentials(forceDialog) {
var node = getNodeFromId(currentNode._id); var node = getNodeFromId(currentNode._id);
@ -8043,7 +8085,6 @@
if (mode == 3) { eventList = currentUserEvents; } if (mode == 3) { eventList = currentUserEvents; }
for (var i in eventList) { if (eventList[i].h == h) { xevent = eventList[i]; break; } } for (var i in eventList) { if (eventList[i].h == h) { xevent = eventList[i]; break; } }
if (xevent) { if (xevent) {
console.log(xevent);
var x = '<div style=overflow-y:auto>'; var x = '<div style=overflow-y:auto>';
for (var i in xevent) { for (var i in xevent) {
if ((i == 'h') || (i == '_id') || (i == 'ids') || (i == 'domain') || (xevent[i] == null) || (typeof xevent[i] == 'object')) continue; if ((i == 'h') || (i == '_id') || (i == 'ids') || (i == 'domain') || (xevent[i] == null) || (typeof xevent[i] == 'object')) continue;