First version with user groups enforcement.

Ylian Saint-Hilaire committed Apr 16, 2019 at 17:32 UTC 17a580c4e8695c479ccf102cf52090e61b6f22bc
4 files changed +154 -24
meshuser.js
+121 -20
@@ -715,7 +715,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
715 var docs = [];
716 for (i in parent.users) {
717 if ((parent.users[i].domain == domain.id) && (parent.users[i].name != '~')) {
718 - docs.push(parent.CloneSafeUser(parent.users[i]));
718 + // If we are part of a user group, we can only see other members of our own group
719 + if ((user.groups == null) || (user.groups.length == 0) || ((parent.users[i].groups != null) && (findOne(parent.users[i].groups, user.groups)))) {
720 + docs.push(parent.CloneSafeUser(parent.users[i]));
721 + }
722 }
723 }
724 try { ws.send(JSON.stringify({ action: 'users', users: docs, tag: command.tag })); } catch (ex) { }
@@ -723,7 +726,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
726 }
727 case 'changeemail':
728 {
726 - // Change the email address
729 + // Change our own email address
730 if ((domain.auth == 'sspi') || (domain.auth == 'ldap')) return;
731 if (common.validateEmail(command.email, 1, 256) == false) return;
732 if (parent.users[req.session.userid].email != command.email) {
@@ -746,7 +749,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
749 } else {
750 message.msg = 'Set email of user ' + user.name + ' to ' + user.email;
751 }
749 - parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, message);
752 +
753 + var targets = ['*', 'server-users', user._id];
754 + if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
755 + parent.parent.DispatchEvent(targets, obj, message);
756
757 // Send the verification email
758 if (parent.parent.mailserver != null) { parent.parent.mailserver.sendAccountCheckMail(domain, user.name, user.email); }
@@ -773,10 +779,36 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
779 if ((user.siteadmin & 2) == 0) break;
780 if (parent.parent.multiServer == null) {
781 // No peering, use simple session counting
776 - for (i in parent.wssessions) { if (parent.wssessions[i][0].domainid == domain.id) { wssessions[i] = parent.wssessions[i].length; } }
782 + for (i in parent.wssessions) {
783 + if (parent.wssessions[i][0].domainid == domain.id) {
784 + if ((user.groups == null) || (user.groups.length == 0)) {
785 + // No user groups, count everything
786 + wssessions[i] = parent.wssessions[i].length;
787 + } else {
788 + // Only count if session is for a user in our user groups
789 + var sessionUser = parent.users[parent.wssessions[i][0].userid];
790 + if ((sessionUser != null) && findOne(sessionUser.groups, user.groups)) {
791 + wssessions[i] = parent.wssessions[i].length;
792 + }
793 + }
794 + }
795 + }
796 } else {
797 // We have peer servers, use more complex session counting
779 - for (i in parent.sessionsCount) { if (i.split('/')[1] == domain.id) { wssessions[i] = parent.sessionsCount[i]; } }
798 + for (i in parent.sessionsCount) {
799 + if (i.split('/')[1] == domain.id) {
800 + if ((user.groups == null) || (user.groups.length == 0)) {
801 + // No user groups, count everything
802 + wssessions[i] = parent.sessionsCount[i];
803 + } else {
804 + // Only count if session is for a user in our user groups
805 + var sessionUser = parent.users[i];
806 + if ((sessionUser != null) && findOne(sessionUser.groups, user.groups)) {
807 + wssessions[i] = parent.sessionsCount[i];
808 + }
809 + }
810 + }
811 + }
812 }
813 try { ws.send(JSON.stringify({ action: 'wssessioncount', wssessions: wssessions, tag: command.tag })); } catch (ex) { } // wssessions is: userid --> count
814 break;
@@ -789,6 +821,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
821 var delusersplit = command.userid.split('/'), deluserid = command.userid, deluser = parent.users[deluserid];
822 if ((deluser == null) || (delusersplit.length != 3) || (delusersplit[1] != domain.id)) break; // Invalid domain, operation only valid for current domain
823 if ((deluser.siteadmin != null) && (deluser.siteadmin > 0) && (user.siteadmin != 0xFFFFFFFF)) break; // Need full admin to remote another administrator
824 + if ((user.groups != null) && (user.groups.length > 0) && ((deluser.groups == null) || (findOne(deluser.groups, user.groups) == false))) break; // Can only perform this operation on other users of our group.
825
826 // Remove all the mesh links to this user
827 if (deluser.links != null) {
@@ -816,7 +849,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
849
850 db.Remove(deluserid);
851 delete parent.users[deluserid];
819 - parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', userid: deluserid, username: deluser.name, action: 'accountremove', msg: 'Account removed', domain: domain.id });
852 +
853 + var targets = ['*', 'server-users'];
854 + if (deluser.groups) { for (var i in deluser.groups) { targets.push('server-users:' + i); } }
855 + parent.parent.DispatchEvent(targets, obj, { etype: 'user', userid: deluserid, username: deluser.name, action: 'accountremove', msg: 'Account removed', domain: domain.id });
856 parent.parent.DispatchEvent([deluserid], obj, 'close');
857
858 break;
@@ -833,7 +869,18 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
869 // Send the notification on all user sessions for this server
870 for (var i in parent.wssessions2) {
871 try {
836 - if (parent.wssessions2[i].domainid == domain.id) { parent.wssessions2[i].send(JSON.stringify(notification)); }
872 + if (parent.wssessions2[i].domainid == domain.id) {
873 + if ((user.groups == null) || (user.groups.length == 0)) {
874 + // We are part of no user groups, send to everyone.
875 + parent.wssessions2[i].send(JSON.stringify(notification));
876 + } else {
877 + // We are part of user groups, only send to sessions of users in our groups.
878 + var sessionUser = parent.users[parent.wssessions2[i].userid];
879 + if ((sessionUser != null) && findOne(sessionUser.groups, user.groups)) {
880 + parent.wssessions2[i].send(JSON.stringify(notification));
881 + }
882 + }
883 + }
884 } catch (ex) { }
885 }
886
@@ -870,6 +917,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
917 var newuser = { type: 'user', _id: newuserid, name: newusername, creation: Math.floor(Date.now() / 1000), domain: domain.id };
918 if (command.email != null) { newuser.email = command.email; } // Email
919 if (command.resetNextLogin === true) { newuser.passchange = -1; } else { newuser.passchange = Math.floor(Date.now() / 1000); }
920 + if ((user.groups != null) && (user.groups.length > 0)) { newuser.groups = user.groups; } // New account are automatically part of our groups.
921 parent.users[newuserid] = newuser;
922
923 // Create a user, generate a salt and hash the password
@@ -878,7 +926,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
926 newuser.salt = salt;
927 newuser.hash = hash;
928 db.SetUser(newuser);
881 - parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: newusername, account: parent.CloneSafeUser(newuser), action: 'accountcreate', msg: 'Account created, email is ' + command.email, domain: domain.id });
929 +
930 + var targets = ['*', 'server-users'];
931 + if (newuser.groups) { for (var i in newuser.groups) { targets.push('server-users:' + i); } }
932 + parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: newusername, account: parent.CloneSafeUser(newuser), action: 'accountcreate', msg: 'Account created, email is ' + command.email, domain: domain.id });
933 });
934 }
935 });
@@ -895,7 +946,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
946 if ((command.emailVerified === true || command.emailVerified === false) && (chguser.emailVerified != command.emailVerified)) { chguser.emailVerified = command.emailVerified; change = 1; }
947 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; }
948 if ((user.siteadmin == 0xFFFFFFFF) && common.validateInt(command.siteadmin) && (chguser.siteadmin != command.siteadmin)) { chguser.siteadmin = command.siteadmin; change = 1; }
949 + 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.
950
951 + // Went sending a notification about a group change, we need to send to all the previous and new groups.
952 + var allTargetGroups = chguser.groups;
953 if ((Array.isArray(command.groups)) && (user._id != command.id)) {
954 if (command.groups.length == 0) {
955 // Remove the user groups
@@ -913,6 +967,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
967
968 // Set the user groups
969 if (chguser.groups != groups2) { chguser.groups = groups2; change = 1; }
970 +
971 + // Add any missing groups in the target list
972 + if (allTargetGroups == null) { allTargetGroups = []; }
973 + for (var i in groups2) { if (allTargetGroups.indexOf(i) == -1) { allTargetGroups.push(i); } }
974 }
975 }
976
@@ -920,7 +978,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
978 // Update the user
979 db.SetUser(chguser);
980 parent.parent.DispatchEvent([chguser._id], obj, 'resubscribe');
923 - parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Account changed: ' + chguser.name, domain: domain.id });
981 +
982 + var targets = ['*', 'server-users', user._id, chguser._id];
983 + if (allTargetGroups) { for (var i in allTargetGroups) { targets.push('server-users:' + i); } }
984 + parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Account changed: ' + chguser.name, domain: domain.id });
985 }
986 if ((chguser.siteadmin) && (chguser.siteadmin != 0xFFFFFFFF) && (chguser.siteadmin & 32)) {
987 // If the user is locked out of this account, disconnect now
@@ -958,7 +1019,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1019 user.passchange = Math.floor(Date.now() / 1000);
1020 delete user.passtype;
1021 db.SetUser(user);
961 - parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
1022 +
1023 + var targets = ['*', 'server-users'];
1024 + if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
1025 + parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
1026
1027 // Send user notification of password change
1028 displayNotificationMessage('Password changed.');
@@ -975,14 +1039,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1039 {
1040 // Change a user's password
1041 if (user.siteadmin != 0xFFFFFFFF) break;
978 - if (common.validateString(command.user, 1, 256) == false) break;
1042 + if (common.validateString(command.userid, 1, 256) == false) break;
1043 if (common.validateString(command.pass, 1, 256) == false) break;
1044 if ((command.hint != null) && (common.validateString(command.hint, 0, 256) == false)) break;
1045 if (typeof command.removeMultiFactor != 'boolean') break;
1046 if (common.checkPasswordRequirements(command.pass, domain.passwordrequirements) == false) break; // Password does not meet requirements
1047
984 - var chguser = parent.users['user/' + domain.id + '/' + command.user.toLowerCase()];
1048 + var chguser = parent.users[command.userid];
1049 if (chguser) {
1050 + // Can only perform this operation on other users of our group.
1051 + if ((user.groups != null) && (user.groups.length > 0) && ((chguser.groups == null) || (findOne(chguser.groups, user.groups) == false))) break;
1052 +
1053 // Compute the password hash & save it
1054 require('./pass').hash(command.pass, function (err, salt, hash) {
1055 if (!err) {
@@ -1001,7 +1068,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1068 if (chguser.otpkeys) { delete chguser.otpkeys; }
1069 }
1070 db.SetUser(chguser);
1004 - parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Changed account credentials.', domain: domain.id });
1071 +
1072 + var targets = ['*', 'server-users', user._id, chguser._id];
1073 + if (chguser.groups) { for (var i in chguser.groups) { targets.push('server-users:' + i); } }
1074 + parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Changed account credentials.', domain: domain.id });
1075 } else {
1076 // Report that the password change failed
1077 // TODO
@@ -1017,6 +1087,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1087 if (common.validateString(command.userid, 1, 2048) == false) break;
1088 if (common.validateString(command.msg, 1, 4096) == false) break;
1089
1090 + // Can only perform this operation on other users of our group.
1091 + var chguser = parent.users[command.userid];
1092 + if (chguser == null) break; // This user does not exists
1093 + if ((user.groups != null) && (user.groups.length > 0) && ((chguser.groups == null) || (findOne(chguser.groups, user.groups) == false))) break;
1094 +
1095 // Create the notification message
1096 var notification = { "action": "msg", "type": "notify", "value": "<b>" + user.name + "</b>: " + EscapeHtml(command.msg), "userid": user._id, "username": user.name };
1097
@@ -1037,6 +1112,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1112 // Setup a user-to-user session
1113 if (common.validateString(command.userid, 1, 2048)) {
1114
1115 + // Can only perform this operation on other users of our group.
1116 + var chguser = parent.users[command.userid];
1117 + if (chguser == null) break; // This user does not exists
1118 + if ((user.groups != null) && (user.groups.length > 0) && ((chguser.groups == null) || (findOne(chguser.groups, user.groups) == false))) break;
1119 +
1120 // Create the notification message
1121 var notification = {
1122 "action": "msg", "type": "notify", "value": "<b>" + user.name + "</b>: Chat Request, Click here to accept.", "userid": user._id, "username": user.name, "tag": 'meshmessenger/' + encodeURIComponent(command.userid) + '/' + encodeURIComponent(user._id)
@@ -1836,6 +1916,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1916 if (common.validateString(command.notes, 1) == false) {
1917 db.Remove('nt' + command.id); // Delete the note for this node
1918 } else {
1919 + // Can only perform this operation on other users of our group.
1920 + var chguser = parent.users[command.id];
1921 + if (chguser == null) break; // This user does not exists
1922 + if ((user.groups != null) && (user.groups.length > 0) && ((chguser.groups == null) || (findOne(chguser.groups, user.groups) == false))) break;
1923 db.Set({ _id: 'nt' + command.id, type: 'note', value: command.notes }); // Set the note for this user
1924 }
1925 }
@@ -1873,7 +1957,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1957 ws.send(JSON.stringify({ action: 'otpauth-setup', success: true })); // Report success
1958
1959 // Notify change
1876 - parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added authentication application.', domain: domain.id });
1960 + var targets = ['*', 'server-users', user._id];
1961 + if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
1962 + parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added authentication application.', domain: domain.id });
1963 } else {
1964 ws.send(JSON.stringify({ action: 'otpauth-setup', success: false })); // Report fail
1965 }
@@ -1892,7 +1978,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1978 ws.send(JSON.stringify({ action: 'otpauth-clear', success: true })); // Report success
1979
1980 // Notify change
1895 - parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Removed authentication application.', domain: domain.id });
1981 + var targets = ['*', 'server-users', user._id];
1982 + if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
1983 + parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Removed authentication application.', domain: domain.id });
1984 } else {
1985 ws.send(JSON.stringify({ action: 'otpauth-clear', success: false })); // Report fail
1986 }
@@ -1927,7 +2015,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2015 }
2016
2017 // Notify change
1930 - parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id });
2018 + var targets = ['*', 'server-users', user._id];
2019 + if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
2020 + parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id });
2021 break;
2022 }
2023 case 'otp-hkey-get':
@@ -1958,7 +2048,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2048 }
2049
2050 // Notify change
1961 - parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Removed security key.', domain: domain.id });
2051 + var targets = ['*', 'server-users', user._id];
2052 + if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
2053 + parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Removed security key.', domain: domain.id });
2054 break;
2055 }
2056 case 'otp-hkey-yubikey-add':
@@ -2004,7 +2096,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2096 ws.send(JSON.stringify({ action: 'otp-hkey-yubikey-add', result: true, name: command.name, index: keyIndex }));
2097
2098 // Notify change TODO: Should be done on all sessions/servers for this user.
2007 - parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id });
2099 + var targets = ['*', 'server-users', user._id];
2100 + if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
2101 + parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id });
2102 } else {
2103 ws.send(JSON.stringify({ action: 'otp-hkey-yubikey-add', result: false, name: command.name }));
2104 }
@@ -2061,7 +2155,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2155 delete obj.hardwareKeyRegistrationRequest;
2156
2157 // Notify change
2064 - parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id });
2158 + var targets = ['*', 'server-users', user._id];
2159 + if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
2160 + parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id });
2161 }, function (error) {
2162 ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: false, error: error, name: command.name, index: keyIndex }));
2163 delete obj.hardwareKeyRegistrationRequest;
@@ -2128,7 +2224,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2224 ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: true, name: command.name, index: keyIndex }));
2225
2226 // Notify change
2131 - parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id });
2227 + var targets = ['*', 'server-users', user._id];
2228 + if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
2229 + parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id });
2230 }, function (error) {
2231 console.log('webauthn-endregister-error', error);
2232 ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: false, error: error, name: command.name, index: keyIndex }));
@@ -2360,5 +2458,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2458 return results;
2459 }
2460
2461 + // Return true if at least one element of arr2 is in arr1
2462 + function findOne(arr1, arr2) { if ((arr1 == null) || (arr2 == null)) return false; return arr2.some(function (v) { return arr1.indexOf(v) >= 0; }); };
2463 +
2464 return obj;
2465 };
\ No newline at end of file
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.3.2-n",
3 + "version": "0.3.2-o",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
views/default.handlebars
+23 -2
@@ -1609,12 +1609,32 @@
1609 var newsiteadmin = message.event.account.siteadmin?message.event.account.siteadmin:0;
1610 var oldsiteadmin = userinfo.siteadmin?userinfo.siteadmin:0;
1611 if ((message.event.account.quota != userinfo.quota) || (((userinfo.siteadmin & 8) == 0) && ((message.event.account.siteadmin & 8) != 0))) { meshserver.send({ action: 'files' }); }
1612 + var oldgroups = userinfo.groups;
1613 userinfo = message.event.account;
1614 if (oldsiteadmin != newsiteadmin) updateSiteAdmin();
1615 updateSelf();
1616 +
1617 + if ((userinfo.siteadmin & 2) != 0) {
1618 + // Compare our groups
1619 + var og = oldgroups ? oldgroups : [];
1620 + var ng = userinfo.groups ? userinfo.groups : [];
1621 + if (og.join(',') != ng.join(',')) {
1622 + // Our groups have changed, re-ask for a list of users.
1623 + users = wssessions = null;
1624 + meshserver.send({ action: 'users' });
1625 + meshserver.send({ action: 'wssessioncount' });
1626 + }
1627 + }
1628 }
1629 if (users == null) break;
1617 - users[message.event.account._id] = message.event.account;
1630 +
1631 + // Check if the account if part of our user group
1632 + if ((userinfo.groups == null) || (userinfo.groups.length == 0) || (findOne(message.event.account.groups, userinfo.groups) == true)) {
1633 + users[message.event.account._id] = message.event.account; // Part of our groups, update this user.
1634 + } else {
1635 + delete users[message.event.account._id]; // No longer part of our groups, remove this user.
1636 + }
1637 +
1638 updateUsers();
1639 break;
1640 }
@@ -7037,7 +7057,7 @@
7057 var removeMultiFactor = false;
7058 if ((tag == 1) && (Q('p4twoFactorRemove').checked == true)) { removeMultiFactor = true; }
7059 if (Q('p4pass1').value == Q('p4pass2').value) {
7040 - var r = { action: 'changeuserpass', user: currentUser.name, pass: Q('p4pass1').value, removeMultiFactor: removeMultiFactor, resetNextLogin: Q('p4resetNextLogin').checked };
7060 + var r = { action: 'changeuserpass', userid: currentUser._id, pass: Q('p4pass1').value, removeMultiFactor: removeMultiFactor, resetNextLogin: Q('p4resetNextLogin').checked };
7061 if (features & 0x00010000) { r.hint = Q('p4hint').value; }
7062 meshserver.send(r);
7063 }
@@ -7694,6 +7714,7 @@
7714 function validateEmail(v) { var emailReg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return emailReg.test(v); } // New version
7715 function isPrivateIP(a) { return (a.startsWith('10.') || a.startsWith('172.16.') || a.startsWith('192.168.')); }
7716 function u2fSupported() { return (window.u2f && ((navigator.userAgent.indexOf('Chrome/') > 0) || (navigator.userAgent.indexOf('Firefox/') > 0) || (navigator.userAgent.indexOf('Opera/') > 0) || (navigator.userAgent.indexOf('Safari/') > 0))); }
7717 + function findOne(arr1, arr2) { if ((arr1 == null) || (arr2 == null)) return false; return arr2.some(function (v) { return arr1.indexOf(v) >= 0; }); };
7718
7719 </script>
7720 </body>
webserver.js
+9 -1
@@ -1770,7 +1770,15 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1770 const subscriptions = [userid, 'server-global'];
1771 if (user.siteadmin != null) {
1772 if (user.siteadmin == 0xFFFFFFFF) subscriptions.push('*');
1773 - if ((user.siteadmin & 2) != 0) subscriptions.push('server-users');
1773 + if ((user.siteadmin & 2) != 0) {
1774 + if ((user.groups == null) || (user.groups.length == 0)) {
1775 + // Subscribe to all user changes
1776 + subscriptions.push('server-users');
1777 + } else {
1778 + // Subscribe to user changes for some groups
1779 + for (var i in user.groups) { subscriptions.push('server-users:' + i); }
1780 + }
1781 + }
1782 }
1783 if (user.links != null) { for (var i in user.links) { subscriptions.push(i); } }
1784 obj.parent.RemoveAllEventDispatch(target);