';
- var count = 1;
- if (currentNode.links != null) {
- // Sort the list of users to display
- var useridlist = [];
- for (var i in currentNode.links) { if (i.startsWith('user/')) { useridlist.push(i); } }
- useridlist.sort();
- for (var i in useridlist) {
- var trash = '', rights = '', userid = useridlist[i], srights = currentNode.links[userid].rights, username = EscapeHtml(userid.split('/')[2]), rights = makeUserDeviceRightsString(srights);
- if ((users != null) && (users[userid] != null)) { username = EscapeHtml(users[userid].name); }
- if ((meshrights & 2) != 0) {
- rights = '
';
+ var count = 1;
+ if (currentNode.links != null) {
+ // Sort the list of users to display
+ var useridlist = [];
+ for (var i in currentNode.links) { if (i.startsWith('user/')) { useridlist.push(i); } }
+ useridlist.sort();
+ for (var i in useridlist) {
+ var trash = '', rights = '', userid = useridlist[i], srights = currentNode.links[userid].rights, username = EscapeHtml(userid.split('/')[2]), rights = makeUserDeviceRightsString(srights);
+ if ((users != null) && (users[userid] != null)) { username = EscapeHtml(users[userid].name); }
+ if ((meshrights & 2) != 0) {
+ trash = '';
+ rights = '
' + rights + '
';
}
+ if (users != null) { username = '' + username + ''; }
+ x += '
' + username + '
' + trash + '
' + rights + '
';
}
- if (count == 1) { x += '
' + "No users with special device permissions" + '
'; }
- x += '
';
}
+ if (count == 1) { x += '
' + "No users with special device permissions" + '
'; }
+ x += '
';
QH('p10html4', x);
// Change the URL
@@ -5117,6 +5124,33 @@
return str.join(', ');
}
+ function makeDeviceGroupRightsString(rights) {
+ if (rights == 0xFFFFFFFF) { return "Full Rights"; }
+ var str = [];
+ if (rights & 1) str.push("Edit Group");
+ if (rights & 2) str.push("Manage Users");
+ if (rights & 4) str.push("Manage Devices");
+ if (rights & 8) {
+ var str1 = [];
+ if (rights & 256) str1.push("No Input");
+ if (rights & 512) str1.push("No Terminal");
+ if (rights & 1024) str1.push("No Files");
+ if (rights & 2048) str1.push("No AMT");
+ if (rights & 4096) str1.push("Limited Input");
+ if (rights & 65536) str1.push("No Desktop");
+ if (str1.length > 0) { str.push('Control (' + str1.join(', ') + ')'); } else { str.push("Control"); }
+ }
+ if (rights & 16) str.push("Console");
+ if (rights & 32) str.push("Server Files");
+ if (rights & 64) str.push("Wake");
+ if (rights & 128) str.push("Notes");
+ if (rights & 8192) str.push("Limit Events");
+ if (rights & 16384) str.push("Chat");
+ if (rights & 32768) str.push("Uninstall");
+ if (str.length == 0) return "No Rights";
+ return str.join(', ');
+ }
+
function writeDeviceEvent(nodeid) {
if (xxdialogMode) return;
setDialogMode(2, "Add Device Event", 3, writeDeviceEventEx, '' + "This will add an entry to this device\'s event log." + '', nodeid);
@@ -5273,7 +5307,7 @@
date = new Date(date.getTime() - (1000 * 60 * 60 * 24)); // Substract one day
}
- QH('p10html2', '
';
+ var username = EscapeHtml(decodeURIComponent(sortedusers[i].name));
+ if (users != null) { username = '' + username + ''; }
+ x += '
' + username + '
' + trash + '
' + rights + '
';
++count;
}
@@ -8490,7 +8529,11 @@
} else if (userid === 4) {
var y = '', selectedMeshId = null, selectedNode = null;
if (selected != null) { selectedNode = getNodeFromId(decodeURIComponent(selected)); if (selectedNode != null) { selectedMeshId = selectedNode.meshid; } }
- for (var i in meshes) { if (selectedMeshId == null) { selectedMeshId = meshes[i]._id; } y += ''; }
+ for (var i in meshes) {
+ if ((meshes[i].links[userinfo._id] != null) && (meshes[i].links[userinfo._id].rights & 7)) { // Only show device groups that we have user administrator for.
+ if (selectedMeshId == null) { selectedMeshId = meshes[i]._id; } y += '';
+ }
+ }
x += addHtmlValue("Device Group", '');
y = '';
for (var i in nodes) { if (nodes[i].meshid == selectedMeshId) { y += ''; } }
@@ -9943,7 +9986,9 @@
// Display all users for this mesh
for (var i in sortedusers) {
var trash = '';
- x += '
';
}
}
}
@@ -10311,30 +10361,28 @@
function drawUserPermissions() {
var count = 1, x = '';
- if (urlargs.dp == 1) { // For testing only
- // Display common devices
- x += ' ' + "Add Device" + '';
- x += '
' + "Common Devices" + '
';
- if (currentUser.links) {
- // Sort the list of devices to display
- var nodelist = [];
- for (var i in currentUser.links) { if (i.startsWith('node/')) { var node = getNodeFromId(i); if (node != null) { nodelist.push(node); } } }
- nodelist.sort(nameSort);
- for (var i in nodelist) {
- var node = nodelist[i], r = currentUser.links[node._id].rights, trash = '', cr = GetNodeRights(node);
- if ((userinfo.links) && (userinfo.links[i] != null) && (userinfo.links[i].rights != null)) { cr = userinfo.links[i].rights; }
- var nodename = node?EscapeHtml(node.name):('' + "Unknown Device" + '');
- if ((cr & 2) != 0) {
- rights = '
';
+ if (currentUser.links) {
+ // Sort the list of devices to display
+ var nodelist = [];
+ for (var i in currentUser.links) { if (i.startsWith('node/')) { var node = getNodeFromId(i); if (node != null) { nodelist.push(node); } } }
+ nodelist.sort(nameSort);
+ for (var i in nodelist) {
+ var node = nodelist[i], r = currentUser.links[node._id].rights, trash = '', cr = GetNodeRights(node);
+ if ((userinfo.links) && (userinfo.links[i] != null) && (userinfo.links[i].rights != null)) { cr = userinfo.links[i].rights; }
+ var nodename = node?EscapeHtml(node.name):('' + "Unknown Device" + '');
+ if ((cr & 2) != 0) {
+ rights = '
';
// Display common device groups
count = 1;
@@ -10345,13 +10393,13 @@
if (currentUser.links) {
for (var i in currentUser.links) {
if (i.startsWith('mesh/')) {
- var cr = 0, r = currentUser.links[i].rights, mesh = meshes[i], trash = '', rights = "Partial Device Group Rights";
+ var cr = 0, r = currentUser.links[i].rights, mesh = meshes[i], trash = '', rights = makeDeviceGroupRightsString(r);
if (mesh == null) { continue; }
if ((userinfo.links) && (userinfo.links[i] != null) && (userinfo.links[i].rights != null)) { cr = userinfo.links[i].rights; }
- var meshname = mesh?EscapeHtml(mesh.name):('' + "Unknown Device Group" + '');
- if (r == 0xFFFFFFFF) rights = "Full Device Group Administrator"; else if (r == 0) rights = "No Rights";
+ var meshname = '' + "Unknown Device Group" + '';
+ if (mesh) { meshname = '' + EscapeHtml(mesh.name) + ''; } else {}
if ((currentUser._id != userinfo._id) && ((cr & 2) != 0)) { trash = ''; }
- x += '
' + meshname + '
' + trash + '
' + rights + '
';
+ x += '
' + meshname + '
' + trash + '
' + rights + '
';
}
}
}
@@ -10372,9 +10420,13 @@
for (var i in currentUser.links) {
if (i.startsWith('ugrp/')) {
var r = currentUser.links[i].rights, group = usergroups[i], trash = '';
- var groupname = (group != null)?EscapeHtml(group.name):('' + "Unknown User Group" + '');
+ var groupname = '' + "Unknown User Group" + '';
+ if (group != null) {
+ groupname = EscapeHtml(group.name);
+ if (usergroups != null) { groupname = '' + groupname + ''; }
+ }
if ((userinfo.siteadmin & 256) != 0) { trash = ''; }
- x += '
' + groupname + '
' + trash + '
';
+ x += '
' + groupname + '
' + trash + '
';
}
}
}
@@ -11193,7 +11245,7 @@
// Update the web page title
if ((currentNode) && (x >= 10) && (x < 20)) {
- document.title = decodeURIComponent('{{{extitle}}}') + ' - ' + currentNode.name + ' - ' + meshes[currentNode.meshid].name;
+ document.title = decodeURIComponent('{{{extitle}}}') + ' - ' + currentNode.name + ((meshes[currentNode.meshid])?(' - ' + meshes[currentNode.meshid].name):'');
} else {
document.title = decodeURIComponent('{{{extitle}}}');
}
diff --git a/webserver.js b/webserver.js
index 73827561..8b203e44 100644
--- a/webserver.js
+++ b/webserver.js
@@ -2474,7 +2474,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (!node.intelamt) { console.log('ERR: Not AMT node'); try { ws.close(); } catch (e) { } return; } // Disconnect websocket
// Check if this user has permission to manage this computer
- if ((obj.GetMeshRights(user, node.meshid) & MESHRIGHT_REMOTECONTROL) == 0) { console.log('ERR: Access denied (2)'); try { ws.close(); } catch (e) { } return; }
+ if ((obj.GetNodeRights(user, node.meshid, node._id) & MESHRIGHT_REMOTECONTROL) == 0) { console.log('ERR: Access denied (2)'); try { ws.close(); } catch (e) { } return; }
// Check what connectivity is available for this node
var state = parent.GetConnectivityState(req.query.host);
@@ -3494,6 +3494,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.handleDevicePowerEvents = function (req, res) {
const domain = checkUserIpAddress(req, res);
if (domain == null) { return; }
+ if ((domain.loginkey != null) && (domain.loginkey.indexOf(req.query.key) == -1)) { res.sendStatus(404); return; } // Check 3FA URL key
if ((domain.id !== '') || (!req.session) || (req.session == null) || (!req.session.userid) || (req.query.id == null) || (typeof req.query.id != 'string')) { res.sendStatus(401); return; }
var x = req.query.id.split('/');
var user = obj.users[req.session.userid];
@@ -3506,7 +3507,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var node = docs[0];
// Check if we have right to this node
- if (obj.GetMeshRights(user, node.meshid) == 0) { res.sendStatus(401); return; }
+ if (obj.GetNodeRights(user, node.meshid, node._id) == 0) { res.sendStatus(401); return; }
// Get the list of power events and send them
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'text/csv', 'Content-Disposition': 'attachment; filename="powerevents.csv"' });
@@ -4374,6 +4375,18 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
return false;
}
+ // Return the user rights for a given node
+ obj.GetNodeRights = function (user, mesh, nodeid) {
+ if ((user == null) || (mesh == null) || (nodeid == null)) { return 0; }
+ if (typeof user == 'string') { user = obj.users[user]; }
+ var r = obj.GetMeshRights(user, mesh);
+ if (r == 0xFFFFFFFF) return r;
+
+ // Check direct device rights using device data
+ if ((user.links != null) && (user.links[nodeid] != null)) { r |= user.links[nodeid].rights; } // TODO: Deal with reverse permissions
+ return r;
+ }
+
// Returns a list of displatch targets for a given mesh
// We have to target the meshid and all user groups for this mesh, plus any added targets
obj.CreateMeshDispatchTargets = function (mesh, addedTargets) {
@@ -4510,7 +4523,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if ((domain.titlepicture == null) && (domain.titlehtml == null)) {
if (domain.title == null) {
xargs.title1 = 'MeshCentral';
- xargs.title2 = '2.0';
+ xargs.title2 = '';
} else {
xargs.title1 = domain.title;
xargs.title2 = domain.title2 ? domain.title2 : '';
@@ -4535,13 +4548,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Check that we are in the same domain and the user has rights over this node.
if ((splitsessionid.length == 4) && (splitsessionid[0] == 'user') && (splitsessionid[1] == domainid)) {
// Check if this user has rights to get this message
- if (obj.GetMeshRights(splitsessionid[0] + '/' + splitsessionid[1] + '/' + splitsessionid[2], meshid) == 0) return; // TODO: Check if this is ok
+ if (obj.GetNodeRights(splitsessionid[0] + '/' + splitsessionid[1] + '/' + splitsessionid[2], meshid, nodeid) == 0) return; // TODO: Check if this is ok
// See if the session is connected. If so, go ahead and send this message to the target node
var ws = obj.wssessions2[command.sessionid];
if (ws != null) {
- command.nodeid = nodeid; // Set the nodeid, required for responses.
- delete command.sessionid; // Remove the sessionid, since we are sending to that sessionid, so it's implyed.
+ command.nodeid = nodeid; // Set the nodeid, required for responses.
+ delete command.sessionid; // Remove the sessionid, since we are sending to that sessionid, so it's implyed.
try { ws.send(JSON.stringify(command)); } catch (ex) { }
} else if (parent.multiServer != null) {
// See if we can send this to a peer server
@@ -4558,7 +4571,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Check that we are in the same domain and the user has rights over this node.
if ((splituserid[0] == 'user') && (splituserid[1] == domainid)) {
// Check if this user has rights to get this message
- if (obj.GetMeshRights(command.userid, meshid) == 0) return; // TODO: Check if this is ok
+ if (obj.GetNodeRights(command.userid, meshid, nodeid) == 0) return; // TODO: Check if this is ok
// See if the session is connected
var sessions = obj.wssessions[command.userid];
@@ -4566,7 +4579,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Go ahead and send this message to the target node
if (sessions != null) {
command.nodeid = nodeid; // Set the nodeid, required for responses.
- delete command.userid; // Remove the userid, since we are sending to that userid, so it's implyed.
+ delete command.userid; // Remove the userid, since we are sending to that userid, so it's implyed.
for (i in sessions) { sessions[i].send(JSON.stringify(command)); }
}
@@ -4581,7 +4594,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Find all connected user sessions with access to this device
for (var userid in obj.wssessions) {
var xsessions = obj.wssessions[userid];
- if (obj.GetMeshRights(userid, meshid) != 0) {
+ if (obj.GetNodeRights(userid, meshid, nodeid) != 0) {
// Send the message to all sessions for this user on this server
for (i in xsessions) { try { xsessions[i].send(cmdstr); } catch (e) { } }
}