From d8548920bca76f80fe67cb480b72f5b6dd0dc84b Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Sat, 4 May 2019 12:55:46 -0700 Subject: [PATCH] User permission fixes, user list export feature. --- meshuser.js | 27 ++++++++++++++++---- package.json | 2 +- views/default-min.handlebars | 2 +- views/default.handlebars | 48 +++++++++++++++++++++++++++++++----- webserver.js | 29 ++++++++++++++++++---- 5 files changed, 90 insertions(+), 18 deletions(-) diff --git a/meshuser.js b/meshuser.js index 7bbe419d..cb146175 100644 --- a/meshuser.js +++ b/meshuser.js @@ -61,7 +61,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use var user = parent.users[obj.user._id]; if (user) { if (parent.parent.multiServer == null) { - parent.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: obj.user.name, count: parent.wssessions[obj.user._id].length, nolog: 1, domain: domain.id }); + var targets = ['*', 'server-users']; + if (obj.user.groups) { for (var i in obj.user.groups) { targets.push('server-users:' + i); } } + parent.parent.DispatchEvent(targets, obj, { action: 'wssessioncount', username: obj.user.name, count: parent.wssessions[obj.user._id].length, nolog: 1, domain: domain.id }); } else { parent.recountSessions(ws.sessionId); // Recount sessions } @@ -203,7 +205,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use parent.wssessions2[ws.sessionId] = ws; if (!parent.wssessions[user._id]) { parent.wssessions[user._id] = [ws]; } else { parent.wssessions[user._id].push(ws); } if (parent.parent.multiServer == null) { - parent.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: user.name, count: parent.wssessions[user._id].length, nolog: 1, domain: domain.id }); + var targets = ['*', 'server-users']; + if (obj.user.groups) { for (var i in obj.user.groups) { targets.push('server-users:' + i); } } + parent.parent.DispatchEvent(targets, obj, { action: 'wssessioncount', username: user.name, count: parent.wssessions[user._id].length, nolog: 1, domain: domain.id }); } else { parent.recountSessions(ws.sessionId); // Recount sessions } @@ -1004,15 +1008,28 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use var chguser = parent.users[command.id]; change = 0; if (chguser) { + // If the target user is admin and we are not admin, no changes can be made. + if ((chguser.siteadmin == 0xFFFFFFFF) && (user.siteadmin != 0xFFFFFFFF)) return; + + // Can only perform this operation on other users of our group. + if (user.siteadmin != 0xFFFFFFFF) { + if ((user.groups != null) && (user.groups.length > 0) && ((chguser.groups == null) || (findOne(chguser.groups, user.groups) == false))) return; + } + + // Validate input if (common.validateString(command.email, 1, 256) && (chguser.email != command.email)) { chguser.email = command.email; change = 1; } + + // Make changes if ((command.emailVerified === true || command.emailVerified === false) && (chguser.emailVerified != command.emailVerified)) { chguser.emailVerified = command.emailVerified; change = 1; } if ((common.validateInt(command.quota, 0) || command.quota == null) && (command.quota != chguser.quota)) { chguser.quota = command.quota; if (chguser.quota == null) { delete chguser.quota; } change = 1; } - if ((user.siteadmin == 0xFFFFFFFF) && common.validateInt(command.siteadmin) && (chguser.siteadmin != command.siteadmin)) { chguser.siteadmin = command.siteadmin; change = 1; } - if ((user.groups != null) && (user.groups.length > 0) && ((chguser.groups == null) || (findOne(chguser.groups, user.groups) == false))) break; // Can only perform this operation on other users of our group. + + // Site admins can change any server rights, user managers can only change AccountLock, NoMeshCmd and NoNewGroups + var chgusersiteadmin = chguser.siteadmin ? chguser.siteadmin : 0; + if (((user.siteadmin == 0xFFFFFFFF) || ((user.siteadmin & 2) && (((chgusersiteadmin ^ command.siteadmin) & 0xFFFFFF1F) == 0))) && common.validateInt(command.siteadmin) && (chguser.siteadmin != command.siteadmin)) { chguser.siteadmin = command.siteadmin; change = 1; } // Went sending a notification about a group change, we need to send to all the previous and new groups. var allTargetGroups = chguser.groups; - if ((Array.isArray(command.groups)) && (user._id != command.id)) { + if ((Array.isArray(command.groups)) && ((user._id != command.id) || (user.siteadmin == 0xFFFFFFFF))) { if (command.groups.length == 0) { // Remove the user groups if (chguser.groups != null) { delete chguser.groups; change = 1; } diff --git a/package.json b/package.json index 11341869..86a4ce43 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.3.3-r", + "version": "0.3.3-s", "keywords": [ "Remote Management", "Intel AMT", diff --git a/views/default-min.handlebars b/views/default-min.handlebars index 4c845294..5a753398 100644 --- a/views/default-min.handlebars +++ b/views/default-min.handlebars @@ -1 +1 @@ - {{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file + {{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file diff --git a/views/default.handlebars b/views/default.handlebars index b7ce816b..2c33a05c 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -300,6 +300,7 @@
+
@@ -1252,6 +1253,7 @@ function addLetterS(x) { return (x > 1) ? 's' : ''; } function onMessage(server, message) { + console.log(message); switch (message.action) { case 'serverstats': { updateGeneralServerStats(message); @@ -6820,6 +6822,35 @@ return false; } + function p4downloadUserInfo() { + if (xxdialogMode) return; + var x = 'Download the list of users with one of the file formats below.

'; + x += addHtmlValue('CSV Format', 'userlist.csv'); + x += addHtmlValue('JSON Format', 'userlist.json'); + setDialogMode(2, "User List Export", 1, null, x); + } + + function p4downloadUserInfoCSV() { + var csv = "id, name, email, creation, lastlogin, groups, authfactors\r\n"; + for (var i in users) { + var multiFactor = false, factors = []; + if ((users[i].otpsecret > 0) || (users[i].otphkeys > 0)) { + multiFactor = true; + if (users[i].otpsecret > 0) { factors.push('AuthApp'); } + if (users[i].otphkeys > 0) { factors.push('SecurityKey'); } + if (users[i].otpkeys > 0) { factors.push('BackupCodes'); } + } + csv += '\"' + users[i]._id + '\",\"' + users[i].name + '\",\"' + (users[i].email ? users[i].email : '') + '\",\"' + (users[i].creation ? new Date(users[i].creation * 1000) : '') + '\",\"' + (users[i].login ? new Date(users[i].login * 1000) : '') + '\",\"' + (users[i].groups ? users[i].groups.join(',') : '') + '\",\"' + (multiFactor ? factors.join(',') : '') + '\"\r\n'; + } + saveAs(new Blob([csv], { type: "application/octet-stream" }), "userlist.csv"); + } + + function p4downloadUserInfoJSON() { + var r = [] + for (var i in users) { r.push(users[i]); } + saveAs(new Blob([JSON.stringify(r)], { type: "application/octet-stream" }), "userlist.json"); + } + function showUserBroadcastDialog() { if (xxdialogMode) return; var x = 'Broadcast a message to all connected users.'; @@ -6923,10 +6954,11 @@ QE('ua_manageusers', userinfo.siteadmin == 0xFFFFFFFF); QE('ua_serverrestore', userinfo.siteadmin == 0xFFFFFFFF); QE('ua_fileaccess', userinfo.siteadmin == 0xFFFFFFFF); + QE('ua_fileaccessquota', userinfo.siteadmin == 0xFFFFFFFF); QE('ua_serverupdate', userinfo.siteadmin == 0xFFFFFFFF); - QE('ua_lockedaccount', userinfo.siteadmin == 0xFFFFFFFF); - QE('ua_nonewgroups', userinfo.siteadmin == 0xFFFFFFFF); - QE('ua_nomeshcmd', userinfo.siteadmin == 0xFFFFFFFF); + QE('ua_lockedaccount', (userinfo.siteadmin & 2) && (user.siteadmin != 0xFFFFFFFF) && (userinfo._id != user._id)); + QE('ua_nonewgroups', (userinfo.siteadmin & 2) && (user.siteadmin != 0xFFFFFFFF) && (userinfo._id != user._id)); + QE('ua_nomeshcmd', (userinfo.siteadmin & 2) && (user.siteadmin != 0xFFFFFFFF) && (userinfo._id != user._id)); Q('ua_fileaccessquota').value = (user.quota != null)?(user.quota / 1024):''; showUserAdminDialogValidate(); return false; @@ -6994,7 +7026,11 @@ var email = user.email?EscapeHtml(user.email):'Not set', everify = ''; if (serverinfo.emailcheck) { everify = ((user.emailVerified == true)?'🗸 ':'🗴 '); } if (user.name.toLowerCase() != user._id.split('/')[2]) { x += addDeviceAttribute('User Identifier', user._id.split('/')[2]); } - x += addDeviceAttribute('Email', everify + "" + email + ' '); + if ((user.siteadmin != 0xFFFFFFFF) || (userinfo.siteadmin == 0xFFFFFFFF)) { // If we are not site admin, we can't change a admin email. + x += addDeviceAttribute('Email', everify + "" + email + ' '); + } else { + x += addDeviceAttribute('Email', everify + email + ' '); + } x += addDeviceAttribute('Server Rights', premsg + "" + msg.join(', ') + ""); if (user.quota) x += addDeviceAttribute('Server Quota', EscapeHtml(parseInt(user.quota) / 1024) + ' k'); x += addDeviceAttribute('Creation', new Date(user.creation * 1000).toLocaleString()); @@ -7013,7 +7049,7 @@ // User Groups var userGroups = 'None'; if (user.groups) { userGroups = ''; for (var i in user.groups) { userGroups += '' + user.groups[i] + ''; } } - x += addDeviceAttribute('User Groups', addLinkConditional(userGroups, 'showUserGroupDialog(event,\"' + userid + '\")', ((userinfo.groups == null) && (userinfo.siteadmin & 2) && (userinfo._id != user._id)))); + x += addDeviceAttribute('User Groups', addLinkConditional(userGroups, 'showUserGroupDialog(event,\"' + userid + '\")', (userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.groups == null) && (userinfo.siteadmin & 2) && (userinfo._id != user._id) && (user._id != 0xFFFFFFFF)))); var multiFactor = 0; if ((user.otpsecret > 0) || (user.otphkeys > 0)) { @@ -7070,7 +7106,7 @@ if (serverinfo.emailcheck) { x += addHtmlValue('Status', ''); } setDialogMode(2, "Change Email for " + EscapeHtml(currentUser.name), 3, p30showUserEmailChangeDialogEx, x); Q('dp30email').focus(); - Q('dp30email').value = currentUser.email; + Q('dp30email').value = (currentUser.email?currentUser.email:''); if (serverinfo.emailcheck) { Q('dp30verified').value = currentUser.emailVerified?1:0; } p30validateEmail(); } diff --git a/webserver.js b/webserver.js index 2c4e2c66..c4af5581 100644 --- a/webserver.js +++ b/webserver.js @@ -727,7 +727,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Save login time user.login = Math.floor(Date.now() / 1000); obj.db.SetUser(user); - obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, account: obj.CloneSafeUser(user), action: 'login', msg: 'Account login', domain: domain.id }); + + // Notify account login + var targets = ['*', 'server-users']; + if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } } + obj.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: obj.CloneSafeUser(user), action: 'login', msg: 'Account login', domain: domain.id }); // Regenerate session when signing in to prevent fixation //req.session.regenerate(function () { @@ -3027,7 +3031,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (oldcount == null) { oldcount = 0; } else { delete obj.sessionsCount[userid]; } if (newcount != oldcount) { x = userid.split('/'); - obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: newcount, domain: x[1], nolog: 1, nopeers: 1 }); + var u = users[userid]; + if (u) { + var targets = ['*', 'server-users']; + if (u.groups) { for (var i in u.groups) { targets.push('server-users:' + i); } } + obj.parent.DispatchEvent(targets, obj, { action: 'wssessioncount', username: x[2], count: newcount, domain: x[1], nolog: 1, nopeers: 1 }); + } } } @@ -3036,7 +3045,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { oldcount = obj.sessionsCount[userid]; if ((oldcount != null) && (oldcount != 0)) { x = userid.split('/'); - obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: 0, domain: x[1], nolog: 1, nopeers: 1 }); + var u = users[userid]; + if (u) { + var targets = ['*', 'server-users']; + if (u.groups) { for (var i in u.groups) { targets.push('server-users:' + i); } } + obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: 0, domain: x[1], nolog: 1, nopeers: 1 }) + } } } @@ -3056,8 +3070,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // If the count changed, update and event if (newcount != oldcount) { x = userid.split('/'); - obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: newcount, domain: x[1], nolog: 1, nopeers: 1 }); - obj.sessionsCount[userid] = newcount; + var u = users[userid]; + if (u) { + var targets = ['*', 'server-users']; + if (u.groups) { for (var i in u.groups) { targets.push('server-users:' + i); } } + obj.parent.DispatchEvent(targets, obj, { action: 'wssessioncount', username: x[2], count: newcount, domain: x[1], nolog: 1, nopeers: 1 }); + obj.sessionsCount[userid] = newcount; + } } } };