Added edituser action to meshctrl.js
Ylian Saint-Hilaire committed
Dec 9, 2020 at 16:34 UTC
90f506716cf1a43125dfed6e0e5be41fedb3ef2d
2 files changed
+131
-28
meshctrl.js
+92
-25
@@ -7,7 +7,7 @@ try { require('ws'); } catch (ex) { console.log('Missing module "ws", type "npm
7
var settings = {};
8
const crypto = require('crypto');
9
const args = require('minimist')(process.argv.slice(2));
10
-const possibleCommands = ['listusers', 'listusersessions', 'listdevicegroups', 'listdevices', 'listusersofdevicegroup', 'serverinfo', 'userinfo', 'adduser', 'removeuser', 'adddevicegroup', 'removedevicegroup', 'editdevicegroup', 'broadcast', 'showevents', 'addusertodevicegroup', 'removeuserfromdevicegroup', 'addusertodevice', 'removeuserfromdevice', 'sendinviteemail', 'generateinvitelink', 'config', 'movetodevicegroup', 'deviceinfo', 'addusergroup', 'listusergroups', 'removeusergroup', 'runcommand', 'shell', 'upload', 'download', 'deviceopenurl', 'devicemessage', 'devicetoast', 'addtousergroup', 'removefromusergroup', 'removeallusersfromusergroup'];
10
+const possibleCommands = ['edituser', 'listusers', 'listusersessions', 'listdevicegroups', 'listdevices', 'listusersofdevicegroup', 'serverinfo', 'userinfo', 'adduser', 'removeuser', 'adddevicegroup', 'removedevicegroup', 'editdevicegroup', 'broadcast', 'showevents', 'addusertodevicegroup', 'removeuserfromdevicegroup', 'addusertodevice', 'removeuserfromdevice', 'sendinviteemail', 'generateinvitelink', 'config', 'movetodevicegroup', 'deviceinfo', 'addusergroup', 'listusergroups', 'removeusergroup', 'runcommand', 'shell', 'upload', 'download', 'deviceopenurl', 'devicemessage', 'devicetoast', 'addtousergroup', 'removefromusergroup', 'removeallusersfromusergroup'];
11
if (args.proxy != null) { try { require('https-proxy-agent'); } catch (ex) { console.log('Missing module "https-proxy-agent", type "npm install https-proxy-agent" to install it.'); return; } }
12
13
if (args['_'].length == 0) {
@@ -27,6 +27,7 @@ if (args['_'].length == 0) {
27
console.log(" DeviceInfo - Show information about a device.");
28
console.log(" Config - Perform operation on config.json file.");
29
console.log(" AddUser - Create a new user account.");
30
+ console.log(" EditUser - Change a user account.");
31
console.log(" RemoveUser - Delete a user account.");
32
console.log(" AddUserGroup - Create a new user group.");
33
console.log(" RemoveUserGroup - Delete a user group.");
@@ -61,7 +62,7 @@ if (args['_'].length == 0) {
62
console.log(" --loginkey [hex] - Server login key in hex.");
63
console.log(" --loginkeyfile [file] - File containing server login key in hex.");
64
console.log(" --logindomain [domainid] - Domain id, default is empty, only used with loginkey.");
64
- console.log(" --proxy [http://proxy:1] - Specify an HTTP proxy.");
65
+ console.log(" --proxy [http://proxy:123] - Specify an HTTP proxy.");
66
return;
67
} else {
68
settings.cmd = args['_'][0].toLowerCase();
@@ -144,6 +145,11 @@ if (args['_'].length == 0) {
145
else { ok = true; }
146
break;
147
}
148
+ case 'edituser': {
149
+ if (args.userid == null) { console.log("Edit account user missing, use --userid [id]"); }
150
+ else { ok = true; }
151
+ break;
152
+ }
153
case 'removeuser': {
154
if (args.userid == null) { console.log("Remove account userid missing, use --userid [id]"); }
155
else { ok = true; }
@@ -366,22 +372,37 @@ if (args['_'].length == 0) {
372
case 'adduser': {
373
console.log("Add a new user account, Example usages:\r\n");
374
console.log(" MeshCtrl AddUser --user newaccountname --pass newpassword");
375
+ console.log(" MeshCtrl AddUser --user newaccountname --randompass --rights full");
376
+ console.log("\r\nRequired arguments:\r\n");
377
+ console.log(" --user [name] - New account name.");
378
+ console.log(" --pass [password] - New account password.");
379
+ console.log(" --randompass - Create account with a random password.");
380
+ console.log("\r\nOptional arguments:\r\n");
381
+ console.log(" --domain [domain] - Account domain, only for cross-domain admins.");
382
+ console.log(" --email [email] - New account email address.");
383
+ console.log(" --emailverified - New account email is verified.");
384
+ console.log(" --resetpass - Request password reset on next login.");
385
+ console.log(" --realname [name] - Set the real name for this account.");
386
+ console.log(" --phone [number] - Set the account phone number.");
387
+ console.log(" --rights [none|full|a,b,c] - Comma seperated list of server permissions. Possible values:");
388
+ console.log(" manageusers,backup,restore,update,fileaccess,locked,nonewgroups,notools,usergroups,recordings,locksettings,allevents");
389
+ break;
390
+ }
391
+ case 'edituser': {
392
+ console.log("Edit a user account, Example usages:\r\n");
393
+ console.log(" MeshCtrl EditUser --userid user --rights locked,locksettings");
394
+ console.log(" MeshCtrl EditUser --userid user --realname Jones");
395
console.log("\r\nRequired arguments:\r\n");
370
- console.log(" --user [name] - New account name.");
371
- console.log(" --pass [password] - New account password.");
372
- console.log(" --randompass - Create account with a random password.");
396
+ console.log(" --userid [name] - User account identifier.");
397
console.log("\r\nOptional arguments:\r\n");
374
- console.log(" --email [email] - New account email address.");
375
- console.log(" --emailverified - New account email is verified.");
376
- console.log(" --resetpass - Request password reset on next login.");
377
- console.log(" --siteadmin - Create the account as full site administrator.");
378
- console.log(" --manageusers - Allow this account to manage server users.");
379
- console.log(" --fileaccess - Allow this account to store server files.");
380
- console.log(" --serverupdate - Allow this account to update the server.");
381
- console.log(" --locked - This account will be locked.");
382
- console.log(" --nonewgroups - Account will not be allowed to create device groups.");
383
- console.log(" --notools - Account not see MeshCMD download links.");
384
- console.log(" --domain [domain] - Account domain, only for cross-domain admins.");
398
+ console.log(" --domain [domain] - Account domain, only for cross-domain admins.");
399
+ console.log(" --email [email] - Account email address.");
400
+ console.log(" --emailverified - Account email is verified.");
401
+ console.log(" --resetpass - Request password reset on next login.");
402
+ console.log(" --realname [name] - Set the real name for this account.");
403
+ console.log(" --phone [number] - Set the account phone number.");
404
+ console.log(" --rights [none|full|a,b,c] - Comma seperated list of server permissions. Possible values:");
405
+ console.log(" manageusers,backup,restore,update,fileaccess,locked,nonewgroups,notools,usergroups,recordings,locksettings,allevents");
406
break;
407
}
408
case 'removeuser': {
@@ -982,20 +1003,32 @@ function serverConnect() {
1003
break;
1004
}
1005
case 'adduser': {
985
- var siteadmin = 0;
986
- if (args.siteadmin) { siteadmin = 0xFFFFFFFF; }
987
- if (args.manageusers) { siteadmin |= 2; }
988
- if (args.fileaccess) { siteadmin |= 8; }
989
- if (args.serverupdate) { siteadmin |= 16; }
990
- if (args.locked) { siteadmin |= 32; }
991
- if (args.nonewgroups) { siteadmin |= 64; }
992
- if (args.notools) { siteadmin |= 128; }
1006
+ var siteadmin = getSiteAdminRights(args);
1007
if (args.randompass) { args.pass = getRandomAmtPassword(); }
1008
var op = { action: 'adduser', username: args.user, pass: args.pass, responseid: 'meshctrl' };
1009
if (args.email) { op.email = args.email; if (args.emailverified) { op.emailVerified = true; } }
1010
if (args.resetpass) { op.resetNextLogin = true; }
997
- if (siteadmin != 0) { op.siteadmin = siteadmin; }
1011
+ if (siteadmin != -1) { op.siteadmin = siteadmin; }
1012
if (args.domain) { op.domain = args.domain; }
1013
+ if (args.phone === true) { op.phone = ''; }
1014
+ if (typeof args.phone == 'string') { op.phone = args.phone; }
1015
+ if (typeof args.realname == 'string') { op.realname = args.realname; }
1016
+ ws.send(JSON.stringify(op));
1017
+ break;
1018
+ }
1019
+ case 'edituser': {
1020
+ var userid = args.userid;
1021
+ if ((args.domain != null) && (userid.indexOf('/') < 0)) { userid = 'user/' + args.domain + '/' + userid; }
1022
+ var siteadmin = getSiteAdminRights(args);
1023
+ var op = { action: 'edituser', userid: userid, responseid: 'meshctrl' };
1024
+ if (args.email) { op.email = args.email; if (args.emailverified) { op.emailVerified = true; } }
1025
+ if (args.resetpass) { op.resetNextLogin = true; }
1026
+ if (siteadmin != -1) { op.siteadmin = siteadmin; }
1027
+ if (args.domain) { op.domain = args.domain; }
1028
+ if (args.phone === true) { op.phone = ''; }
1029
+ if (typeof args.phone == 'string') { op.phone = args.phone; }
1030
+ if (typeof args.realname == 'string') { op.realname = args.realname; }
1031
+ if (args.realname === true) { op.realname = ''; }
1032
ws.send(JSON.stringify(op));
1033
break;
1034
}
@@ -1269,6 +1302,39 @@ function serverConnect() {
1302
}
1303
});
1304
1305
+ function getSiteAdminRights(args) {
1306
+ var siteadmin = -1;
1307
+ if (typeof args.rights == 'number') {
1308
+ siteadmin = args.rights;
1309
+ } else if (typeof args.rights == 'string') {
1310
+ siteadmin = 0;
1311
+ var srights = args.rights.toLowerCase().split(',');
1312
+ if (srights.indexOf('full') != -1) { siteadmin = 0xFFFFFFFF; }
1313
+ if (srights.indexOf('none') != -1) { siteadmin = 0x00000000; }
1314
+ if (srights.indexOf('backup') != -1) { siteadmin |= 0x00000001; }
1315
+ if (srights.indexOf('manageusers') != -1) { siteadmin |= 0x00000002; }
1316
+ if (srights.indexOf('restore') != -1) { siteadmin |= 0x00000004; }
1317
+ if (srights.indexOf('fileaccess') != -1) { siteadmin |= 0x00000008; }
1318
+ if (srights.indexOf('update') != -1) { siteadmin |= 0x00000010; }
1319
+ if (srights.indexOf('locked') != -1) { siteadmin |= 0x00000020; }
1320
+ if (srights.indexOf('nonewgroups') != -1) { siteadmin |= 0x00000040; }
1321
+ if (srights.indexOf('notools') != -1) { siteadmin |= 0x00000080; }
1322
+ if (srights.indexOf('usergroups') != -1) { siteadmin |= 0x00000100; }
1323
+ if (srights.indexOf('recordings') != -1) { siteadmin |= 0x00000200; }
1324
+ if (srights.indexOf('locksettings') != -1) { siteadmin |= 0x00000400; }
1325
+ if (srights.indexOf('allevents') != -1) { siteadmin |= 0x00000800; }
1326
+ }
1327
+
1328
+ if (args.siteadmin) { siteadmin = 0xFFFFFFFF; }
1329
+ if (args.manageusers) { if (siteadmin == -1) { siteadmin = 0; } siteadmin |= 2; }
1330
+ if (args.fileaccess) { if (siteadmin == -1) { siteadmin = 0; } siteadmin |= 8; }
1331
+ if (args.serverupdate) { if (siteadmin == -1) { siteadmin = 0; } siteadmin |= 16; }
1332
+ if (args.locked) { if (siteadmin == -1) { siteadmin = 0; } siteadmin |= 32; }
1333
+ if (args.nonewgroups) { if (siteadmin == -1) { siteadmin = 0; } siteadmin |= 64; }
1334
+ if (args.notools) { if (siteadmin == -1) { siteadmin = 0; } siteadmin |= 128; }
1335
+ return siteadmin;
1336
+ }
1337
+
1338
ws.on('close', function() { process.exit(); });
1339
ws.on('error', function (err) {
1340
if (err.code == 'ENOTFOUND') { console.log('Unable to resolve ' + url); }
@@ -1360,6 +1426,7 @@ function serverConnect() {
1426
case 'msg': // SHELL
1427
case 'toast': // TOAST
1428
case 'adduser': // ADDUSER
1429
+ case 'edituser': // EDITUSER
1430
case 'deleteuser': // REMOVEUSER
1431
case 'createmesh': // ADDDEVICEGROUP
1432
case 'deletemesh': // REMOVEDEVICEGROUP
meshuser.js
+39
-3
@@ -1931,6 +1931,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1931
if (command.email != null) { newuser.email = command.email.toLowerCase(); if (command.emailVerified === true) { newuser.emailVerified = true; } } // Email
1932
if (command.resetNextLogin === true) { newuser.passchange = -1; } else { newuser.passchange = Math.floor(Date.now() / 1000); }
1933
if (user.groups) { newuser.groups = user.groups; } // New accounts are automatically part of our groups (Realms).
1934
+ if (common.validateString(command.realname, 1, 256)) { newuser.realname = command.realname; }
1935
+ if ((command.consent != null) && (typeof command.consent == 'number')) { if (command.consent == 0) { delete chguser.consent; } else { newuser.consent = command.consent; } change = 1; }
1936
+ if ((command.phone != null) && (typeof command.phone == 'string') && ((command.phone == '') || isPhoneNumber(command.phone))) { if (command.phone == '') { delete newuser.phone; } else { newuser.phone = command.phone; } change = 1; }
1937
1938
// Auto-join any user groups
1939
if (typeof newuserdomain.newaccountsusergroups == 'object') {
@@ -1997,8 +2000,37 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2000
// Must be user administrator or edit self.
2001
if (((user.siteadmin & 2) == 0) && (user._id != command.id)) break;
2002
2003
+ // User the username as userid if needed
2004
+ if ((typeof command.username == 'string') && (command.userid == null)) { command.userid = command.username; }
2005
+ if ((typeof command.id == 'string') && (command.userid == null)) { command.userid = command.id; }
2006
+
2007
+ // Edit a user account
2008
+ var err = null, editusersplit, edituserid, edituser, edituserdomain;
2009
+ try {
2010
+ if ((user.siteadmin & 2) == 0) { err = 'Permission denied'; }
2011
+ else if (common.validateString(command.userid, 1, 2048) == false) { err = 'Invalid userid'; }
2012
+ else {
2013
+ if (command.userid.indexOf('/') < 0) { command.userid = 'user/' + domain.id + '/' + command.userid; }
2014
+ editusersplit = command.userid.split('/');
2015
+ edituserid = command.userid;
2016
+ edituser = parent.users[edituserid];
2017
+ if (edituser == null) { err = 'User does not exists'; }
2018
+ else if ((obj.crossDomain !== true) && ((editusersplit.length != 3) || (editusersplit[1] != domain.id))) { err = 'Invalid domain'; } // Invalid domain, operation only valid for current domain
2019
+ else if ((edituser.siteadmin === SITERIGHT_ADMIN) && (user.siteadmin != SITERIGHT_ADMIN)) { err = 'Permission denied'; } // Need full admin to remote another administrator
2020
+ else if ((obj.crossDomain !== true) && (user.groups != null) && (user.groups.length > 0) && ((edituser.groups == null) || (findOne(edituser.groups, user.groups) == false))) { err = 'Invalid user group'; } // Can only perform this operation on other users of our group.
2021
+ }
2022
+ } catch (ex) { err = 'Validation exception: ' + ex; }
2023
+
2024
+ // Handle any errors
2025
+ if (err != null) {
2026
+ if (command.responseid != null) {
2027
+ try { ws.send(JSON.stringify({ action: 'edituser', responseid: command.responseid, result: err })); } catch (ex) { }
2028
+ }
2029
+ break;
2030
+ }
2031
+
2032
// Edit a user account, may involve changing email or administrator permissions
2001
- var chguser = parent.users[command.id];
2033
+ var chguser = parent.users[edituserid];
2034
change = 0;
2035
if (chguser) {
2036
// If the target user is admin and we are not admin, no changes can be made.
@@ -2010,7 +2042,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2042
}
2043
2044
// Fetch and validate the user domain
2013
- var edituserdomainid = command.id.split('/')[1];
2045
+ var edituserdomainid = edituserid.split('/')[1];
2046
if ((obj.crossDomain !== true) && (edituserdomainid != domain.id)) break;
2047
var edituserdomain = parent.parent.config.domains[edituserdomainid];
2048
if (edituserdomain == null) break;
@@ -2023,7 +2055,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2055
}
2056
}
2057
2026
- // Validate and change realm name
2058
+ // Validate and change real name
2059
if (common.validateString(command.realname, 0, 256) && (chguser.realname != command.realname)) {
2060
if (command.realname == '') { delete chguser.realname; } else { chguser.realname = command.realname; }
2061
change = 1;
@@ -2032,6 +2064,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2064
// Make changes
2065
if ((command.emailVerified === true || command.emailVerified === false) && (chguser.emailVerified != command.emailVerified)) { chguser.emailVerified = command.emailVerified; change = 1; }
2066
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; }
2067
+ if (command.resetNextLogin === true) { chguser.passchange = -1; }
2068
if ((command.consent != null) && (typeof command.consent == 'number')) { if (command.consent == 0) { delete chguser.consent; } else { chguser.consent = command.consent; } change = 1; }
2069
if ((command.phone != null) && (typeof command.phone == 'string') && ((command.phone == '') || isPhoneNumber(command.phone))) { if (command.phone == '') { delete chguser.phone; } else { chguser.phone = command.phone; } change = 1; }
2070
@@ -2089,6 +2122,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2122
parent.parent.DispatchEvent([chguser._id], obj, 'close'); // Disconnect all this user's sessions
2123
}
2124
}
2125
+
2126
+ // OK Response
2127
+ if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'edituser', responseid: command.responseid, result: 'ok' })); } catch (ex) { } }
2128
break;
2129
}
2130
case 'usergroups':