From 02430e3a63515068f8b3c2c17c5d0edfcd4f9ddc Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Wed, 27 May 2020 18:26:54 -0700 Subject: [PATCH] User management improvements. --- meshuser.js | 40 ++++++- public/scripts/amt-wsman-0.2.0-min.js | 2 +- views/default.handlebars | 4 +- webserver.js | 154 ++++++++++++++++++++++++-- 4 files changed, 183 insertions(+), 17 deletions(-) diff --git a/meshuser.js b/meshuser.js index e4cf10f0..9283301d 100644 --- a/meshuser.js +++ b/meshuser.js @@ -1433,8 +1433,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } case 'deleteuser': { - console.log(command); - // Delete a user account var err = null, delusersplit, deluserid, deluser, deluserdomain; try { @@ -1494,12 +1492,23 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come. parent.parent.DispatchEvent(parent.CreateNodeDispatchTargets(node.meshid, node._id), obj, event); }); + } else if (i.startsWith('ugrp/')) { + // Get the device group + var ugroup = parent.userGroups[i]; + if (ugroup) { + // Remove user from the user group + if (ugroup.links[deluser._id] != null) { delete ugroup.links[deluser._id]; parent.db.Set(ugroup); } + + // Notify user group change + change = 'Removed user ' + deluser.name + ' from user group ' + ugroup.name; + var event = { etype: 'ugrp', userid: user._id, username: user.name, ugrpid: ugroup._id, name: ugroup.name, desc: ugroup.desc, action: 'usergroupchange', links: ugroup.links, msg: 'Removed user ' + deluser.name + ' from user group ' + ugroup.name, addUserDomain: deluserdomain.id }; + if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user group. Another event will come. + parent.parent.DispatchEvent(['*', ugroup._id, user._id, deluser._id], obj, event); + } } } } - // TODO (UserGroups): Remove user groups?? - db.Remove('ws' + deluser._id); // Remove user web state db.Remove('nt' + deluser._id); // Remove notes for this user @@ -1707,6 +1716,29 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (command.resetNextLogin === true) { newuser.passchange = -1; } else { newuser.passchange = Math.floor(Date.now() / 1000); } if (user.groups) { newuser.groups = user.groups; } // New accounts are automatically part of our groups (Realms). + // Auto-join any user groups + if (typeof newuserdomain.newaccountsusergroups == 'object') { + for (var i in newuserdomain.newaccountsusergroups) { + var ugrpid = newuserdomain.newaccountsusergroups[i]; + if (ugrpid.indexOf('/') < 0) { ugrpid = 'ugrp/' + newuserdomain.id + '/' + ugrpid; } + var ugroup = parent.userGroups[ugrpid]; + if (ugroup != null) { + // Add group to the user + if (newuser.links == null) { newuser.links = {}; } + newuser.links[ugroup._id] = { rights: 1 }; + + // Add user to the group + ugroup.links[newuser._id] = { userid: newuser._id, name: newuser.name, rights: 1 }; + db.Set(ugroup); + + // Notify user group change + var event = { etype: 'ugrp', userid: user._id, username: user.name, ugrpid: ugroup._id, name: ugroup.name, desc: ugroup.desc, action: 'usergroupchange', links: ugroup.links, msg: 'Added user ' + newuser.name + ' to user group ' + ugroup.name, addUserDomain: newuserdomain.id }; + if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user group. Another event will come. + parent.parent.DispatchEvent(['*', ugroup._id, user._id, newuser._id], obj, event); + } + } + } + parent.users[newuserid] = newuser; // Create a user, generate a salt and hash the password diff --git a/public/scripts/amt-wsman-0.2.0-min.js b/public/scripts/amt-wsman-0.2.0-min.js index 293387e7..389c6296 100644 --- a/public/scripts/amt-wsman-0.2.0-min.js +++ b/public/scripts/amt-wsman-0.2.0-min.js @@ -1 +1 @@ -var WsmanStackCreateService=function(e,s,r,a,o,t){var p={};function l(e){if(!e)return"";var s=" ";for(var r in e)e.hasOwnProperty(r)&&0===r.indexOf("@")&&(s+=r.substring(1)+'="'+e[r]+'" ');return s}function w(e){if(!e)return"";if("string"==typeof e)return e;if(e.InstanceID)return''+e.InstanceID+"";var s="";for(var r in e)if(e.hasOwnProperty(r)){if(s+='',e[r].ReferenceParameters){s+="",s+=""+e[r].Address+""+e[r].ReferenceParameters.ResourceURI+"";var a=e[r].ReferenceParameters.SelectorSet.Selector;if(Array.isArray(a))for(var o=0;o"+a[o].Value+"";else s+=""+a.Value+"";s+=""}else s+=e[r];s+=""}return s+=""}return p.NextMessageId=1,p.Address="/wsman",p.comm=CreateWsmanComm(e,s,r,a,o,t),p.PerformAjax=function(e,o,s,r,a){null==a&&(a=""),p.comm.PerformAjax('
"+e,function(e,s,r){if(200==s){var a=p.ParseWsman(e);a&&null!=a?o(p,a.Header.ResourceURI,a,200,r):o(p,null,{Header:{HttpError:s}},601,r)}else o(p,null,{Header:{HttpError:s}},s,r)},s,r)},p.CancelAllQueries=function(e){p.comm.CancelAllQueries(e)},p.GetNameFromUrl=function(e){var s=e.lastIndexOf("/");return-1==s?e:e.substring(s+1)},p.ExecSubscribe=function(e,s,r,a,o,t,n,l,d,c){var m="",i="";null!=d&&null!=c&&(m="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken"+d+''+c+"",i=''),l=null!=l&&null!=l?""+l+"":"";var u="http://schemas.xmlsoap.org/ws/2004/08/eventing/Subscribe"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"+w(n)+m+'
'+r+""+i+"PT0.000000S";p.PerformAjax(u+"
",a,o,t,'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing" xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust" xmlns:se="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:m="http://x.com"')},p.ExecUnSubscribe=function(e,s,r,a,o){var t="http://schemas.xmlsoap.org/ws/2004/08/eventing/Unsubscribe"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"+w(o)+"";p.PerformAjax(t+"",s,r,a,'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing"')},p.ExecPut=function(e,s,r,a,o,t){var n="http://schemas.xmlsoap.org/ws/2004/09/transfer/Put"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60.000S"+w(t)+""+function(e,s){if(!e||null==s)return"";var r=p.GetNameFromUrl(e),a="';for(var o in s)if(s.hasOwnProperty(o)&&0!==o.indexOf("__")&&0!==o.indexOf("@")&&void 0!==s[o]&&null!==s[o]&&"function"!=typeof s[o])if("object"==typeof s[o]&&s[o].ReferenceParameters){a+=""+s[o].Address+""+s[o].ReferenceParameters.ResourceURI+"";var t=s[o].ReferenceParameters.SelectorSet.Selector;if(Array.isArray(t))for(var n=0;n"+t[n].Value+"";else a+=""+t.Value+"";a+=""}else if(Array.isArray(s[o]))for(n=0;n"+s[o][n].toString()+"";else a+=""+s[o].toString()+"";return a+=""}(e,s);p.PerformAjax(n+"",r,a,o)},p.ExecCreate=function(e,s,r,a,o,t){var n=p.GetNameFromUrl(e),l="http://schemas.xmlsoap.org/ws/2004/09/transfer/Create"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S"+w(t)+"';for(var d in s)l+=""+s[d]+"";p.PerformAjax(l+"",r,a,o)},p.ExecCreateXml=function(e,s,r,a,o){var t=p.GetNameFromUrl(e);p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/transfer/Create"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60.000S'+s+"",r,a,o)},p.ExecDelete=function(e,s,r,a,o){var t="http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S"+w(s)+"";p.PerformAjax(t,r,a,o)},p.ExecGet=function(e,s,r,a){p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/transfer/Get"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S",s,r,a)},p.ExecMethod=function(e,s,r,a,o,t,n){var l="";for(var d in r)if(null!=r[d])if(Array.isArray(r[d]))for(var c in r[d])l+=""+r[d][c]+"";else l+=""+r[d]+"";p.ExecMethodXml(e,s,l,a,o,t,n)},p.ExecMethodXml=function(e,s,r,a,o,t,n){p.PerformAjax(e+"/"+s+""+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S"+w(n)+"'+r+"",a,o,t)},p.ExecEnum=function(e,s,r,a){p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate"+p.Address+""+e+""+p.NextMessageId+++'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S',s,r,a)},p.ExecPull=function(e,s,r,a,o){p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Pull"+p.Address+""+e+""+p.NextMessageId+++'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S'+s+"99999999",r,a,o)},p.ParseWsman=function(s){try{s.childNodes||(s=function(e){{if(window.DOMParser)return(new DOMParser).parseFromString(e,"text/xml");var s=new ActiveXObject("Microsoft.XMLDOM");return s.async=!1,s.loadXML(e),s}}(s));var e,r={Header:{}},a=s.getElementsByTagName("Header")[0];if(!(a=a||s.getElementsByTagName("a:Header")[0]))return null;for(var o=0;o'+e.InstanceID+"";var s="";for(var r in e)if(e.hasOwnProperty(r)){if(s+='',e[r].ReferenceParameters){s+="",s+=""+e[r].Address+""+e[r].ReferenceParameters.ResourceURI+"";var a=e[r].ReferenceParameters.SelectorSet.Selector;if(Array.isArray(a))for(var o=0;o"+a[o].Value+"";else s+=""+a.Value+"";s+=""}else s+=e[r];s+=""}return s+=""}return p.NextMessageId=1,p.Address="/wsman",p.comm=CreateWsmanComm(e,s,r,a,o,t),p.PerformAjax=function(e,o,s,r,a){null==a&&(a=""),p.comm.PerformAjax('
"+e,function(e,s,r){if(200==s){var a=p.ParseWsman(e);a&&null!=a?o(p,a.Header.ResourceURI,a,200,r):o(p,null,{Header:{HttpError:s}},601,r)}else o(p,null,{Header:{HttpError:s}},s,r)},s,r)},p.CancelAllQueries=function(e){p.comm.CancelAllQueries(e)},p.GetNameFromUrl=function(e){var s=e.lastIndexOf("/");return-1==s?e:e.substring(s+1)},p.ExecSubscribe=function(e,s,r,a,o,t,n,l,c,d){var m="",i="";null!=c&&null!=d&&(m="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken"+c+''+d+"",i=''),l=null!=l&&null!=l?""+l+"":"";var u="http://schemas.xmlsoap.org/ws/2004/08/eventing/Subscribe"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"+w(n)+m+'
'+r+""+i+"PT0.000000S";p.PerformAjax(u+"
",a,o,t,'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing" xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust" xmlns:se="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:m="http://x.com"')},p.ExecUnSubscribe=function(e,s,r,a,o){var t="http://schemas.xmlsoap.org/ws/2004/08/eventing/Unsubscribe"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"+w(o)+"";p.PerformAjax(t+"",s,r,a,'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing"')},p.ExecPut=function(e,s,r,a,o,t){var n="http://schemas.xmlsoap.org/ws/2004/09/transfer/Put"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60.000S"+w(t)+""+function(e,s){if(!e||null==s)return"";var r=p.GetNameFromUrl(e),a="';for(var o in s)if(s.hasOwnProperty(o)&&0!==o.indexOf("__")&&0!==o.indexOf("@")&&void 0!==s[o]&&null!==s[o]&&"function"!=typeof s[o])if("object"==typeof s[o]&&s[o].ReferenceParameters){a+=""+s[o].Address+""+s[o].ReferenceParameters.ResourceURI+"";var t=s[o].ReferenceParameters.SelectorSet.Selector;if(Array.isArray(t))for(var n=0;n"+t[n].Value+"";else a+=""+t.Value+"";a+=""}else if(Array.isArray(s[o]))for(n=0;n"+s[o][n].toString()+"";else a+=""+s[o].toString()+"";return a+=""}(e,s);p.PerformAjax(n+"",r,a,o)},p.ExecCreate=function(e,s,r,a,o,t){var n=p.GetNameFromUrl(e),l="http://schemas.xmlsoap.org/ws/2004/09/transfer/Create"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S"+w(t)+"';for(var c in s)l+=""+s[c]+"";p.PerformAjax(l+"",r,a,o)},p.ExecCreateXml=function(e,s,r,a,o){var t=p.GetNameFromUrl(e);p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/transfer/Create"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60.000S'+s+"",r,a,o)},p.ExecDelete=function(e,s,r,a,o){var t="http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S"+w(s)+"";p.PerformAjax(t,r,a,o)},p.ExecGet=function(e,s,r,a){p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/transfer/Get"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S",s,r,a)},p.ExecMethod=function(e,s,r,a,o,t,n){var l="";for(var c in r)if(null!=r[c])if(Array.isArray(r[c]))for(var d in r[c])l+=""+r[c][d]+"";else l+=""+r[c]+"";p.ExecMethodXml(e,s,l,a,o,t,n)},p.ExecMethodXml=function(e,s,r,a,o,t,n){p.PerformAjax(e+"/"+s+""+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S"+w(n)+"'+r+"",a,o,t)},p.ExecEnum=function(e,s,r,a){p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate"+p.Address+""+e+""+p.NextMessageId+++'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S',s,r,a)},p.ExecPull=function(e,s,r,a,o){p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Pull"+p.Address+""+e+""+p.NextMessageId+++'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S'+s+"99999999",r,a,o)},p.ParseWsman=function(s){try{s.childNodes||(s=function(e){{if(window.DOMParser)return(new DOMParser).parseFromString(e,"text/xml");var s=new ActiveXObject("Microsoft.XMLDOM");return s.async=!1,s.loadXML(e),s}}(s));var e,r={Header:{}},a=s.getElementsByTagName("Header")[0];if(!(a=a||s.getElementsByTagName("a:Header")[0]))return null;for(var o=0;o' + "Default" + '')); x += addDeviceAttribute("Group Identifier", EscapeHtml(group._id)); @@ -10972,7 +10972,7 @@ var email = user.email?EscapeHtml(user.email):'' + "Not set" + '', everify = ''; if (serverinfo.emailcheck) { everify = ((user.emailVerified == true) ? ' ' : ' '); } - if (serverinfo.crossDomain) { + if ((serverinfo.crossDomain) || (debugmode != 0)) { var d = user._id.split('/')[1]; x += addDeviceAttribute("Domain", ((d != '')?EscapeHtml(d):('' + "Default" + ''))); x += addDeviceAttribute("User Identifier", EscapeHtml(user._id)); diff --git a/webserver.js b/webserver.js index e2d72ceb..dfe20180 100644 --- a/webserver.js +++ b/webserver.js @@ -385,15 +385,36 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (user == null) { // Create a new user var user = { type: 'user', _id: userid, name: username, creation: Math.floor(Date.now() / 1000), login: Math.floor(Date.now() / 1000), domain: domain.id }; - if (email) { - user['email'] = email; - user['emailVerified'] = true; - } + if (email) { user['email'] = email; user['emailVerified'] = true; } if (domain.newaccountsrights) { user.siteadmin = domain.newaccountsrights; } if (obj.common.validateStrArray(domain.newaccountrealms)) { user.groups = domain.newaccountrealms; } var usercount = 0; for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } } if (usercount == 0) { user.siteadmin = 4294967295; /*if (domain.newaccounts === 2) { delete domain.newaccounts; }*/ } // If this is the first user, give the account site admin. + + // Auto-join any user groups + if (typeof domain.newaccountsusergroups == 'object') { + for (var i in domain.newaccountsusergroups) { + var ugrpid = domain.newaccountsusergroups[i]; + if (ugrpid.indexOf('/') < 0) { ugrpid = 'ugrp/' + domain.id + '/' + ugrpid; } + var ugroup = obj.userGroups[ugrpid]; + if (ugroup != null) { + // Add group to the user + if (user.links == null) { user.links = {}; } + user.links[ugroup._id] = { rights: 1 }; + + // Add user to the group + ugroup.links[user._id] = { userid: user._id, name: user.name, rights: 1 }; + db.Set(ugroup); + + // Notify user group change + var event = { etype: 'ugrp', ugrpid: ugroup._id, name: ugroup.name, desc: ugroup.desc, action: 'usergroupchange', links: ugroup.links, msg: 'Added user ' + user.name + ' to user group ' + ugroup.name, addUserDomain: domain.id }; + if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user group. Another event will come. + parent.DispatchEvent(['*', ugroup._id, user._id], obj, event); + } + } + } + obj.users[user._id] = user; obj.db.SetUser(user); var event = { etype: 'user', userid: userid, username: username, account: obj.CloneSafeUser(user), action: 'accountcreate', msg: 'Account created, name is ' + name, domain: domain.id }; @@ -483,6 +504,30 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { var usercount = 0; for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } } if (usercount == 0) { user.siteadmin = 4294967295; /*if (domain.newaccounts === 2) { delete domain.newaccounts; }*/ } // If this is the first user, give the account site admin. + + // Auto-join any user groups + if (typeof domain.newaccountsusergroups == 'object') { + for (var i in domain.newaccountsusergroups) { + var ugrpid = domain.newaccountsusergroups[i]; + if (ugrpid.indexOf('/') < 0) { ugrpid = 'ugrp/' + domain.id + '/' + ugrpid; } + var ugroup = obj.userGroups[ugrpid]; + if (ugroup != null) { + // Add group to the user + if (user.links == null) { user.links = {}; } + user.links[ugroup._id] = { rights: 1 }; + + // Add user to the group + ugroup.links[user._id] = { userid: user._id, name: user.name, rights: 1 }; + db.Set(ugroup); + + // Notify user group change + var event = { etype: 'ugrp', ugrpid: ugroup._id, name: ugroup.name, desc: ugroup.desc, action: 'usergroupchange', links: ugroup.links, msg: 'Added user ' + user.name + ' to user group ' + ugroup.name, addUserDomain: domain.id }; + if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user group. Another event will come. + parent.DispatchEvent(['*', ugroup._id, user._id], obj, event); + } + } + } + obj.users[user._id] = user; obj.db.SetUser(user); var event = { etype: 'user', userid: user._id, username: user.name, account: obj.CloneSafeUser(user), action: 'accountcreate', msg: 'Account created, name is ' + name, domain: domain.id }; @@ -1147,6 +1192,30 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (obj.common.validateStrArray(domain.newaccountrealms)) { user.groups = domain.newaccountrealms; } if ((domain.passwordrequirements != null) && (domain.passwordrequirements.hint === true) && (req.body.apasswordhint)) { var hint = req.body.apasswordhint; if (hint.length > 250) { hint = hint.substring(0, 250); } user.passhint = hint; } if (domainUserCount == 0) { user.siteadmin = 4294967295; /*if (domain.newaccounts === 2) { delete domain.newaccounts; }*/ } // If this is the first user, give the account site admin. + + // Auto-join any user groups + if (typeof domain.newaccountsusergroups == 'object') { + for (var i in domain.newaccountsusergroups) { + var ugrpid = domain.newaccountsusergroups[i]; + if (ugrpid.indexOf('/') < 0) { ugrpid = 'ugrp/' + domain.id + '/' + ugrpid; } + var ugroup = obj.userGroups[ugrpid]; + if (ugroup != null) { + // Add group to the user + if (user.links == null) { user.links = {}; } + user.links[ugroup._id] = { rights: 1 }; + + // Add user to the group + ugroup.links[user._id] = { userid: user._id, name: user.name, rights: 1 }; + db.Set(ugroup); + + // Notify user group change + var event = { etype: 'ugrp', ugrpid: ugroup._id, name: ugroup.name, desc: ugroup.desc, action: 'usergroupchange', links: ugroup.links, msg: 'Added user ' + user.name + ' to user group ' + ugroup.name, addUserDomain: domain.id }; + if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user group. Another event will come. + parent.DispatchEvent(['*', ugroup._id, user._id], obj, event); + } + } + } + obj.users[user._id] = user; req.session.userid = user._id; req.session.domainid = domain.id; @@ -1619,20 +1688,20 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { for (var i in deluser.links) { if (i.startsWith('mesh/')) { // Get the device group - mesh = parent.meshes[i]; + var mesh = obj.meshes[i]; if (mesh) { // Remove user from the mesh if (mesh.links[deluser._id] != null) { delete mesh.links[deluser._id]; parent.db.Set(mesh); } // Notify mesh change - change = 'Removed user ' + deluser.name + ' from group ' + mesh.name; + var change = 'Removed user ' + deluser.name + ' from group ' + mesh.name; var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id, invite: mesh.invite }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come. - parent.parent.DispatchEvent(['*', mesh._id, deluser._id, user._id], obj, event); + parent.DispatchEvent(['*', mesh._id, deluser._id, user._id], obj, event); } } else if (i.startsWith('node/')) { // Get the node and the rights for this node - parent.GetNodeWithRights(domain, deluser, i, function (node, rights, visible) { + obj.GetNodeWithRights(domain, deluser, i, function (node, rights, visible) { if ((node == null) || (node.links == null) || (node.links[deluser._id] == null)) return; // Remove the link and save the node to the database @@ -1641,10 +1710,23 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { db.Set(obj.cleanDevice(node)); // Event the node change - var event = { etype: 'node', userid: user._id, username: user.name, action: 'changenode', nodeid: node._id, domain: domain.id, msg: (command.rights == 0) ? ('Removed user device rights for ' + node.name) : ('Changed user device rights for ' + node.name), node: parent.CloneSafeNode(node) } + var event = { etype: 'node', userid: user._id, username: user.name, action: 'changenode', nodeid: node._id, domain: domain.id, msg: ('Removed user device rights for ' + node.name), node: obj.CloneSafeNode(node) } if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come. - parent.parent.DispatchEvent(['*', node.meshid, node._id], obj, event); + parent.DispatchEvent(['*', node.meshid, node._id], obj, event); }); + } else if (i.startsWith('ugrp/')) { + // Get the device group + var ugroup = obj.userGroups[i]; + if (ugroup) { + // Remove user from the user group + if (ugroup.links[deluser._id] != null) { delete ugroup.links[deluser._id]; parent.db.Set(ugroup); } + + // Notify user group change + var change = 'Removed user ' + deluser.name + ' from user group ' + ugroup.name; + var event = { etype: 'ugrp', userid: user._id, username: user.name, ugrpid: ugroup._id, name: ugroup.name, desc: ugroup.desc, action: 'usergroupchange', links: ugroup.links, msg: 'Removed user ' + deluser.name + ' from user group ' + ugroup.name, addUserDomain: domain.id }; + if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user group. Another event will come. + parent.DispatchEvent(['*', ugroup._id, user._id, deluser._id], obj, event); + } } } } @@ -1765,6 +1847,34 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (domain.newaccountsrights) { user.siteadmin = domain.newaccountsrights; } // New accounts automatically assigned server rights. if (newAccountRealms) { user.groups = newAccountRealms; } // New accounts automatically part of some groups (Realms). obj.users[userid] = user; + + // Auto-join any user groups + var newaccountsusergroups = null; + if (typeof domain.newaccountsusergroups == 'object') { newaccountsusergroups = domain.newaccountsusergroups; } + if (typeof domain.authstrategies[req.user.strategy].newaccountsusergroups == 'object') { newaccountsusergroups = domain.authstrategies[req.user.strategy].newaccountsusergroups; } + if (newaccountsusergroups) { + for (var i in newaccountsusergroups) { + var ugrpid = newaccountsusergroups[i]; + if (ugrpid.indexOf('/') < 0) { ugrpid = 'ugrp/' + domain.id + '/' + ugrpid; } + var ugroup = obj.userGroups[ugrpid]; + if (ugroup != null) { + // Add group to the user + if (user.links == null) { user.links = {}; } + user.links[ugroup._id] = { rights: 1 }; + + // Add user to the group + ugroup.links[user._id] = { userid: user._id, name: user.name, rights: 1 }; + db.Set(ugroup); + + // Notify user group change + var event = { etype: 'ugrp', ugrpid: ugroup._id, name: ugroup.name, desc: ugroup.desc, action: 'usergroupchange', links: ugroup.links, msg: 'Added user ' + user.name + ' to user group ' + ugroup.name, addUserDomain: domain.id }; + if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user group. Another event will come. + parent.DispatchEvent(['*', ugroup._id, user._id], obj, event); + } + } + } + + // Save the user obj.db.SetUser(user); // Event user creation @@ -1914,6 +2024,30 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (obj.common.validateStrArray(domain.newaccountrealms)) { user2.groups = domain.newaccountrealms; } for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } } if (usercount == 0) { user2.siteadmin = 4294967295; } // If this is the first user, give the account site admin. + + // Auto-join any user groups + if (typeof domain.newaccountsusergroups == 'object') { + for (var i in domain.newaccountsusergroups) { + var ugrpid = domain.newaccountsusergroups[i]; + if (ugrpid.indexOf('/') < 0) { ugrpid = 'ugrp/' + domain.id + '/' + ugrpid; } + var ugroup = obj.userGroups[ugrpid]; + if (ugroup != null) { + // Add group to the user + if (user2.links == null) { user2.links = {}; } + user2.links[ugroup._id] = { rights: 1 }; + + // Add user to the group + ugroup.links[user2._id] = { userid: user2._id, name: user2.name, rights: 1 }; + db.Set(ugroup); + + // Notify user group change + var event = { etype: 'ugrp', ugrpid: ugroup._id, name: ugroup.name, desc: ugroup.desc, action: 'usergroupchange', links: ugroup.links, msg: 'Added user ' + user2.name + ' to user group ' + ugroup.name, addUserDomain: domain.id }; + if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user group. Another event will come. + parent.DispatchEvent(['*', ugroup._id, user2._id], obj, event); + } + } + } + obj.users[req.session.userid] = user2; obj.db.SetUser(user2); var event = { etype: 'user', userid: req.session.userid, username: req.connection.user, account: obj.CloneSafeUser(user2), action: 'accountcreate', msg: 'Domain account created, user ' + req.connection.user, domain: domain.id };