Added guest sharing list to device group panel.

Ylian Saint-Hilaire committed Dec 1, 2021 at 14:53 UTC e199c0c0735b41a44c53d13f3893aca594e352b7
2 files changed +137 -10
meshuser.js
+97 -8
@@ -3228,6 +3228,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
3228 break;
3229 }
3230
3231 + // This is to change device guest sharing to the new device group
3232 + var changeDeviceShareMeshIdNodeCount = command.nodeids.length;
3233 + var changeDeviceShareMeshIdNodeList = [];
3234 +
3235 // For each nodeid, change the group
3236 for (var i = 0; i < command.nodeids.length; i++) {
3237 var xnodeid = command.nodeids[i];
@@ -3236,14 +3240,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
3240 // Get the node and the rights for this node
3241 parent.GetNodeWithRights(domain, user, xnodeid, function (node, rights, visible) {
3242 // Check if we found this device
3239 - if (node == null) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'changeDeviceMesh', responseid: command.responseid, result: 'Device not found' })); } catch (ex) { } } return; }
3243 + if (node == null) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'changeDeviceMesh', responseid: command.responseid, result: 'Device not found' })); } catch (ex) { } } changeDeviceShareMeshIdNodeCount--; return; }
3244
3245 // Check if already in the right mesh
3242 - if (node.meshid == command.meshid) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'changeDeviceMesh', responseid: command.responseid, result: 'Device already in correct group' })); } catch (ex) { } } return; }
3246 + if (node.meshid == command.meshid) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'changeDeviceMesh', responseid: command.responseid, result: 'Device already in correct group' })); } catch (ex) { } } changeDeviceShareMeshIdNodeCount--; return; }
3247
3248 // Make sure both source and target mesh are the same type
3245 - try { if (parent.meshes[node.meshid].mtype != parent.meshes[command.meshid].mtype) return; } catch (e) {
3249 + try { if (parent.meshes[node.meshid].mtype != parent.meshes[command.meshid].mtype) { changeDeviceShareMeshIdNodeCount--; return; } } catch (e) {
3250 if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'changeDeviceMesh', responseid: command.responseid, result: 'Device groups are of different types' })); } catch (ex) { } }
3251 + changeDeviceShareMeshIdNodeCount--;
3252 return;
3253 };
3254
@@ -3251,10 +3256,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
3256 const targetMeshRights = parent.GetMeshRights(user, command.meshid);
3257 if (((rights & MESHRIGHT_EDITMESH) == 0) || ((targetMeshRights & MESHRIGHT_EDITMESH) == 0)) {
3258 if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'changeDeviceMesh', responseid: command.responseid, result: 'Permission denied' })); } catch (ex) { } }
3259 + changeDeviceShareMeshIdNodeCount--;
3260 return;
3261 }
3262
3263 // Perform the switch, start by saving the node with the new meshid.
3264 + changeDeviceShareMeshIdNodeList.push(node._id);
3265 + changeDeviceShareMeshIdNodeCount--;
3266 + if (changeDeviceShareMeshIdNodeCount == 0) { changeDeviceShareMeshId(changeDeviceShareMeshIdNodeList, command.meshid); }
3267 const oldMeshId = node.meshid;
3268 node.meshid = command.meshid;
3269 db.Set(parent.cleanDevice(node));
@@ -4577,6 +4586,70 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
4586 ws.send(JSON.stringify({ action: 'createInviteLink', meshid: command.meshid, url: url, expire: command.expire, cookie: inviteCookie, responseid: command.responseid, tag: command.tag }));
4587 break;
4588 }
4589 + case 'deviceMeshShares': {
4590 + if (domain.guestdevicesharing === false) return; // This feature is not allowed.
4591 + var err = null;
4592 +
4593 + // Argument validation
4594 + if (common.validateString(command.meshid, 8, 128) == false) { err = 'Invalid device group id'; } // Check the meshid
4595 + else if (command.meshid.indexOf('/') == -1) { command.meshid = 'mesh/' + domain.id + '/' + command.meshid; }
4596 + else if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) { err = 'Invalid domain'; } // Invalid domain, operation only valid for current domain
4597 + else {
4598 + // Check if we have rights on this device group
4599 + mesh = parent.meshes[command.meshid];
4600 + if (mesh == null) { err = 'Invalid device group id'; } // Check the meshid
4601 + else if (parent.GetMeshRights(user, mesh) == 0) { err = 'Access denied'; }
4602 + }
4603 +
4604 + // Handle any errors
4605 + if (err != null) {
4606 + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'deviceShares', responseid: command.responseid, result: err })); } catch (ex) { } }
4607 + break;
4608 + }
4609 +
4610 + // Get all device shares
4611 + parent.db.GetAllTypeNoTypeField('deviceshare', domain.id, function (err, docs) {
4612 + if (err != null) return;
4613 + var now = Date.now(), okDocs = [];
4614 + for (var i = 0; i < docs.length; i++) {
4615 + const doc = docs[i];
4616 + if ((doc.expireTime != null) && (doc.expireTime < now)) {
4617 + // This share is expired.
4618 + parent.db.Remove(doc._id, function () { });
4619 +
4620 + // Send device share update
4621 + var targets = parent.CreateNodeDispatchTargets(doc.xmeshid, doc.nodeid, ['server-users', user._id]);
4622 + parent.parent.DispatchEvent(targets, obj, { etype: 'node', meshid: doc.xmeshid, nodeid: doc.nodeid, action: 'deviceShareUpdate', domain: domain.id, deviceShares: okDocs, nolog: 1 });
4623 + } else {
4624 + if (doc.xmeshid == null) {
4625 + // This is an old share with missing meshid, fix it here.
4626 + const f = function fixShareMeshId(err, nodes) {
4627 + if (err != null) return;
4628 + if (nodes.length == 1) {
4629 + // Add the meshid to the device share
4630 + fixShareMeshId.xdoc.xmeshid = nodes[0].meshid;
4631 + fixShareMeshId.xdoc.type = 'deviceshare';
4632 + delete fixShareMeshId.xdoc.meshid;
4633 + parent.db.Set(fixShareMeshId.xdoc);
4634 + } else {
4635 + // This node no longer exists, remove the device share.
4636 + parent.db.Remove(fixShareMeshId.xdoc._id);
4637 + }
4638 + }
4639 + f.xdoc = doc;
4640 + db.Get(doc.nodeid, f);
4641 + } else if (doc.xmeshid == command.meshid) {
4642 + // This share is ok, remove extra data we don't need to send.
4643 + delete doc._id; delete doc.domain; delete doc.type; delete doc.xmeshid;
4644 + if (doc.userid != user._id) { delete doc.url; } // If this is not the user who created this link, don't give the link.
4645 + okDocs.push(doc);
4646 + }
4647 + }
4648 + }
4649 + try { ws.send(JSON.stringify({ action: 'deviceMeshShares', meshid: command.meshid, deviceShares: okDocs })); } catch (ex) { }
4650 + });
4651 + break;
4652 + }
4653 case 'deviceShares': {
4654 if (domain.guestdevicesharing === false) return; // This feature is not allowed.
4655 var err = null;
@@ -4619,7 +4692,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
4692 parent.db.Remove(doc._id, function () { }); removed = true;
4693 } else {
4694 // This share is ok, remove extra data we don't need to send.
4622 - delete doc._id; delete doc.domain; delete doc.nodeid; delete doc.type;
4695 + delete doc._id; delete doc.domain; delete doc.nodeid; delete doc.type; delete doc.xmeshid;
4696 if (doc.userid != user._id) { delete doc.url; } // If this is not the user who created this link, don't give the link.
4697 okDocs.push(doc);
4698 }
@@ -4810,7 +4883,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
4883 try { ws.send(JSON.stringify(command)); } catch (ex) { }
4884
4885 // Create a device sharing database entry
4813 - var shareEntry = { _id: 'deviceshare-' + publicid, type: 'deviceshare', nodeid: node._id, p: command.p, domain: node.domain, publicid: publicid, userid: user._id, guestName: command.guestname, consent: command.consent, url: url };
4886 + var shareEntry = { _id: 'deviceshare-' + publicid, type: 'deviceshare', xmeshid: node.meshid, nodeid: node._id, p: command.p, domain: node.domain, publicid: publicid, userid: user._id, guestName: command.guestname, consent: command.consent, url: url };
4887 if ((startTime != null) && (expireTime != null)) { shareEntry.startTime = startTime; shareEntry.expireTime = expireTime; }
4888 if (command.viewOnly === true) { shareEntry.viewOnly = true; }
4889 parent.db.Set(shareEntry);
@@ -4819,9 +4892,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
4892 var targets = parent.CreateNodeDispatchTargets(node.meshid, node._id, ['server-users', user._id]);
4893 var event;
4894 if ((startTime != null) && (expireTime != null)) {
4822 - event = { etype: 'node', userid: user._id, username: user.name, nodeid: node._id, action: 'addedDeviceShare', msg: 'Added device share: ' + command.guestname + '.', msgid: 101, msgArgs: [command.guestname, 'DATETIME:' + startTime, 'DATETIME:' + expireTime], domain: domain.id };
4895 + event = { etype: 'node', userid: user._id, username: user.name, meshid: node.meshid, nodeid: node._id, action: 'addedDeviceShare', msg: 'Added device share: ' + command.guestname + '.', msgid: 101, msgArgs: [command.guestname, 'DATETIME:' + startTime, 'DATETIME:' + expireTime], domain: domain.id };
4896 } else {
4824 - event = { etype: 'node', userid: user._id, username: user.name, nodeid: node._id, action: 'addedDeviceShare', msg: 'Added device share ' + command.guestname + ' with unlimited time.', msgid: 131, msgArgs: [command.guestname], domain: domain.id };
4897 + event = { etype: 'node', userid: user._id, username: user.name, meshid: node.meshid, nodeid: node._id, action: 'addedDeviceShare', msg: 'Added device share ' + command.guestname + ' with unlimited time.', msgid: 131, msgArgs: [command.guestname], domain: domain.id };
4898 }
4899 parent.parent.DispatchEvent(targets, obj, event);
4900
@@ -4835,7 +4908,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
4908 const doc = docs[i];
4909 if (doc.expireTime < now) { parent.db.Remove(doc._id, function () { }); delete docs[i]; } else {
4910 // This share is ok, remove extra data we don't need to send.
4838 - delete doc._id; delete doc.domain; delete doc.nodeid; delete doc.type;
4911 + delete doc._id; delete doc.domain; delete doc.nodeid; delete doc.type; delete doc.xmeshid;
4912 }
4913 }
4914
@@ -6687,6 +6760,22 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
6760 }
6761 }
6762
6763 + // Update all device shares for a nodeid list to a new meshid
6764 + // This is used when devices move to a new device group, changes are not evented.
6765 + function changeDeviceShareMeshId(nodes, meshid) {
6766 + parent.db.GetAllTypeNoTypeField('deviceshare', domain.id, function (err, docs) {
6767 + if (err != null) return;
6768 + for (var i = 0; i < docs.length; i++) {
6769 + const doc = docs[i];
6770 + if (nodes.indexOf(doc.nodeid) >= 0) {
6771 + doc.xmeshid = meshid;
6772 + doc.type = 'deviceshare';
6773 + db.Set(doc);
6774 + }
6775 + }
6776 + });
6777 + }
6778 +
6779 // Return detailed information about all nodes this user has access to
6780 function getAllDeviceDetailedInfo(type, func) {
6781 // Get all device groups this user has access to
views/default.handlebars
+40 -2
@@ -2359,7 +2359,14 @@
2359 if (message.nodeid != deviceSharesReq) break;
2360 deviceSharesNode = message.nodeid;
2361 deviceShares = message.deviceShares;
2362 - if (currentNode._id == message.nodeid) { gotoDevice(currentNode._id, xxcurrentView, true); }
2362 + if ((currentNode != null) && (currentNode._id == message.nodeid)) { gotoDevice(currentNode._id, xxcurrentView, true); }
2363 + break;
2364 + }
2365 + case 'deviceMeshShares': {
2366 + if (message.meshid != deviceSharesReq) break;
2367 + deviceSharesNode = message.meshid;
2368 + deviceShares = message.deviceShares;
2369 + if ((currentMesh != null) && (currentMesh._id == message.meshid)) { p20updateMesh(); }
2370 break;
2371 }
2372 case 'getsysinfo': {
@@ -11701,6 +11708,37 @@
11708
11709 x += '</tbody></table>';
11710
11711 + // Show device shares
11712 + if ((deviceShares != null) && (deviceSharesNode == currentMesh._id) && (deviceShares.length > 0)) {
11713 + x += '<p></p><table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse" border=0 cellpadding=2 cellspacing=0 width=100%><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:left;width:430px>' + "Active Device Sharing" + '</th><th scope=col style=text-align:left></th></tr>';
11714 + count = 1;
11715 + for (var i = 0; i < deviceShares.length; i++) {
11716 + var dshare = deviceShares[i], trash = '';
11717 + if (dshare.url != null) { trash += '<a href="' + dshare.url + '" rel="noreferrer noopener" target=_blank title="' + "Device Sharing Link" + '" style=cursor:pointer><img src=images/link2.png border=0 height=10 width=10></a> '; }
11718 + trash += '<a href=# onclick=\'return p30removeDeviceSharing(event,"' + encodeURIComponentEx(dshare.nodeid) + '","' + encodeURIComponentEx(dshare.publicid) + '","' + encodeURIComponentEx(dshare.guestName) + '")\' title="' + "Remove device sharing" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
11719 + var type = ['', "Terminal", "Desktop", "Desktop + Terminal", "Files", "Terminal + Files", "Desktop + Files", "Desktop + Terminal + Files"][dshare.p];
11720 + var details = type;
11721 + if ((dshare.startTime != null) && (dshare.expireTime != null)) { details = format("{0}, {1} to {2}", type, printFlexDateTime(new Date(dshare.startTime)), printFlexDateTime(new Date(dshare.expireTime))); }
11722 + if (((dshare.p & 2) != 0) && (dshare.viewOnly === true)) { details += ", View only desktop"; }
11723 + if (dshare.consent != null) {
11724 + if (dshare.consent == 0) { details += ", No Consent"; } else {
11725 + if ((dshare.consent & 0x0038) != 0) { details += ", Prompt for consent"; }
11726 + if ((dshare.consent & 0x0040) != 0) { details += ", Toolbar"; }
11727 + }
11728 + }
11729 + var guestName = EscapeHtml(dshare.guestName);
11730 + if (dshare.publicid.startsWith('AS:node/')) { guestName = '<i>' + "Agent Self-Share" + '</i>'; }
11731 + x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td style=width:30%><div class=m' + 2 + '></div><div>&nbsp;' + guestName + '<div></div></div></td><td style=width:70%><div style=float:right>' + trash + '</div><div>' + details + '</div></td></tr>';
11732 + }
11733 + x += '</tbody></table>';
11734 + } else {
11735 + // Request device sharing
11736 + if ((deviceSharesNode != currentMesh._id) && (deviceSharesReq != currentMesh._id)) {
11737 + deviceSharesReq = currentMesh._id;
11738 + meshserver.send({ action: 'deviceMeshShares', meshid: currentMesh._id });
11739 + }
11740 + }
11741 +
11742 // Display list of devices in this device group
11743 count = 0;
11744 nodes.sort(deviceSort);
@@ -11717,7 +11755,7 @@
11755 y += '<tr style=' + (((count % 2) == 0) ? ';background-color:#DDD' : '') + '><td style=width:30%><div onclick=\'gotoDevice("' + node._id + '",10);haltEvent(event);\' style=float:left class="j' + node.icon + gray + '"></div>&nbsp;<a onclick=\'gotoDevice("' + node._id + '",10);haltEvent(event);\'>' + EscapeHtml(node.name) + '</a></div></div></td><td style=width:70%><div style=float:right>' + PowerStateStr(node.pwr) + '&nbsp;</div><div>' + (node.osdesc ? EscapeHtml(node.osdesc) : '') + '</div></td></tr>';
11756 ++count;
11757 }
11720 - if (count == 0) { y += '<tr><td><i>' + "None " + '</i></td></tr>'; }
11758 + if (count == 0) { y += '<tr><td><i>' + "None" + '</i></td></tr>'; }
11759 y += '</tbody></table>';
11760
11761 // If we are full administrator on this mesh, allow deletion of the mesh