\ No newline at end of file
diff --git a/views/default-mobile.handlebars b/views/default-mobile.handlebars
index 0b1eeaef..78ded960 100644
--- a/views/default-mobile.handlebars
+++ b/views/default-mobile.handlebars
@@ -585,6 +585,8 @@
var attemptWebRTC = ((features & 128) != 0);
var StatusStrs = ['Disconnected', 'Connecting...', 'Setup...', 'Connected', 'Intel® AMT Connected'];
var files;
+ var passRequirements = "{{{passRequirements}}}";
+ if (passRequirements != "") { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); }
function startup() {
if ((features & 32) == 0) {
@@ -989,15 +991,15 @@
function account_showChangePassword() {
if (xxdialogMode) return;
var x = "';
setDialogMode(2, "Change Password", 0, null, x);
- account_validateDeleteAccount();
+ account_validateNewPassword();
Q('apassword1').focus();
}
@@ -1024,13 +1026,20 @@
}
function account_validateNewPassword() {
- QE('account_dlgOkButton', (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value));
- var r = '';
+ var r = '', ok = (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value);
if (Q('apassword1').value != '') {
- var passStrength = checkPasswordStrength(Q('apassword1').value);
- if (passStrength >= 80) { r = 'Strong'; } else if (passStrength >= 60) { r = 'Good'; } else { r = 'Weak'; }
+ if (passRequirements == null || passRequirements == '') {
+ // No password requirements, display password strength
+ var passStrength = checkPasswordStrength(Q('apassword1').value);
+ if (passStrength >= 80) { r = '●'; } else if (passStrength >= 60) { r = '●'; } else { r = '●'; }
+ } else {
+ // Password requirements provided, use that
+ var passReq = checkPasswordRequirements(Q('apassword1').value, passRequirements);
+ if (passReq == false) { ok = false; r = '●' }
+ }
}
QH('dxPassWarn', r);
+ QE('account_dlgOkButton', ok);
}
// Return a password strength score
@@ -1042,6 +1051,25 @@
return parseInt(r + (varCount - 1) * 10);
}
+ // Check password requirements
+ function checkPasswordRequirements(password, requirements) {
+ if ((requirements == null) || (requirements == '') || (typeof requirements != 'object')) return true;
+ if (requirements.min) { if (password.length < requirements.min) return false; }
+ if (requirements.max) { if (password.length > requirements.max) return false; }
+ var num = 0, lower = 0, upper = 0, nonalpha = 0;
+ for (var i = 0; i < password.length; i++) {
+ if (/\d/.test(password[i])) { num++; }
+ if (/[a-z]/.test(password[i])) { lower++; }
+ if (/[A-Z]/.test(password[i])) { upper++; }
+ if (/\W/.test(password[i])) { nonalpha++; }
+ }
+ if (requirements.num && (num < requirements.num)) return false;
+ if (requirements.lower && (lower < requirements.lower)) return false;
+ if (requirements.upper && (upper < requirements.upper)) return false;
+ if (requirements.nonalpha && (nonalpha < requirements.nonalpha)) return false;
+ return true;
+ }
+
function updateMeshes() {
var r = '', count = 0;
for (i in meshes) {
diff --git a/views/default.handlebars b/views/default.handlebars
index 148dc4e0..0371a07b 100644
--- a/views/default.handlebars
+++ b/views/default.handlebars
@@ -874,6 +874,8 @@
var webPageFullScreen = getstore('webPageFullScreen', true);
if (webPageFullScreen == 'false') { webPageFullScreen = false; }
if (webPageFullScreen == 'true') { webPageFullScreen = true; }
+ var passRequirements = "{{{passRequirements}}}";
+ if (passRequirements != "") { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); }
function startup() {
if ((features & 32) == 0) {
@@ -5025,7 +5027,7 @@
function account_validateEmail(e, email) {
QE('idx_dlgOkButton', validateEmail(Q('dp2email').value) && (Q('dp2email').value != userinfo.email));
- if ((x == true) && (e != null) && (e.keyCode == 13)) { dialogclose(1); }
+ if ((e != null) && (e.keyCode == 13)) { dialogclose(1); }
}
function account_changeEmail() {
@@ -5051,16 +5053,16 @@
if (xxdialogMode) return;
var x = "Change your account password by entering the new password twice in the boxes below.
";
x += "';
setDialogMode(2, "Change Password", 0, null, x);
- account_validateDeleteAccount();
Q('apassword1').focus();
+ account_validateNewPassword();
}
function account_createMesh() {
@@ -5087,13 +5089,20 @@
}
function account_validateNewPassword() {
- QE('account_dlgOkButton', (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value));
- var r = '';
+ var r = '', ok = (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value);
if (Q('apassword1').value != '') {
- var passStrength = checkPasswordStrength(Q('apassword1').value);
- if (passStrength >= 80) { r = 'Strong'; } else if (passStrength >= 60) { r = 'Good'; } else { r = 'Weak'; }
+ if (passRequirements == null || passRequirements == '') {
+ // No password requirements, display password strength
+ var passStrength = checkPasswordStrength(Q('apassword1').value);
+ if (passStrength >= 80) { r = 'Strong'; } else if (passStrength >= 60) { r = 'Good'; } else { r = 'Weak'; }
+ } else {
+ // Password requirements provided, use that
+ var passReq = checkPasswordRequirements(Q('apassword1').value, passRequirements);
+ if (passReq == false) { ok = false; r = 'Policy' }
+ }
}
QH('dxPassWarn', r);
+ QE('account_dlgOkButton', ok);
}
// Return a password strength score
@@ -5105,6 +5114,25 @@
return parseInt(r + (varCount - 1) * 10);
}
+ // Check password requirements
+ function checkPasswordRequirements(password, requirements) {
+ if ((requirements == null) || (requirements == '') || (typeof requirements != 'object')) return true;
+ if (requirements.min) { if (password.length < requirements.min) return false; }
+ if (requirements.max) { if (password.length > requirements.max) return false; }
+ var num = 0, lower = 0, upper = 0, nonalpha = 0;
+ for (var i = 0; i < password.length; i++) {
+ if (/\d/.test(password[i])) { num++; }
+ if (/[a-z]/.test(password[i])) { lower++; }
+ if (/[A-Z]/.test(password[i])) { upper++; }
+ if (/\W/.test(password[i])) { nonalpha++; }
+ }
+ if (requirements.num && (num < requirements.num)) return false;
+ if (requirements.lower && (lower < requirements.lower)) return false;
+ if (requirements.upper && (upper < requirements.upper)) return false;
+ if (requirements.nonalpha && (nonalpha < requirements.nonalpha)) return false;
+ return true;
+ }
+
function updateMeshes() {
var r = '';
var c = 0, count = 0;
@@ -5826,7 +5854,7 @@
function showCreateNewAccountDialogValidate() {
if ((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);
+ 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));
}
function showCreateNewAccountDialogEx() {
diff --git a/views/login-min.handlebars b/views/login-min.handlebars
index 1a0a2ab3..5f3278d3 100644
--- a/views/login-min.handlebars
+++ b/views/login-min.handlebars
@@ -1 +1 @@
- MeshCentral - Login
{{{title}}}
{{{title2}}}
↔
Welcome
Connect to your home or office devices from anywhere in the world using MeshCentral, the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the "My Devices" section of this web site and you will be able to monitor them and take control of them.
X
\ No newline at end of file
+ MeshCentral - Login
{{{title}}}
{{{title2}}}
↔
Welcome
Connect to your home or office devices from anywhere in the world using MeshCentral, the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the "My Devices" section of this web site and you will be able to monitor them and take control of them.
X
\ No newline at end of file
diff --git a/views/login-mobile-min.handlebars b/views/login-mobile-min.handlebars
index c13d6e9e..5217cf66 100644
--- a/views/login-mobile-min.handlebars
+++ b/views/login-mobile-min.handlebars
@@ -1 +1 @@
- MeshCentral - Login
{{{title}}}
{{{title2}}}
X
\ No newline at end of file
+ MeshCentral - Login
{{{title}}}
{{{title2}}}
X
\ No newline at end of file
diff --git a/views/login-mobile.handlebars b/views/login-mobile.handlebars
index 0abdc1da..4293fb7e 100644
--- a/views/login-mobile.handlebars
+++ b/views/login-mobile.handlebars
@@ -179,6 +179,8 @@
var newAccountPass = parseInt('{{{newAccountPass}}}');
var emailCheck = ('{{{emailcheck}}}' == 'true');
var features = parseInt('{{{features}}}');
+ var passRequirements = "{{{passRequirements}}}";
+ if (passRequirements != "") { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); }
function startup() {
if ((features & 32) == 0) {
@@ -233,15 +235,29 @@
setDialogMode(0);
var ok = ((Q('ausername').value.length > 0) && (Q('ausername').value.indexOf(' ') == -1) && (validateEmail(Q('aemail').value) == true) && (Q('apassword1').value.length > 0) && (Q('apassword2').value == Q('apassword1').value));
if ((newAccountPass == 1) && (Q('anewaccountpass').value.length == 0)) { ok = false; }
- QE('createButton', ok);
if (Q('apassword1').value == '') {
QH('passWarning', '');
} else {
- var passStrength = checkPasswordStrength(Q('apassword1').value);
- if (passStrength >= 80) { QH('passWarning', 'Strong Password'); }
- else if (passStrength >= 60) { QH('passWarning', 'Good Password'); }
- else { QH('passWarning', 'Weak Password'); }
+ if (passRequirements == null || passRequirements == '') {
+ // No password requirements, display password strength
+ var passStrength = checkPasswordStrength(Q('apassword1').value);
+ if (passStrength >= 80) { QH('passWarning', 'Strong Password'); }
+ else if (passStrength >= 60) { QH('passWarning', 'Good Password'); }
+ else { QH('passWarning', 'Weak Password'); }
+ } else {
+ // Password requirements provided, use that
+ var passReq = checkPasswordRequirements(Q('apassword1').value, passRequirements);
+ if (passReq == false) {
+ ok = false;
+ //QS('nuPass1').color = '#7b241c';
+ //QS('nuPass2').color = '#7b241c';
+ QH('passWarning', 'Password Policy'); // TODO: Display problem hint
+ } else {
+ QH('passWarning', '');
+ }
+ }
}
+ QE('createButton', ok);
if ((e != null) && (e.keyCode == 13)) {
if (box == 1) { Q('aemail').focus(); }
if (box == 2) { Q('apassword1').focus(); }
@@ -272,7 +288,25 @@
return parseInt(r + (varCount - 1) * 10);
}
-
+ // Check password requirements
+ function checkPasswordRequirements(password, requirements) {
+ if ((requirements == null) || (requirements == '') || (typeof requirements != 'object')) return true;
+ if (requirements.min) { if (password.length < requirements.min) return false; }
+ if (requirements.max) { if (password.length > requirements.max) return false; }
+ var num = 0, lower = 0, upper = 0, nonalpha = 0;
+ for (var i = 0; i < password.length; i++) {
+ if (/\d/.test(password[i])) { num++; }
+ if (/[a-z]/.test(password[i])) { lower++; }
+ if (/[A-Z]/.test(password[i])) { upper++; }
+ if (/\W/.test(password[i])) { nonalpha++; }
+ }
+ if (requirements.num && (num < requirements.num)) return false;
+ if (requirements.lower && (lower < requirements.lower)) return false;
+ if (requirements.upper && (upper < requirements.upper)) return false;
+ if (requirements.nonalpha && (nonalpha < requirements.nonalpha)) return false;
+ return true;
+ }
+
//
// POPUP DIALOG
//
diff --git a/views/login.handlebars b/views/login.handlebars
index 57be4ab4..9750d48e 100644
--- a/views/login.handlebars
+++ b/views/login.handlebars
@@ -257,6 +257,8 @@
var passhint = "{{{passhint}}}";
var newAccountPass = parseInt('{{{newAccountPass}}}');
var emailCheck = ('{{{emailcheck}}}' == 'true');
+ var passRequirements = "{{{passRequirements}}}";
+ if (passRequirements != "") { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); }
var features = parseInt('{{{features}}}');
var webPageFullScreen = getstore('webPageFullScreen', true);
if (webPageFullScreen == 'false') { webPageFullScreen = false; }
@@ -312,7 +314,7 @@
if (e != null) { haltEvent(e); }
}
- function validateCreate(box,e) {
+ function validateCreate(box, e) {
setDialogMode(0);
var userok = (Q('ausername').value.length > 0) && (Q('ausername').value.indexOf(' ') == -1);
var emailok = (validateEmail(Q('aemail').value) == true);
@@ -328,14 +330,27 @@
QS('nuPass2').color = pass2ok?'black':'#7b241c';
QS('nuToken').color = newAccOk?'black':'#7b241c';
- QE('createButton', ok);
if (Q('apassword1').value == '') {
QH('passWarning', '');
} else {
- var passStrength = checkPasswordStrength(Q('apassword1').value);
- if (passStrength >= 80) { QH('passWarning', 'Strong Password'); }
- else if (passStrength >= 60) { QH('passWarning', 'Good Password'); }
- else { QH('passWarning', 'Weak Password'); }
+ if (passRequirements == null || passRequirements == '') {
+ // No password requirements, display password strength
+ var passStrength = checkPasswordStrength(Q('apassword1').value);
+ if (passStrength >= 80) { QH('passWarning', 'Strong Password'); }
+ else if (passStrength >= 60) { QH('passWarning', 'Good Password'); }
+ else { QH('passWarning', 'Weak Password'); }
+ } else {
+ // Password requirements provided, use that
+ var passReq = checkPasswordRequirements(Q('apassword1').value, passRequirements);
+ if (passReq == false) {
+ ok = false;
+ QS('nuPass1').color = '#7b241c';
+ QS('nuPass2').color = '#7b241c';
+ QH('passWarning', 'Password Policy'); // TODO: Display problem hint
+ } else {
+ QH('passWarning', '');
+ }
+ }
}
if ((e != null) && (e.keyCode == 13)) {
if (box == 1) { Q('aemail').focus(); }
@@ -346,6 +361,7 @@
if (box == 6) { Q('createButton').click(); }
}
if (e != null) { haltEvent(e); }
+ QE('createButton', ok);
}
function validateReset(e) {
@@ -367,6 +383,24 @@
return parseInt(r + (varCount - 1) * 10);
}
+ // Check password requirements
+ function checkPasswordRequirements(password, requirements) {
+ if ((requirements == null) || (requirements == '') || (typeof requirements != 'object')) return true;
+ if (requirements.min) { if (password.length < requirements.min) return false; }
+ if (requirements.max) { if (password.length > requirements.max) return false; }
+ var num = 0, lower = 0, upper = 0, nonalpha = 0;
+ for (var i = 0; i < password.length; i++) {
+ if (/\d/.test(password[i])) { num++; }
+ if (/[a-z]/.test(password[i])) { lower++; }
+ if (/[A-Z]/.test(password[i])) { upper++; }
+ if (/\W/.test(password[i])) { nonalpha++; }
+ }
+ if (requirements.num && (num < requirements.num)) return false;
+ if (requirements.lower && (lower < requirements.lower)) return false;
+ if (requirements.upper && (upper < requirements.upper)) return false;
+ if (requirements.nonalpha && (nonalpha < requirements.nonalpha)) return false;
+ return true;
+ }
//
// POPUP DIALOG
diff --git a/views/messenger-min.handlebars b/views/messenger-min.handlebars
index b7d8ae23..b6c9e7a9 100644
--- a/views/messenger-min.handlebars
+++ b/views/messenger-min.handlebars
@@ -1,4 +1,4 @@
- MeshMessenger