diff --git a/meshuser.js b/meshuser.js index 9758c3a4..9d117ad3 100644 --- a/meshuser.js +++ b/meshuser.js @@ -91,7 +91,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { obj.ws.HandleEvent = function (source, event) { if (!event.domain || event.domain == obj.domain.id) { try { - if (event == 'close') { obj.req.session.destroy(); obj.ws.close(); } + if (event == 'close') { req.session.destroy(); obj.close(); } else if (event == 'resubscribe') { user.subscriptions = obj.parent.subscribe(user._id, ws); } else if (event == 'updatefiles') { updateUserFiles(user, ws, domain); } else { ws.send(JSON.stringify({ action: 'event', event: event })); } @@ -280,10 +280,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { { if ((command.limit == null) || (typeof command.limit != 'number')) { // Send the list of all events for this session - obj.db.GetEvents(user.subscriptions, domain.id, function (err, docs) { if (err != null) return; ws.send(JSON.stringify({ action: 'events', events: docs, tag: command.tag })); }); + obj.db.GetEvents(user.subscriptions, domain.id, function (err, docs) { if (err != null) return; try { ws.send(JSON.stringify({ action: 'events', events: docs, tag: command.tag })); } catch (ex) { } }); } else { // Send the list of most recent events for this session, up to 'limit' count - obj.db.GetEventsWithLimit(user.subscriptions, domain.id, command.limit, function (err, docs) { if (err != null) return; ws.send(JSON.stringify({ action: 'events', events: docs, tag: command.tag })); }); + obj.db.GetEventsWithLimit(user.subscriptions, domain.id, command.limit, function (err, docs) { if (err != null) return; try { ws.send(JSON.stringify({ action: 'events', events: docs, tag: command.tag })); } catch (ex) { } }); } break; } @@ -452,6 +452,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { delete userinfo.passtype; obj.parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: userinfo, action: 'accountchange', msg: 'Account changed: ' + command.name, domain: domain.id }) } + if ((chguser.siteadmin) && (chguser.siteadmin != 0xFFFFFFFF) && (chguser.siteadmin & 32)) { + obj.parent.parent.DispatchEvent([chguser._id], obj, 'close'); // Disconnect all this user's sessions + } } } break; diff --git a/package.json b/package.json index cf0c7a83..643605de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.1.6-h", + "version": "0.1.6-j", "keywords": [ "Remote Management", "Intel AMT", diff --git a/views/default.handlebars b/views/default.handlebars index a23c5efd..7dcba268 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -3457,7 +3457,7 @@ function dmousedown(e) { if (!xxdialogMode && desktop != null && Q('DeskControl').checked) desktop.m.mousedown(e) } function dmouseup(e) { if (!xxdialogMode && desktop != null && Q('DeskControl').checked) desktop.m.mouseup(e) } function dmousemove(e) { if (!xxdialogMode && desktop != null && Q('DeskControl').checked) desktop.m.mousemove(e) } - function dmousewheel(e) { if (!xxdialogMode && desktop != null && Q('DeskControl').checked) { desktop.m.mousewheel(e); haltEvent(e); return true; } return false; } + function dmousewheel(e) { if (!xxdialogMode && desktop != null && Q('DeskControl').checked && desktop.m.mousewheel) { desktop.m.mousewheel(e); haltEvent(e); return true; } return false; } function drotate(x) { if (!xxdialogMode && desktop != null) { desktop.m.setRotation(desktop.m.rotation + x); deskAdjust(); deskAdjust(); } } function stopProcess(id, name) { setDialogMode(2, "Process Control", 3, stopProcessEx, 'Stop process #' + id + ' "' + name + '"?', id); } function stopProcessEx(buttons, tag) { meshserver.send({ action: 'msg', type:'pskill', nodeid: currentNode._id, value: tag }); setTimeout(refreshDeskTools, 300); } @@ -4856,8 +4856,9 @@ if (self) { msg += ""; } } if (msg != '') msg += ', '; - if (self) { msg += ""; } - if ((user.siteadmin == null) || (user.siteadmin == 0)) { + if (self) { msg += ""; } + if ((user.siteadmin != null) && (user.siteadmin == 32)) { msg += "Locked, "; } + if ((user.siteadmin == null) || (user.siteadmin == 0) || (user.siteadmin == 32)) { msg += "User"; } else if (user.siteadmin == 8) { msg += "User with server files"; @@ -4938,6 +4939,7 @@ function showUserAdminDialog(e, userid) { if (xxdialogMode) return; haltEvent(e); + userid = decodeURIComponent(userid); var x = '
'; x += 'Server Files, k max, blank for default

'; x += 'Full Administrator
'; @@ -4945,9 +4947,10 @@ x += 'Server Restore
'; x += 'Server Updates
'; x += 'Manage Users
'; + x += '
Lock Account
'; x += '
'; - var user = users[userid]; - setDialogMode(2, "Site Permissions", 3, showUserAdminDialogEx, x, user); + var user = users[userid.toLowerCase()]; + setDialogMode(2, "Server Permissions", 3, showUserAdminDialogEx, x, user); if (user.siteadmin && user.siteadmin != 0) { Q('ua_fulladmin').checked = (user.siteadmin == 0xFFFFFFFF); Q('ua_serverbackup').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 1) != 0)); // Server Backup @@ -4955,6 +4958,7 @@ Q('ua_serverrestore').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 4) != 0)); // Server Restore Q('ua_fileaccess').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 8) != 0)); // Server Files Q('ua_serverupdate').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 16) != 0)); // Server Update + Q('ua_lockedaccount').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 32) != 0)); // Account locked } QE('ua_fulladmin', userinfo.siteadmin == 0xFFFFFFFF); QE('ua_serverbackup', userinfo.siteadmin == 0xFFFFFFFF); @@ -4974,7 +4978,7 @@ QE('ua_serverrestore', !Q('ua_fulladmin').checked); QE('ua_fileaccess', !Q('ua_fulladmin').checked); QE('ua_serverupdate', !Q('ua_fulladmin').checked); - QE('ua_fileaccessquota', Q('ua_fileaccess').checked || Q('ua_fulladmin').checked); + QE('ua_fileaccessquota', Q('ua_fileaccess').checked && !Q('ua_fulladmin').checked); } } @@ -4986,6 +4990,7 @@ if (Q('ua_serverrestore').checked == true) siteadmin += 4; if (Q('ua_fileaccess').checked == true) siteadmin += 8; if (Q('ua_serverupdate').checked == true) siteadmin += 16; + if (Q('ua_lockedaccount').checked == true) siteadmin += 32; } var x = { action: 'edituser', name: user.name, siteadmin: siteadmin }; if (isNaN(quota) == false) { x.quota = (quota * 1024); } diff --git a/webserver.js b/webserver.js index 66f314ae..baf89c4e 100644 --- a/webserver.js +++ b/webserver.js @@ -88,6 +88,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate const SITERIGHT_SERVERRESTORE = 4; const SITERIGHT_FILEACCESS = 8; const SITERIGHT_SERVERUPDATE = 16; + const SITERIGHT_LOCKED = 32; // Setup SSPI authentication if needed if ((obj.parent.platform == 'win32') && (obj.args.nousers != true) && (obj.parent.config != null) && (obj.parent.config.domains != null)) { @@ -207,6 +208,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate if (hash == user.hash) { // Update the password to the stronger format. require('./pass').hash(pass, function (err, salt, hash) { if (err) throw err; user.salt = salt; user.hash = hash; delete user.passtype; obj.db.SetUser(user); }); + if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { fn('locked'); return; } return fn(null, user._id); } fn(new Error('invalid password'), null, user.passhint); @@ -215,7 +217,10 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Default strong password hashing (pbkdf2 SHA384) require('./pass').hash(pass, user.salt, function (err, hash) { if (err) return fn(err); - if (hash == user.hash) return fn(null, user._id); + if (hash == user.hash) { + if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { fn('locked'); return; } + return fn(null, user._id); + } fn(new Error('invalid password'), null, user.passhint); }); } @@ -330,7 +335,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'login', msg: 'Account login', domain: domain.id }) } else { delete req.session.loginmode; - req.session.error = 'Login failed, check username and password.'; + if (err == 'locked') { req.session.error = 'Account locked.'; } else { req.session.error = 'Login failed, check username and password.'; } if ((passhint != null) && (passhint.length > 0)) { req.session.passhint = passhint; } else {