New command run dialog box can now run scripts that are stored on the server.
Ylian Saint-Hilaire committed
Aug 19, 2022 at 17:30 UTC
d171d2af8241cd5f2d9a71154f007ce6ee9f2277
3 files changed
+122
-87
meshuser.js
+89
-70
@@ -2784,92 +2784,111 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2784
{
2785
if (common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
2786
if (typeof command.type != 'number') break; // Check command type
2787
- if (typeof command.cmds != 'string') break; // Check commands
2787
if (typeof command.runAsUser != 'number') { command.runAsUser = 0; } // Check runAsUser
2788
2790
- for (i in command.nodeids) {
2791
- var nodeid = command.nodeids[i], err = null;
2789
+ const processRunCommand = function (command) {
2790
+ for (i in command.nodeids) {
2791
+ var nodeid = command.nodeids[i], err = null;
2792
2793
- // Argument validation
2794
- if (common.validateString(nodeid, 1, 1024) == false) { err = 'Invalid nodeid'; } // Check nodeid
2795
- else {
2796
- if (nodeid.indexOf('/') == -1) { nodeid = 'node/' + domain.id + '/' + nodeid; }
2797
- if ((nodeid.split('/').length != 3) || (nodeid.split('/')[1] != domain.id)) { err = 'Invalid domain'; } // Invalid domain, operation only valid for current domain
2798
- }
2799
- if (err != null) {
2800
- if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: err })); } catch (ex) { } }
2801
- continue;
2802
- }
2803
-
2804
- // Get the node and the rights for this node
2805
- parent.GetNodeWithRights(domain, user, nodeid, function (node, rights, visible) {
2806
- // Check if this node was found
2807
- if (node == null) {
2808
- if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Invalid nodeid' })); } catch (ex) { } }
2809
- return;
2793
+ // Argument validation
2794
+ if (common.validateString(nodeid, 1, 1024) == false) { err = 'Invalid nodeid'; } // Check nodeid
2795
+ else {
2796
+ if (nodeid.indexOf('/') == -1) { nodeid = 'node/' + domain.id + '/' + nodeid; }
2797
+ if ((nodeid.split('/').length != 3) || (nodeid.split('/')[1] != domain.id)) { err = 'Invalid domain'; } // Invalid domain, operation only valid for current domain
2798
+ }
2799
+ if (err != null) {
2800
+ if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: err })); } catch (ex) { } }
2801
+ continue;
2802
}
2803
2812
- if (command.type == 4) {
2813
- // This is an agent console command
2814
-
2815
- // Check we have the rights to run commands on this device, MESHRIGHT_REMOTECONTROL & MESHRIGHT_AGENTCONSOLE are needed
2816
- if ((rights & 24) != 24) {
2817
- if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Access denied' })); } catch (ex) { } }
2804
+ // Get the node and the rights for this node
2805
+ parent.GetNodeWithRights(domain, user, nodeid, function (node, rights, visible) {
2806
+ // Check if this node was found
2807
+ if (node == null) {
2808
+ if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Invalid nodeid' })); } catch (ex) { } }
2809
return;
2810
}
2811
2821
- // Send the commands to the agent
2822
- var agent = parent.wsagents[node._id];
2823
- if ((agent != null) && (agent.authenticated == 2) && (agent.agentInfo != null)) {
2824
- try { agent.send(JSON.stringify({ action: 'msg', type: 'console', value: command.cmds, rights: rights, sessionid: ws.sessionId })); } catch (ex) { }
2825
- if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'OK' })); } catch (ex) { } }
2826
- } else {
2827
- if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Agent not connected' })); } catch (ex) { } }
2828
- }
2829
- } else {
2830
- // This is a standard (bash/shell/powershell) command.
2812
+ if (command.type == 4) {
2813
+ // This is an agent console command
2814
2832
- // Check we have the rights to run commands on this device
2833
- if ((rights & MESHRIGHT_REMOTECOMMAND) == 0) {
2834
- if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Access denied' })); } catch (ex) { } }
2835
- return;
2836
- }
2815
+ // Check we have the rights to run commands on this device, MESHRIGHT_REMOTECONTROL & MESHRIGHT_AGENTCONSOLE are needed
2816
+ if ((rights & 24) != 24) {
2817
+ if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Access denied' })); } catch (ex) { } }
2818
+ return;
2819
+ }
2820
2838
- // Get the agent and run the commands
2839
- var agent = parent.wsagents[node._id];
2840
- if ((agent != null) && (agent.authenticated == 2) && (agent.agentInfo != null)) {
2841
- // Check if this agent is correct for this command type
2842
- // command.type 1 = Windows Command, 2 = Windows PowerShell, 3 = Linux/BSD/macOS
2843
- var commandsOk = false;
2844
- if ((agent.agentInfo.agentId > 0) && (agent.agentInfo.agentId < 5)) {
2845
- // Windows Agent
2846
- if ((command.type == 1) || (command.type == 2)) { commandsOk = true; }
2847
- else if (command.type === 0) { command.type = 1; commandsOk = true; } // Set the default type of this agent
2821
+ // Send the commands to the agent
2822
+ var agent = parent.wsagents[node._id];
2823
+ if ((agent != null) && (agent.authenticated == 2) && (agent.agentInfo != null)) {
2824
+ try { agent.send(JSON.stringify({ action: 'msg', type: 'console', value: command.cmds, rights: rights, sessionid: ws.sessionId })); } catch (ex) { }
2825
+ if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'OK' })); } catch (ex) { } }
2826
} else {
2849
- // Non-Windows Agent
2850
- if (command.type == 3) { commandsOk = true; }
2851
- else if (command.type === 0) { command.type = 3; commandsOk = true; } // Set the default type of this agent
2827
+ if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Agent not connected' })); } catch (ex) { } }
2828
}
2853
- if (commandsOk == true) {
2854
- // Send the commands to the agent
2855
- try { agent.send(JSON.stringify({ action: 'runcommands', type: command.type, cmds: command.cmds, runAsUser: command.runAsUser })); } catch (ex) { }
2856
- if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'OK' })); } catch (ex) { } }
2829
+ } else {
2830
+ // This is a standard (bash/shell/powershell) command.
2831
2858
- // Send out an event that these commands where run on this device
2859
- var targets = parent.CreateNodeDispatchTargets(node.meshid, node._id, ['server-users', user._id]);
2860
- var msgid = 24; // "Running commands"
2861
- if (command.type == 1) { msgid = 99; } // "Running commands as user"
2862
- if (command.type == 2) { msgid = 100; } // "Running commands as user if possible"
2863
- 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 };
2864
- parent.parent.DispatchEvent(targets, obj, event);
2832
+ // Check we have the rights to run commands on this device
2833
+ if ((rights & MESHRIGHT_REMOTECOMMAND) == 0) {
2834
+ if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Access denied' })); } catch (ex) { } }
2835
+ return;
2836
+ }
2837
+
2838
+ // Get the agent and run the commands
2839
+ var agent = parent.wsagents[node._id];
2840
+ if ((agent != null) && (agent.authenticated == 2) && (agent.agentInfo != null)) {
2841
+ // Check if this agent is correct for this command type
2842
+ // command.type 1 = Windows Command, 2 = Windows PowerShell, 3 = Linux/BSD/macOS
2843
+ var commandsOk = false;
2844
+ if ((agent.agentInfo.agentId > 0) && (agent.agentInfo.agentId < 5)) {
2845
+ // Windows Agent
2846
+ if ((command.type == 1) || (command.type == 2)) { commandsOk = true; }
2847
+ else if (command.type === 0) { command.type = 1; commandsOk = true; } // Set the default type of this agent
2848
+ } else {
2849
+ // Non-Windows Agent
2850
+ if (command.type == 3) { commandsOk = true; }
2851
+ else if (command.type === 0) { command.type = 3; commandsOk = true; } // Set the default type of this agent
2852
+ }
2853
+ if (commandsOk == true) {
2854
+ // Send the commands to the agent
2855
+ try { agent.send(JSON.stringify({ action: 'runcommands', type: command.type, cmds: command.cmds, runAsUser: command.runAsUser })); } catch (ex) { }
2856
+ if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'OK' })); } catch (ex) { } }
2857
+
2858
+ // Send out an event that these commands where run on this device
2859
+ var targets = parent.CreateNodeDispatchTargets(node.meshid, node._id, ['server-users', user._id]);
2860
+ var msgid = 24; // "Running commands"
2861
+ if (command.type == 1) { msgid = 99; } // "Running commands as user"
2862
+ if (command.type == 2) { msgid = 100; } // "Running commands as user if possible"
2863
+ 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 };
2864
+ parent.parent.DispatchEvent(targets, obj, event);
2865
+ } else {
2866
+ if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Invalid command type' })); } catch (ex) { } }
2867
+ }
2868
} else {
2866
- if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Invalid command type' })); } catch (ex) { } }
2869
+ if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Agent not connected' })); } catch (ex) { } }
2870
}
2868
- } else {
2869
- if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Agent not connected' })); } catch (ex) { } }
2871
}
2871
- }
2872
- });
2872
+ });
2873
+ }
2874
+ }
2875
+
2876
+ if (typeof command.cmdpath == 'string') {
2877
+ // If a server command path is used, load the script from the path
2878
+ var file = parent.getServerFilePath(user, domain, command.cmdpath);
2879
+ if (file != null) {
2880
+ fs.readFile(file.fullpath, function (err, data) {
2881
+ // If loaded correctly, run loaded commands
2882
+ if ((err != null) || (data == null) || (data.length == 0) || (data.length > 65535)) return;
2883
+ command.cmds = data.toString();
2884
+ delete command.cmdpath;
2885
+ processRunCommand(command);
2886
+ });
2887
+ }
2888
+ } else if (typeof command.cmds == 'string') {
2889
+ // Run provided commands
2890
+ if (command.cmds.length > 65535) return;
2891
+ processRunCommand(command);
2892
}
2893
break;
2894
}
public/styles/style.css
+2
-2
@@ -591,14 +591,14 @@ body {
591
width: 260px;
592
}
593
594
-#d3serveraction {
594
+#d3serveraction, #d2serveraction {
595
width: 100%;
596
background-color: #d3d9d6;
597
text-align: left;
598
padding: 3px;
599
}
600
601
-#d3serverfiles {
601
+#d3serverfiles, #d2serverfiles {
602
width: 100%;
603
height: 150px;
604
background-color: white;
views/default.handlebars
+31
-15
@@ -1281,7 +1281,7 @@
1281
</div>
1282
<div id=d3servermode>
1283
<div id=d3serveraction valign=bottom>
1284
- <input type=button id=p3FolderUp disabled="disabled" onclick=d3folderup() value="Up" />
1284
+ <input type=button id=p3FolderUp disabled="disabled" onclick=d3folderup() value="Up" /> <span id=p3CurrentFolder></span>
1285
</div>
1286
<div id=d3serverfiles></div>
1287
</div>
@@ -5731,15 +5731,17 @@
5731
x += '</select>';
5732
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>';
5733
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>';
5734
- //if (userinfo.siteadmin & 8) { x += '<option value=2' + ((runopt.source == 2)?' selected':'') + '>' + "Commands from file on server" + '</option>'; }
5734
+ if (userinfo.siteadmin & 8) { x += '<option value=2' + ((runopt.source == 2)?' selected':'') + '>' + "Commands from file on server" + '</option>'; }
5735
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>';
5736
x += '<div id=d2runfile style=display:none><input id=d2runfileex type=file onchange=d2runCommandValidate() id=d2localFile name=files onchange=d2runCommandValidate() /></div>';
5737
- x += '<div id=d2runsfile style=display:none>bb</div>';
5737
+ 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" /> <span id=p2CurrentFolder></span></div><div id=d2serverfiles></div></div>'; }
5738
setDialogMode(2, "Run Commands", 3, d2groupActionFunctionRunCommands, x, options);
5739
Q('d2runcmd').focus();
5740
d2runCommandValidate();
5741
+ if (userinfo.siteadmin & 8) { d3fileoptions = { dialog: 2, files: 'd2serverfiles', folderup: 'p2FolderUp', currentFolder: 'p2CurrentFolder', func: null }; d3updatefiles(); } // Update the server files
5742
}
5743
}
5744
+
5745
function d2runCommandValidate() {
5746
QV('d2cmduser', Q('d2cmdtype').value < 4);
5747
QV('d2runcmd', Q('d2cmdsource').value == 0);
@@ -5751,6 +5753,7 @@
5753
if (Q('d2cmdsource').value == 2) { ok = false; } // From server file
5754
QE('idx_dlgOkButton', ok);
5755
}
5756
+
5757
function d2groupActionFunctionRunCommands(b, options) {
5758
var type = 3;
5759
try { type = parseInt(Q('d2cmdtype').value); } catch (ex) { }
@@ -5770,7 +5773,11 @@
5773
}
5774
if (Q('d2cmdsource').value == 2) {
5775
// From server file
5773
- cmd.cmds = '';
5776
+ var files = d3getFileSel();
5777
+ if (files.length != 1) return;
5778
+ cmd.cmdpath = d3filetreelocation.join('/') + '/' + files[0];
5779
+ meshserver.send(cmd);
5780
+ if (options.func) { options.func(); }
5781
}
5782
}
5783
@@ -16115,6 +16122,7 @@
16122
//
16123
16124
function d3init() {
16125
+ d3fileoptions = { dialog: 1, filter: 'd3filter', files: 'd3serverfiles', folderup: 'p3FolderUp', currentFolder: 'p3CurrentFolder', func: d3setActions };
16126
Q('d3localFile').value = '';
16127
Q('d3localFile').accept = Q('d3filter').value;
16128
d3modechange();
@@ -16129,10 +16137,11 @@
16137
16138
var d3filetreelinkpath;
16139
var d3filetreelocation = [];
16132
-
16140
+ var d3fileoptions = null;
16141
function d3updatefiles() {
16134
- if (Q('d3uploadMode').value == 1) return;
16135
- var html1 = '', html2 = '', filetreex = filetree, folderdepth = 1, publicPath = null;
16142
+ if (d3fileoptions == null) return;
16143
+ if ((d3fileoptions.filter == 'd3filter') && (Q('d3uploadMode').value == 1)) return;
16144
+ var html1 = '', html2 = '', filetreex = filetree, folderdepth = 1, publicPath = null, lastFolderName = '';
16145
16146
// Navigate to path location, build the paths at the same time
16147
var d3filetreelocation2 = [], oldlinkpath = d3filetreelinkpath, checkedBoxes = [], checkboxes = document.getElementsByName('fc');
@@ -16150,6 +16159,7 @@
16159
if (d3filetreelinkpath != '') { d3filetreelinkpath += '/' + d3filetreelocation[i]; if (folderdepth > 2) { publicPath += '/' + d3filetreelocation[i]; } }
16160
}
16161
filetreex = filetreex.f[d3filetreelocation[i]];
16162
+ lastFolderName = filetreex.n;
16163
folderdepth++;
16164
} else {
16165
break;
@@ -16161,7 +16171,8 @@
16171
var filetreexx = p5sort_files(filetreex.f);
16172
16173
// File filter
16164
- var fileFilter = Q('d3filter').value
16174
+ var fileFilter = '';
16175
+ if (d3fileoptions.filter) { fileFilter = Q(d3fileoptions.filter).value };
16176
16177
// Display all files and folders at this location
16178
for (var i in filetreexx) {
@@ -16189,19 +16200,24 @@
16200
if (f.t < 3) { html1 += h; } else { html2 += h; }
16201
}
16202
16192
- QH('d3serverfiles', html1 + html2);
16193
- QE('p3FolderUp', d3filetreelocation.length > 0);
16194
- d3setActions();
16203
+ if (d3fileoptions.currentFolder) { QH(d3fileoptions.currentFolder, lastFolderName); }
16204
+ QH(d3fileoptions.files, html1 + html2);
16205
+ QE(d3fileoptions.folderup, d3filetreelocation.length > 0);
16206
+ if (d3fileoptions.func) { d3fileoptions.func(); }
16207
}
16208
16209
function d3folderset(x) { d3filetreelocation.push(decodeURIComponent(x)); d3updatefiles(); return false; }
16210
function d3folderup(x) { if (x == null) { d3filetreelocation.pop(); } else { while (d3filetreelocation.length > x) { d3filetreelocation.pop(); } } d3updatefiles(); }
16211
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; }
16212
function d3setActions() {
16201
- var mode = Q('d3uploadMode').value;
16202
- if (mode == 1) {
16203
- QE('idx_dlgOkButton', Q('d3localFile').value.length > 0);
16204
- } else {
16213
+ if (d3fileoptions.dialog == 1) {
16214
+ var mode = Q('d3uploadMode').value;
16215
+ if (mode == 1) {
16216
+ QE('idx_dlgOkButton', Q('d3localFile').value.length > 0);
16217
+ } else {
16218
+ QE('idx_dlgOkButton', d3getFileSel().length == 1);
16219
+ }
16220
+ } else if (d3fileoptions.dialog == 2) {
16221
QE('idx_dlgOkButton', d3getFileSel().length == 1);
16222
}
16223
}