mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2025-11-07 12:52:54 -05:00
Added server password timeout and reset on next login.
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -243,7 +243,7 @@
|
||||
<div style="margin-left:9px;margin-bottom:8px">
|
||||
<div style="margin-top:5px"><span id="verifyEmailId" style="display:none"><a onclick="account_showVerifyEmail()" style="cursor:pointer">Verify email</a></span></div>
|
||||
<div style="margin-top:5px"><a onclick="account_showChangeEmail()" style="cursor:pointer">Change email address</a></div>
|
||||
<div style="margin-top:5px"><a onclick="account_showChangePassword()" style="cursor:pointer">Change password</a></div>
|
||||
<div style="margin-top:5px"><a onclick="account_showChangePassword()" style="cursor:pointer">Change password</a><span id="p2nextPasswordUpdateTime"></span></div>
|
||||
<div style="margin-top:5px"><a onclick="account_showDeleteAccount()" style="cursor:pointer">Delete account</a></div>
|
||||
</div>
|
||||
<br style=clear:both />
|
||||
@@ -654,8 +654,21 @@
|
||||
QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
|
||||
QV('manageAuthApp', features & 4096);
|
||||
QV('manageOtp', ((features & 4096) != 0) && ((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0)));
|
||||
|
||||
if (typeof userinfo.passchange == 'number') {
|
||||
if (userinfo.passchange == -1) { QH('p2nextPasswordUpdateTime', ' - Reset on next login.'); }
|
||||
else if ((passRequirements != null) && (typeof passRequirements.reset == 'number')) {
|
||||
var seconds = (userinfo.passchange) + (passRequirements.reset * 86400) - Math.floor(Date.now() / 1000);
|
||||
if (seconds < 0) { QH('p2nextPasswordUpdateTime', ' - Reset on next login.'); }
|
||||
else if (seconds < 3600) { QH('p2nextPasswordUpdateTime', ' - Reset in ' + Math.floor(seconds / 60) + ' minute' + addLetterS(Math.floor(seconds / 60)) + '.'); }
|
||||
else if (seconds < 86400) { QH('p2nextPasswordUpdateTime', ' - Reset in ' + Math.floor(seconds / 3600) + ' hour' + addLetterS(Math.floor(seconds / 3600)) + '.'); }
|
||||
else { QH('p2nextPasswordUpdateTime', ' - Reset in ' + Math.floor(seconds / 86400) + ' day' + addLetterS(Math.floor(seconds / 86400)) + '.'); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addLetterS(x) { return (x > 1) ? 's' : ''; }
|
||||
|
||||
function onMessage(server, message) {
|
||||
switch (message.action) {
|
||||
case 'serverinfo': {
|
||||
|
||||
@@ -260,7 +260,7 @@
|
||||
<p style="margin-left:40px">
|
||||
<span id="verifyEmailId" style="display:none"><a onclick="account_showVerifyEmail()" style="cursor:pointer">Verify email</a><br /></span>
|
||||
<a onclick="account_showChangeEmail()" style="cursor:pointer">Change email address</a><br />
|
||||
<a onclick="account_showChangePassword()" style="cursor:pointer">Change password</a><br />
|
||||
<a onclick="account_showChangePassword()" style="cursor:pointer">Change password</a><span id="p2nextPasswordUpdateTime"></span><br />
|
||||
<a onclick="account_showDeleteAccount()" style="cursor:pointer">Delete account</a><br />
|
||||
</p>
|
||||
<br style=clear:both />
|
||||
@@ -1190,8 +1190,21 @@
|
||||
QV('authAppSetupCheck', userinfo.otpsecret == 1);
|
||||
QV('authKeySetupCheck', userinfo.otphkeys > 0);
|
||||
QV('authCodesSetupCheck', userinfo.otpkeys > 0);
|
||||
|
||||
if (typeof userinfo.passchange == 'number') {
|
||||
if (userinfo.passchange == -1) { QH('p2nextPasswordUpdateTime', ' - Reset on next login.'); }
|
||||
else if ((passRequirements != null) && (typeof passRequirements.reset == 'number')) {
|
||||
var seconds = (userinfo.passchange) + (passRequirements.reset * 86400) - Math.floor(Date.now() / 1000);
|
||||
if (seconds < 0) { QH('p2nextPasswordUpdateTime', ' - Reset on next login.'); }
|
||||
else if (seconds < 3600) { QH('p2nextPasswordUpdateTime', ' - Reset in ' + Math.floor(seconds / 60) + ' minute' + addLetterS(Math.floor(seconds / 60)) + '.'); }
|
||||
else if (seconds < 86400) { QH('p2nextPasswordUpdateTime', ' - Reset in ' + Math.floor(seconds / 3600) + ' hour' + addLetterS(Math.floor(seconds / 3600)) + '.'); }
|
||||
else { QH('p2nextPasswordUpdateTime', ' - Reset in ' + Math.floor(seconds / 86400) + ' day' + addLetterS(Math.floor(seconds / 86400)) + '.'); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addLetterS(x) { return (x > 1) ? 's' : ''; }
|
||||
|
||||
function onMessage(server, message) {
|
||||
switch (message.action) {
|
||||
case 'serverstats': {
|
||||
@@ -1767,7 +1780,9 @@
|
||||
}
|
||||
case 'login': {
|
||||
// Update the last login time
|
||||
if (users != null && users['user/' + domain + '/' + message.event.username.toLowerCase()]) { users['user/' + domain + '/' + message.event.username.toLowerCase()].login = Math.floor(message.event.time / 1000); }
|
||||
if (users != null && users['user/' + domain + '/' + message.event.username.toLowerCase()]) {
|
||||
users['user/' + domain + '/' + message.event.username.toLowerCase()].login = Math.floor(new Date(message.event.time).getTime() / 1000);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'scanamtdevice': {
|
||||
@@ -6584,6 +6599,8 @@
|
||||
if (user.quota) x += addDeviceAttribute('Server Quota', EscapeHtml(parseInt(user.quota) / 1024) + ' k');
|
||||
x += addDeviceAttribute('Creation', new Date(user.creation * 1000).toLocaleString());
|
||||
if (user.login) x += addDeviceAttribute('Last Login', new Date(user.login * 1000).toLocaleString());
|
||||
if (user.passchange == -1) { x += addDeviceAttribute('Password', 'Will be changed on next login.'); }
|
||||
else if (user.passchange) { x += addDeviceAttribute('Password', 'Last changed: ' + new Date(user.passchange * 1000).toLocaleString()); }
|
||||
|
||||
var linkCount = 0, linkCountStr = '<i>None<i>';
|
||||
if (user.links) {
|
||||
@@ -6674,6 +6691,7 @@
|
||||
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>');
|
||||
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>'; }
|
||||
x += '<div><input id=p4resetNextLogin type=checkbox />Force password reset on next login.</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);
|
||||
showCreateNewAccountDialogValidate(1);
|
||||
@@ -6683,7 +6701,7 @@
|
||||
function p30showUserChangePassDialogEx(b, tag) {
|
||||
var removeMultiFactor = false;
|
||||
if ((tag == 1) && (Q('p4twoFactorRemove').checked == true)) { removeMultiFactor = true; }
|
||||
if (Q('p4pass1').value == Q('p4pass2').value) { meshserver.send({ action: 'changeuserpass', user: currentUser.name, pass: Q('p4pass1').value, hint: Q('p4hint').value, removeMultiFactor: removeMultiFactor }); }
|
||||
if (Q('p4pass1').value == Q('p4pass2').value) { meshserver.send({ action: 'changeuserpass', user: currentUser.name, pass: Q('p4pass1').value, hint: Q('p4hint').value, removeMultiFactor: removeMultiFactor, resetNextLogin: Q('p4resetNextLogin').checked }); }
|
||||
}
|
||||
|
||||
function p30showDeleteUserDialog() {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -188,6 +188,35 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id=resetpasswordpanel style="position:relative;background-color: #979797;border-radius:16px;width:300px;padding:16px;text-align:center;display:none">
|
||||
<form action=resetpassword method=post>
|
||||
<div id=message6>
|
||||
{{{message}}}
|
||||
</div>
|
||||
<div id="rpasswordPolicyCallout" style="left:-10px;width:100px;display:none;position:absolute;background-color:#FFC;border-radius:5px;padding:5px;box-shadow:0px 0px 15px #666;font-size:10px"></div>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="rnuPass1" width=100 align=right>Password:</td>
|
||||
<td><input id=rapassword1 type=password name=rpassword1 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validatePassReset(3,event) onkeyup=validatePassReset(3,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="rnuPass2" align=right>Password:</td>
|
||||
<td><input id=rapassword2 type=password name=rpassword2 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validatePassReset(4,event) onkeyup=validatePassReset(4,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="rnuHint" align=right>Password Hint:</td>
|
||||
<td><input id=rapasswordhint type=text name=rpasswordhint autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validatePassReset(5,event) onkeyup=validatePassReset(5,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=2>
|
||||
<div style=float:right><input id=resetPassButton type=submit value="Reset Password" disabled="disabled" /></div>
|
||||
<div id=rpassWarning style="padding-top:6px"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
@@ -288,6 +317,7 @@
|
||||
QV('message3', false);
|
||||
QV('message4', false);
|
||||
QV('message5', false);
|
||||
QV('message6', false);
|
||||
go(x);
|
||||
}
|
||||
|
||||
@@ -299,10 +329,13 @@
|
||||
QV('resetpanel', x == 3);
|
||||
QV('tokenpanel', x == 4);
|
||||
QV('resettokenpanel', x == 5);
|
||||
QV('resetpasswordpanel', x == 6);
|
||||
if (x == 1) { Q('username').focus(); }
|
||||
if (x == 2) { Q('ausername').focus(); }
|
||||
if (x == 3) { Q('remail').focus(); }
|
||||
if (x == 4) { Q('tokenInput').focus(); }
|
||||
if (x == 5) { Q('resetTokenInput').focus(); }
|
||||
if (x == 6) { Q('rapassword1').focus(); }
|
||||
}
|
||||
|
||||
function validateLogin(box, e) {
|
||||
@@ -355,6 +388,52 @@
|
||||
if (e != null) { haltEvent(e); }
|
||||
}
|
||||
|
||||
function validatePassReset(box, e) {
|
||||
setDialogMode(0);
|
||||
var pass1ok = (Q('rapassword1').value.length > 0);
|
||||
var pass2ok = (Q('rapassword2').value.length > 0) && (Q('rapassword2').value == Q('rapassword1').value);
|
||||
var ok = (pass1ok && pass2ok);
|
||||
|
||||
// Color the fields
|
||||
QS('rnuPass1').color = pass1ok ? 'black' : '#7b241c';
|
||||
QS('rnuPass2').color = pass2ok ? 'black' : '#7b241c';
|
||||
|
||||
if (Q('rapassword1').value == '') {
|
||||
QH('rpassWarning', '');
|
||||
QV('rpasswordPolicyCallout', false);
|
||||
} else {
|
||||
if (passRequirements == null || passRequirements == '') {
|
||||
// No password requirements, display password strength
|
||||
var passStrength = checkPasswordStrength(Q('rapassword1').value);
|
||||
if (passStrength >= 80) { QH('rpassWarning', '<span style=color:green><b>Strong Password</b><span>'); }
|
||||
else if (passStrength >= 60) { QH('rpassWarning', '<span style=color:blue><b>Good Password</b><span>'); }
|
||||
else { QH('rpassWarning', '<span style=color:red><b>Weak Password</b><span>'); }
|
||||
} else {
|
||||
// Password requirements provided, use that
|
||||
var passReq = checkPasswordRequirements(Q('rapassword1').value, passRequirements);
|
||||
if (passReq == false) {
|
||||
ok = false;
|
||||
QS('rnuPass1').color = '#7b241c';
|
||||
QS('rnuPass2').color = '#7b241c';
|
||||
QH('rpassWarning', '<div style=color:red;cursor:pointer onclick=showPasswordPolicy()><b>Password Policy</b><div>'); // This is also a link to the password policy
|
||||
QV('rpasswordPolicyCallout', true);
|
||||
QH('rpasswordPolicyCallout', passwordPolicyText(Q('rapassword1').value));
|
||||
} else {
|
||||
QH('rpassWarning', '');
|
||||
QV('rpasswordPolicyCallout', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((e != null) && (e.keyCode == 13)) {
|
||||
if (box == 2) { Q('rapassword1').focus(); }
|
||||
if (box == 3) { Q('rapassword2').focus(); }
|
||||
if (box == 4) { Q('rapasswordhint').focus(); }
|
||||
if (box == 6) { Q('resetPassButton').click(); }
|
||||
}
|
||||
if (e != null) { haltEvent(e); }
|
||||
QE('resetPassButton', ok);
|
||||
}
|
||||
|
||||
function validateReset(e) {
|
||||
setDialogMode(0);
|
||||
var x = validateEmail(Q('remail').value);
|
||||
|
||||
@@ -167,19 +167,19 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="nuPass1" align=right>Password:</td>
|
||||
<td><input id=apassword1 type=password name=password1 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(3) onkeyup=validateCreate(3,event) /></td>
|
||||
<td><input id=apassword1 type=password name=password1 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(3,event) onkeyup=validateCreate(3,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="nuPass2" align=right>Password:</td>
|
||||
<td><input id=apassword2 type=password name=password2 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(4) onkeyup=validateCreate(4,event) /></td>
|
||||
<td><input id=apassword2 type=password name=password2 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(4,event) onkeyup=validateCreate(4,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="nuHint" align=right>Password Hint:</td>
|
||||
<td><input id=apasswordhint type=text name=apasswordhint autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(5) onkeyup=validateCreate(5,event) /></td>
|
||||
<td><input id=apasswordhint type=text name=apasswordhint autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(5,event) onkeyup=validateCreate(5,event) /></td>
|
||||
</tr>
|
||||
<tr id=newAccountPass title="Enter the account creation token">
|
||||
<td id="nuToken" align=right>Creation Token:</td>
|
||||
<td><input id=anewaccountpass type=password name=anewaccountpass autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(6) onkeyup=validateCreate(6,event) /></td>
|
||||
<td><input id=anewaccountpass type=password name=anewaccountpass autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(6,event) onkeyup=validateCreate(6,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=2>
|
||||
@@ -258,6 +258,35 @@
|
||||
<hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
|
||||
</form>
|
||||
</div>
|
||||
<div id=resetpasswordpanel style="position:relative;background-color: #979797;border-radius:16px;width:300px;padding:16px;text-align:center;display:none">
|
||||
<form action=resetpassword method=post>
|
||||
<div id=message6>
|
||||
{{{message}}}
|
||||
</div>
|
||||
<div id="rpasswordPolicyCallout" style="left:-10px;width:100px;display:none;position:absolute;background-color:#FFC;border-radius:5px;padding:5px;box-shadow:0px 0px 15px #666;font-size:10px"></div>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="rnuPass1" width=100 align=right>Password:</td>
|
||||
<td><input id=rapassword1 type=password name=rpassword1 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validatePassReset(3,event) onkeyup=validatePassReset(3,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="rnuPass2" align=right>Password:</td>
|
||||
<td><input id=rapassword2 type=password name=rpassword2 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validatePassReset(4,event) onkeyup=validatePassReset(4,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="rnuHint" align=right>Password Hint:</td>
|
||||
<td><input id=rapasswordhint type=text name=rpasswordhint autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validatePassReset(5,event) onkeyup=validatePassReset(5,event) /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=2>
|
||||
<div style=float:right><input id=resetPassButton type=submit value="Reset Password" disabled="disabled" /></div>
|
||||
<div id=rpassWarning style="padding-top:6px"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -373,6 +402,7 @@
|
||||
QV('message3', false);
|
||||
QV('message4', false);
|
||||
QV('message5', false);
|
||||
QV('message6', false);
|
||||
go(x);
|
||||
}
|
||||
|
||||
@@ -384,10 +414,13 @@
|
||||
QV('resetpanel', x == 3);
|
||||
QV('tokenpanel', x == 4);
|
||||
QV('resettokenpanel', x == 5);
|
||||
QV('resetpasswordpanel', x == 6);
|
||||
if (x == 1) { Q('username').focus(); }
|
||||
if (x == 2) { Q('ausername').focus(); }
|
||||
if (x == 3) { Q('remail').focus(); }
|
||||
if (x == 4) { Q('tokenInput').focus(); }
|
||||
if (x == 5) { Q('resetTokenInput').focus(); }
|
||||
if (x == 6) { Q('rapassword1').focus(); }
|
||||
}
|
||||
|
||||
function validateLogin(box, e) {
|
||||
@@ -452,6 +485,52 @@
|
||||
QE('createButton', ok);
|
||||
}
|
||||
|
||||
function validatePassReset(box, e) {
|
||||
setDialogMode(0);
|
||||
var pass1ok = (Q('rapassword1').value.length > 0);
|
||||
var pass2ok = (Q('rapassword2').value.length > 0) && (Q('rapassword2').value == Q('rapassword1').value);
|
||||
var ok = (pass1ok && pass2ok);
|
||||
|
||||
// Color the fields
|
||||
QS('rnuPass1').color = pass1ok ? 'black' : '#7b241c';
|
||||
QS('rnuPass2').color = pass2ok ? 'black' : '#7b241c';
|
||||
|
||||
if (Q('rapassword1').value == '') {
|
||||
QH('rpassWarning', '');
|
||||
QV('rpasswordPolicyCallout', false);
|
||||
} else {
|
||||
if (passRequirements == null || passRequirements == '') {
|
||||
// No password requirements, display password strength
|
||||
var passStrength = checkPasswordStrength(Q('rapassword1').value);
|
||||
if (passStrength >= 80) { QH('rpassWarning', '<span style=color:green><b>Strong Password</b><span>'); }
|
||||
else if (passStrength >= 60) { QH('rpassWarning', '<span style=color:blue><b>Good Password</b><span>'); }
|
||||
else { QH('rpassWarning', '<span style=color:red><b>Weak Password</b><span>'); }
|
||||
} else {
|
||||
// Password requirements provided, use that
|
||||
var passReq = checkPasswordRequirements(Q('rapassword1').value, passRequirements);
|
||||
if (passReq == false) {
|
||||
ok = false;
|
||||
QS('rnuPass1').color = '#7b241c';
|
||||
QS('rnuPass2').color = '#7b241c';
|
||||
QH('rpassWarning', '<div style=color:red;cursor:pointer onclick=showPasswordPolicy()><b>Password Policy</b><div>'); // This is also a link to the password policy
|
||||
QV('rpasswordPolicyCallout', true);
|
||||
QH('rpasswordPolicyCallout', passwordPolicyText(Q('rapassword1').value));
|
||||
} else {
|
||||
QH('rpassWarning', '');
|
||||
QV('rpasswordPolicyCallout', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((e != null) && (e.keyCode == 13)) {
|
||||
if (box == 2) { Q('rapassword1').focus(); }
|
||||
if (box == 3) { Q('rapassword2').focus(); }
|
||||
if (box == 4) { Q('rapasswordhint').focus(); }
|
||||
if (box == 6) { Q('resetPassButton').click(); }
|
||||
}
|
||||
if (e != null) { haltEvent(e); }
|
||||
QE('resetPassButton', ok);
|
||||
}
|
||||
|
||||
function passwordPolicyText(pass) {
|
||||
var policy = '<div style=text-align:left>';
|
||||
var counts = strCount(pass);
|
||||
|
||||
Reference in New Issue
Block a user