mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2025-02-12 11:01:52 +00:00
Updated Windows agent, security improvements.
This commit is contained in:
parent
027a9944f5
commit
524f910fd5
15 changed files with 195 additions and 63 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2018-2019 Intel Corporation
|
Copyright 2018 Intel Corporation
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -193,11 +193,14 @@ function monitorinfo()
|
||||||
this._X11 = this._gm.CreateNativeProxy(this.Location_X11LIB);
|
this._X11 = this._gm.CreateNativeProxy(this.Location_X11LIB);
|
||||||
this._X11.CreateMethod('XChangeProperty');
|
this._X11.CreateMethod('XChangeProperty');
|
||||||
this._X11.CreateMethod('XCloseDisplay');
|
this._X11.CreateMethod('XCloseDisplay');
|
||||||
|
this._X11.CreateMethod('XConnectionNumber');
|
||||||
|
this._X11.CreateMethod('XConvertSelection');
|
||||||
this._X11.CreateMethod('XCreateGC');
|
this._X11.CreateMethod('XCreateGC');
|
||||||
this._X11.CreateMethod('XCreateWindow');
|
this._X11.CreateMethod('XCreateWindow');
|
||||||
this._X11.CreateMethod('XCreateSimpleWindow');
|
this._X11.CreateMethod('XCreateSimpleWindow');
|
||||||
this._X11.CreateMethod('XDefaultColormap');
|
this._X11.CreateMethod('XDefaultColormap');
|
||||||
this._X11.CreateMethod('XDefaultScreen');
|
this._X11.CreateMethod('XDefaultScreen');
|
||||||
|
this._X11.CreateMethod('XDestroyWindow');
|
||||||
this._X11.CreateMethod('XDrawLine');
|
this._X11.CreateMethod('XDrawLine');
|
||||||
this._X11.CreateMethod('XDisplayHeight');
|
this._X11.CreateMethod('XDisplayHeight');
|
||||||
this._X11.CreateMethod('XDisplayWidth');
|
this._X11.CreateMethod('XDisplayWidth');
|
||||||
|
@ -209,8 +212,11 @@ function monitorinfo()
|
||||||
this._X11.CreateMethod('XInternAtom');
|
this._X11.CreateMethod('XInternAtom');
|
||||||
this._X11.CreateMethod('XMapWindow');
|
this._X11.CreateMethod('XMapWindow');
|
||||||
this._X11.CreateMethod({ method: 'XNextEvent', threadDispatch: true });
|
this._X11.CreateMethod({ method: 'XNextEvent', threadDispatch: true });
|
||||||
|
this._X11.CreateMethod({ method: 'XNextEvent', newName: 'XNextEventSync' });
|
||||||
this._X11.CreateMethod('XOpenDisplay');
|
this._X11.CreateMethod('XOpenDisplay');
|
||||||
|
this._X11.CreateMethod('XPending');
|
||||||
this._X11.CreateMethod('XRootWindow');
|
this._X11.CreateMethod('XRootWindow');
|
||||||
|
this._X11.CreateMethod('XSelectInput');
|
||||||
this._X11.CreateMethod('XScreenCount');
|
this._X11.CreateMethod('XScreenCount');
|
||||||
this._X11.CreateMethod('XScreenOfDisplay');
|
this._X11.CreateMethod('XScreenOfDisplay');
|
||||||
this._X11.CreateMethod('XSelectInput');
|
this._X11.CreateMethod('XSelectInput');
|
||||||
|
@ -220,7 +226,7 @@ function monitorinfo()
|
||||||
this._X11.CreateMethod('XSetLineAttributes');
|
this._X11.CreateMethod('XSetLineAttributes');
|
||||||
this._X11.CreateMethod('XSetNormalHints');
|
this._X11.CreateMethod('XSetNormalHints');
|
||||||
this._X11.CreateMethod('XSetSubwindowMode');
|
this._X11.CreateMethod('XSetSubwindowMode');
|
||||||
|
this._X11.CreateMethod('XSync');
|
||||||
this._X11.CreateMethod('XBlackPixel');
|
this._X11.CreateMethod('XBlackPixel');
|
||||||
this._X11.CreateMethod('XWhitePixel');
|
this._X11.CreateMethod('XWhitePixel');
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
63
meshuser.js
63
meshuser.js
|
@ -128,13 +128,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
if (user == null) { try { obj.ws.close(); } catch (e) { } return; }
|
if (user == null) { try { obj.ws.close(); } catch (e) { } return; }
|
||||||
|
|
||||||
// Check if we have exceeded the user session limit
|
// Check if we have exceeded the user session limit
|
||||||
if (typeof domain.limits.maxusersessions == 'number') {
|
if ((typeof domain.limits.maxusersessions == 'number') || (typeof domain.limits.maxsingleusersessions == 'number')) {
|
||||||
// Count the number of user sessions for this domain
|
// Count the number of user sessions for this domain
|
||||||
var domainUserSessionCount = 0;
|
var domainUserSessionCount = 0, selfUserSessionCount = 0;
|
||||||
for (var i in obj.parent.wssessions2) { if (obj.parent.wssessions2[i].domainid == domain.id) { domainUserSessionCount++; } }
|
for (var i in obj.parent.wssessions2) { if (obj.parent.wssessions2[i].domainid == domain.id) { domainUserSessionCount++; if (obj.parent.wssessions2[i].userid == user._id) { selfUserSessionCount++; } } }
|
||||||
|
|
||||||
// Check if we have too many user sessions
|
// Check if we have too many user sessions
|
||||||
if (domainUserSessionCount >= domain.limits.maxusersessions) {
|
if (((typeof domain.limits.maxusersessions == 'number') && (domainUserSessionCount >= domain.limits.maxusersessions)) || ((typeof domain.limits.maxsingleusersessions == 'number') && (selfUserSessionCount >= domain.limits.maxsingleusersessions))) {
|
||||||
ws.send(JSON.stringify({ action: 'stopped', msg: 'Session count exceed' }));
|
ws.send(JSON.stringify({ action: 'stopped', msg: 'Session count exceed' }));
|
||||||
try { obj.ws.close(); } catch (e) { }
|
try { obj.ws.close(); } catch (e) { }
|
||||||
return;
|
return;
|
||||||
|
@ -708,6 +708,45 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'changepassword':
|
||||||
|
{
|
||||||
|
// Change our own password
|
||||||
|
if (obj.common.validateString(command.oldpass, 1, 256) == false) break;
|
||||||
|
if (obj.common.validateString(command.newpass, 1, 256) == false) break;
|
||||||
|
if (obj.common.validateString(command.hint, 0, 256) == false) break;
|
||||||
|
if (obj.common.checkPasswordRequirements(command.newpass, domain.passwordrequirements) == false) break; // Password does not meet requirements
|
||||||
|
|
||||||
|
// Start by checking the old password
|
||||||
|
obj.parent.checkUserPassword(domain, user, command.oldpass, function (result) {
|
||||||
|
if (result == true) {
|
||||||
|
// Update the password
|
||||||
|
require('./pass').hash(command.newpass, function (err, salt, hash) {
|
||||||
|
if (err) {
|
||||||
|
// Send user notification of error
|
||||||
|
displayNotificationMessage('Error, password not changed.');
|
||||||
|
} else {
|
||||||
|
// Change the password
|
||||||
|
var hint = command.hint;
|
||||||
|
if (hint.length > 250) hint = hint.substring(0, 250);
|
||||||
|
user.salt = salt;
|
||||||
|
user.hash = hash;
|
||||||
|
user.passhint = req.body.apasswordhint;
|
||||||
|
user.passchange = Math.floor(Date.now() / 1000);
|
||||||
|
delete user.passtype;
|
||||||
|
obj.db.SetUser(user);
|
||||||
|
obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
|
||||||
|
|
||||||
|
// Send user notification of password change
|
||||||
|
displayNotificationMessage('Password changed.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Send user notification of error
|
||||||
|
displayNotificationMessage('Current password not correct.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'changeuserpass':
|
case 'changeuserpass':
|
||||||
{
|
{
|
||||||
// Change a user's password
|
// Change a user's password
|
||||||
|
@ -718,22 +757,27 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
if (typeof command.removeMultiFactor != 'boolean') break;
|
if (typeof command.removeMultiFactor != 'boolean') break;
|
||||||
if (obj.common.checkPasswordRequirements(command.pass, domain.passwordrequirements) == false) break; // Password does not meet requirements
|
if (obj.common.checkPasswordRequirements(command.pass, domain.passwordrequirements) == false) break; // Password does not meet requirements
|
||||||
|
|
||||||
var chguserid = 'user/' + domain.id + '/' + command.user.toLowerCase(), chguser = obj.parent.users[chguserid];
|
var chguser = obj.parent.users['user/' + domain.id + '/' + command.user.toLowerCase()];
|
||||||
if (chguser && chguser.salt) {
|
if (chguser) {
|
||||||
// Compute the password hash & save it
|
// Compute the password hash & save it
|
||||||
require('./pass').hash(command.pass, chguser.salt, function (err, hash) {
|
require('./pass').hash(command.pass, function (err, salt, hash) {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
var annonceChange = false;
|
var annonceChange = false;
|
||||||
|
chguser.salt = salt;
|
||||||
chguser.hash = hash;
|
chguser.hash = hash;
|
||||||
chguser.passhint = command.hint;
|
chguser.passhint = command.hint;
|
||||||
|
chguser.passchange = Math.floor(Date.now() / 1000);
|
||||||
|
delete chguser.passtype; // Remove the password type if one was present.
|
||||||
if (command.removeMultiFactor == true) {
|
if (command.removeMultiFactor == true) {
|
||||||
if (chguser.otpsecret) { delete chguser.otpsecret; annonceChange = true; }
|
if (chguser.otpsecret) { delete chguser.otpsecret; annonceChange = true; }
|
||||||
if (chguser.otphkeys) { delete chguser.otphkeys; annonceChange = true; }
|
if (chguser.otphkeys) { delete chguser.otphkeys; annonceChange = true; }
|
||||||
if (chguser.otpkeys) { delete chguser.otpkeys; annonceChange = true; }
|
if (chguser.otpkeys) { delete chguser.otpkeys; annonceChange = true; }
|
||||||
}
|
}
|
||||||
obj.db.SetUser(chguser);
|
obj.db.SetUser(chguser);
|
||||||
|
|
||||||
if (annonceChange == true) { obj.parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: obj.parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Removed 2nd factor auth.', domain: domain.id }); }
|
if (annonceChange == true) { obj.parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: obj.parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Removed 2nd factor auth.', domain: domain.id }); }
|
||||||
|
} else {
|
||||||
|
// Report that the password change failed
|
||||||
|
// TODO
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1775,6 +1819,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Display a notification message for this session only.
|
||||||
|
function displayNotificationMessage(msg, tag) { ws.send(JSON.stringify({ "action": "msg", "type": "notify", "value": msg, "userid": user._id, "username": user.name, "tag": tag })); }
|
||||||
|
|
||||||
// Read the folder and all sub-folders and serialize that into json.
|
// Read the folder and all sub-folders and serialize that into json.
|
||||||
function readFilesRec(path) {
|
function readFilesRec(path) {
|
||||||
var r = {}, dir = obj.fs.readdirSync(path);
|
var r = {}, dir = obj.fs.readdirSync(path);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "meshcentral",
|
"name": "meshcentral",
|
||||||
"version": "0.2.8-b",
|
"version": "0.2.8-c",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Remote Management",
|
"Remote Management",
|
||||||
"Intel AMT",
|
"Intel AMT",
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5479,20 +5479,31 @@
|
||||||
|
|
||||||
function account_showChangePassword() {
|
function account_showChangePassword() {
|
||||||
if (xxdialogMode) return;
|
if (xxdialogMode) return;
|
||||||
var x = "Change your account password by entering the new password twice in the boxes below.<br /><br />";
|
var x = "Change your account password by entering the old password and new password twice in the boxes below. Password hint can be used but is not recommanded.<br /><br />";
|
||||||
x += "<form action='" + domainUrl + "changepassword' method=post><table style=margin-left:60px><tr>";
|
//x += "<form action='" + domainUrl + "changepassword' method=post>";
|
||||||
x += "<td align=right>Password:</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b><span id=dxPassWarn></span></b></td>";
|
x += "<table style=margin-left:60px>";
|
||||||
x += "</tr><tr><td align=right>Password:</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /></td>";
|
x += "<tr><td align=right>Old password:</td><td><input id=apassword0 type=password name=apassword0 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b></b></td></tr>";
|
||||||
x += "</tr><tr><td align=right>Password Hint:</td><td><input id=apasswordhint name=apasswordhint maxlength=250 type=text autocomplete=off /></td>";
|
x += "<tr><td align=right>New password:</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b><span id=dxPassWarn></span></b></td></tr>";
|
||||||
x += '</tr></table><br /><div style=padding:10px;margin-bottom:4px>';
|
x += "<tr><td align=right>New password:</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /></td></tr>";
|
||||||
x += '<input id=account_dlgCancelButton type=button value=Cancel style=float:right;width:80px;margin-left:5px onclick=dialogclose(0)>';
|
x += "<tr><td align=right>Password hint:</td><td><input id=apasswordhint name=apasswordhint maxlength=250 type=text autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /></td></tr>";
|
||||||
x += '<input id=account_dlgOkButton type=submit value=OK style="float:right;width:80px" onclick=dialogclose(1)>';
|
x += '</table>'
|
||||||
x += '</div><br /></form>';
|
if (passRequirements) { var r = []; for (var i in passRequirements) { r.push(i + ':' + passRequirements[i]); } x += '<br /><span style=font-size:x-small>Requirements: ' + r.join(', ') + '.</span>'; }
|
||||||
setDialogMode(2, "Change Password", 0, null, x);
|
x += '<br />';
|
||||||
Q('apassword1').focus();
|
//x += '<br /><div style=padding:10px;margin-bottom:4px>';
|
||||||
|
//x += '<input id=account_dlgCancelButton type=button value=Cancel style=float:right;width:80px;margin-left:5px onclick=dialogclose(0)>';
|
||||||
|
//x += '<input id=account_dlgOkButton type=submit value=OK style="float:right;width:80px" onclick=dialogclose(1)>';
|
||||||
|
//x += '</div><br /></form>';
|
||||||
|
setDialogMode(2, "Change Password", 3, account_showChangePasswordEx, x);
|
||||||
|
Q('apassword0').focus();
|
||||||
account_validateNewPassword();
|
account_validateNewPassword();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function account_showChangePasswordEx() {
|
||||||
|
if (Q('apassword1').value == Q('apassword2').value) {
|
||||||
|
meshserver.send({ action: 'changepassword', oldpass: Q('apassword0').value, newpass: Q('apassword1').value, hint: Q('apasswordhint').value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function account_createMesh() {
|
function account_createMesh() {
|
||||||
if (xxdialogMode) return;
|
if (xxdialogMode) return;
|
||||||
|
|
||||||
|
@ -5522,7 +5533,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function account_validateNewPassword() {
|
function account_validateNewPassword() {
|
||||||
var r = '', ok = (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value);
|
var r = '', ok = (Q('apassword0').value.length > 0) && (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value) && (Q('apassword0').value != Q('apassword1').value) && (Q('apasswordhint').value != Q('apassword1').value);
|
||||||
if (Q('apassword1').value != '') {
|
if (Q('apassword1').value != '') {
|
||||||
if (passRequirements == null || passRequirements == '') {
|
if (passRequirements == null || passRequirements == '') {
|
||||||
// No password requirements, display password strength
|
// No password requirements, display password strength
|
||||||
|
@ -5535,7 +5546,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QH('dxPassWarn', r);
|
QH('dxPassWarn', r);
|
||||||
QE('account_dlgOkButton', ok);
|
//QE('account_dlgOkButton', ok);
|
||||||
|
QE('idx_dlgOkButton', ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a password strength score
|
// Return a password strength score
|
||||||
|
@ -6344,6 +6356,7 @@
|
||||||
x += addHtmlValue('Email', '<input id=p4email style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
x += addHtmlValue('Email', '<input id=p4email style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
||||||
x += addHtmlValue('Password', '<input id=p4pass1 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
x += addHtmlValue('Password', '<input id=p4pass1 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
||||||
x += addHtmlValue('Password', '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
x += addHtmlValue('Password', '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
||||||
|
if (passRequirements) { var r = []; for (var i in passRequirements) { r.push(i + ':' + passRequirements[i]); } x += '<div style=font-size:x-small;padding:6px>Requirements: ' + r.join(', ') + '.</div>'; }
|
||||||
setDialogMode(2, "Create Account", 3, showCreateNewAccountDialogEx, x);
|
setDialogMode(2, "Create Account", 3, showCreateNewAccountDialogEx, x);
|
||||||
showCreateNewAccountDialogValidate();
|
showCreateNewAccountDialogValidate();
|
||||||
Q('p4name').focus();
|
Q('p4name').focus();
|
||||||
|
@ -6351,7 +6364,9 @@
|
||||||
|
|
||||||
function showCreateNewAccountDialogValidate(x) {
|
function showCreateNewAccountDialogValidate(x) {
|
||||||
if ((x == null) && (Q('p4email').value.length > 0) && (validateEmail(Q('p4email').value)) == false) { QE('idx_dlgOkButton', false); return; }
|
if ((x == null) && (Q('p4email').value.length > 0) && (validateEmail(Q('p4email').value)) == false) { QE('idx_dlgOkButton', false); return; }
|
||||||
QE('idx_dlgOkButton', (!Q('p4name') || ((Q('p4name').value.length > 0) && (Q('p4name').value.indexOf(' ') == -1))) && Q('p4pass1').value.length > 0 && Q('p4pass1').value == Q('p4pass2').value && checkPasswordRequirements(Q('p4pass1').value, passRequirements));
|
var ok = (!Q('p4name') || ((Q('p4name').value.length > 0) && (Q('p4name').value.indexOf(' ') == -1))) && Q('p4pass1').value.length > 0 && Q('p4pass1').value == Q('p4pass2').value && checkPasswordRequirements(Q('p4pass1').value, passRequirements);
|
||||||
|
if (ok && passRequirements) { if (checkPasswordRequirements(Q('p4pass1').value, passRequirements) == false) { ok = false; } }
|
||||||
|
QE('idx_dlgOkButton', ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showCreateNewAccountDialogEx() {
|
function showCreateNewAccountDialogEx() {
|
||||||
|
@ -6440,8 +6455,8 @@
|
||||||
if (activeSessions == 0) { Q('MainUserImage').classList.add('gray'); }
|
if (activeSessions == 0) { Q('MainUserImage').classList.add('gray'); }
|
||||||
|
|
||||||
// Server permissions
|
// Server permissions
|
||||||
var msg = '';
|
var msg = '', premsg = '';
|
||||||
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { msg += "Locked account, "; }
|
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { premsg = '<img src="images/padlock12.png" height=12 width=8 title="Account is locked" style="margin-top:2px" /> '; msg += 'Locked account, '; }
|
||||||
if ((user.siteadmin == null) || (user.siteadmin == 0) || (user.siteadmin == 32)) { msg += "No server rights"; } else if (user.siteadmin == 8) { msg += "Access to server files"; } else if (user.siteadmin == 0xFFFFFFFF) { msg += "Full administrator"; } else { msg += "Partial rights"; }
|
if ((user.siteadmin == null) || (user.siteadmin == 0) || (user.siteadmin == 32)) { msg += "No server rights"; } else if (user.siteadmin == 8) { msg += "Access to server files"; } else if (user.siteadmin == 0xFFFFFFFF) { msg += "Full administrator"; } else { msg += "Partial rights"; }
|
||||||
|
|
||||||
// Show user attributes
|
// Show user attributes
|
||||||
|
@ -6449,7 +6464,7 @@
|
||||||
var email = user.email?EscapeHtml(user.email):'<i>Not set</i>', everify = '';
|
var email = user.email?EscapeHtml(user.email):'<i>Not set</i>', everify = '';
|
||||||
if (serverinfo.emailcheck) { everify = ((user.emailVerified == true)?'<b style=color:green;cursor:pointer title="Email is verified">🗸</b> ':'<b style=color:red;cursor:pointer title="Email not verified">🗴</b> '); }
|
if (serverinfo.emailcheck) { everify = ((user.emailVerified == true)?'<b style=color:green;cursor:pointer title="Email is verified">🗸</b> ':'<b style=color:red;cursor:pointer title="Email not verified">🗴</b> '); }
|
||||||
x += addDeviceAttribute('Email', everify + "<a style=cursor:pointer onclick=p30showUserEmailChangeDialog(event,\"" + userid + "\")>" + email + '</a> <a style=cursor:pointer onclick=doemail(event,\"' + user.email + '\")><img class=hoverButton width=10 height=10 src="images/link1.png" /></a>');
|
x += addDeviceAttribute('Email', everify + "<a style=cursor:pointer onclick=p30showUserEmailChangeDialog(event,\"" + userid + "\")>" + email + '</a> <a style=cursor:pointer onclick=doemail(event,\"' + user.email + '\")><img class=hoverButton width=10 height=10 src="images/link1.png" /></a>');
|
||||||
x += addDeviceAttribute('Server Rights', "<a style=cursor:pointer onclick=showUserAdminDialog(event,\"" + userid + "\")>" + msg + "</a>");
|
x += addDeviceAttribute('Server Rights', premsg + "<a style=cursor:pointer onclick=showUserAdminDialog(event,\"" + userid + "\")>" + msg + "</a>");
|
||||||
if (user.quota) x += addDeviceAttribute('Server Quota', EscapeHtml(parseInt(user.quota) / 1024) + ' k');
|
if (user.quota) x += addDeviceAttribute('Server Quota', EscapeHtml(parseInt(user.quota) / 1024) + ' k');
|
||||||
x += addDeviceAttribute('Creation', new Date(user.creation * 1000).toLocaleString());
|
x += addDeviceAttribute('Creation', new Date(user.creation * 1000).toLocaleString());
|
||||||
if (user.login) x += addDeviceAttribute('Last Login', new Date(user.login * 1000).toLocaleString());
|
if (user.login) x += addDeviceAttribute('Last Login', new Date(user.login * 1000).toLocaleString());
|
||||||
|
@ -6460,7 +6475,7 @@
|
||||||
if (user.otpsecret > 0) { factors.push('Authentication App'); }
|
if (user.otpsecret > 0) { factors.push('Authentication App'); }
|
||||||
if (user.otphkeys > 0) { factors.push('Security Key'); }
|
if (user.otphkeys > 0) { factors.push('Security Key'); }
|
||||||
if (user.otpkeys > 0) { factors.push('Backup Codes'); }
|
if (user.otpkeys > 0) { factors.push('Backup Codes'); }
|
||||||
x += addDeviceAttribute('Security', factors.join(', '));
|
x += addDeviceAttribute('Security', '<img src="images/key12.png" height=12 width=11 title="2nd factor authentication enabled" style="margin-top:2px" /> ' + factors.join(', '));
|
||||||
}
|
}
|
||||||
|
|
||||||
x += '</table></div><br />';
|
x += '</table></div><br />';
|
||||||
|
@ -6534,7 +6549,8 @@
|
||||||
x += addHtmlValue('Password', '<input id=p4pass1 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate(1) onkeyup=showCreateNewAccountDialogValidate(1)></input>');
|
x += addHtmlValue('Password', '<input id=p4pass1 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate(1) onkeyup=showCreateNewAccountDialogValidate(1)></input>');
|
||||||
x += addHtmlValue('Password', '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate(1) onkeyup=showCreateNewAccountDialogValidate(1)></input>');
|
x += addHtmlValue('Password', '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate(1) onkeyup=showCreateNewAccountDialogValidate(1)></input>');
|
||||||
x += addHtmlValue('Password hint', '<input id=p4hint type=text style=width:230px maxlength=256></input>');
|
x += addHtmlValue('Password hint', '<input id=p4hint type=text style=width:230px maxlength=256></input>');
|
||||||
if (multiFactor == 1) { x += '<input id=p4twoFactorRemove type=checkbox />Remove all 2nd factor authentication.'; }
|
if (passRequirements) { var r = []; for (var i in passRequirements) { r.push(i + ':' + passRequirements[i]); } x += '<div style=font-size:x-small;padding:6px>Requirements: ' + r.join(', ') + '.</div>'; }
|
||||||
|
if (multiFactor == 1) { x += '<div><input id=p4twoFactorRemove type=checkbox />Remove all 2nd factor authentication.</div>'; }
|
||||||
setDialogMode(2, "Change Password for " + EscapeHtml(currentUser.name), 3, p30showUserChangePassDialogEx, x, multiFactor);
|
setDialogMode(2, "Change Password for " + EscapeHtml(currentUser.name), 3, p30showUserChangePassDialogEx, x, multiFactor);
|
||||||
showCreateNewAccountDialogValidate(1);
|
showCreateNewAccountDialogValidate(1);
|
||||||
Q('p4pass1').focus();
|
Q('p4pass1').focus();
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -424,7 +424,7 @@
|
||||||
ok = false;
|
ok = false;
|
||||||
QS('nuPass1').color = '#7b241c';
|
QS('nuPass1').color = '#7b241c';
|
||||||
QS('nuPass2').color = '#7b241c';
|
QS('nuPass2').color = '#7b241c';
|
||||||
QH('passWarning', '<span style=color:red><b>Password Policy</b><span>'); // TODO: Display problem hint
|
QH('passWarning', '<div style=color:red;cursor:pointer onclick=showPasswordPolicy()><b>Password Policy</b><div>'); // This is also a link to the password policy
|
||||||
} else {
|
} else {
|
||||||
QH('passWarning', '');
|
QH('passWarning', '');
|
||||||
}
|
}
|
||||||
|
@ -442,6 +442,18 @@
|
||||||
QE('createButton', ok);
|
QE('createButton', ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showPasswordPolicy() {
|
||||||
|
var policy = '<div style=text-align:left>';
|
||||||
|
if (passRequirements.min) { policy += 'Minimum length: ' + passRequirements.min + '<br />'; }
|
||||||
|
if (passRequirements.max) { policy += 'Maximum length: ' + passRequirements.max + '<br />'; }
|
||||||
|
if (passRequirements.upper) { policy += 'Upper case: ' + passRequirements.upper + '<br />'; }
|
||||||
|
if (passRequirements.lower) { policy += 'Lower case: ' + passRequirements.lower + '<br />'; }
|
||||||
|
if (passRequirements.numeric) { policy += 'Numeric: ' + passRequirements.numeric + '<br />'; }
|
||||||
|
if (passRequirements.nonalpha) { policy += 'Non-alphanumeric: ' + passRequirements.nonalpha + '<br />'; }
|
||||||
|
policy += '</div>';
|
||||||
|
messagebox("Password Policy", policy);
|
||||||
|
}
|
||||||
|
|
||||||
function validateReset(e) {
|
function validateReset(e) {
|
||||||
setDialogMode(0);
|
setDialogMode(0);
|
||||||
var x = validateEmail(Q('remail').value);
|
var x = validateEmail(Q('remail').value);
|
||||||
|
|
109
webserver.js
109
webserver.js
|
@ -421,12 +421,21 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
if (checkUserOneTimePasswordRequired(domain, user)) {
|
if (checkUserOneTimePasswordRequired(domain, user)) {
|
||||||
checkUserOneTimePassword(req, domain, user, req.body.token, req.body.hwtoken, function (result) {
|
checkUserOneTimePassword(req, domain, user, req.body.token, req.body.hwtoken, function (result) {
|
||||||
if (result == false) {
|
if (result == false) {
|
||||||
|
var randomWaitTime = 0;
|
||||||
|
|
||||||
// 2-step auth is required, but the token is not present or not valid.
|
// 2-step auth is required, but the token is not present or not valid.
|
||||||
if ((req.body.token != null) || (req.body.hwtoken != null)) { req.session.error = '<b style=color:#8C001A>Invalid token, try again.</b>'; }
|
if ((req.body.token != null) || (req.body.hwtoken != null)) {
|
||||||
req.session.loginmode = '4';
|
randomWaitTime = 2000 + (obj.crypto.randomBytes(2).readUInt16BE(0) % 4095); // This is a fail, wait a random time. 2 to 6 seconds.
|
||||||
req.session.tokenusername = xusername;
|
req.session.error = '<b style=color:#8C001A>Invalid token, try again.</b>';
|
||||||
req.session.tokenpassword = xpassword;
|
}
|
||||||
res.redirect(domain.url);
|
|
||||||
|
// Wait and redirect the user
|
||||||
|
setTimeout(function () {
|
||||||
|
req.session.loginmode = '4';
|
||||||
|
req.session.tokenusername = xusername;
|
||||||
|
req.session.tokenpassword = xpassword;
|
||||||
|
res.redirect(domain.url);
|
||||||
|
}, randomWaitTime);
|
||||||
} else {
|
} else {
|
||||||
// Login succesful
|
// Login succesful
|
||||||
completeLoginRequest(req, res, domain, user, userid);
|
completeLoginRequest(req, res, domain, user, userid);
|
||||||
|
@ -438,15 +447,20 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
// Login succesful
|
// Login succesful
|
||||||
completeLoginRequest(req, res, domain, user, userid);
|
completeLoginRequest(req, res, domain, user, userid);
|
||||||
} else {
|
} else {
|
||||||
//console.log('passhint', passhint);
|
// Login failed, wait a random delay
|
||||||
delete req.session.loginmode;
|
setTimeout(function () {
|
||||||
if (err == 'locked') { req.session.error = '<b style=color:#8C001A>Account locked.</b>'; } else { req.session.error = '<b style=color:#8C001A>Login failed, check username and password.</b>'; }
|
// If the account is locked, display that.
|
||||||
if ((passhint != null) && (passhint.length > 0)) {
|
if (err == 'locked') { req.session.error = '<b style=color:#8C001A>Account locked.</b>'; } else { req.session.error = '<b style=color:#8C001A>Login failed, check username and password.</b>'; }
|
||||||
req.session.passhint = passhint;
|
|
||||||
} else {
|
// Clean up login mode and display password hint if present.
|
||||||
delete req.session.passhint;
|
delete req.session.loginmode;
|
||||||
}
|
if ((passhint != null) && (passhint.length > 0)) {
|
||||||
res.redirect(domain.url);
|
req.session.passhint = passhint;
|
||||||
|
} else {
|
||||||
|
delete req.session.passhint;
|
||||||
|
}
|
||||||
|
res.redirect(domain.url);
|
||||||
|
}, 2000 + (obj.crypto.randomBytes(2).readUInt16BE(0) % 4095)); // Wait for 2 to ~6 seconds.
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -547,6 +561,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
user.salt = salt;
|
user.salt = salt;
|
||||||
user.hash = hash;
|
user.hash = hash;
|
||||||
|
delete user.passtype;
|
||||||
obj.db.SetUser(user);
|
obj.db.SetUser(user);
|
||||||
|
|
||||||
// Send the verification email
|
// Send the verification email
|
||||||
|
@ -688,6 +703,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
userinfo = obj.users[user._id];
|
userinfo = obj.users[user._id];
|
||||||
userinfo.salt = salt;
|
userinfo.salt = salt;
|
||||||
userinfo.hash = hash;
|
userinfo.hash = hash;
|
||||||
|
delete userinfo.passtype;
|
||||||
userinfo.passchange = Math.floor(Date.now() / 1000);
|
userinfo.passchange = Math.floor(Date.now() / 1000);
|
||||||
userinfo.passhint = null;
|
userinfo.passhint = null;
|
||||||
//delete userinfo.otpsecret; // Currently a email password reset will turn off 2-step login.
|
//delete userinfo.otpsecret; // Currently a email password reset will turn off 2-step login.
|
||||||
|
@ -758,28 +774,63 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check a user's password
|
||||||
|
obj.checkUserPassword = function(domain, user, password, func) {
|
||||||
|
// Check the old password
|
||||||
|
if (user.passtype != null) {
|
||||||
|
// IIS default clear or weak password hashing (SHA-1)
|
||||||
|
require('./pass').iishash(user.passtype, password, user.salt, function (err, hash) {
|
||||||
|
if (err) return func(false);
|
||||||
|
if (hash == user.hash) {
|
||||||
|
if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { return func(false); } // Account is locked
|
||||||
|
return func(true); // Allow password change
|
||||||
|
}
|
||||||
|
func(false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Default strong password hashing (pbkdf2 SHA384)
|
||||||
|
require('./pass').hash(password, user.salt, function (err, hash) {
|
||||||
|
if (err) return func(false);
|
||||||
|
if (hash == user.hash) {
|
||||||
|
if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { return func(false); } // Account is locked
|
||||||
|
return func(true); // Allow password change
|
||||||
|
}
|
||||||
|
func(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle password changes
|
// Handle password changes
|
||||||
function handlePasswordChangeRequest(req, res) {
|
function handlePasswordChangeRequest(req, res) {
|
||||||
const domain = checkUserIpAddress(req, res);
|
const domain = checkUserIpAddress(req, res);
|
||||||
if ((domain == null) || (domain.auth == 'sspi')) return;
|
if ((domain == null) || (domain.auth == 'sspi')) return;
|
||||||
|
|
||||||
// Check if the user is logged and we have all required parameters
|
// Check if the user is logged and we have all required parameters
|
||||||
if (!req.session || !req.session.userid || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url); return; }
|
if (!req.session || !req.session.userid || !req.body.apassword0 || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url); return; }
|
||||||
|
|
||||||
// Update the password
|
// Get the current user
|
||||||
require('./pass').hash(req.body.apassword1, function (err, salt, hash) {
|
var user = obj.users[req.session.userid];
|
||||||
if (err) throw err;
|
if (!user) { res.redirect(domain.url); return; }
|
||||||
var hint = req.body.apasswordhint;
|
|
||||||
if (hint.length > 250) hint = hint.substring(0, 250);
|
// Check old password
|
||||||
var user = obj.users[req.session.userid];
|
obj.checkUserPassword(domain, user, req.body.apassword0, function (result) {
|
||||||
user.salt = salt;
|
if (result == true) {
|
||||||
user.hash = hash;
|
// Update the password
|
||||||
user.passchange = Math.floor(Date.now() / 1000);
|
require('./pass').hash(req.body.apassword1, function (err, salt, hash) {
|
||||||
user.passhint = req.body.apasswordhint;
|
if (err) throw err;
|
||||||
obj.db.SetUser(user);
|
var hint = req.body.apasswordhint;
|
||||||
req.session.viewmode = 2;
|
if (hint.length > 250) hint = hint.substring(0, 250);
|
||||||
res.redirect(domain.url);
|
user.salt = salt;
|
||||||
obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
|
user.hash = hash;
|
||||||
|
user.passhint = req.body.apasswordhint;
|
||||||
|
user.passchange = Math.floor(Date.now() / 1000);
|
||||||
|
delete user.passtype;
|
||||||
|
obj.db.SetUser(user);
|
||||||
|
req.session.viewmode = 2;
|
||||||
|
res.redirect(domain.url);
|
||||||
|
obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue