Added support for pre-defined scripts.

This commit is contained in:
Ylian Saint-Hilaire 2022-08-19 20:15:06 -07:00
parent d171d2af82
commit f6c3883735
4 changed files with 136 additions and 6 deletions

View File

@ -383,6 +383,39 @@
"expire": { "type": "number", "description": "When set, limits the self-created guest sharing link to this number of minutes." } "expire": { "type": "number", "description": "When set, limits the self-created guest sharing link to this number of minutes." }
} }
}, },
"PreconfiguredScripts": {
"type": "array",
"default": null,
"description": "When set, your can try click the run button to run on of these scripts on the remote device.",
"items": {
"type": "object",
"required": [ "name", "type" ],
"properties": {
"name": {
"description": "Name of the script.",
"type": "string"
},
"type": {
"description": "The type of script.",
"type": "string",
"enum": [ "bat", "ps1", "sh", "agent" ]
},
"runas": {
"description": "How to run this script, does not appy to agent scripts.",
"type": "string",
"enum": ["agent", "userfirst", "user"]
},
"cmd": {
"description": "The command or \\r\\n seperated commands to run, if set do not use the file key.",
"type": "string"
},
"file": {
"description": "The script file path and name, if set do not use the cmd key. This file path starts in meshcentral-data.",
"type": "string"
}
}
}
},
"preConfiguredRemoteInput": { "preConfiguredRemoteInput": {
"type": "array", "type": "array",
"default": null, "default": null,

View File

@ -593,6 +593,18 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if ((typeof domain.terminal.linuxshell == 'string') && (domain.terminal.linuxshell != 'any')) { serverinfo.linuxshell = domain.terminal.linuxshell; } if ((typeof domain.terminal.linuxshell == 'string') && (domain.terminal.linuxshell != 'any')) { serverinfo.linuxshell = domain.terminal.linuxshell; }
} }
if (Array.isArray(domain.preconfiguredremoteinput)) { serverinfo.preConfiguredRemoteInput = domain.preconfiguredremoteinput; } if (Array.isArray(domain.preconfiguredremoteinput)) { serverinfo.preConfiguredRemoteInput = domain.preconfiguredremoteinput; }
if (Array.isArray(domain.preconfiguredscripts)) {
const r = [];
for (var i in domain.preconfiguredscripts) {
const types = ['', 'bat', 'ps1', 'sh', 'agent']; // 1 = Windows Command, 2 = Windows PowerShell, 3 = Linux, 4 = Agent
const script = domain.preconfiguredscripts[i];
if ((typeof script.name == 'string') && (script.name.length <= 32) && (typeof script.type == 'string') && ((typeof script.file == 'string') || (typeof script.cmd == 'string'))) {
const s = { name: script.name, type: types.indexOf(script.type.toLowerCase()) };
if (s.type > 0) { r.push(s); }
}
}
serverinfo.preConfiguredScripts = r;
}
// Send server information // Send server information
try { ws.send(JSON.stringify({ action: 'serverinfo', serverinfo: serverinfo })); } catch (ex) { } try { ws.send(JSON.stringify({ action: 'serverinfo', serverinfo: serverinfo })); } catch (ex) { }
@ -2783,8 +2795,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
case 'runcommands': case 'runcommands':
{ {
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.presetcmd != 'number') {
if (typeof command.type != 'number') break; // Check command type if (typeof command.type != 'number') break; // Check command type
if (typeof command.runAsUser != 'number') { command.runAsUser = 0; } // Check runAsUser if (typeof command.runAsUser != 'number') { command.runAsUser = 0; } // Check runAsUser
}
const processRunCommand = function (command) { const processRunCommand = function (command) {
for (i in command.nodeids) { for (i in command.nodeids) {
@ -2873,7 +2887,39 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
} }
} }
if (typeof command.cmdpath == 'string') { if (typeof command.presetcmd == 'number') {
// If a pre-set command is used, load the command
if (Array.isArray(domain.preconfiguredscripts) == false) return;
const script = domain.preconfiguredscripts[command.presetcmd];
if (script == null) return;
delete command.presetcmd;
// Decode script type
const types = ['', 'bat', 'ps1', 'sh', 'agent']; // 1 = Windows Command, 2 = Windows PowerShell, 3 = Linux, 4 = Agent
if (typeof script.type == 'string') { const stype = types.indexOf(script.type.toLowerCase()); if (stype > 0) { command.type = stype; } }
if (command.type == null) return;
// Decode script runas
if (command.type != 4) {
const runAsModes = ['agent', 'userfirst', 'user']; // 0 = AsAgent, 1 = UserFirst, 2 = UserOnly
if (typeof script.runas == 'string') { const srunas = runAsModes.indexOf(script.runas.toLowerCase()); if (srunas >= 0) { command.runAsUser = srunas; } }
}
if (typeof script.file == 'string') {
// The pre-defined script commands are in a file, load it
const scriptPath = parent.common.joinPath(parent.parent.datapath, script.file);
fs.readFile(scriptPath, 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();
processRunCommand(command);
});
} else if (typeof script.cmd == 'string') {
// The pre-defined script commands are right in the config.json, use that
command.cmds = script.cmd;
processRunCommand(command);
}
} else if (typeof command.cmdpath == 'string') {
// If a server command path is used, load the script from the path // If a server command path is used, load the script from the path
var file = parent.getServerFilePath(user, domain, command.cmdpath); var file = parent.getServerFilePath(user, domain, command.cmdpath);
if (file != null) { if (file != null) {

View File

@ -243,6 +243,31 @@
} }
] ]
}, },
"PreconfiguredScripts": [
{
"name": "Run NotePad as user",
"file": "scripts/notepad.bat",
"type": "bat",
"runas": "user"
},
{
"name": "Run NotePad as agent",
"cmd": "notepad.exe",
"type": "bat",
"runas": "agent"
},
{
"name": "Run echo",
"cmd": "echo \"hello world\"",
"type": "sh",
"runas": "agent"
},
{
"name": "Agent Update",
"cmd": "agentupdate",
"type": "agent"
}
],
"PreconfiguredRemoteInput": [ "PreconfiguredRemoteInput": [
{ {
"name": "CompanyUrl", "name": "CompanyUrl",

View File

@ -138,7 +138,10 @@
<span id="deskPreConfigShortcutContextMenu2"></span> <span id="deskPreConfigShortcutContextMenu2"></span>
<div class="cmtext" onclick="cmdeskpreconfigtypeaction(-1,event)">Customize...</div> <div class="cmtext" onclick="cmdeskpreconfigtypeaction(-1,event)">Customize...</div>
</div> </div>
<div id="deskPreConfigScriptContextMenu" class="contextMenu noselect" style="display:none;min-width:0px;max-height:calc(80% - 50px);overflow-y:auto">
<span id="deskPreConfigScriptContextMenu1"></span>
<span id="deskPreConfigScriptContextMenu2"></span>
</div>
<!-- <!--
<div id="pluginTabContextMenu" class="contextMenu noselect" style="display:none;min-width:0px"> <div id="pluginTabContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
<div id="cxclose" class="cmtext" onclick="pluginTabClose(event)">Close Tab</div> <div id="cxclose" class="cmtext" onclick="pluginTabClose(event)">Close Tab</div>
@ -703,7 +706,7 @@
<span id="DeskTimer" style="line-height:22px" title="Session time"></span> <span id="DeskTimer" style="line-height:22px" title="Session time"></span>
<input id=DeskToolsButton type=button value=Tools title="Toggle tools view" onkeypress="return false" onkeydown="return false" onclick="toggleDeskTools()" /> <input id=DeskToolsButton type=button value=Tools title="Toggle tools view" onkeypress="return false" onkeydown="return false" onclick="toggleDeskTools()" />
<span>&nbsp;</span> <span>&nbsp;</span>
<span id=DeskRunButton class="deskarea" title="Run a script on this computer"><img class="desktopButtons" src='images/icon-play.png' onclick=runDeviceCmd() height=16 width=16 style=padding-top:2px /></span> <span id=DeskRunButton cmenu="deskPreConfigScriptContextMenu" class="deskarea" title="Run a script on this computer"><img class="desktopButtons" src='images/icon-play.png' onclick=runDeviceCmd() height=16 width=16 style=padding-top:2px /></span>
<span id=DeskChatButton class="deskarea" title="Open chat window to this computer"><img class="desktopButtons" src='images/icon-chat.png' onclick=deviceChat(event) height=16 width=16 style=padding-top:2px /></span> <span id=DeskChatButton class="deskarea" title="Open chat window to this computer"><img class="desktopButtons" src='images/icon-chat.png' onclick=deviceChat(event) height=16 width=16 style=padding-top:2px /></span>
<span id=DeskNotifyButton title="Display a notification on the remote computer"><img class="desktopButtons" src='images/icon-notify.png' onclick=deviceToastFunction() height=16 width=16 style=padding-top:2px /></span> <span id=DeskNotifyButton title="Display a notification on the remote computer"><img class="desktopButtons" src='images/icon-notify.png' onclick=deviceToastFunction() height=16 width=16 style=padding-top:2px /></span>
<span id=DeskLockButton title="Lock the remote computer"><img src='images/icon-lock.png' class="desktopButtons" onclick=deviceLockFunction() height=16 width=16 /></span> <span id=DeskLockButton title="Lock the remote computer"><img src='images/icon-lock.png' class="desktopButtons" onclick=deviceLockFunction() height=16 width=16 /></span>
@ -2373,11 +2376,24 @@
} }
} }
if (serverinfo.preConfiguredRemoteInput) { if (serverinfo.preConfiguredRemoteInput) {
// Setup the pre-configured keyboard remote input strings
var x = ''; var x = '';
for (var i in serverinfo.preConfiguredRemoteInput) { x += '<div class="cmtext" onclick="cmdeskpreconfigtypeaction(' + i + ',event)">' + EscapeHtml(serverinfo.preConfiguredRemoteInput[i].name) + '</div>'; } for (var i in serverinfo.preConfiguredRemoteInput) { x += '<div class="cmtext" onclick="cmdeskpreconfigtypeaction(' + i + ',event)">' + EscapeHtml(serverinfo.preConfiguredRemoteInput[i].name) + '</div>'; }
if (x != '') { x += '<hr />'; } if (x != '') { x += '<hr />'; }
QH('deskPreConfigShortcutContextMenu1', x); QH('deskPreConfigShortcutContextMenu1', x);
} }
if (serverinfo.preConfiguredScripts) {
// Setup the pre-configured scripts
var x = '', y = '';
for (var i in serverinfo.preConfiguredScripts) {
var s = serverinfo.preConfiguredScripts[i];
var z = '<div class="cmtext" onclick="cmdeskpreconfigscriptaction(' + i + ',event)">' + EscapeHtml(serverinfo.preConfiguredScripts[i].name) + '</div>';
if (s.type != 3) { x += z; }
if (s.type >= 3) { y += z; }
}
QH('deskPreConfigScriptContextMenu1', x); // Windows devices
QH('deskPreConfigScriptContextMenu2', y); // Other devices
}
break; break;
} }
case 'userinfo': { case 'userinfo': {
@ -6265,6 +6281,10 @@
else { showDeskTypeEx(deskKeyboardStrings[action - 1000].v); } // Type user pre-configured input string else { showDeskTypeEx(deskKeyboardStrings[action - 1000].v); } // Type user pre-configured input string
} }
function cmdeskpreconfigscriptaction(action) {
meshserver.send({ action: 'runcommands', nodeids: [ currentNode._id ], presetcmd: action });
}
function p13deletefileCm(b, file) { function p13deletefileCm(b, file) {
files.sendText({ action: 'rm', reqid: 1, path: p13filetreelocation.join('/'), delfiles: [ file.n ], rec: false }); files.sendText({ action: 'rm', reqid: 1, path: p13filetreelocation.join('/'), delfiles: [ file.n ], rec: false });
p13folderup(999); p13folderup(999);
@ -6300,6 +6320,7 @@
QV('deskPlayerContextMenu', false); QV('deskPlayerContextMenu', false);
QV('deskKeyShortcutContextMenu', false); QV('deskKeyShortcutContextMenu', false);
QV('deskPreConfigShortcutContextMenu', false); QV('deskPreConfigShortcutContextMenu', false);
QV('deskPreConfigScriptContextMenu', false);
QV('expandAllContextMenu', false); QV('expandAllContextMenu', false);
//QV('pluginTabContextMenu', false); //QV('pluginTabContextMenu', false);
contextelement = null; contextelement = null;
@ -6951,6 +6972,10 @@
if (!currentNode || currentNode._id != node._id || refresh == true) { if (!currentNode || currentNode._id != node._id || refresh == true) {
currentNode = node; currentNode = node;
// Pre defined scripts
QV('deskPreConfigScriptContextMenu1', (currentNode.mtype == 2) && isWindowsNode(currentNode)); // Windows devices
QV('deskPreConfigScriptContextMenu2', (currentNode.mtype == 2) && !isWindowsNode(currentNode)); // Other devices
// Device Notification // Device Notification
QV('p10deviceNotify', (currentNode.sessions != null) && ((currentNode.sessions.kvm != null) || (currentNode.sessions.terminal != null) || (currentNode.sessions.files != null) || (currentNode.sessions.tcp != null) || (currentNode.sessions.udp != null))); QV('p10deviceNotify', (currentNode.sessions != null) && ((currentNode.sessions.kvm != null) || (currentNode.sessions.terminal != null) || (currentNode.sessions.files != null) || (currentNode.sessions.tcp != null) || (currentNode.sessions.udp != null)));
QV('p10deviceStar', stars[currentNode._id] == 1); QV('p10deviceStar', stars[currentNode._id] == 1);
@ -7221,7 +7246,7 @@
if (((meshrights & (4 + 8 + 64 + 262144)) != 0) && (node.mtype < 3) && ((node.agent == null) || (node.agent.id != 34))) { x += '<input type=button value="' + "Actions" + '" title="' + "Perform power actions on the device" + '" onclick=deviceActionFunction() />'; } if (((meshrights & (4 + 8 + 64 + 262144)) != 0) && (node.mtype < 3) && ((node.agent == null) || (node.agent.id != 34))) { x += '<input type=button value="' + "Actions" + '" title="' + "Perform power actions on the device" + '" onclick=deviceActionFunction() />'; }
x += '<input type=button value="' + "Notes" + '" title="' + "View notes about this device" + '" onclick=showNotes(' + ((meshrights & 128) == 0) + ',"' + encodeURIComponentEx(node._id) + '") />'; x += '<input type=button value="' + "Notes" + '" title="' + "View notes about this device" + '" onclick=showNotes(' + ((meshrights & 128) == 0) + ',"' + encodeURIComponentEx(node._id) + '") />';
x += '<input type=button value="' + "Log Event" + '" title="' + "Write an event for this device" + '" onclick=writeDeviceEvent("' + encodeURIComponentEx(node._id) + '") />'; x += '<input type=button value="' + "Log Event" + '" title="' + "Write an event for this device" + '" onclick=writeDeviceEvent("' + encodeURIComponentEx(node._id) + '") />';
if ((node.mtype == 2) && (connectivity & 1) && ((meshrights & 131072) != 0)) { x += '<input type=button value="' + "Run" + '" title="' + "Run commands on this device" + '" onclick=runDeviceCmd("' + encodeURIComponentEx(node._id) + '") />'; } if ((node.mtype == 2) && (connectivity & 1) && ((meshrights & 131072) != 0)) { x += '<input type=button cmenu=deskPreConfigScriptContextMenu value="' + "Run" + '" title="' + "Run commands on this device" + '" onclick=runDeviceCmd("' + encodeURIComponentEx(node._id) + '") />'; }
if (node.mtype != 4) { if (node.mtype != 4) {
if ((meshrights & 8) && ((connectivity & 1) || ((node.pmt == 1) && ((features2 & 2) != 0)))) { x += '<input type=button value="' + "Message" + '" title="' + "Display a text message on the remote device" + '" onclick=deviceMessageFunction() />'; } if ((meshrights & 8) && ((connectivity & 1) || ((node.pmt == 1) && ((features2 & 2) != 0)))) { x += '<input type=button value="' + "Message" + '" title="' + "Display a text message on the remote device" + '" onclick=deviceMessageFunction() />'; }
//if ((connectivity & 1) && (meshrights & 8) && (node.agent.id < 5)) { x += '<input type=button value=Toast title="' + "Display a text message of the remote device" + '" onclick=deviceToastFunction() />'; } //if ((connectivity & 1) && (meshrights & 8) && (node.agent.id < 5)) { x += '<input type=button value=Toast title="' + "Display a text message of the remote device" + '" onclick=deviceToastFunction() />'; }
@ -17899,6 +17924,7 @@
} }
function round(value, precision) { var multiplier = Math.pow(10, precision || 0); return Math.round(value * multiplier) / multiplier; } function round(value, precision) { var multiplier = Math.pow(10, precision || 0); return Math.round(value * multiplier) / multiplier; }
function safeNewWindow(url, target) { var newWindow = window.open(url, target, 'noopener,noreferrer'); if (newWindow) { newWindow.opener = null; } } function safeNewWindow(url, target) { var newWindow = window.open(url, target, 'noopener,noreferrer'); if (newWindow) { newWindow.opener = null; } }
function isWindowsNode(node) { if ((node.mtype != 2) || (node.agent == null) || (node.agent.id == null)) return false; return ([1,2,3,4,21,22,34].indexOf(node.agent.id) >= 0); }
// Webkit seems to have a problem with "download" tag causing "network error", but openning the download in a hidden frame fixes it. // Webkit seems to have a problem with "download" tag causing "network error", but openning the download in a hidden frame fixes it.
// So we do that for all browsers except FireFox // So we do that for all browsers except FireFox