From 3c91389c209b41f1db44218b2f18d50a3f55bbab Mon Sep 17 00:00:00 2001 From: Max Prokhorov Date: Sat, 15 Sep 2018 22:43:55 +0300 Subject: [PATCH 1/3] Update password UI * revise first-time-use screen * generate password (policy applies) * hide/reveal password text for sta/admin settings * changed autocomplete='...' for some inputs to avoid completion --- code/html/custom.css | 74 ++++++++++++++++++++++++++++------- code/html/custom.js | 92 ++++++++++++++++++++++++++++++++++++-------- code/html/index.html | 62 ++++++++++++++++------------- 3 files changed, 171 insertions(+), 57 deletions(-) diff --git a/code/html/custom.css b/code/html/custom.css index 6f03f475..af494464 100644 --- a/code/html/custom.css +++ b/code/html/custom.css @@ -50,10 +50,6 @@ h2 { display: block; } -.content { - margin: 0; -} - .page { margin-top: 10px; } @@ -98,16 +94,12 @@ div.center { display: none; } -#credentials { - font-size: 200%; - height: 100px; - left: 50%; - margin-left: -200px; - margin-top: -50px; - position: fixed; - text-align: center; - top: 50%; - width: 400px; +.content #password { + margin: 0 auto; +} + +.content #layout { + margin: 0; } div.state { @@ -206,6 +198,10 @@ div.state { background: rgb(255, 128, 0); /* orange */ } +.button-generate-password { + background: rgb(66, 184, 221); /* blue */ +} + .button-upgrade-browse, .button-clear-filters, .button-clear-messages, @@ -448,3 +444,53 @@ table.dataTable.display tbody td { height: 400px; margin-bottom: 10px; } + +/* ----------------------------------------------------------------------------- + Password input controls + -------------------------------------------------------------------------- */ +.password-reveal { + font-family: EmojiSymbols,Segoe UI Symbol; + background: rgba(0,0,0,0); + display: inline-block; + float: right; + z-index: 50; + margin-top: 6px; + margin-left: -30px; + vertical-align: middle; + font-size: 1.2em; + height: 100%; +} + +.password-reveal:after { + content: "👁"; +} + +input[type="password"] + .password-reveal { + color: rgba(205, 205, 205, 0.3); +} + +input[type="text"] + .password-reveal { + color: rgba(66, 184, 221, 0.8); +} + +.no-select { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +input::-ms-clear, +input::-ms-reveal { + display: none; +} + +/* css minifier must not combine these. + * style will not apply otherwise */ +input::-ms-input-placeholder { + color: #ccd; +} + +input::placeholder { + color: #ccc; +} diff --git a/code/html/custom.js b/code/html/custom.js index e0646b30..5ca9558b 100644 --- a/code/html/custom.js +++ b/code/html/custom.js @@ -146,8 +146,7 @@ function loadTimeZones() { } -function validateForm(form) { - +function validatePassword(password) { // http://www.the-art-of-web.com/javascript/validate-password/ // at least one lowercase and one uppercase letter or number // at least eight characters (letters, numbers or special characters) @@ -155,16 +154,26 @@ function validateForm(form) { // MUST be 8..63 printable ASCII characters. See: // https://en.wikipedia.org/wiki/Wi-Fi_Protected_Access#Target_users_(authentication_key_distribution) // https://github.com/xoseperez/espurna/issues/1151 + var re_password = /^(?=.*[A-Z\d])(?=.*[a-z])[\w~!@#$%^&*\(\)<>,.\?;:{}\[\]\\|]{8,63}$/; + return ( + (password !== undefined) + && (typeof password === "string") + && (password.length > 0) + && re_password.test(password) + ); +} + +function validateForm(form) { // password var adminPass1 = $("input[name='adminPass']", form).first().val(); - if (adminPass1.length > 0 && !re_password.test(adminPass1)) { + if (!validatePassword(adminPass1)) { alert("The password you have entered is not valid, it must be 8..63 characters and have at least 1 lowercase and 1 uppercase / number!"); return false; } - var adminPass2 = $("input[name='adminPass']", form).last().val(); + var adminPass2 = $("input[name='adminPass_confirm']", form).last().val(); if (adminPass1 !== adminPass2) { alert("Passwords are different!"); return false; @@ -225,6 +234,12 @@ function addValue(data, name, value) { "node", "key", "topic" ]; + + // join both adminPass and ..._confirm + if (name.startsWith("adminPass")) { + name = "adminPass"; + } + if (name in data) { if (!Array.isArray(data[name])) { data[name] = [data[name]]; @@ -260,26 +275,67 @@ function getData(form) { } -function randomString(length, chars) { - var mask = ""; - if (chars.indexOf("a") > -1) { mask += "abcdefghijklmnopqrstuvwxyz"; } - if (chars.indexOf("A") > -1) { mask += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; } - if (chars.indexOf("#") > -1) { mask += "0123456789"; } - if (chars.indexOf("@") > -1) { mask += "ABCDEF"; } - if (chars.indexOf("!") > -1) { mask += "~`!@#$%^&*()_+-={}[]:\";'<>?,./|\\"; } - var result = ""; - for (var i = length; i > 0; --i) { - result += mask[Math.round(Math.random() * (mask.length - 1))]; +function randomString(length, args) { + if (typeof args === "undefined") { + args = { + lowercase: true, + uppercase: true, + numbers: true, + special: true + } } - return result; + + var mask = ""; + if (args.lowercase) { mask += "abcdefghijklmnopqrstuvwxyz"; } + if (args.uppercase) { mask += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; } + if (args.numbers || args.hex) { mask += "0123456789"; } + if (args.hex) { mask += "ABCDEF"; } + if (args.special) { mask += "~`!@#$%^&*()_+-={}[]:\";'<>?,./|\\"; } + + var source = new Uint32Array(length); + var result = new Array(length); + + window.crypto.getRandomValues(source).forEach(function(value, i) { + result[i] = mask[value % mask.length]; + }); + + return result.join(""); } function generateAPIKey() { - var apikey = randomString(16, "@#"); + var apikey = randomString(16, {hex: true}); $("input[name='apiKey']").val(apikey); return false; } +function generatePassword() { + var password = ""; + do { + password = randomString(10); + } while (!validatePassword(password)); + + return password; +} + +function toggleVisiblePassword() { + var elem = this.previousElementSibling; + if (elem.type === "password") { + elem.type = "text"; + } else { + elem.type = "password"; + } + return false; +} + +function doGeneratePassword() { + $("input", $("#formPassword")) + .val(generatePassword()) + .each(function() { + this.type = "text"; + }); + return false; +} + function getJson(str) { try { return JSON.parse(str); @@ -753,6 +809,7 @@ function addNetwork() { $(this).attr("tabindex", tabindex); tabindex++; }); + $(".password-reveal", line).on("click", toggleVisiblePassword); $(line).find(".button-del-network").on("click", delNetwork); $(line).find(".button-more-network").on("click", moreNetwork); line.appendTo("#networks"); @@ -1575,12 +1632,15 @@ $(function() { createCheckboxes(); setInterval(function() { keepTime(); }, 1000); + $(".password-reveal").on("click", toggleVisiblePassword); + $("#menuLink").on("click", toggleMenu); $(".pure-menu-link").on("click", showPanel); $("progress").attr({ value: 0, max: 100 }); $(".button-update").on("click", doUpdate); $(".button-update-password").on("click", doUpdatePassword); + $(".button-generate-password").on("click", doGeneratePassword); $(".button-reboot").on("click", doReboot); $(".button-reconnect").on("click", doReconnect); $(".button-wifi-scan").on("click", doScan); diff --git a/code/html/index.html b/code/html/index.html index bde3aaa9..b1053014 100644 --- a/code/html/index.html +++ b/code/html/index.html @@ -30,38 +30,43 @@
-
+

SECURITY

-

Before using this device you have to change the default password for the user 'admin'. This password will be used for the AP mode hotspot, the web interface (where you are now) and the over-the-air updates.

+

Before using this device you have to change the default password for the user admin. This password will be used for the AP mode hotspot, the web interface (where you are now) and the over-the-air updates.

-
- - -
-
- The administrator password is used to access this web interface (user 'admin'), but also to connect to the device when in AP mode or to flash a new firmware over-the-air (OTA).
- It must be 8..63 characters (numbers and letters and any of these special characters: _,.;:~!?@#$%^&*<>\|(){}[]) and have at least one lowercase and one uppercase or one number.
+ + +
- - + + +
+
-
- +
+
+ Password must be 8..63 characters (numbers and letters and any of these special characters: _,.;:~!?@#$%^&*<>\|(){}[]) and have at least one lowercase and one uppercase or one number.
+
+ + +
+ +
+ +
-
-
@@ -523,18 +528,18 @@
- + + +
+ + +
The administrator password is used to access this web interface (user 'admin'), but also to connect to the device when in AP mode or to flash a new firmware over-the-air (OTA).
It must be 8..63 characters (numbers and letters and any of these special characters: _,.;:~!?@#$%^&*<>\|(){}[]) and have at least one lowercase and one uppercase or one number.
-
- - -
-
@@ -783,12 +788,13 @@
- +
- + +
@@ -1096,12 +1102,13 @@
- +
- + +
@@ -1363,7 +1370,8 @@
- + + @@ -1586,7 +1594,7 @@
-
+
From 822837f7b20365ad07dfa95be1712450d848af60 Mon Sep 17 00:00:00 2001 From: Max Prokhorov Date: Mon, 17 Sep 2018 00:57:34 +0300 Subject: [PATCH 2/3] Use separate form for each panel --- code/html/custom.js | 6 ++--- code/html/index.html | 57 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/code/html/custom.js b/code/html/custom.js index 5ca9558b..de0c5af7 100644 --- a/code/html/custom.js +++ b/code/html/custom.js @@ -532,11 +532,11 @@ function doReconnect(ask) { function doUpdate() { - var form = $("#formSave"); - if (validateForm(form)) { + var forms = $(".form-settings"); + if (validateForm(forms)) { // Get data - sendConfig(getData(form)); + sendConfig(getData(forms)); // Empty special fields $(".pwrExpected").val(0); diff --git a/code/html/index.html b/code/html/index.html index b1053014..2ccf660a 100644 --- a/code/html/index.html +++ b/code/html/index.html @@ -28,7 +28,7 @@
-
+
@@ -314,8 +314,7 @@
- - +
@@ -387,7 +386,9 @@
+ +
@@ -419,8 +420,10 @@
+
- + +
@@ -506,8 +509,10 @@
- +
+ +
@@ -620,7 +625,9 @@
+
+
@@ -662,7 +669,9 @@
+
+
@@ -686,8 +695,10 @@
+
- + +
@@ -716,10 +727,12 @@
+
+
+
-

MESSAGES

@@ -758,8 +771,10 @@

- +
+ +
@@ -879,7 +894,9 @@
+
+
@@ -923,7 +940,9 @@
+
+
@@ -970,7 +989,9 @@
+
+
@@ -1024,7 +1045,9 @@
+
+
@@ -1066,7 +1089,9 @@
+
+
@@ -1115,7 +1140,9 @@
+
+
@@ -1147,8 +1174,10 @@
+
- + +
@@ -1304,9 +1333,11 @@
- +
+ - + +
@@ -1326,10 +1357,10 @@
-
- +
+
From 399458d632df631310dcbfdaebcaa136260bdfce Mon Sep 17 00:00:00 2001 From: Max Prokhorov Date: Tue, 18 Sep 2018 07:55:11 +0300 Subject: [PATCH 3/3] Submit password on Enter --- code/html/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/html/index.html b/code/html/index.html index 2ccf660a..4d3b0cb5 100644 --- a/code/html/index.html +++ b/code/html/index.html @@ -62,7 +62,7 @@
- +