Added session termination feature except for desktop multiplexor.

Ylian Saint-Hilaire committed Oct 19, 2021 at 11:47 UTC bd2e4f4d2bbbd32312b32fdde5580491fc3b3bfd
3 files changed +85 -7
agents/meshcore.js
+22 -1
@@ -1015,6 +1015,22 @@ function handleServerCommand(data) {
1015 }
1016 break;
1017 }
1018 + case 'endtunnel': {
1019 + // Terminate one or more tunnels
1020 + if ((data.rights != 4294967295) && (data.xuserid != data.userid)) return; // This command requires full admin rights on the device or user self-closes it's own sessions
1021 + for (var i in tunnels) {
1022 + if ((tunnels[i].userid == data.xuserid) && (tunnels[i].guestname == data.guestname)) {
1023 + var disconnect = false;
1024 + if ((data.protocol == 'kvm') && (tunnels[i].protocol == 2)) { disconnect = true; }
1025 + else if ((data.protocol == 'terminal') && (tunnels[i].protocol == 1)) { disconnect = true; }
1026 + else if ((data.protocol == 'files') && (tunnels[i].protocol == 5)) { disconnect = true; }
1027 + else if ((data.protocol == 'tcp') && (tunnels[i].tcpport != null)) { disconnect = true; }
1028 + else if ((data.protocol == 'udp') && (tunnels[i].udpport != null)) { disconnect = true; }
1029 + if (disconnect) { if (tunnels[i].s != null) { tunnels[i].s.end(); } else { tunnels[i].end(); } }
1030 + }
1031 + }
1032 + break;
1033 + }
1034 case 'messagebox': {
1035 // Display a message box
1036 if (data.title && data.msg) {
@@ -3988,7 +4004,12 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
4004 }
4005 case 'tunnels': { // Show the list of current tunnels
4006 response = '';
3991 - for (var i in tunnels) { response += 'Tunnel #' + i + ', ' + tunnels[i].url + '\r\n'; }
4007 + for (var i in tunnels) {
4008 + response += 'Tunnel #' + i + ', ' + tunnels[i].protocol; //tunnels[i].url
4009 + if (tunnels[i].userid) { response += ', ' + tunnels[i].userid; }
4010 + if (tunnels[i].guestname) { response += '/' + tunnels[i].guestname; }
4011 + response += '\r\n'
4012 + }
4013 if (response == '') { response = 'No websocket sessions.'; }
4014 break;
4015 }
meshuser.js
+23
@@ -5660,6 +5660,29 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5660
5661 break;
5662 }
5663 + case 'endDesktopMultiplex': {
5664 + var err = null, xuser = null;
5665 + try {
5666 + if (command.xuserid.indexOf('/') < 0) { command.xuserid = 'user/' + domain.id + '/' + command.xuserid; }
5667 + if (common.validateString(command.nodeid, 1, 1024) == false) { err = 'Invalid device identifier'; } // Check the meshid
5668 + else if (command.nodeid.indexOf('/') == -1) { command.nodeid = 'node/' + domain.id + '/' + command.nodeid; }
5669 + const xusersplit = command.xuserid.split('/');
5670 + xuser = parent.users[command.xuserid];
5671 + if (xuser == null) { err = 'User does not exists'; }
5672 + else if ((obj.crossDomain !== true) && ((xusersplit.length != 3) || (xusersplit[1] != domain.id))) { err = 'Invalid domain'; } // Invalid domain, operation only valid for current domain
5673 + } catch (ex) { err = 'Validation exception: ' + ex; }
5674 +
5675 + // Handle any errors
5676 + if (err != null) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'changeusernotify', responseid: command.responseid, result: err })); } catch (ex) { } } break; }
5677 +
5678 + // Get the node and the rights for this node
5679 + parent.GetNodeWithRights(domain, xuser, command.nodeid, function (node, rights, visible) {
5680 + if ((rights != 0xFFFFFFFF) && (xuser._id != command.xuserid)) return;
5681 + //console.log('TODO');
5682 + });
5683 +
5684 + break;
5685 + }
5686 default: {
5687 // Unknown user action
5688 console.log('Unknown action from user ' + user.name + ': ' + command.action + '.');
views/default.handlebars
+40 -6
@@ -4461,28 +4461,62 @@
4461 for (var i in node.sessions) {
4462 if ((i == 'kvm') && (node.sessions.multidesk == null)) {
4463 x += '<u>' + "Remote Desktop" + '</u>';
4464 - for (var j in node.sessions.kvm) { x += addHtmlValue2(getUserName(j), (node.sessions.kvm[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.kvm[j]))); }
4464 + for (var j in node.sessions.kvm) {
4465 + var trash = '';
4466 + if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("kvm", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
4467 + x += addHtmlValue2(getUserName(j), ((node.sessions.kvm[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.kvm[j]))) + trash);
4468 + }
4469 } else if (i == 'multidesk') {
4470 x += '<u>' + "Remote Desktop" + '</u>';
4467 - for (var j in node.sessions.multidesk) { x += addHtmlValue2(getUserName(j), ((node.sessions.multidesk[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.multidesk[j])))); }
4471 + for (var j in node.sessions.multidesk) {
4472 + var trash = '';
4473 + if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("multidesk", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
4474 + x += addHtmlValue2(getUserName(j), ((node.sessions.multidesk[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.multidesk[j]))) + trash);
4475 + }
4476 } else if (i == 'terminal') {
4477 x += '<u>' + "Terminal" + '</u>';
4470 - for (var j in node.sessions.terminal) { x += addHtmlValue2(getUserName(j), ((node.sessions.terminal[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.terminal[j])))); }
4478 + for (var j in node.sessions.terminal) {
4479 + var trash = '';
4480 + if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("terminal", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
4481 + x += addHtmlValue2(getUserName(j), ((node.sessions.terminal[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.terminal[j]))) + trash);
4482 + }
4483 } else if (i == 'files') {
4484 x += '<u>' + "Files" + '</u>';
4473 - for (var j in node.sessions.files) { x += addHtmlValue2(getUserName(j), ((node.sessions.files[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.files[j])))); }
4485 + for (var j in node.sessions.files) {
4486 + var trash = '';
4487 + if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("files", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
4488 + x += addHtmlValue2(getUserName(j), ((node.sessions.files[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.files[j]))) + trash);
4489 + }
4490 } else if (i == 'tcp') {
4491 x += '<u>' + "TCP Routing" + '</u>';
4476 - for (var j in node.sessions.tcp) { x += addHtmlValue2(getUserName(j), ((node.sessions.tcp[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.tcp[j])))); }
4492 + for (var j in node.sessions.tcp) {
4493 + var trash = '';
4494 + if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("tcp", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
4495 + x += addHtmlValue2(getUserName(j), ((node.sessions.tcp[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.tcp[j]))) + trash);
4496 + }
4497 } else if (i == 'udp') {
4498 x += '<u>' + "UDP Routing" + '</u>';
4479 - for (var j in node.sessions.udp) { x += addHtmlValue2(getUserName(j), ((node.sessions.udp[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.udp[j])))); }
4499 + for (var j in node.sessions.udp) {
4500 + var trash = '';
4501 + if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("udp", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
4502 + x += addHtmlValue2(getUserName(j), ((node.sessions.udp[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.udp[j]))) + trash);
4503 + }
4504 }
4505 }
4506 if (x != '') { setDialogMode(2, "Sessions" + ' - ' + EscapeHtml(node.name), 1, null, x, 'SESSIONS-' + node._id); } else { setDialogMode(0); }
4507 return false;
4508 }
4509
4510 + function endDeviceSession(protocol, nodeid, userid) {
4511 + var userIdSplit = decodeURIComponent(userid).split('/'), uid = userIdSplit[0] + '/' + userIdSplit[1] + '/' + userIdSplit[2], guestname = null;
4512 + if ((userIdSplit.length == 4) && (userIdSplit[3].startsWith('guest:'))) { guestname = atob(userIdSplit[3].substring(6)); }
4513 + if (protocol == 'multidesk') {
4514 + meshserver.send({ action: 'endDesktopMultiplex', nodeid: decodeURIComponent(nodeid), xuserid: uid, guestname, guestname });
4515 + } else {
4516 + meshserver.send({ action: 'msg', type: 'endtunnel', nodeid: decodeURIComponent(nodeid), xuserid: uid, guestname, guestname, protocol: protocol });
4517 + }
4518 + }
4519 +
4520 // Show currently active sessions on this device
4521 function showDeviceMessages(nodeid, force, e) {
4522 if (e) haltEvent(e);