diff --git a/apprelays.js b/apprelays.js index a70f8500..5c90564d 100644 --- a/apprelays.js +++ b/apprelays.js @@ -215,28 +215,33 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) { } } - // Save SSH credentials into device + // Save RDP credentials into database function saveRdpCredentials() { if (domain.allowsavingdevicecredentials == false) return; parent.parent.db.Get(obj.nodeid, function (err, nodes) { if ((err != null) || (nodes == null) || (nodes.length != 1)) return; const node = nodes[0]; - const changed = (node.rdp == null); + if (node.rdp == null) { node.rdp = {}; } - // Check if credentials are the same - if ((typeof node.rdp == 'object') && (node.rdp.d == obj.infos.domain) && (node.rdp.u == obj.infos.username) && (node.rdp.p == obj.infos.password)) return; + // Check if credentials are already set + if ((typeof node.rdp[obj.userid] == 'object') && (node.rdp[obj.userid].d == obj.infos.domain) && (node.rdp[obj.userid].u == obj.infos.username) && (node.rdp[obj.userid].p == obj.infos.password)) return; + + // Clear up any existing credentials or credentials for users that don't exist anymore + for (var i in node.rdp) { if (!i.startsWith('user/') || (parent.users[i] == null)) { delete node.rdp[i]; } } + + // Clear legacy credentials + delete node.rdp.d; + delete node.rdp.u; + delete node.rdp.p; // Save the credentials - node.rdp = { d: obj.infos.domain, u: obj.infos.username, p: obj.infos.password }; + node.rdp[obj.userid] = { d: obj.infos.domain, u: obj.infos.username, p: obj.infos.password }; parent.parent.db.Set(node); - // Event node change if needed - if (changed) { - // Event the node change - const event = { etype: 'node', action: 'changenode', nodeid: obj.nodeid, domain: domain.id, userid: obj.userid, node: parent.CloneSafeNode(node), msg: "Changed RDP credentials" }; - if (parent.parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. - parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(node.meshid, [obj.nodeid]), obj, event); - } + // Event the node change + const event = { etype: 'node', action: 'changenode', nodeid: obj.nodeid, domain: domain.id, userid: obj.userid, node: parent.CloneSafeNode(node), msg: "Changed RDP credentials" }; + if (parent.parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. + parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(node.meshid, [obj.nodeid]), obj, event); }); } @@ -299,10 +304,10 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) { // Check if we need to load server stored credentials if ((typeof obj.infos.options == 'object') && (obj.infos.options.useServerCreds == true)) { // Check if RDP credentials exist - if ((domain.allowsavingdevicecredentials !== false) && (typeof node.rdp == 'object') && (typeof node.rdp.d == 'string') && (typeof node.rdp.u == 'string') && (typeof node.rdp.p == 'string')) { - obj.infos.domain = node.rdp.d; - obj.infos.username = node.rdp.u; - obj.infos.password = node.rdp.p; + if ((domain.allowsavingdevicecredentials !== false) && (typeof node.rdp == 'object') && (typeof node.rdp[obj.userid] == 'object') && (typeof node.rdp[obj.userid].d == 'string') && (typeof node.rdp[obj.userid].u == 'string') && (typeof node.rdp[obj.userid].p == 'string')) { + obj.infos.domain = node.rdp[obj.userid].d; + obj.infos.username = node.rdp[obj.userid].u; + obj.infos.password = node.rdp[obj.userid].p; startTcpServer(); } else { // No server credentials. @@ -382,7 +387,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) { module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) { const Net = require('net'); const WebSocket = require('ws'); - + // SerialTunnel object is used to embed SSH within another connection. function SerialTunnel(options) { const obj = new require('stream').Duplex(options); @@ -447,36 +452,43 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) { delete obj.cookie; delete obj.nodeid; delete obj.meshid; + delete obj.userid; delete obj.ws; }; - // Save SSH credentials into device + // Save SSH credentials into database function saveSshCredentials(keep) { if (((keep != 1) && (keep != 2)) || (domain.allowsavingdevicecredentials == false)) return; parent.parent.db.Get(obj.nodeid, function (err, nodes) { if ((err != null) || (nodes == null) || (nodes.length != 1)) return; const node = nodes[0]; - const changed = (node.ssh == null); + if (node.ssh == null) { node.ssh = {}; } // Check if credentials are the same - //if ((typeof node.ssh == 'object') && (node.ssh.u == obj.username) && (node.ssh.p == obj.password)) return; // TODO + //if ((typeof node.ssh[obj.userid] == 'object') && (node.ssh[obj.userid].u == obj.username) && (node.ssh[obj.userid].p == obj.password)) return; // TODO + + // Clear up any existing credentials or credentials for users that don't exist anymore + for (var i in node.ssh) { if (!i.startsWith('user/') || (parent.users[i] == null)) { delete node.ssh[i]; } } + + // Clear legacy credentials + delete node.ssh.u; + delete node.ssh.p; + delete node.ssh.k; + delete node.ssh.kp; // Save the credentials if (obj.password != null) { - node.ssh = { u: obj.username, p: obj.password }; + node.ssh[obj.userid] = { u: obj.username, p: obj.password }; } else if (obj.privateKey != null) { - node.ssh = { u: obj.username, k: obj.privateKey }; - if (keep == 2) { node.ssh.kp = obj.privateKeyPass; } + node.ssh[obj.userid] = { u: obj.username, k: obj.privateKey }; + if (keep == 2) { node.ssh[obj.userid].kp = obj.privateKeyPass; } } else return; parent.parent.db.Set(node); - // Event node change if needed - if (changed) { - // Event the node change - const event = { etype: 'node', action: 'changenode', nodeid: obj.nodeid, domain: domain.id, userid: obj.userid, node: parent.CloneSafeNode(node), msg: "Changed SSH credentials" }; - if (parent.parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. - parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(node.meshid, [obj.nodeid]), obj, event); - } + // Event the node change + const event = { etype: 'node', action: 'changenode', nodeid: obj.nodeid, domain: domain.id, userid: obj.userid, node: parent.CloneSafeNode(node), msg: "Changed SSH credentials" }; + if (parent.parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. + parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(node.meshid, [obj.nodeid]), obj, event); }); } @@ -592,24 +604,24 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) { parent.parent.db.Get(obj.cookie.nodeid, function (err, nodes) { if ((err != null) || (nodes == null) || (nodes.length != 1)) return; const node = nodes[0]; - if ((domain.allowsavingdevicecredentials === false) || (node.ssh == null) || (typeof node.ssh != 'object') || (typeof node.ssh.u != 'string') || ((typeof node.ssh.p != 'string') && (typeof node.ssh.k != 'string'))) { + if ((domain.allowsavingdevicecredentials === false) || (node.ssh == null) || (typeof node.ssh != 'object') || (node.ssh[user._id] == null) || (typeof node.ssh[user._id].u != 'string') || ((typeof node.ssh[user._id].p != 'string') && (typeof node.ssh[user._id].k != 'string'))) { // Send a request for SSH authentication try { ws.send(JSON.stringify({ action: 'sshauth' })) } catch (ex) { } - } else if ((domain.allowsavingdevicecredentials !== false) && (node.ssh != null) && (typeof node.ssh.k == 'string') && (node.ssh.kp == null)) { + } else if ((domain.allowsavingdevicecredentials !== false) && (node.ssh != null) && (typeof node.ssh[user._id].k == 'string') && (node.ssh[user._id].kp == null)) { // Send a request for SSH authentication with option for only the private key password - obj.username = node.ssh.u; - obj.privateKey = node.ssh.k; + obj.username = node.ssh[user._id].u; + obj.privateKey = node.ssh[user._id].k; try { ws.send(JSON.stringify({ action: 'sshauth', askkeypass: true })) } catch (ex) { } } else { // Use our existing credentials obj.termSize = msg; delete obj.keep; - obj.username = node.ssh.u; - if (typeof node.ssh.p == 'string') { - obj.password = node.ssh.p; - } else if (typeof node.ssh.k == 'string') { - obj.privateKey = node.ssh.k; - obj.privateKeyPass = node.ssh.kp; + obj.username = node.ssh[user._id].u; + if (typeof node.ssh[user._id].p == 'string') { + obj.password = node.ssh[user._id].p; + } else if (typeof node.ssh[user._id].k == 'string') { + obj.privateKey = node.ssh[user._id].k; + obj.privateKeyPass = node.ssh[user._id].kp; } startRelayConnection(); } @@ -786,30 +798,37 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u parent.parent.db.Get(obj.nodeid, function (err, nodes) { if ((err != null) || (nodes == null) || (nodes.length != 1)) return; const node = nodes[0]; - const changed = (node.ssh == null); + if (node.ssh == null) { node.ssh = {}; } // Check if credentials are the same //if ((typeof node.ssh == 'object') && (node.ssh.u == obj.username) && (node.ssh.p == obj.password)) return; // TODO + // Clear up any existing credentials or credentials for users that don't exist anymore + for (var i in node.ssh) { if (!i.startsWith('user/') || (parent.users[i] == null)) { delete node.ssh[i]; } } + + // Clear legacy credentials + delete node.ssh.u; + delete node.ssh.p; + delete node.ssh.k; + delete node.ssh.kp; + // Save the credentials if (obj.password != null) { - node.ssh = { u: obj.username, p: obj.password }; + node.ssh[user._id] = { u: obj.username, p: obj.password }; } else if (obj.privateKey != null) { - node.ssh = { u: obj.username, k: obj.privateKey }; - if (keep == 2) { node.ssh.kp = obj.privateKeyPass; } + node.ssh[user._id] = { u: obj.username, k: obj.privateKey }; + if (keep == 2) { node.ssh[user._id].kp = obj.privateKeyPass; } } else return; parent.parent.db.Set(node); - // Event node change if needed - if (changed) { - // Event the node change - const event = { etype: 'node', action: 'changenode', nodeid: obj.nodeid, domain: domain.id, userid: user._id, username: user.name, node: parent.CloneSafeNode(node), msg: "Changed SSH credentials" }; - if (parent.parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. - parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(node.meshid, [obj.nodeid]), obj, event); - } + // Event the node change + const event = { etype: 'node', action: 'changenode', nodeid: obj.nodeid, domain: domain.id, userid: user._id, username: user.name, node: parent.CloneSafeNode(node), msg: "Changed SSH credentials" }; + if (parent.parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. + parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(node.meshid, [obj.nodeid]), obj, event); }); } + // Start the looppback server function startRelayConnection(authCookie) { try { @@ -1032,22 +1051,22 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u ws._socket.resume(); // Check if we have SSH credentials for this device - if ((domain.allowsavingdevicecredentials === false) || (node.ssh == null) || (typeof node.ssh != 'object') || (typeof node.ssh.u != 'string') || ((typeof node.ssh.p != 'string') && (typeof node.ssh.k != 'string'))) { + if ((domain.allowsavingdevicecredentials === false) || (node.ssh == null) || (typeof node.ssh != 'object') || (node.ssh[user._id] == null) || (typeof node.ssh[user._id].u != 'string') || ((typeof node.ssh[user._id].p != 'string') && (typeof node.ssh[user._id].k != 'string'))) { // Send a request for SSH authentication try { ws.send(JSON.stringify({ action: 'sshauth' })) } catch (ex) { } - } else if ((typeof node.ssh.k == 'string') && (typeof node.ssh.kp != 'string')) { + } else if ((typeof node.ssh[user._id].k == 'string') && (typeof node.ssh[user._id].kp != 'string')) { // Send a request for SSH authentication with option for only the private key password - obj.username = node.ssh.u; - obj.privateKey = node.ssh.k; + obj.username = node.ssh[user._id].u; + obj.privateKey = node.ssh[user._id].k; try { ws.send(JSON.stringify({ action: 'sshauth', askkeypass: true })) } catch (ex) { } } else { // Use our existing credentials - obj.username = node.ssh.u; - if (typeof node.ssh.p == 'string') { - obj.password = node.ssh.p; - } else if (typeof node.ssh.k == 'string') { - obj.privateKey = node.ssh.k; - obj.privateKeyPass = node.ssh.kp; + obj.username = node.ssh[user._id].u; + if (typeof node.ssh[user._id].p == 'string') { + obj.password = node.ssh[user._id].p; + } else if (typeof node.ssh[user._id].k == 'string') { + obj.privateKey = node.ssh[user._id].k; + obj.privateKeyPass = node.ssh[user._id].kp; } try { ws.send(JSON.stringify({ action: 'sshautoauth' })) } catch (ex) { } } @@ -1132,30 +1151,37 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user parent.parent.db.Get(obj.nodeid, function (err, nodes) { if ((err != null) || (nodes == null) || (nodes.length != 1)) return; const node = nodes[0]; - const changed = (node.ssh == null); + if (node.rdp == null) { node.rdp = {}; } // Check if credentials are the same - //if ((typeof node.ssh == 'object') && (node.ssh.u == obj.username) && (node.ssh.p == obj.password)) return; // TODO + //if ((typeof node.ssh[obj.userid] == 'object') && (node.ssh[obj.userid].u == obj.username) && (node.ssh[obj.userid].p == obj.password)) return; // TODO + + // Clear up any existing credentials or credentials for users that don't exist anymore + for (var i in node.ssh) { if (!i.startsWith('user/') || (parent.users[i] == null)) { delete node.ssh[i]; } } + + // Clear legacy credentials + delete node.ssh.u; + delete node.ssh.p; + delete node.ssh.k; + delete node.ssh.kp; // Save the credentials if (obj.password != null) { - node.ssh = { u: obj.username, p: obj.password }; + node.ssh[user._id] = { u: obj.username, p: obj.password }; } else if (obj.privateKey != null) { - node.ssh = { u: obj.username, k: obj.privateKey }; - if (keep == 2) { node.ssh.kp = obj.privateKeyPass; } + node.ssh[user._id] = { u: obj.username, k: obj.privateKey }; + if (keep == 2) { node.ssh[user._id].kp = obj.privateKeyPass; } } else return; parent.parent.db.Set(node); - // Event node change if needed - if (changed) { - // Event the node change - const event = { etype: 'node', action: 'changenode', nodeid: obj.nodeid, domain: domain.id, userid: user._id, username: user.name, node: parent.CloneSafeNode(node), msg: "Changed SSH credentials" }; - if (parent.parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. - parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(node.meshid, [obj.nodeid]), obj, event); - } + // Event the node change + const event = { etype: 'node', action: 'changenode', nodeid: obj.nodeid, domain: domain.id, userid: user._id, username: user.name, node: parent.CloneSafeNode(node), msg: "Changed SSH credentials" }; + if (parent.parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. + parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(node.meshid, [obj.nodeid]), obj, event); }); } + // Start the looppback server function startRelayConnection(authCookie) { try { @@ -1554,22 +1580,22 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user ws._socket.resume(); // Check if we have SSH credentials for this device - if ((domain.allowsavingdevicecredentials === false) || (node.ssh == null) || (typeof node.ssh != 'object') || (typeof node.ssh.u != 'string') || ((typeof node.ssh.p != 'string') && (typeof node.ssh.k != 'string'))) { + if ((domain.allowsavingdevicecredentials === false) || (node.ssh == null) || (typeof node.ssh != 'object') || (node.ssh[user._id] == null) || (typeof node.ssh[user._id].u != 'string') || ((typeof node.ssh[user._id].p != 'string') && (typeof node.ssh[user._id].k != 'string'))) { // Send a request for SSH authentication try { ws.send(JSON.stringify({ action: 'sshauth' })) } catch (ex) { } - } else if ((typeof node.ssh.k == 'string') && (typeof node.ssh.kp != 'string')) { + } else if ((typeof node.ssh[user._id].k == 'string') && (typeof node.ssh[user._id].kp != 'string')) { // Send a request for SSH authentication with option for only the private key password - obj.username = node.ssh.u; - obj.privateKey = node.ssh.k; + obj.username = node.ssh[user._id].u; + obj.privateKey = node.ssh[user._id].k; try { ws.send(JSON.stringify({ action: 'sshauth', askkeypass: true })) } catch (ex) { } } else { // Use our existing credentials - obj.username = node.ssh.u; - if (typeof node.ssh.p == 'string') { - obj.password = node.ssh.p; - } else if (typeof node.ssh.k == 'string') { - obj.privateKey = node.ssh.k; - obj.privateKeyPass = node.ssh.kp; + obj.username = node.ssh[user._id].u; + if (typeof node.ssh[user._id].p == 'string') { + obj.password = node.ssh[user._id].p; + } else if (typeof node.ssh[user._id].k == 'string') { + obj.privateKey = node.ssh[user._id].k; + obj.privateKeyPass = node.ssh[user._id].kp; } // Create a mesh relay authentication cookie diff --git a/docs/docs/meshcentral/debugging.md b/docs/docs/meshcentral/debugging.md index c86f8a77..b34360d5 100644 --- a/docs/docs/meshcentral/debugging.md +++ b/docs/docs/meshcentral/debugging.md @@ -1,5 +1,7 @@ ## Websockets Video +Make sure you understand how MeshCentral works with your browser using chrome developer tools. +
@@ -97,5 +99,3 @@ Of course, this is just for debugging. aka trying figure out what this is ![ID](images/determine-id.png) - -## General server statistics related \ No newline at end of file diff --git a/meshcentral.js b/meshcentral.js index 501bf03e..a59dc6bf 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -2693,8 +2693,8 @@ function CreateMeshCentralServer(config, args) { // List of possible mesh agent install scripts const meshToolsList = { 'MeshCentralRouter': { localname: 'MeshCentralRouter.exe', dlname: 'winrouter' }, - 'MeshCentralAssistant': { localname: 'MeshCentralAssistant.exe', dlname: 'winassistant', winhash: true }, - 'MeshCentralRouterMacOS': { localname: 'MeshCentralRouter.dmg', dlname: 'MeshCentralRouter.dmg' } + 'MeshCentralAssistant': { localname: 'MeshCentralAssistant.exe', dlname: 'winassistant', winhash: true } + //'MeshCentralRouterMacOS': { localname: 'MeshCentralRouter.dmg', dlname: 'MeshCentralRouter.dmg' } }; // Update the list of available mesh agents diff --git a/meshuser.js b/meshuser.js index 74f5d58d..7f5bbb34 100644 --- a/meshuser.js +++ b/meshuser.js @@ -423,9 +423,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use else if (event == 'updatefiles') { updateUserFiles(user, ws, domain); } else { // If updating guest device shares, if we are updating a user that is not creator of the share, remove the URL. - if ((event.action == 'deviceShareUpdate') && (Array.isArray(event.deviceShares))) { + if (((event.action == 'deviceShareUpdate') && (Array.isArray(event.deviceShares))) || ((event.action == 'changenode') && (event.node != null) && ((event.node.rdp != null) || (event.node.ssh != null)))) { event = common.Clone(event); - for (var i in event.deviceShares) { if (event.deviceShares[i].userid != user._id) { delete event.deviceShares[i].url; } } + if ((event.action == 'deviceShareUpdate') && (Array.isArray(event.deviceShares))) { + for (var i in event.deviceShares) { if (event.deviceShares[i].userid != user._id) { delete event.deviceShares[i].url; } } + } + if ((event.action == 'changenode') && (event.node != null) && ((event.node.rdp != null) || (event.node.ssh != null))) { + // Clean up RDP & SSH credentials + if ((event.node.rdp != null) && (typeof event.node.rdp[user._id] == 'number')) { event.node.rdp = event.node.rdp[user._id]; } else { delete event.node.rdp; } + if ((event.node.ssh != null) && (typeof event.node.ssh[user._id] == 'number')) { event.node.ssh = event.node.ssh[user._id]; } else { delete event.node.ssh; } + } } // This is a MeshCentral Satellite message @@ -730,18 +737,18 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Remove SSH credentials if present if (docs[i].ssh != null) { - if (docs[i].ssh.u) { - if (docs[i].ssh.k && docs[i].ssh.kp) { docs[i].ssh = 2; } // Username, key and password - else if (docs[i].ssh.k) { docs[i].ssh = 3; } // Username and key. No password. - else if (docs[i].ssh.p) { docs[i].ssh = 1; } // Username and password + if ((docs[i].ssh[obj.user._id] != null) && (docs[i].ssh[obj.user._id].u)) { + if (docs[i].ssh.k && docs[i].ssh[obj.user._id].kp) { docs[i].ssh = 2; } // Username, key and password + else if (docs[i].ssh[obj.user._id].k) { docs[i].ssh = 3; } // Username and key. No password. + else if (docs[i].ssh[obj.user._id].p) { docs[i].ssh = 1; } // Username and password else { delete docs[i].ssh; } } else { delete docs[i].ssh; } } - // Remove RDP credentials if present - if (docs[i].rdp != null) { docs[i].rdp = 1; } + // Remove RDP credentials if present, only set to 1 if our userid has RDP credentials + if ((docs[i].rdp != null) && (docs[i].rdp[obj.user._id] != null)) { docs[i].rdp = 1; } else { delete docs[i].rdp; } // Remove Intel AMT credential if present if (docs[i].intelamt != null) { @@ -3014,13 +3021,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } if ((typeof command.ssh == 'number') && (command.ssh == 0)) { - if (node.ssh != null) { delete node.ssh; change = 1; changes.push('ssh'); } // Delete the SSH cendentials + if ((node.ssh != null) && (node.ssh[user._id] != null)) { delete node.ssh[user._id]; change = 1; changes.push('ssh'); } // Delete the SSH cendentials } if ((typeof command.rdp == 'number') && (command.rdp == 0)) { - if (node.rdp != null) { delete node.rdp; change = 1; changes.push('rdp'); } // Delete the RDP cendentials + if ((node.rdp != null) && (node.rdp[user._id] != null)) { delete node.rdp[user._id]; change = 1; changes.push('rdp'); } // Delete the RDP cendentials } + // Clean up any legacy RDP and SSH credentials + if (node.rdp != null) { delete node.rdp.d; delete node.rdp.u; delete node.rdp.p; } + if (node.ssh != null) { delete node.ssh.u; delete node.ssh.p; delete node.ssh.k; delete node.ssh.kp; } + if (domain.geolocation && command.userloc && ((node.userloc == null) || (command.userloc[0] != node.userloc[0]) || (command.userloc[1] != node.userloc[1]))) { change = 1; if ((command.userloc.length == 0) && (node.userloc)) { diff --git a/translate/translate.json b/translate/translate.json index e7bf2927..ac15ae46 100644 --- a/translate/translate.json +++ b/translate/translate.json @@ -1183,6 +1183,8 @@ }, { "en": ", SFTP", + "nl": ", SFTP", + "pl": ", SFTP", "xloc": [ "default-mobile.handlebars->11->468", "default.handlebars->39->1328" @@ -1190,6 +1192,8 @@ }, { "en": ", SSH", + "nl": ", SSH", + "pl": ", SSH", "xloc": [ "default.handlebars->39->1296" ] @@ -11437,7 +11441,7 @@ { "en": "Browser Size", "nl": "Browser grootte", - "pl": "Rozmiar Przeglądarki", + "pl": "Jak Okna Przeglądarki", "xloc": [ "default.handlebars->container->dialog->dialogBody->dialog7->d7rdpkvm->3->d7rdpsize->3" ] @@ -12148,7 +12152,7 @@ { "en": "Canvas Size", "nl": "Canvas grootte", - "pl": "Rozmiar Zakrycia", + "pl": "Okno Robocze", "xloc": [ "default.handlebars->container->dialog->dialogBody->dialog7->d7rdpkvm->3->d7rdpsize->1" ] @@ -20887,7 +20891,7 @@ { "en": "Disable Cursor Settings", "nl": "Cursorinstellingen uitschakelen", - "pl": "Wyłączenie Ustawień Kursora", + "pl": "Wyłącz Ustawienia Kursora", "xloc": [ "default.handlebars->container->dialog->dialogBody->dialog7->d7rdpkvm->5->d7rdpflags->11" ] @@ -20895,7 +20899,7 @@ { "en": "Disable Cursor Shadow", "nl": "Cursorschaduw uitschakelen", - "pl": "Wyłączenie Cienia Kursora", + "pl": "Wyłącz Cień Kursora", "xloc": [ "default.handlebars->container->dialog->dialogBody->dialog7->d7rdpkvm->5->d7rdpflags->9" ] @@ -20903,7 +20907,7 @@ { "en": "Disable Full Window Drag", "nl": "Volledig venster slepen uitschakelen", - "pl": "Wyłączenie Przenoszenie Całego Okna", + "pl": "Wyłącz Przeciąganie Okna z Podglądem", "xloc": [ "default.handlebars->container->dialog->dialogBody->dialog7->d7rdpkvm->5->d7rdpflags->3" ] @@ -20911,7 +20915,7 @@ { "en": "Disable Menu Animations", "nl": "Menu animaties uitschakelen", - "pl": "Wyłączenie Animacji Menu", + "pl": "Wyłącz Animację Menu", "xloc": [ "default.handlebars->container->dialog->dialogBody->dialog7->d7rdpkvm->5->d7rdpflags->5" ] @@ -20919,7 +20923,7 @@ { "en": "Disable Theming", "nl": "Thema's uitschakelen", - "pl": "Wyłączenie Tematów Wizualnych", + "pl": "Wyłącz Temat Wizualny", "xloc": [ "default.handlebars->container->dialog->dialogBody->dialog7->d7rdpkvm->5->d7rdpflags->7" ] @@ -20927,7 +20931,7 @@ { "en": "Disable Wallpaper", "nl": "Achtergrond uitschakelen", - "pl": "Wyłączenie Tapety", + "pl": "Wyłącz Tapetę Pulpitu", "xloc": [ "default.handlebars->container->dialog->dialogBody->dialog7->d7rdpkvm->5->d7rdpflags->1" ] @@ -21211,7 +21215,7 @@ { "en": "Display Size", "nl": "Scherm grootte", - "pl": "Rozmiar Wyświetlacza", + "pl": "Rozdzielczość Wyświetlacza", "xloc": [ "default.handlebars->container->dialog->dialogBody->dialog7->d7rdpkvm->3->1" ] @@ -26265,7 +26269,7 @@ "ja": "速い", "ko": "빠른", "nl": "Snel", - "pl": "Szybko", + "pl": "Duża", "pt": "Rápido", "pt-br": "Rápido", "ru": "Быстро", @@ -27393,7 +27397,7 @@ "ja": "フレームレート", "ko": "프레임 속도", "nl": "Frameverhouding", - "pl": "Liczba klatek na sekundę", + "pl": "Prędkość odświeżania", "pt": "Taxa de quadros", "pt-br": "Taxa de quadros", "ru": "Частота кадров", @@ -36863,12 +36867,16 @@ }, { "en": "Local network connection thru a relay agent.", + "nl": "Lokale netwerkverbinding via een relay-agent.", + "pl": "Lokalne połączenie sieciowe za pośrednictwem przekierowania agenta.", "xloc": [ "default.handlebars->39->384" ] }, { "en": "Local network connection.", + "nl": "Lokale netwerkverbinding.", + "pl": "Lokalne połączenie sieciowe.", "xloc": [ "default.handlebars->39->386" ] @@ -37110,7 +37118,7 @@ "it": "Blocca su Disconnessione", "ja": "ロックオン切断", "nl": "Vergrendelen bij verbreken", - "pl": "Zablokuj na Odłącz", + "pl": "Zablokuj po Odłączeniu", "pt-br": "Bloquear ao Desconectar", "ru": "Заблокировать при отключении", "tr": "Bağlantıyı Keste Kilitle", @@ -39003,7 +39011,7 @@ "ja": "中", "ko": "중간", "nl": "Gemiddeld", - "pl": "Średnio", + "pl": "Średnia", "pt": "Médio", "pt-br": "Médio", "ru": "Средний", @@ -49028,7 +49036,7 @@ { "en": "RDP Connect", "nl": "RDP verbinden", - "pl": "Połącz za pomocą RDP", + "pl": "Połącz (protokół RDP)", "xloc": [ "default.handlebars->container->column_l->p11->deskarea0->deskarea1->3->connectbutton1rspan" ] @@ -50034,6 +50042,7 @@ }, { "en": "Remember password", + "pl": "Zapamiętaj hasło", "nl": "Onthoud wachtwoord", "xloc": [ "default-mobile.handlebars->11->461", @@ -50115,6 +50124,7 @@ { "en": "Remember user & key", "nl": "Onthoud gebruikeruser & sleutel", + "pl": "Zapamiętaj użytkownika i klucz", "xloc": [ "default-mobile.handlebars->11->460", "default.handlebars->39->1311", @@ -53671,6 +53681,8 @@ }, { "en": "SFTP Connect", + "nl": "SFTP verbinding", + "pl": "Połącz z SFTP", "xloc": [ "default-mobile.handlebars->container->page_content->column_l->p10->p10files->p13toolbar->1->0->1->3", "default.handlebars->container->column_l->p13->p13toolbar->1->0->1->3" @@ -53956,6 +53968,8 @@ }, { "en": "SSH Connect", + "nl": "SSH verbinding", + "pl": "Połącz z SSH", "xloc": [ "default-mobile.handlebars->container->page_content->column_l->p10->p10terminal->termTable->termarea1->1->3->connectbutton2sspan", "default.handlebars->container->column_l->p12->termTable->1->1->0->1->3->connectbutton2sspan" @@ -54083,6 +54097,7 @@ { "en": "SSH-User+Key", "nl": "SSH-gebruiker+sleutel", + "pl": "SSH-Uużytkownik+Klucz", "xloc": [ "default-mobile.handlebars->11->324", "default-mobile.handlebars->11->328", @@ -54093,6 +54108,7 @@ { "en": "SSH-User+Key+Pass", "nl": "SSH-gebruiker+sleutel+wachtwoord", + "pl": "SSH-Uużytkownik+Klucz+Hasło", "xloc": [ "default-mobile.handlebars->11->323", "default-mobile.handlebars->11->327", @@ -54103,6 +54119,7 @@ { "en": "SSH-User+Pass", "nl": "SSH-gebruiker+wachtwoord", + "pl": "SSH-Uużytkownik+Hasło", "xloc": [ "default-mobile.handlebars->11->322", "default-mobile.handlebars->11->326", @@ -54589,7 +54606,7 @@ { "en": "Screen Size", "nl": "Scherm grootte", - "pl": "Rozmiar Ekranu", + "pl": "Jak Całego Ekranu", "xloc": [ "default.handlebars->container->dialog->dialogBody->dialog7->d7rdpkvm->3->d7rdpsize->5" ] @@ -58727,7 +58744,7 @@ "ja": "スロー", "ko": "느린", "nl": "Traag", - "pl": "Powoli", + "pl": "Mała", "pt": "Lento", "pt-br": "Lento", "ru": "Медленно", @@ -60543,6 +60560,7 @@ { "en": "Stored Key", "nl": "Opgeslagen sleutel", + "pl": "Przechowywany Klucz", "xloc": [ "default-mobile.handlebars->11->448", "default.handlebars->39->1299", @@ -60803,7 +60821,7 @@ "ja": "マウスボタンを交換する", "ko": "마우스 버튼 교체", "nl": "Wissel muisknoppen", - "pl": "Zamień Przyciski Myszy", + "pl": "Zamień Strony Przycisków Myszy", "pt": "Botões de troca do mouse", "pt-br": "Trocar botões do mouse", "ru": "Поменять местами кнопки мыши", @@ -68120,7 +68138,7 @@ "ja": "非常に遅い", "ko": "아주 느린", "nl": "Erg traag", - "pl": "Bardzo powoli", + "pl": "Bardzo mała", "pt": "Muito devagar", "pt-br": "Muito lento", "ru": "Очень медленно", @@ -75430,4 +75448,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/views/default.handlebars b/views/default.handlebars index 6e5a0798..0028b6a0 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -5132,7 +5132,7 @@ if (serverinfo.https == true) { portStr = (serverinfo.port == 443)?'':(':' + serverinfo.port); } else { portStr = (serverinfo.port == 80) ? '' : (':' + serverinfo.port); } // Add Linux/macOS binary installer option - var binaryInstallAgentsOrder = [ 6, 5, 10005, 25, 26, 28, 30, 32, 37, 47, 40, 41 ]; + var binaryInstallAgentsOrder = [ 6, 5, 10005, 25, 26, 28, 30, 32, 37, 40, 41 ]; var binaryInstallAgents = { 5 : 'Linux x86-32', 6 : 'Linux x86-64', 10005 : 'Apple OSX Universal', 25 : 'Linux ARM-HF, Rasberry Pi', 26 : 'Linux ARM64-HF', 28: 'Linux MIPS24KC (OpenWRT)', 30 : 'FreeBSD x86-64', 32: 'Linux ARM 64 bit (glibc/2.24 NOKVM)', 36: 'OpenWRT x86-64', 37: 'OpenBSD x86-64', 40: 'Linux MIPSEL24KC (OpenWRT)', 41: 'ARMADA/CORTEX-A53/MUSL (OpenWRT)' }; for (var i in binaryInstallAgentsOrder) { moreoptions += '' } x += '
'; diff --git a/webserver.js b/webserver.js index 72b2e943..a3c79bf6 100644 --- a/webserver.js +++ b/webserver.js @@ -7533,12 +7533,18 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF if ((r.pmt != null) || (r.ssh != null) || (r.rdp != null) || ((r.intelamt != null) && ((r.intelamt.pass != null) || (r.intelamt.mpspass != null)))) { r = Object.assign({}, r); // Shallow clone if (r.pmt != null) { r.pmt = 1; } - if (r.ssh && r.ssh.u) { - if (r.ssh.p) { r.ssh = 1; } // Username and password - else if (r.ssh.k && r.ssh.kp) { r.ssh = 2; } // Username, key and password - else if (r.ssh.k) { r.ssh = 3; } // Username and key. No password. + if (r.ssh != null) { + var n = {}; + for (var i in r.ssh) { + if (i.startsWith('user/')) { + if (r.ssh[i].p) { n[i] = 1; } // Username and password + else if (r.ssh[i].k && r.ssh[i].kp) { n[i] = 2; } // Username, key and password + else if (r.ssh[i].k) { n[i] = 3; } // Username and key. No password. + } + } + r.ssh = n; } - if (r.rdp != null) { r.rdp = 1; } + if (r.rdp != null) { var n = {}; for (var i in r.rdp) { if (i.startsWith('user/')) { n[i] = 1; } } r.rdp = n; } if ((r.intelamt != null) && ((r.intelamt.pass != null) || (r.intelamt.mpspass != null))) { r.intelamt = Object.assign({}, r.intelamt); // Shallow clone if (r.intelamt.pass != null) { r.intelamt.pass = 1; }; // Remove the Intel AMT administrator password from the node