diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index 52b994db..2406eb29 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -93,6 +93,9 @@ #define WEBSERVER_PORT 80 #define DNS_PORT 53 +#define WEB_MODE_NORMAL 0 +#define WEB_MODE_PASSWORD 1 + #define AP_MODE AP_MODE_ALONE #define AP_MODE_IP "192.168.4.1" #define AP_MODE_GW "192.168.4.1" diff --git a/code/espurna/data/index.html.gz b/code/espurna/data/index.html.gz index 2ed0b47a..e46b5c7d 100644 Binary files a/code/espurna/data/index.html.gz and b/code/espurna/data/index.html.gz differ diff --git a/code/espurna/data/password.html.gz b/code/espurna/data/password.html.gz deleted file mode 100644 index 8f9b50ba..00000000 Binary files a/code/espurna/data/password.html.gz and /dev/null differ diff --git a/code/espurna/data/script.js.gz b/code/espurna/data/script.js.gz deleted file mode 100644 index 30e476c9..00000000 Binary files a/code/espurna/data/script.js.gz and /dev/null differ diff --git a/code/espurna/data/style.css.gz b/code/espurna/data/style.css.gz deleted file mode 100644 index c6e249dc..00000000 Binary files a/code/espurna/data/style.css.gz and /dev/null differ diff --git a/code/espurna/web.ino b/code/espurna/web.ino index 25da21c1..d86735c0 100644 --- a/code/espurna/web.ino +++ b/code/espurna/web.ino @@ -101,6 +101,8 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { JsonArray& config = root["config"]; DEBUG_MSG("[WEBSOCKET] Parsing configuration data\n"); + unsigned char webMode = WEB_MODE_NORMAL; + bool save = false; bool changed = false; bool changedMQTT = false; @@ -166,6 +168,11 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { } } + if (key == "webMode") { + webMode = value.toInt(); + continue; + } + // Check password if (key == "adminPass1") { adminPass = value; @@ -222,36 +229,40 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { } - // Checkboxes - if (apiEnabled != (getSetting("apiEnabled").toInt() == 1)) { - setSetting("apiEnabled", apiEnabled); - save = changed = true; - } - #if ENABLE_FAUXMO - if (fauxmoEnabled != (getSetting("fauxmoEnabled").toInt() == 1)) { - setSetting("fauxmoEnabled", fauxmoEnabled); + if (webMode == WEB_MODE_NORMAL) { + + // Checkboxes + if (apiEnabled != (getSetting("apiEnabled").toInt() == 1)) { + setSetting("apiEnabled", apiEnabled); save = changed = true; } - #endif + #if ENABLE_FAUXMO + if (fauxmoEnabled != (getSetting("fauxmoEnabled").toInt() == 1)) { + setSetting("fauxmoEnabled", fauxmoEnabled); + save = changed = true; + } + #endif - // Clean wifi networks - for (int i = 0; i < network; i++) { - if (getSetting("pass" + String(i)).length() == 0) delSetting("pass" + String(i)); - if (getSetting("ip" + String(i)).length() == 0) delSetting("ip" + String(i)); - if (getSetting("gw" + String(i)).length() == 0) delSetting("gw" + String(i)); - if (getSetting("mask" + String(i)).length() == 0) delSetting("mask" + String(i)); - if (getSetting("dns" + String(i)).length() == 0) delSetting("dns" + String(i)); - } - for (int i = network; i 0) { - save = changed = true; + // Clean wifi networks + for (int i = 0; i < network; i++) { + if (getSetting("pass" + String(i)).length() > 0) delSetting("pass" + String(i)); + if (getSetting("ip" + String(i)).length() == 0) delSetting("ip" + String(i)); + if (getSetting("gw" + String(i)).length() == 0) delSetting("gw" + String(i)); + if (getSetting("mask" + String(i)).length() == 0) delSetting("mask" + String(i)); + if (getSetting("dns" + String(i)).length() == 0) delSetting("dns" + String(i)); + } + for (int i = network; i 0) { + save = changed = true; + } + delSetting("ssid" + String(i)); + delSetting("pass" + String(i)); + delSetting("ip" + String(i)); + delSetting("gw" + String(i)); + delSetting("mask" + String(i)); + delSetting("dns" + String(i)); } - delSetting("ssid" + String(i)); - delSetting("pass" + String(i)); - delSetting("ip" + String(i)); - delSetting("gw" + String(i)); - delSetting("mask" + String(i)); - delSetting("dns" + String(i)); + } // Save settings @@ -297,130 +308,146 @@ void _wsStart(uint32_t client_id) { DynamicJsonBuffer jsonBuffer; JsonObject& root = jsonBuffer.createObject(); - root["app"] = APP_NAME; - root["version"] = APP_VERSION; - root["buildDate"] = __DATE__; - root["buildTime"] = __TIME__; - - root["manufacturer"] = String(MANUFACTURER); - root["chipid"] = chipid; - root["mac"] = WiFi.macAddress(); - root["device"] = String(DEVICE); - root["hostname"] = getSetting("hostname", HOSTNAME); - root["network"] = getNetwork(); - root["deviceip"] = getIP(); - - root["mqttStatus"] = mqttConnected(); - root["mqttServer"] = getSetting("mqttServer", MQTT_SERVER); - root["mqttPort"] = getSetting("mqttPort", MQTT_PORT); - root["mqttUser"] = getSetting("mqttUser"); - root["mqttPassword"] = getSetting("mqttPassword"); - root["mqttTopic"] = getSetting("mqttTopic", MQTT_TOPIC); - - JsonArray& relay = root.createNestedArray("relayStatus"); - for (unsigned char relayID=0; relayID 1) { - root["multirelayVisible"] = 1; - root["relaySync"] = getSetting("relaySync", RELAY_SYNC); - } + bool changePassword = false; + #if FORCE_CHANGE_PASS == 1 + String adminPass = getSetting("adminPass", ADMIN_PASS); + if (adminPass.equals(ADMIN_PASS)) changePassword = true; + #endif - root["webPort"] = getSetting("webPort", WEBSERVER_PORT).toInt(); + if (changePassword) { - root["apiEnabled"] = getSetting("apiEnabled").toInt() == 1; - root["apiKey"] = getSetting("apiKey"); + root["webMode"] = WEB_MODE_PASSWORD; - root["tmpUnits"] = getSetting("tmpUnits", TMP_UNITS).toInt(); + } else { - #if ENABLE_DOMOTICZ + root["webMode"] = WEB_MODE_NORMAL; + + root["app"] = APP_NAME; + root["version"] = APP_VERSION; + root["buildDate"] = __DATE__; + root["buildTime"] = __TIME__; + + root["manufacturer"] = String(MANUFACTURER); + root["chipid"] = chipid; + root["mac"] = WiFi.macAddress(); + root["device"] = String(DEVICE); + root["hostname"] = getSetting("hostname", HOSTNAME); + root["network"] = getNetwork(); + root["deviceip"] = getIP(); + + root["mqttStatus"] = mqttConnected(); + root["mqttServer"] = getSetting("mqttServer", MQTT_SERVER); + root["mqttPort"] = getSetting("mqttPort", MQTT_PORT); + root["mqttUser"] = getSetting("mqttUser"); + root["mqttPassword"] = getSetting("mqttPassword"); + root["mqttTopic"] = getSetting("mqttTopic", MQTT_TOPIC); + + JsonArray& relay = root.createNestedArray("relayStatus"); + for (unsigned char relayID=0; relayID 1) { + root["multirelayVisible"] = 1; + root["relaySync"] = getSetting("relaySync", RELAY_SYNC); + } - root["dczVisible"] = 1; - root["dczTopicIn"] = getSetting("dczTopicIn", DOMOTICZ_IN_TOPIC); - root["dczTopicOut"] = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC); + root["webPort"] = getSetting("webPort", WEBSERVER_PORT).toInt(); - JsonArray& dczRelayIdx = root.createNestedArray("dczRelayIdx"); - for (byte i=0; irequestAuthentication(); - - #if FORCE_CHANGE_PASS == 1 - String password = getSetting("adminPass", ADMIN_PASS); - if (password.equals(ADMIN_PASS)) { - request->send(SPIFFS, "/password.html"); - } else { - request->send(SPIFFS, "/index.html"); - } - #else - request->send(SPIFFS, "/index.html"); - #endif + request->send(SPIFFS, "/index.html"); } void _onAuth(AsyncWebServerRequest *request) { diff --git a/code/gulpfile.js b/code/gulpfile.js index 35279c9f..a890410c 100644 --- a/code/gulpfile.js +++ b/code/gulpfile.js @@ -91,4 +91,4 @@ gulp.task('html', function() { /* Build file system */ gulp.task('buildfs_split', ['clean', 'files', 'html']); gulp.task('buildfs_inline', ['clean', 'files', 'inline']); -gulp.task('default', ['buildfs_split']); +gulp.task('default', ['buildfs_inline']); diff --git a/code/html/custom.css b/code/html/custom.css index 8a1aac33..9f633c0e 100644 --- a/code/html/custom.css +++ b/code/html/custom.css @@ -28,6 +28,7 @@ width: 100px; margin: 5px auto; } +.button-update-password, .button-update { background: #1f8dd6; } @@ -86,3 +87,6 @@ div.hint { .pure-form .center { margin: .5em 0 .2em; } +.webmode { + display: none; +} diff --git a/code/html/custom.js b/code/html/custom.js index b89abc5b..7204d646 100644 --- a/code/html/custom.js +++ b/code/html/custom.js @@ -10,9 +10,7 @@ function checkPassword(str) { return re.test(str); } -function validateForm() { - - var form = $("#formSave"); +function validateForm(form) { // password var adminPass1 = $("input[name='adminPass1']", form).val(); @@ -32,8 +30,9 @@ function validateForm() { } function doUpdate() { - if (validateForm()) { - var data = $("#formSave").serializeArray(); + var form = $("#formSave"); + if (validateForm(form)) { + var data = form.serializeArray(); websock.send(JSON.stringify({'config': data})); $(".powExpected").val(0); $("input[name='powExpectedReset']") @@ -43,6 +42,15 @@ function doUpdate() { return false; } +function doUpdatePassword() { + var form = $("#formPassword"); + if (validateForm(form)) { + var data = form.serializeArray(); + websock.send(JSON.stringify({'config': data})); + } + return false; +} + function doReset() { var response = window.confirm("Are you sure you want to reset the device?"); if (response == false) return false; @@ -168,6 +176,24 @@ function addNetwork() { } +function forgetCredentials() { + $.ajax({ + 'method': 'GET', + 'url': '/', + 'async': false, + 'username': "logmeout", + 'password': "123456", + 'headers': { "Authorization": "Basic xxx" } + }).done(function(data) { + return false; + // If we don't get an error, we actually got an error as we expect an 401! + }).fail(function(){ + // We expect to get an 401 Unauthorized error! In this case we are successfully + // logged out and we redirect the user. + return true; + }); +} + function processData(data) { // title @@ -185,31 +211,21 @@ function processData(data) { Object.keys(data).forEach(function(key) { + // Web Modes + if (key == "webMode") { + password = data.webMode == 1; + $("#layout").toggle(data.webMode == 0); + $("#password").toggle(data.webMode == 1); + } + // Actions if (key == "action") { if (data.action == "reload") { - if (password) { - - // Forget current authentication - $.ajax({ - 'method': 'GET', - 'url': '/', - 'async': false, - 'username': "logmeout", - 'password': "123456", - 'headers': { "Authorization": "Basic xxx" } - }).done(function(data) { - // If we don't get an error, we actually got an error as we expect an 401! - }).fail(function(){ - // We expect to get an 401 Unauthorized error! In this case we are successfully - // logged out and we redirect the user. - window.location = "/"; - }); - - } else { + if (password) forgetCredentials(); + setTimeout(function() { window.location = "/"; - } + }, 1000); } return; @@ -368,6 +384,7 @@ function init() { $("#menuLink").on('click', toggleMenu); $(".button-update").on('click', doUpdate); + $(".button-update-password").on('click', doUpdatePassword); $(".button-reset").on('click', doReset); $(".button-reconnect").on('click', doReconnect); $(".button-apikey").on('click', doGenerateAPIKey); diff --git a/code/html/index.html b/code/html/index.html index 08450f43..f8b7dd78 100644 --- a/code/html/index.html +++ b/code/html/index.html @@ -19,7 +19,52 @@ -
+
+ +
+ +
+ + + +
+ +
+

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.

+
+ +
+ +
+ +
+ + +
 
+
+ 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 should have at least eight characters (letters, numbers or the underscore) and at least one number, one lowercase and one uppercase letter.
+
+ +
+ + +
+ + + +
+
+
+ +
+ +
+ +
+ +