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);