New command run dialog box can now run scripts that are stored on the server.

This commit is contained in:
Ylian Saint-Hilaire 2022-08-19 17:30:20 -07:00
parent ab84719afe
commit d171d2af82
3 changed files with 123 additions and 88 deletions

View File

@ -2784,92 +2784,111 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
{ {
if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
if (typeof command.type != 'number') break; // Check command type if (typeof command.type != 'number') break; // Check command type
if (typeof command.cmds != 'string') break; // Check commands
if (typeof command.runAsUser != 'number') { command.runAsUser = 0; } // Check runAsUser if (typeof command.runAsUser != 'number') { command.runAsUser = 0; } // Check runAsUser
for (i in command.nodeids) { const processRunCommand = function (command) {
var nodeid = command.nodeids[i], err = null; for (i in command.nodeids) {
var nodeid = command.nodeids[i], err = null;
// Argument validation // Argument validation
if (common.validateString(nodeid, 1, 1024) == false) { err = 'Invalid nodeid'; } // Check nodeid if (common.validateString(nodeid, 1, 1024) == false) { err = 'Invalid nodeid'; } // Check nodeid
else { else {
if (nodeid.indexOf('/') == -1) { nodeid = 'node/' + domain.id + '/' + nodeid; } if (nodeid.indexOf('/') == -1) { nodeid = 'node/' + domain.id + '/' + nodeid; }
if ((nodeid.split('/').length != 3) || (nodeid.split('/')[1] != domain.id)) { err = 'Invalid domain'; } // Invalid domain, operation only valid for current domain if ((nodeid.split('/').length != 3) || (nodeid.split('/')[1] != domain.id)) { err = 'Invalid domain'; } // Invalid domain, operation only valid for current domain
} }
if (err != null) { if (err != null) {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: err })); } catch (ex) { } } if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: err })); } catch (ex) { } }
continue; continue;
}
// Get the node and the rights for this node
parent.GetNodeWithRights(domain, user, nodeid, function (node, rights, visible) {
// Check if this node was found
if (node == null) {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Invalid nodeid' })); } catch (ex) { } }
return;
} }
if (command.type == 4) { // Get the node and the rights for this node
// This is an agent console command parent.GetNodeWithRights(domain, user, nodeid, function (node, rights, visible) {
// Check if this node was found
// Check we have the rights to run commands on this device, MESHRIGHT_REMOTECONTROL & MESHRIGHT_AGENTCONSOLE are needed if (node == null) {
if ((rights & 24) != 24) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Invalid nodeid' })); } catch (ex) { } }
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Access denied' })); } catch (ex) { } }
return; return;
} }
// Send the commands to the agent if (command.type == 4) {
var agent = parent.wsagents[node._id]; // This is an agent console command
if ((agent != null) && (agent.authenticated == 2) && (agent.agentInfo != null)) {
try { agent.send(JSON.stringify({ action: 'msg', type: 'console', value: command.cmds, rights: rights, sessionid: ws.sessionId })); } catch (ex) { }
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'OK' })); } catch (ex) { } }
} else {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Agent not connected' })); } catch (ex) { } }
}
} else {
// This is a standard (bash/shell/powershell) command.
// Check we have the rights to run commands on this device // Check we have the rights to run commands on this device, MESHRIGHT_REMOTECONTROL & MESHRIGHT_AGENTCONSOLE are needed
if ((rights & MESHRIGHT_REMOTECOMMAND) == 0) { if ((rights & 24) != 24) {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Access denied' })); } catch (ex) { } } if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Access denied' })); } catch (ex) { } }
return; 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 if (command.type === 0) { command.type = 1; commandsOk = true; } // Set the default type of this agent
} else {
// Non-Windows Agent
if (command.type == 3) { commandsOk = true; }
else if (command.type === 0) { command.type = 3; commandsOk = true; } // Set the default type of this agent
} }
if (commandsOk == true) {
// Send the commands to the agent // Send the commands to the agent
try { agent.send(JSON.stringify({ action: 'runcommands', type: command.type, cmds: command.cmds, runAsUser: command.runAsUser })); } catch (ex) { } var agent = parent.wsagents[node._id];
if ((agent != null) && (agent.authenticated == 2) && (agent.agentInfo != null)) {
try { agent.send(JSON.stringify({ action: 'msg', type: 'console', value: command.cmds, rights: rights, sessionid: ws.sessionId })); } catch (ex) { }
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'OK' })); } catch (ex) { } } if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'OK' })); } catch (ex) { } }
// Send out an event that these commands where run on this device
var targets = parent.CreateNodeDispatchTargets(node.meshid, node._id, ['server-users', user._id]);
var msgid = 24; // "Running commands"
if (command.type == 1) { msgid = 99; } // "Running commands as user"
if (command.type == 2) { msgid = 100; } // "Running commands as user if possible"
var event = { etype: 'node', userid: user._id, username: user.name, nodeid: node._id, action: 'runcommands', msg: 'Running commands', msgid: msgid, cmds: command.cmds, cmdType: command.type, runAsUser: command.runAsUser, domain: domain.id };
parent.parent.DispatchEvent(targets, obj, event);
} else { } else {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Invalid command type' })); } catch (ex) { } } if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Agent not connected' })); } catch (ex) { } }
} }
} else { } else {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Agent not connected' })); } catch (ex) { } } // This is a standard (bash/shell/powershell) command.
// Check we have the rights to run commands on this device
if ((rights & MESHRIGHT_REMOTECOMMAND) == 0) {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Access denied' })); } catch (ex) { } }
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 if (command.type === 0) { command.type = 1; commandsOk = true; } // Set the default type of this agent
} else {
// Non-Windows Agent
if (command.type == 3) { commandsOk = true; }
else if (command.type === 0) { command.type = 3; commandsOk = true; } // Set the default type of this agent
}
if (commandsOk == true) {
// Send the commands to the agent
try { agent.send(JSON.stringify({ action: 'runcommands', type: command.type, cmds: command.cmds, runAsUser: command.runAsUser })); } catch (ex) { }
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'OK' })); } catch (ex) { } }
// Send out an event that these commands where run on this device
var targets = parent.CreateNodeDispatchTargets(node.meshid, node._id, ['server-users', user._id]);
var msgid = 24; // "Running commands"
if (command.type == 1) { msgid = 99; } // "Running commands as user"
if (command.type == 2) { msgid = 100; } // "Running commands as user if possible"
var event = { etype: 'node', userid: user._id, username: user.name, nodeid: node._id, action: 'runcommands', msg: 'Running commands', msgid: msgid, cmds: command.cmds, cmdType: command.type, runAsUser: command.runAsUser, domain: domain.id };
parent.parent.DispatchEvent(targets, obj, event);
} else {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Invalid command type' })); } catch (ex) { } }
}
} else {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Agent not connected' })); } catch (ex) { } }
}
} }
} });
}); }
}
if (typeof command.cmdpath == 'string') {
// If a server command path is used, load the script from the path
var file = parent.getServerFilePath(user, domain, command.cmdpath);
if (file != null) {
fs.readFile(file.fullpath, function (err, data) {
// If loaded correctly, run loaded commands
if ((err != null) || (data == null) || (data.length == 0) || (data.length > 65535)) return;
command.cmds = data.toString();
delete command.cmdpath;
processRunCommand(command);
});
}
} else if (typeof command.cmds == 'string') {
// Run provided commands
if (command.cmds.length > 65535) return;
processRunCommand(command);
} }
break; break;
} }

View File

@ -591,14 +591,14 @@ body {
width: 260px; width: 260px;
} }
#d3serveraction { #d3serveraction, #d2serveraction {
width: 100%; width: 100%;
background-color: #d3d9d6; background-color: #d3d9d6;
text-align: left; text-align: left;
padding: 3px; padding: 3px;
} }
#d3serverfiles { #d3serverfiles, #d2serverfiles {
width: 100%; width: 100%;
height: 150px; height: 150px;
background-color: white; background-color: white;

View File

@ -1281,7 +1281,7 @@
</div> </div>
<div id=d3servermode> <div id=d3servermode>
<div id=d3serveraction valign=bottom> <div id=d3serveraction valign=bottom>
<input type=button id=p3FolderUp disabled="disabled" onclick=d3folderup() value="Up" />&nbsp; <input type=button id=p3FolderUp disabled="disabled" onclick=d3folderup() value="Up" />&nbsp;<span id=p3CurrentFolder></span>
</div> </div>
<div id=d3serverfiles></div> <div id=d3serverfiles></div>
</div> </div>
@ -5731,15 +5731,17 @@
x += '</select>'; x += '</select>';
x += '<select id=d2cmduser style=width:100%;margin-bottom:4px><option value=0' + ((runopt.runAs == 0)?' selected':'') + '>' + "Run as agent" + '</option><option value=1' + ((runopt.runAs == 1)?' selected':'') + '>' + "Run as user, agent if no user" + '</option><option value=2' + ((runopt.runAs == 2)?' selected':'') + '>' + "Must run as user" + '</option></select>'; x += '<select id=d2cmduser style=width:100%;margin-bottom:4px><option value=0' + ((runopt.runAs == 0)?' selected':'') + '>' + "Run as agent" + '</option><option value=1' + ((runopt.runAs == 1)?' selected':'') + '>' + "Run as user, agent if no user" + '</option><option value=2' + ((runopt.runAs == 2)?' selected':'') + '>' + "Must run as user" + '</option></select>';
x += '<select id=d2cmdsource onclick=d2runCommandValidate() style=width:100%;margin-bottom:4px><option value=0' + ((runopt.source == 0)?' selected':'') + '>' + "Commands from text box" + '</option><option value=1' + ((runopt.source == 1)?' selected':'') + '>' + "Commands from file" + '</option>'; x += '<select id=d2cmdsource onclick=d2runCommandValidate() style=width:100%;margin-bottom:4px><option value=0' + ((runopt.source == 0)?' selected':'') + '>' + "Commands from text box" + '</option><option value=1' + ((runopt.source == 1)?' selected':'') + '>' + "Commands from file" + '</option>';
//if (userinfo.siteadmin & 8) { x += '<option value=2' + ((runopt.source == 2)?' selected':'') + '>' + "Commands from file on server" + '</option>'; } if (userinfo.siteadmin & 8) { x += '<option value=2' + ((runopt.source == 2)?' selected':'') + '>' + "Commands from file on server" + '</option>'; }
x += '</select><textarea id=d2runcmd onkeyup=d2runCommandValidate() style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll>' + (runopt.cmd ? EscapeHtml(decodeURIComponent(runopt.cmd)) : '') + '</textarea>'; x += '</select><textarea id=d2runcmd onkeyup=d2runCommandValidate() style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll>' + (runopt.cmd ? EscapeHtml(decodeURIComponent(runopt.cmd)) : '') + '</textarea>';
x += '<div id=d2runfile style=display:none><input id=d2runfileex type=file onchange=d2runCommandValidate() id=d2localFile name=files onchange=d2runCommandValidate() /></div>'; x += '<div id=d2runfile style=display:none><input id=d2runfileex type=file onchange=d2runCommandValidate() id=d2localFile name=files onchange=d2runCommandValidate() /></div>';
x += '<div id=d2runsfile style=display:none>bb</div>'; if (userinfo.siteadmin & 8) { x += '<div id=d2runsfile style=display:none><div id=d2serveraction valign=bottom><input type=button id=p2FolderUp disabled="disabled" onclick=d3folderup() value="Up" />&nbsp;<span id=p2CurrentFolder></span></div><div id=d2serverfiles></div></div>'; }
setDialogMode(2, "Run Commands", 3, d2groupActionFunctionRunCommands, x, options); setDialogMode(2, "Run Commands", 3, d2groupActionFunctionRunCommands, x, options);
Q('d2runcmd').focus(); Q('d2runcmd').focus();
d2runCommandValidate(); d2runCommandValidate();
if (userinfo.siteadmin & 8) { d3fileoptions = { dialog: 2, files: 'd2serverfiles', folderup: 'p2FolderUp', currentFolder: 'p2CurrentFolder', func: null }; d3updatefiles(); } // Update the server files
} }
} }
function d2runCommandValidate() { function d2runCommandValidate() {
QV('d2cmduser', Q('d2cmdtype').value < 4); QV('d2cmduser', Q('d2cmdtype').value < 4);
QV('d2runcmd', Q('d2cmdsource').value == 0); QV('d2runcmd', Q('d2cmdsource').value == 0);
@ -5751,6 +5753,7 @@
if (Q('d2cmdsource').value == 2) { ok = false; } // From server file if (Q('d2cmdsource').value == 2) { ok = false; } // From server file
QE('idx_dlgOkButton', ok); QE('idx_dlgOkButton', ok);
} }
function d2groupActionFunctionRunCommands(b, options) { function d2groupActionFunctionRunCommands(b, options) {
var type = 3; var type = 3;
try { type = parseInt(Q('d2cmdtype').value); } catch (ex) { } try { type = parseInt(Q('d2cmdtype').value); } catch (ex) { }
@ -5770,7 +5773,11 @@
} }
if (Q('d2cmdsource').value == 2) { if (Q('d2cmdsource').value == 2) {
// From server file // From server file
cmd.cmds = ''; var files = d3getFileSel();
if (files.length != 1) return;
cmd.cmdpath = d3filetreelocation.join('/') + '/' + files[0];
meshserver.send(cmd);
if (options.func) { options.func(); }
} }
} }
@ -16115,6 +16122,7 @@
// //
function d3init() { function d3init() {
d3fileoptions = { dialog: 1, filter: 'd3filter', files: 'd3serverfiles', folderup: 'p3FolderUp', currentFolder: 'p3CurrentFolder', func: d3setActions };
Q('d3localFile').value = ''; Q('d3localFile').value = '';
Q('d3localFile').accept = Q('d3filter').value; Q('d3localFile').accept = Q('d3filter').value;
d3modechange(); d3modechange();
@ -16129,10 +16137,11 @@
var d3filetreelinkpath; var d3filetreelinkpath;
var d3filetreelocation = []; var d3filetreelocation = [];
var d3fileoptions = null;
function d3updatefiles() { function d3updatefiles() {
if (Q('d3uploadMode').value == 1) return; if (d3fileoptions == null) return;
var html1 = '', html2 = '', filetreex = filetree, folderdepth = 1, publicPath = null; if ((d3fileoptions.filter == 'd3filter') && (Q('d3uploadMode').value == 1)) return;
var html1 = '', html2 = '', filetreex = filetree, folderdepth = 1, publicPath = null, lastFolderName = '';
// Navigate to path location, build the paths at the same time // Navigate to path location, build the paths at the same time
var d3filetreelocation2 = [], oldlinkpath = d3filetreelinkpath, checkedBoxes = [], checkboxes = document.getElementsByName('fc'); var d3filetreelocation2 = [], oldlinkpath = d3filetreelinkpath, checkedBoxes = [], checkboxes = document.getElementsByName('fc');
@ -16150,6 +16159,7 @@
if (d3filetreelinkpath != '') { d3filetreelinkpath += '/' + d3filetreelocation[i]; if (folderdepth > 2) { publicPath += '/' + d3filetreelocation[i]; } } if (d3filetreelinkpath != '') { d3filetreelinkpath += '/' + d3filetreelocation[i]; if (folderdepth > 2) { publicPath += '/' + d3filetreelocation[i]; } }
} }
filetreex = filetreex.f[d3filetreelocation[i]]; filetreex = filetreex.f[d3filetreelocation[i]];
lastFolderName = filetreex.n;
folderdepth++; folderdepth++;
} else { } else {
break; break;
@ -16161,7 +16171,8 @@
var filetreexx = p5sort_files(filetreex.f); var filetreexx = p5sort_files(filetreex.f);
// File filter // File filter
var fileFilter = Q('d3filter').value var fileFilter = '';
if (d3fileoptions.filter) { fileFilter = Q(d3fileoptions.filter).value };
// Display all files and folders at this location // Display all files and folders at this location
for (var i in filetreexx) { for (var i in filetreexx) {
@ -16189,19 +16200,24 @@
if (f.t < 3) { html1 += h; } else { html2 += h; } if (f.t < 3) { html1 += h; } else { html2 += h; }
} }
QH('d3serverfiles', html1 + html2); if (d3fileoptions.currentFolder) { QH(d3fileoptions.currentFolder, lastFolderName); }
QE('p3FolderUp', d3filetreelocation.length > 0); QH(d3fileoptions.files, html1 + html2);
d3setActions(); QE(d3fileoptions.folderup, d3filetreelocation.length > 0);
if (d3fileoptions.func) { d3fileoptions.func(); }
} }
function d3folderset(x) { d3filetreelocation.push(decodeURIComponent(x)); d3updatefiles(); return false; } function d3folderset(x) { d3filetreelocation.push(decodeURIComponent(x)); d3updatefiles(); return false; }
function d3folderup(x) { if (x == null) { d3filetreelocation.pop(); } else { while (d3filetreelocation.length > x) { d3filetreelocation.pop(); } } d3updatefiles(); } function d3folderup(x) { if (x == null) { d3filetreelocation.pop(); } else { while (d3filetreelocation.length > x) { d3filetreelocation.pop(); } } d3updatefiles(); }
function d3getFileSel() { var cc = []; var checkboxes = document.getElementsByName('fcx'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { cc.push(checkboxes[i].value) } } return cc; } function d3getFileSel() { var cc = []; var checkboxes = document.getElementsByName('fcx'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { cc.push(checkboxes[i].value) } } return cc; }
function d3setActions() { function d3setActions() {
var mode = Q('d3uploadMode').value; if (d3fileoptions.dialog == 1) {
if (mode == 1) { var mode = Q('d3uploadMode').value;
QE('idx_dlgOkButton', Q('d3localFile').value.length > 0); if (mode == 1) {
} else { QE('idx_dlgOkButton', Q('d3localFile').value.length > 0);
} else {
QE('idx_dlgOkButton', d3getFileSel().length == 1);
}
} else if (d3fileoptions.dialog == 2) {
QE('idx_dlgOkButton', d3getFileSel().length == 1); QE('idx_dlgOkButton', d3getFileSel().length == 1);
} }
} }