From fc406b9b55a62e94813ed0f9fdcc2227cae6334d Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Mon, 15 Apr 2019 15:05:09 -0700 Subject: [PATCH] Improved LDAP support and user groups. --- meshuser.js | 33 ++++++++++----- package.json | 2 +- views/default-min.handlebars | 2 +- views/default-mobile.handlebars | 32 +++++++-------- views/default.handlebars | 72 +++++++++++++++++++-------------- webserver.js | 61 ++++++++++++++++++++++++---- 6 files changed, 137 insertions(+), 65 deletions(-) diff --git a/meshuser.js b/meshuser.js index 02ef275f..dcf72728 100644 --- a/meshuser.js +++ b/meshuser.js @@ -887,8 +887,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use case 'edituser': { // Edit a user account, may involve changing email or administrator permissions - if (((user.siteadmin & 2) != 0) || (user.name == command.name)) { - var chguserid = 'user/' + domain.id + '/' + command.name.toLowerCase(), chguser = parent.users[chguserid]; + if (((user.siteadmin & 2) != 0) || (user._id == command.id)) { + var chguser = parent.users[command.id]; change = 0; if (chguser) { if (common.validateString(command.email, 1, 256) && (chguser.email != command.email)) { chguser.email = command.email; change = 1; } @@ -896,20 +896,34 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if ((common.validateInt(command.quota, 0) || command.quota == null) && (command.quota != chguser.quota)) { chguser.quota = command.quota; if (chguser.quota == null) { delete chguser.quota; } change = 1; } if ((user.siteadmin == 0xFFFFFFFF) && common.validateInt(command.siteadmin) && (chguser.siteadmin != command.siteadmin)) { chguser.siteadmin = command.siteadmin; change = 1; } - if ((Array.isArray(command.groups)) && (user.name != command.name)) { + if ((Array.isArray(command.groups)) && (user._id != command.id)) { if (command.groups.length == 0) { + // Remove the user groups if (chguser.groups != null) { delete chguser.groups; change = 1; } } else { - if (chguser.groups != command.groups) { chguser.groups = command.groups; change = 1; } + // Arrange the user groups + var groups2 = []; + for (var i in command.groups) { + if (typeof command.groups[i] == 'string') { + var gname = command.groups[i].trim().toLowerCase(); + if ((gname.length > 0) && (gname.length <= 64) && (groups2.indexOf(gname) == -1)) { groups2.push(gname); } + } + } + groups2.sort(); + + // Set the user groups + if (chguser.groups != groups2) { chguser.groups = groups2; change = 1; } } } if (change == 1) { + // Update the user db.SetUser(chguser); parent.parent.DispatchEvent([chguser._id], obj, 'resubscribe'); - parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Account changed: ' + command.name, domain: domain.id }); + 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 }); } if ((chguser.siteadmin) && (chguser.siteadmin != 0xFFFFFFFF) && (chguser.siteadmin & 32)) { + // If the user is locked out of this account, disconnect now parent.parent.DispatchEvent([chguser._id], obj, 'close'); // Disconnect all this user's sessions } } @@ -1206,7 +1220,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use parent.parent.DispatchEvent([newuser._id], obj, 'resubscribe'); // Add a user to the mesh - mesh.links[newuserid] = { name: newuser.name, rights: command.meshadmin }; + mesh.links[newuserid] = { userid: newuser.id, name: newuser.name, rights: command.meshadmin }; db.Set(common.escapeLinksFieldName(mesh)); // Notify mesh change @@ -1647,10 +1661,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (command.intelamt.tls && (command.intelamt.tls != node.intelamt.tls)) { change = 1; node.intelamt.tls = command.intelamt.tls; changes.push('Intel AMT TLS'); } } if (command.tags) { // Node grouping tag, this is a array of strings that can't be empty and can't contain a comma - var ok = true; + var ok = true, group2 = []; if (common.validateString(command.tags, 0, 4096) == true) { command.tags = command.tags.split(','); } - if (common.validateStrArray(command.tags, 1, 256) == true) { var groupTags = command.tags; for (var i in groupTags) { groupTags[i] = groupTags[i].trim(); if ((groupTags[i] == '') || (groupTags[i].indexOf(',') >= 0)) { ok = false; } } } - if (ok == true) { groupTags.sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); node.tags = groupTags; change = 1; } + for (var i in command.tags) { var tname = command.tags[i].trim(); if ((tname.length > 0) && (tname.length < 64) && (group2.indexOf(tname) == -1)) { group2.push(tname); } } + group2.sort(); + if (node.tags != group2) { node.tags = group2; change = 1; } } else if ((command.tags === '') && node.tags) { delete node.tags; change = 1; } if (change == 1) { diff --git a/package.json b/package.json index 640795b1..c47c5b64 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.3.2-l", + "version": "0.3.2-m", "keywords": [ "Remote Management", "Intel AMT", diff --git a/views/default-min.handlebars b/views/default-min.handlebars index bc2be9ee..5954aa17 100644 --- a/views/default-min.handlebars +++ b/views/default-min.handlebars @@ -1 +1 @@ - {{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

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

{{{logoutControl}}}

 

\ No newline at end of file diff --git a/views/default-mobile.handlebars b/views/default-mobile.handlebars index 17712320..2c5a6563 100644 --- a/views/default-mobile.handlebars +++ b/views/default-mobile.handlebars @@ -813,7 +813,7 @@ } case 'createmesh': { // A new mesh was created - if (message.event.links['user/' + domain + '/' + userinfo.name.toLowerCase()] != null) { // Check if this is a mesh create for a mesh we own. If site administrator, we get all messages so need to ignore some. + if (message.event.links[userinfo._id] != null) { // Check if this is a mesh create for a mesh we own. If site administrator, we get all messages so need to ignore some. meshes[message.event.meshid] = { _id: message.event.meshid, name: message.event.name, mtype: message.event.mtype, desc: message.event.desc, links: message.event.links }; updateMeshes(); updateDevices(); @@ -834,7 +834,7 @@ meshes[message.event.meshid].links = message.event.links; // Check if we lost rights to this mesh in this change. - if (meshes[message.event.meshid].links['user/' + domain + '/' + userinfo.name.toLowerCase()] == null) { + if (meshes[message.event.meshid].links[userinfo._id] == null) { if ((xxcurrentView == 20) && (currentMesh == meshes[message.event.meshid])) go(2); delete meshes[message.event.meshid]; @@ -1250,7 +1250,7 @@ count++; // Mesh rights - var meshrights = meshes[i].links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + var meshrights = meshes[i].links[userinfo._id].rights; var rights = 'Partial Rights'; if (meshrights == 0xFFFFFFFF) rights = 'Full Administrator'; else if (meshrights == 0) rights = 'No Rights'; @@ -1541,7 +1541,7 @@ // Go thru the list of nodes and display them for (var i in nodes) { if (nodes[i].v == false) continue; - var mesh2 = meshes[nodes[i].meshid], meshlinks = mesh2.links['user/' + domain + '/' + userinfo.name.toLowerCase()]; + var mesh2 = meshes[nodes[i].meshid], meshlinks = mesh2.links[userinfo._id]; if (meshlinks == null) continue; var meshrights = meshlinks.rights; @@ -1612,7 +1612,7 @@ // Display all empty meshes, we need to do this because users can add devices to these at any time. if (sort == 0) { for (var i in meshes) { - var mesh = meshes[i], meshlink = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()]; + var mesh = meshes[i], meshlink = mesh.links[userinfo._id]; if (meshlink != null) { var meshrights = meshlink.rights; if (displayedMeshes[mesh._id] == null) { @@ -1700,7 +1700,7 @@ function getNodeRights(nodeid) { var node = getNodeFromId(nodeid), mesh = meshes[node.meshid]; - return mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + return mesh.links[userinfo._id].rights; } var currentDevicePanel = 0; @@ -1722,7 +1722,7 @@ if (node == null) { goBack(); return; } var mesh = meshes[node.meshid]; if (mesh == null) { goBack(); return; } - var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + var meshrights = mesh.links[userinfo._id].rights; if (!currentNode || currentNode._id != node._id || refresh == true) { currentNode = node; @@ -1882,7 +1882,7 @@ function setupDeviceMenu(op, obj) { var meshrights = 0; - if (currentNode) { meshrights = meshes[currentNode.meshid].links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; } + if (currentNode) { meshrights = meshes[currentNode.meshid].links[userinfo._id].rights; } if (op != null) { currentDevicePanel = op; } QV('p10general', currentDevicePanel == 0); QV('p10desktop', currentDevicePanel == 1); // Show if we have remote control rights or desktop view only rights @@ -1896,7 +1896,7 @@ function deviceActionFunction() { if (xxdialogMode) return; - var meshrights = meshes[currentNode.meshid].links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + var meshrights = meshes[currentNode.meshid].links[userinfo._id].rights; var x = "Select an operation to perform on this device.

"; var y = ''; x += '

'; - var currentMeshLinks = currentMesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()]; + var currentMeshLinks = currentMesh.links[userinfo._id]; if (currentMeshLinks && ((currentMeshLinks.rights & 2) != 0)) { x += '
Add User
'; } /* @@ -2988,7 +2988,7 @@ } function p20validateAddMeshUserDialog() { - var meshrights = currentMesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + var meshrights = currentMesh.links[userinfo._id].rights; QE('idx_dlgOkButton', (Q('dp20username').value.length > 0)); QE('p20fulladmin', meshrights == 0xFFFFFFFF); QE('p20editmesh', (!Q('p20fulladmin').checked) && (meshrights == 0xFFFFFFFF)); @@ -3027,7 +3027,7 @@ function p20viewuser(userid) { if (xxdialogMode) return; userid = decodeURIComponent(userid); - var r = '', cmeshrights = currentMesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights, meshrights = currentMesh.links[userid].rights; + var r = '', cmeshrights = currentMesh.links[userinfo._id].rights, meshrights = currentMesh.links[userid].rights; if (meshrights == 0xFFFFFFFF) r = ', Full Administrator'; else { if ((meshrights & 1) != 0) r += ', Edit Device Group'; if ((meshrights & 2) != 0) r += ', Manage Device Group Users'; @@ -3046,7 +3046,7 @@ if (r == '') { r = 'No Rights'; } var buttons = 1, x = addHtmlValue('User', EscapeHtml(decodeURIComponent(userid.split('/')[2]))); x += addHtmlValue('Permissions', r); - if ((('user/' + domain + '/' + userinfo.name.toLowerCase()) != userid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) buttons += 4; + if (((userinfo._id) != userid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) buttons += 4; setDialogMode(2, "Mesh User", buttons, p20viewuserEx, x, userid); } diff --git a/views/default.handlebars b/views/default.handlebars index 09427ecd..9a395662 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -1627,7 +1627,7 @@ } case 'createmesh': { // A new mesh was created - if (message.event.links['user/' + domain + '/' + userinfo.name.toLowerCase()] != null) { // Check if this is a mesh create for a mesh we own. If site administrator, we get all messages so need to ignore some. + if (message.event.links[userinfo._id] != null) { // Check if this is a mesh create for a mesh we own. If site administrator, we get all messages so need to ignore some. meshes[message.event.meshid] = { _id: message.event.meshid, name: message.event.name, mtype: message.event.mtype, desc: message.event.desc, links: message.event.links }; masterUpdate(4 + 128); meshserver.send({ action: 'files' }); @@ -1650,7 +1650,7 @@ if (message.event.amt) { meshes[message.event.meshid].amt = message.event.amt; } // Check if we lost rights to this mesh in this change. - if (meshes[message.event.meshid].links['user/' + domain + '/' + userinfo.name.toLowerCase()] == null) { + if (meshes[message.event.meshid].links[userinfo._id] == null) { if ((xxcurrentView == 20) && (currentMesh == meshes[message.event.meshid])) go(2); delete meshes[message.event.meshid]; @@ -1932,7 +1932,7 @@ // Check what keys we are allows to send if (currentNode != null) { var mesh = meshes[currentNode.meshid]; - var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + var meshrights = mesh.links[userinfo._id].rights; var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0))); if (inputAllowed == false) return false; var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0))); @@ -1991,7 +1991,7 @@ // Check what keys we are allows to send if (currentNode != null) { var mesh = meshes[currentNode.meshid]; - var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + var meshrights = mesh.links[userinfo._id].rights; var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0))); if (inputAllowed == false) return false; var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0))); @@ -2025,7 +2025,7 @@ // Check what keys we are allows to send if (currentNode != null) { var mesh = meshes[currentNode.meshid]; - var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + var meshrights = mesh.links[userinfo._id].rights; var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0))); if (inputAllowed == false) return false; var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0))); @@ -2124,7 +2124,7 @@ for (var i in nodes) { var node = nodes[i]; if (node.v == false) continue; - var mesh2 = meshes[node.meshid], meshlinks = mesh2.links['user/' + domain + '/' + userinfo.name.toLowerCase()]; + var mesh2 = meshes[node.meshid], meshlinks = mesh2.links[userinfo._id]; if (meshlinks == null) continue; var meshrights = meshlinks.rights; if ((view == 3) && (mesh2.mtype == 1)) continue; @@ -2242,7 +2242,7 @@ // Display all empty meshes, we need to do this because users can add devices to these at any time. if ((sort == 0) && (Q('SearchInput').value == '') && (view < 3)) { for (var i in meshes) { - var mesh = meshes[i], meshlink = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()]; + var mesh = meshes[i], meshlink = mesh.links[userinfo._id]; if (meshlink != null) { var meshrights = meshlink.rights; if (displayedMeshes[mesh._id] == null) { @@ -2355,7 +2355,7 @@ } function toggleKvmDevice(nodeid) { - var node = getNodeFromId(nodeid), mesh = meshes[node.meshid], meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + var node = getNodeFromId(nodeid), mesh = meshes[node.meshid], meshrights = mesh.links[userinfo._id].rights; if ((meshrights & 8) || (meshrights & 256)) { // Requires remote control rights or desktop view only rights //var conn = 0; //if ((node.conn & 1) != 0) { conn = 1; } else if ((node.conn & 6) != 0) { conn = 2; } // Check what type of connect we can do (Agent vs AMT) @@ -2892,7 +2892,7 @@ var nodeid = contextelement.children[1].attributes.onclick.value; var node = getNodeFromId(nodeid.substring(12, nodeid.length - 18)); var mesh = meshes[node.meshid]; - var meshlinks = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()]; + var meshlinks = mesh.links[userinfo._id]; var meshrights = meshlinks.rights; var consoleRights = ((meshrights & 16) != 0); @@ -3556,7 +3556,7 @@ function getNodeRights(nodeid) { var node = getNodeFromId(nodeid), mesh = meshes[node.meshid]; - return mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + return mesh.links[userinfo._id].rights; } var currentNode; @@ -3582,7 +3582,7 @@ //disconnectAllKvmFunction(); var node = getNodeFromId(nodeid); var mesh = meshes[node.meshid]; - var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + var meshrights = mesh.links[userinfo._id].rights; if (!currentNode || currentNode._id != node._id || refresh == true) { currentNode = node; @@ -3841,7 +3841,7 @@ function deviceActionFunction() { if (xxdialogMode) return; - var meshrights = meshes[currentNode.meshid].links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + var meshrights = meshes[currentNode.meshid].links[userinfo._id].rights; var x = "Select an operation to perform on this device.

"; var y = '", count = 0; for (var i in meshes) { - var meshrights = meshes[i].links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + var meshrights = meshes[i].links[userinfo._id].rights; if ((meshes[i]._id != targetMeshId) && (meshrights & 4)) { count++; y += ""; } } y += ""; @@ -4130,7 +4130,7 @@ function p10showiconselector() { if (xxdialogMode) return; var mesh = meshes[currentNode.meshid]; - var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + var meshrights = mesh.links[userinfo._id].rights; if ((meshrights & 4) == 0) return; var x = '
'; @@ -4227,7 +4227,7 @@ var mesh = meshes[currentNode.meshid]; var deskState = 0; if (desktop != null) { deskState = desktop.State; } - var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + var meshrights = mesh.links[userinfo._id].rights; // Show the right buttons QV('disconnectbutton1span', (deskState != 0)); @@ -5508,7 +5508,7 @@ consoleNode = currentNode; var mesh = meshes[consoleNode.meshid]; - var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + var meshrights = mesh.links[userinfo._id].rights; if ((meshrights & 16) != 0) { if (consoleNode.consoleText == null) { consoleNode.consoleText = ''; } if (samenode == false) { @@ -5880,7 +5880,7 @@ // Mesh rights var meshrights = 0; - if (meshes[i].links['user/' + domain + '/' + userinfo.name.toLowerCase()]) { meshrights = meshes[i].links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; } + if (meshes[i].links[userinfo._id]) { meshrights = meshes[i].links[userinfo._id].rights; } var rights = 'Partial Rights'; if (meshrights == 0xFFFFFFFF) rights = 'Full Administrator'; else if (meshrights == 0) rights = 'No Rights'; @@ -5942,7 +5942,7 @@ QH('p20meshName', EscapeHtml(currentMesh.name)); var meshtype = 'Unknown #' + currentMesh.mtype; var meshrights = 0; - try { meshrights = currentMesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; } catch (ex) { } + try { meshrights = currentMesh.links[userinfo._id].rights; } catch (ex) { } if (currentMesh.mtype == 1) meshtype = 'Intel® AMT only, no agent'; if (currentMesh.mtype == 2) meshtype = 'Managed using a software agent'; @@ -5996,7 +5996,7 @@ if (meshrights & 1) { x += '
'; } x += '

'; - var currentMeshLinks = currentMesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()]; + var currentMeshLinks = currentMesh.links[userinfo._id]; if (currentMeshLinks && ((currentMeshLinks.rights & 2) != 0)) { x += ' Add User'; } if ((meshrights & 4) != 0) { @@ -6031,7 +6031,12 @@ // Sort the users for this mesh var count = 1, sortedusers = []; - for (var i in currentMesh.links) { sortedusers.push({ id: i, name: i.split('/')[2], rights: currentMesh.links[i].rights }); } + for (var i in currentMesh.links) { + var uname = i.split('/')[2]; + if (currentMesh.links[i].name) { uname = currentMesh.links[i].name; } + if (i == userinfo._id) { uname = userinfo.name; } + sortedusers.push({ id: i, name: uname, rights: currentMesh.links[i].rights }); + } sortedusers.sort(function(a, b) { if (a.name > b.name) return 1; if (a.name < b.name) return -1; return 0; }); // Display all users for this mesh @@ -6208,7 +6213,7 @@ } function p20validateAddMeshUserDialog() { - var meshrights = currentMesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; + var meshrights = currentMesh.links[userinfo._id].rights; QE('idx_dlgOkButton', (Q('dp20username').value.length > 0)); QE('p20fulladmin', meshrights == 0xFFFFFFFF); QE('p20editmesh', (!Q('p20fulladmin').checked) && (meshrights == 0xFFFFFFFF)); @@ -6249,7 +6254,7 @@ function p20viewuser(userid) { if (xxdialogMode) return; userid = decodeURIComponent(userid); - var r = '', cmeshrights = currentMesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights, meshrights = currentMesh.links[userid].rights; + var r = '', cmeshrights = currentMesh.links[userinfo._id].rights, meshrights = currentMesh.links[userid].rights; if (meshrights == 0xFFFFFFFF) r = ', Full Administrator (all rights)'; else { if ((meshrights & 1) != 0) r += ', Edit Device Group'; if ((meshrights & 2) != 0) r += ', Manage Device Group Users'; @@ -6267,9 +6272,14 @@ } r = r.substring(2); if (r == '') { r = 'No Rights'; } - var buttons = 1, x = addHtmlValue('User Name', EscapeHtml(decodeURIComponent(userid.split('/')[2]))); + var uname = userid.split('/')[2]; + if (users) { uname = users[userid].name; } + if (userinfo._id == userid) { uname = userinfo.name; } + var buttons = 1, x = addHtmlValue('User Name', EscapeHtml(uname)); + if (userid.split('/')[2] != uname) { x += addHtmlValue('User Identifier', EscapeHtml(userid.split('/')[2])); } + x += addHtmlValue('Permissions', r); - if ((('user/' + domain + '/' + userinfo.name.toLowerCase()) != userid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) buttons += 4; + if (((userinfo._id) != userid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) buttons += 4; setDialogMode(2, "Device Group User", buttons, p20viewuserEx, x, userid); } @@ -6800,7 +6810,7 @@ function showUserGroupDialogEx(event, user) { var groups = Q('dp4usergroups').value, g = groups.split(','), g2 = []; for (var j in g) { var x = g[j].trim(); if (x.length > 0) { g2.push(x); } } - meshserver.send({ action: 'edituser', name: user.name, groups: g2 }); + meshserver.send({ action: 'edituser', id: user._id, groups: g2 }); } function showUserAdminDialog(e, userid) { @@ -6871,7 +6881,7 @@ if (Q('ua_nonewgroups').checked == true) siteadmin += 64; if (Q('ua_nomeshcmd').checked == true) siteadmin += 128; } - var x = { action: 'edituser', name: user.name, siteadmin: siteadmin }; + var x = { action: 'edituser', id: user._id, siteadmin: siteadmin }; if (isNaN(quota) == false) { x.quota = (quota * 1024); } meshserver.send(x); } @@ -6900,12 +6910,13 @@ var msg = [], premsg = ''; if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { premsg = ' '; msg.push("Locked account"); } if ((user.siteadmin == null) || ((user.siteadmin & (0xFFFFFFFF - 224)) == 0)) { msg.push("No server rights"); } else if (user.siteadmin == 8) { msg.push("Access to server files"); } else if (user.siteadmin == 0xFFFFFFFF) { msg.push("Full administrator"); } else { msg.push("Partial rights"); } - if ((user.siteadmin != null) && ((user.siteadmin & (64 + 128)) != 0)) { msg.push("Restrictions"); } + if ((user.siteadmin != null) && (user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & (64 + 128)) != 0)) { msg.push("Restrictions"); } // Show user attributes var x = '
'; var email = user.email?EscapeHtml(user.email):'Not set', everify = ''; if (serverinfo.emailcheck) { everify = ((user.emailVerified == true)?'🗸 ':'🗴 '); } + if (user.name.toLowerCase() != user._id.split('/')[2]) { x += addDeviceAttribute('User Identifier', user._id.split('/')[2]); } x += addDeviceAttribute('Email', everify + "" + email + ''); x += addDeviceAttribute('Server Rights', premsg + "" + msg.join(', ') + ""); if (user.quota) x += addDeviceAttribute('Server Quota', EscapeHtml(parseInt(user.quota) / 1024) + ' k'); @@ -6923,8 +6934,9 @@ x += addDeviceAttribute('Device Groups', linkCountStr); // User Groups - if (user.groups) { linkCountStr = EscapeHtml(user.groups.join(', ')); } else { linkCountStr = 'None'; } - x += addDeviceAttribute('User Groups', addLinkConditional(linkCountStr, 'showUserGroupDialog(event,\"' + userid + '\")', ((userinfo.groups == null) && (userinfo.siteadmin & 2) && (userinfo._id != user._id)))); + var userGroups = 'None'; + if (user.groups) { userGroups = ''; for (var i in user.groups) { userGroups += '' + user.groups[i] + ''; } } + x += addDeviceAttribute('User Groups', addLinkConditional(userGroups, 'showUserGroupDialog(event,\"' + userid + '\")', ((userinfo.groups == null) && (userinfo.siteadmin & 2) && (userinfo._id != user._id)))); var multiFactor = 0; if ((user.otpsecret > 0) || (user.otphkeys > 0)) { @@ -6995,7 +7007,7 @@ // Send to the server the new user's email address and validation status function p30showUserEmailChangeDialogEx() { - var x = { action: 'edituser', name: currentUser.name, email: Q('dp30email').value }; + var x = { action: 'edituser', id: currentUser._id, email: Q('dp30email').value }; if (serverinfo.emailcheck) { x.emailVerified = (Q('dp30verified').value == 1); } meshserver.send(x); } diff --git a/webserver.js b/webserver.js index 923e858e..480c66dc 100644 --- a/webserver.js +++ b/webserver.js @@ -226,23 +226,46 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { fn(new Error('invalid password')); return; } else { + var username = xxuser['displayName']; + if (domain.ldapusername) { username = xxuser[domain.ldapusername]; } var shortname = null; - if (xxuser.name) { shortname = xxuser.name; } - else if (xxuser.cn) { shortname = xxuser.cn; } - if (shortname == null) { fn(new Error('no short name')); return; } + if (domain.ldapuserbinarykey) { + // Use a binary key as the userid + if (xxuser[domain.ldapuserbinarykey]) { shortname = Buffer.from(xxuser[domain.ldapuserbinarykey], 'binary').toString('hex'); } + } else if (domain.ldapuserkey) { + // Use a string key as the userid + if (xxuser[domain.ldapuserkey]) { shortname = xxuser[domain.ldapuserkey]; } + } else { + // Use the default key as the userid + if (xxuser.objectSid) { shortname = Buffer.from(xxuser.objectSid, 'binary').toString('hex').toLowerCase(); } + else if (xxuser.objectGUID) { shortname = Buffer.from(xxuser.objectGUID, 'binary').toString('hex').toLowerCase(); } + else if (xxuser.name) { shortname = xxuser.name; } + else if (xxuser.cn) { shortname = xxuser.cn; } + } + if (username == null) { fn(new Error('no user name')); return; } + if (shortname == null) { fn(new Error('no user identifier')); return; } var userid = 'user/' + domain.id + '/' + shortname; var user = obj.users[userid]; + if (user == null) { - var user = { type: 'user', _id: userid, name: shortname, creation: Math.floor(Date.now() / 1000), login: Math.floor(Date.now() / 1000), domain: domain.id }; + // 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 }; var usercount = 0; for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } } if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; if (domain.newaccounts === 2) { domain.newaccounts = 0; } } // If this is the first user, give the account site admin. obj.users[user._id] = user; obj.db.SetUser(user); - obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, account: obj.CloneSafeUser(user), action: 'accountcreate', msg: 'Account created, name is ' + name, domain: domain.id }); + obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', userid: userid, username: username, account: obj.CloneSafeUser(user), action: 'accountcreate', msg: 'Account created, name is ' + name, domain: domain.id }); return fn(null, user._id); } else { // This is an existing user + // If the display username has changes, update it. + if (user.name != username) { + user.name = username; + db.SetUser(user); + parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: obj.CloneSafeUser(user), action: 'accountchange', msg: 'Changed account display name to ' + username, domain: domain.id }); + } + // If user is locker out, block here. if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { fn('locked'); return; } return fn(null, user._id); } @@ -255,11 +278,26 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { try { ldap.close(); } catch (ex) { console.log(ex); } // Close the LDAP object if (err) { fn(new Error('invalid password')); return; } var shortname = null; - if (xxuser.name) { shortname = xxuser.name; } - else if (xxuser.cn) { shortname = xxuser.cn; } - if (shortname == null) { fn(new Error('no short name')); return; } + var username = xxuser['displayName']; + if (domain.ldapusername) { username = xxuser[domain.ldapusername]; } + if (domain.ldapuserbinarykey) { + // Use a binary key as the userid + if (xxuser[domain.ldapuserbinarykey]) { shortname = Buffer.from(xxuser[domain.ldapuserbinarykey], 'binary').toString('hex').toLowerCase(); } + } else if (domain.ldapuserkey) { + // Use a string key as the userid + if (xxuser[domain.ldapuserkey]) { shortname = xxuser[domain.ldapuserkey]; } + } else { + // Use the default key as the userid + if (xxuser.objectSid) { shortname = Buffer.from(xxuser.objectSid, 'binary').toString('hex').toLowerCase(); } + else if (xxuser.objectGUID) { shortname = Buffer.from(xxuser.objectGUID, 'binary').toString('hex').toLowerCase(); } + else if (xxuser.name) { shortname = xxuser.name; } + else if (xxuser.cn) { shortname = xxuser.cn; } + } + if (username == null) { fn(new Error('no user name')); return; } + if (shortname == null) { fn(new Error('no user identifier')); return; } var userid = 'user/' + domain.id + '/' + shortname; var user = obj.users[userid]; + if (user == null) { // This user does not exist, create a new account. var user = { type: 'user', _id: userid, name: shortname, creation: Math.floor(Date.now() / 1000), login: Math.floor(Date.now() / 1000), domain: domain.id }; @@ -272,6 +310,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { return fn(null, user._id); } else { // This is an existing user + // If the display username has changes, update it. + if (user.name != username) { + user.name = username; + db.SetUser(user); + parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: obj.CloneSafeUser(user), action: 'accountchange', msg: 'Changed account display name to ' + username, domain: domain.id }); + } + // If user is locker out, block here. if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { fn('locked'); return; } return fn(null, user._id); }