From 6533143e8019ebf5c7164b8e7c3038fe72bd745c Mon Sep 17 00:00:00 2001 From: lumapu Date: Tue, 4 Apr 2023 13:51:11 +0200 Subject: [PATCH] 0.6.2 * fix login from multiple clients #819 * fix login screen on small displays --- src/CHANGES.md | 4 ++ src/app.h | 4 +- src/appInterface.h | 3 +- src/defines.h | 2 +- src/web/RestApi.h | 64 ++++++++++++------------ src/web/html/login.html | 4 +- src/web/html/style.css | 29 ++++++----- src/web/web.h | 107 +++++++++++++++------------------------- 8 files changed, 101 insertions(+), 116 deletions(-) diff --git a/src/CHANGES.md b/src/CHANGES.md index 0a2613ae..3702d0e6 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,9 @@ # Development Changes +## 0.6.2 - 2023-04-04 +* fix login from multiple clients #819 +* fix login screen on small displays + ## 0.6.1 - 2023-04-01 * merge LED fix - LED1 shows MqTT state, LED configureable active high/low #839 * only publish new inverter data #826 diff --git a/src/app.h b/src/app.h index 6dfc404c..5ea40968 100644 --- a/src/app.h +++ b/src/app.h @@ -157,8 +157,8 @@ class app : public IApp, public ah::Scheduler { return mMqtt.getRxCnt(); } - bool getProtection() { - return mWeb.getProtection(); + bool getProtection(AsyncWebServerRequest *request) { + return mWeb.isProtected(request); } uint8_t getIrqPin(void) { diff --git a/src/appInterface.h b/src/appInterface.h index a79dcdb1..44491d91 100644 --- a/src/appInterface.h +++ b/src/appInterface.h @@ -8,6 +8,7 @@ #include "defines.h" #include "hm/hmSystem.h" +#include "ESPAsyncWebServer.h" // abstract interface to App. Make members of App accessible from child class // like web or API without forward declaration @@ -47,7 +48,7 @@ class IApp { virtual uint32_t getMqttRxCnt() = 0; virtual uint32_t getMqttTxCnt() = 0; - virtual bool getProtection() = 0; + virtual bool getProtection(AsyncWebServerRequest *request) = 0; }; #endif /*__IAPP_H__*/ diff --git a/src/defines.h b/src/defines.h index 28275eff..5d01003b 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 6 -#define VERSION_PATCH 1 +#define VERSION_PATCH 2 //------------------------------------- typedef struct { diff --git a/src/web/RestApi.h b/src/web/RestApi.h index 8c193e28..2d617847 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -77,19 +77,19 @@ class RestApi { JsonObject root = response->getRoot(); String path = request->url().substring(5); - if(path == "html/system") getHtmlSystem(root); - else if(path == "html/logout") getHtmlLogout(root); - else if(path == "html/reboot") getHtmlReboot(root); - else if(path == "html/save") getHtmlSave(root); - else if(path == "system") getSysInfo(root); - else if(path == "generic") getGeneric(root); - else if(path == "reboot") getReboot(root); + if(path == "html/system") getHtmlSystem(request, root); + else if(path == "html/logout") getHtmlLogout(request, root); + else if(path == "html/reboot") getHtmlReboot(request, root); + else if(path == "html/save") getHtmlSave(request, root); + else if(path == "system") getSysInfo(request, root); + else if(path == "generic") getGeneric(request, root); + else if(path == "reboot") getReboot(request, root); else if(path == "statistics") getStatistics(root); else if(path == "inverter/list") getInverterList(root); - else if(path == "index") getIndex(root); - else if(path == "setup") getSetup(root); + else if(path == "index") getIndex(request, root); + else if(path == "setup") getSetup(request, root); else if(path == "setup/networks") getNetworks(root); - else if(path == "live") getLive(root); + else if(path == "live") getLive(request, root); else if(path == "record/info") getRecord(root, InverterDevInform_All); else if(path == "record/alarm") getRecord(root, AlarmData); else if(path == "record/config") getRecord(root, SystemConfigPara); @@ -189,10 +189,10 @@ class RestApi { fp.close(); } - void getGeneric(JsonObject obj) { + void getGeneric(AsyncWebServerRequest *request, JsonObject obj) { obj[F("wifi_rssi")] = (WiFi.status() != WL_CONNECTED) ? 0 : WiFi.RSSI(); obj[F("ts_uptime")] = mApp->getUptime(); - obj[F("menu_prot")] = mApp->getProtection(); + obj[F("menu_prot")] = mApp->getProtection(request); obj[F("menu_mask")] = (uint16_t)(mConfig->sys.protectionMask ); obj[F("menu_protEn")] = (bool) (strlen(mConfig->sys.adminPwd) > 0); @@ -203,7 +203,7 @@ class RestApi { #endif } - void getSysInfo(JsonObject obj) { + void getSysInfo(AsyncWebServerRequest *request, JsonObject obj) { obj[F("ssid")] = mConfig->sys.stationSsid; obj[F("device_name")] = mConfig->sys.deviceName; obj[F("dark_mode")] = (bool)mConfig->sys.darkMode; @@ -218,7 +218,7 @@ class RestApi { obj[F("heap_free")] = mHeapFree; obj[F("sketch_total")] = ESP.getFreeSketchSpace(); obj[F("sketch_used")] = ESP.getSketchSize() / 1024; // in kb - getGeneric(obj); + getGeneric(request, obj); getRadio(obj.createNestedObject(F("radio"))); getStatistics(obj.createNestedObject(F("statistics"))); @@ -252,34 +252,34 @@ class RestApi { obj[F("schMax")] = max; } - void getHtmlSystem(JsonObject obj) { - getSysInfo(obj.createNestedObject(F("system"))); - getGeneric(obj.createNestedObject(F("generic"))); + void getHtmlSystem(AsyncWebServerRequest *request, JsonObject obj) { + getSysInfo(request, obj.createNestedObject(F("system"))); + getGeneric(request, obj.createNestedObject(F("generic"))); obj[F("html")] = F("Factory Reset

Reboot"); } - void getHtmlLogout(JsonObject obj) { - getGeneric(obj.createNestedObject(F("generic"))); + void getHtmlLogout(AsyncWebServerRequest *request, JsonObject obj) { + getGeneric(request, obj.createNestedObject(F("generic"))); obj[F("refresh")] = 3; obj[F("refresh_url")] = "/"; obj[F("html")] = F("succesfully logged out"); } - void getHtmlReboot(JsonObject obj) { - getGeneric(obj.createNestedObject(F("generic"))); + void getHtmlReboot(AsyncWebServerRequest *request, JsonObject obj) { + getGeneric(request, obj.createNestedObject(F("generic"))); obj[F("refresh")] = 20; obj[F("refresh_url")] = "/"; obj[F("html")] = F("rebooting ..."); } - void getHtmlSave(JsonObject obj) { - getGeneric(obj.createNestedObject(F("generic"))); + void getHtmlSave(AsyncWebServerRequest *request, JsonObject obj) { + getGeneric(request, obj.createNestedObject(F("generic"))); obj["pending"] = (bool)mApp->getSavePending(); obj["success"] = (bool)mApp->getLastSaveSucceed(); } - void getReboot(JsonObject obj) { - getGeneric(obj.createNestedObject(F("generic"))); + void getReboot(AsyncWebServerRequest *request, JsonObject obj) { + getGeneric(request, obj.createNestedObject(F("generic"))); obj[F("refresh")] = 10; obj[F("refresh_url")] = "/"; obj[F("html")] = F("reboot. Autoreload after 10 seconds"); @@ -430,8 +430,8 @@ class RestApi { obj[F("disp_bsy")] = (mConfig->plugin.display.type < 10) ? DEF_PIN_OFF : mConfig->plugin.display.disp_busy; } - void getIndex(JsonObject obj) { - getGeneric(obj.createNestedObject(F("generic"))); + void getIndex(AsyncWebServerRequest *request, JsonObject obj) { + getGeneric(request, obj.createNestedObject(F("generic"))); obj[F("ts_now")] = mApp->getTimestamp(); obj[F("ts_sunrise")] = mApp->getSunrise(); obj[F("ts_sunset")] = mApp->getSunset(); @@ -479,9 +479,9 @@ class RestApi { info.add(F("MQTT publishes in a fixed interval of ") + String(mConfig->mqtt.interval) + F(" seconds")); } - void getSetup(JsonObject obj) { - getGeneric(obj.createNestedObject(F("generic"))); - getSysInfo(obj.createNestedObject(F("system"))); + void getSetup(AsyncWebServerRequest *request, JsonObject obj) { + getGeneric(request, obj.createNestedObject(F("generic"))); + getSysInfo(request, obj.createNestedObject(F("system"))); //getInverterList(obj.createNestedObject(F("inverter"))); getMqtt(obj.createNestedObject(F("mqtt"))); getNtp(obj.createNestedObject(F("ntp"))); @@ -497,8 +497,8 @@ class RestApi { mApp->getAvailNetworks(obj); } - void getLive(JsonObject obj) { - getGeneric(obj.createNestedObject(F("generic"))); + void getLive(AsyncWebServerRequest *request, JsonObject obj) { + getGeneric(request, obj.createNestedObject(F("generic"))); obj[F("refresh")] = mConfig->nrf.sendInterval; for (uint8_t fld = 0; fld < sizeof(acList); fld++) { diff --git a/src/web/html/login.html b/src/web/html/login.html index e790f6a4..001e0453 100644 --- a/src/web/html/login.html +++ b/src/web/html/login.html @@ -11,8 +11,8 @@

AhoyDTU

-
-
+
+
diff --git a/src/web/html/style.css b/src/web/html/style.css index ca4b0c9a..415d5b40 100644 --- a/src/web/html/style.css +++ b/src/web/html/style.css @@ -546,6 +546,18 @@ div.hr { width: 100%; } +#login { + width: 450px; + height: 200px; + border: 1px solid #ccc; + background-color: var(--input-bg); + position: absolute; + top: 50%; + left: 50%; + margin-top: -160px; + margin-left: -225px; +} + @media(max-width: 500px) { div.ch .unit, div.ch-iv .unit { font-size: 18px; @@ -559,6 +571,11 @@ div.hr { .subgrp { width: 180px; } + + #login { + margin-left: -150px; + width: 300px; + } } #serial { @@ -578,18 +595,6 @@ div.hr { margin-top: 15px; } -#login { - width: 450px; - height: 200px; - border: 1px solid #ccc; - background-color: var(--input-bg); - position: absolute; - top: 50%; - left: 50%; - margin-top: -160px; - margin-left: -225px; -} - .head { background-color: var(--primary); diff --git a/src/web/web.h b/src/web/web.h index 4d322fac..6b7aaed1 100644 --- a/src/web/web.h +++ b/src/web/web.h @@ -126,8 +126,19 @@ class Web { mProtected = protect; } - bool getProtection() { - return mProtected; + bool isProtected(AsyncWebServerRequest *request) { + bool prot; + prot = mProtected; + if(!prot) { + uint8_t ip[4]; + ah::ip2Arr(ip, request->client()->remoteIP().toString().c_str()); + for(uint8_t i = 0; i < 4; i++) { + if(mLoginIp[i] != ip[i]) + prot = true; + } + } + + return prot; } void showUpdate2(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { @@ -216,7 +227,7 @@ class Web { } private: - void checkRedirect(AsyncWebServerRequest *request) { + inline void checkRedirect(AsyncWebServerRequest *request) { if ((mConfig->sys.protectionMask & PROT_MASK_INDEX) != PROT_MASK_INDEX) request->redirect(F("/index")); else if ((mConfig->sys.protectionMask & PROT_MASK_LIVE) != PROT_MASK_LIVE) @@ -229,15 +240,18 @@ class Web { request->redirect(F("/login")); } + void checkProtection(AsyncWebServerRequest *request) { + if(isProtected(request)) { + checkRedirect(request); + return; + } + } + void onUpdate(AsyncWebServerRequest *request) { DPRINTLN(DBG_VERBOSE, F("onUpdate")); - if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_UPDATE)) { - if (mProtected) { - checkRedirect(request); - return; - } - } + if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_UPDATE)) + checkProtection(request); AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), update_html, update_html_len); response->addHeader(F("Content-Encoding"), "gzip"); @@ -290,12 +304,8 @@ class Web { void onIndex(AsyncWebServerRequest *request) { DPRINTLN(DBG_VERBOSE, F("onIndex")); - if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_INDEX)) { - if (mProtected) { - checkRedirect(request); - return; - } - } + if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_INDEX)) + checkProtection(request); AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), index_html, index_html_len); response->addHeader(F("Content-Encoding"), "gzip"); @@ -308,6 +318,7 @@ class Web { if (request->args() > 0) { if (String(request->arg("pwd")) == String(mConfig->sys.adminPwd)) { mProtected = false; + ah::ip2Arr(mLoginIp, request->client()->remoteIP().toString().c_str()); request->redirect("/"); } } @@ -320,10 +331,7 @@ class Web { void onLogout(AsyncWebServerRequest *request) { DPRINTLN(DBG_VERBOSE, F("onLogout")); - if (mProtected) { - checkRedirect(request); - return; - } + checkProtection(request); mProtected = true; @@ -367,10 +375,8 @@ class Web { } void showNotFound(AsyncWebServerRequest *request) { - if (mProtected) - checkRedirect(request); - else - request->redirect("/setup"); + checkProtection(request); + request->redirect("/setup"); } void onReboot(AsyncWebServerRequest *request) { @@ -381,10 +387,7 @@ class Web { } void showErase(AsyncWebServerRequest *request) { - if (mProtected) { - checkRedirect(request); - return; - } + checkProtection(request); DPRINTLN(DBG_VERBOSE, F("showErase")); mApp->eraseSettings(false); @@ -392,10 +395,7 @@ class Web { } void showFactoryRst(AsyncWebServerRequest *request) { - if (mProtected) { - checkRedirect(request); - return; - } + checkProtection(request); DPRINTLN(DBG_VERBOSE, F("showFactoryRst")); String content = ""; @@ -424,12 +424,8 @@ class Web { void onSetup(AsyncWebServerRequest *request) { DPRINTLN(DBG_VERBOSE, F("onSetup")); - if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_SETUP)) { - if (mProtected) { - checkRedirect(request); - return; - } - } + if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_SETUP)) + checkProtection(request); AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), setup_html, setup_html_len); response->addHeader(F("Content-Encoding"), "gzip"); @@ -439,10 +435,7 @@ class Web { void showSave(AsyncWebServerRequest *request) { DPRINTLN(DBG_VERBOSE, F("showSave")); - if (mProtected) { - checkRedirect(request); - return; - } + checkProtection(request); if (request->args() == 0) return; @@ -605,12 +598,8 @@ class Web { void onLive(AsyncWebServerRequest *request) { DPRINTLN(DBG_VERBOSE, F("onLive")); - if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_LIVE)) { - if (mProtected) { - checkRedirect(request); - return; - } - } + if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_LIVE)) + checkProtection(request); AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), visualization_html, visualization_html_len); response->addHeader(F("Content-Encoding"), "gzip"); @@ -620,13 +609,6 @@ class Web { } void onAbout(AsyncWebServerRequest *request) { - if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_LIVE)) { - if (mProtected) { - checkRedirect(request); - return; - } - } - AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), about_html, about_html_len); response->addHeader(F("Content-Encoding"), "gzip"); response->addHeader(F("content-type"), "text/html; charset=UTF-8"); @@ -643,12 +625,8 @@ class Web { void onSerial(AsyncWebServerRequest *request) { DPRINTLN(DBG_VERBOSE, F("onSerial")); - if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_SERIAL)) { - if (mProtected) { - checkRedirect(request); - return; - } - } + if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_SERIAL)) + checkProtection(request); AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), serial_html, serial_html_len); response->addHeader(F("Content-Encoding"), "gzip"); @@ -658,12 +636,8 @@ class Web { void onSystem(AsyncWebServerRequest *request) { DPRINTLN(DBG_VERBOSE, F("onSystem")); - if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_SYSTEM)) { - if (mProtected) { - checkRedirect(request); - return; - } - } + if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_SYSTEM)) + checkProtection(request); AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), system_html, system_html_len); response->addHeader(F("Content-Encoding"), "gzip"); @@ -840,6 +814,7 @@ class Web { AsyncEventSource mEvts; bool mProtected; uint32_t mLogoutTimeout; + uint8_t mLoginIp[4]; IApp *mApp; HMSYSTEM *mSys;