diff --git a/README.md b/README.md index db67387..7bb876a 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,7 @@ Users report that [ESP_Flasher](https://github.com/Jason2866/ESP_Flasher/release ## First configuration * After the initial flashing of the microcontroller, an Access Point called "OpenDTU-*" is opened. The default password is "openDTU42". * Use a web browser to open the address [http://192.168.4.1](http://192.168.4.1) -* Navigate to Settings --> Network Settings and enter your WiFi credentials +* Navigate to Settings --> Network Settings and enter your WiFi credentials. The username to access the config menu is "admin" and the password the same as for accessing the Access Point (default: "openDTU42"). * OpenDTU then simultaneously connects to your WiFi AP with this credentials. Navigate to Info --> Network and look into section "Network Interface (Station)" for the IP address received via DHCP. * When OpenDTU is connected to a configured WiFI AP, the "OpenDTU-*" Access Point is closed after 3 minutes. * OpenDTU needs access to a working NTP server to get the current date & time. Both are sent to the inverter with each request. Default NTP server is pool.ntp.org. If your network has different requirements please change accordingly (Settings --> NTP Settings). diff --git a/include/WebApi.h b/include/WebApi.h index cf7805a..699b9cd 100644 --- a/include/WebApi.h +++ b/include/WebApi.h @@ -24,6 +24,8 @@ public: void init(); void loop(); + static bool checkCredentials(AsyncWebServerRequest* request); + private: AsyncWebServer _server; AsyncEventSource _events; diff --git a/include/WebApi_security.h b/include/WebApi_security.h index d94a7ee..ea9dd75 100644 --- a/include/WebApi_security.h +++ b/include/WebApi_security.h @@ -12,5 +12,7 @@ private: void onPasswordGet(AsyncWebServerRequest* request); void onPasswordPost(AsyncWebServerRequest* request); + void onAuthenticateGet(AsyncWebServerRequest* request); + AsyncWebServer* _server; }; \ No newline at end of file diff --git a/include/defaults.h b/include/defaults.h index 298a87f..9656901 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -9,6 +9,7 @@ #define ACCESS_POINT_NAME "OpenDTU-" #define ACCESS_POINT_PASSWORD "openDTU42" +#define AUTH_USERNAME "admin" #define ADMIN_TIMEOUT 180 #define WIFI_RECONNECT_TIMEOUT 15 diff --git a/src/WebApi.cpp b/src/WebApi.cpp index be8203d..f6a6355 100644 --- a/src/WebApi.cpp +++ b/src/WebApi.cpp @@ -5,6 +5,7 @@ #include "WebApi.h" #include "ArduinoJson.h" #include "AsyncJson.h" +#include "Configuration.h" #include "defaults.h" WebApiClass::WebApiClass() @@ -55,4 +56,22 @@ void WebApiClass::loop() _webApiWsLive.loop(); } +bool WebApiClass::checkCredentials(AsyncWebServerRequest* request) +{ + CONFIG_T& config = Configuration.get(); + if (request->authenticate(AUTH_USERNAME, config.Security_Password)) { + return true; + } + + AsyncWebServerResponse* r = request->beginResponse(401); + + // WebAPI should set the X-Requested-With to prevent browser internal auth dialogs + if (!request->hasHeader("X-Requested-With")) { + r->addHeader(F("WWW-Authenticate"), F("Basic realm=\"Login Required\"")); + } + request->send(r); + + return false; +} + WebApiClass WebApi; \ No newline at end of file diff --git a/src/WebApi_security.cpp b/src/WebApi_security.cpp index 2009be9..96802fb 100644 --- a/src/WebApi_security.cpp +++ b/src/WebApi_security.cpp @@ -6,6 +6,7 @@ #include "ArduinoJson.h" #include "AsyncJson.h" #include "Configuration.h" +#include "WebApi.h" #include "helper.h" void WebApiSecurityClass::init(AsyncWebServer* server) @@ -16,6 +17,7 @@ void WebApiSecurityClass::init(AsyncWebServer* server) _server->on("/api/security/password", HTTP_GET, std::bind(&WebApiSecurityClass::onPasswordGet, this, _1)); _server->on("/api/security/password", HTTP_POST, std::bind(&WebApiSecurityClass::onPasswordPost, this, _1)); + _server->on("/api/security/authenticate", HTTP_GET, std::bind(&WebApiSecurityClass::onAuthenticateGet, this, _1)); } void WebApiSecurityClass::loop() @@ -24,6 +26,10 @@ void WebApiSecurityClass::loop() void WebApiSecurityClass::onPasswordGet(AsyncWebServerRequest* request) { + if (!WebApi.checkCredentials(request)) { + return; + } + AsyncJsonResponse* response = new AsyncJsonResponse(); JsonObject root = response->getRoot(); const CONFIG_T& config = Configuration.get(); @@ -36,6 +42,10 @@ void WebApiSecurityClass::onPasswordGet(AsyncWebServerRequest* request) void WebApiSecurityClass::onPasswordPost(AsyncWebServerRequest* request) { + if (!WebApi.checkCredentials(request)) { + return; + } + AsyncJsonResponse* response = new AsyncJsonResponse(); JsonObject retMsg = response->getRoot(); retMsg[F("type")] = F("warning"); @@ -87,6 +97,21 @@ void WebApiSecurityClass::onPasswordPost(AsyncWebServerRequest* request) retMsg[F("type")] = F("success"); retMsg[F("message")] = F("Settings saved!"); + response->setLength(); + request->send(response); +} + +void WebApiSecurityClass::onAuthenticateGet(AsyncWebServerRequest* request) +{ + if (!WebApi.checkCredentials(request)) { + return; + } + + AsyncJsonResponse* response = new AsyncJsonResponse(); + JsonObject retMsg = response->getRoot(); + retMsg[F("type")] = F("success"); + retMsg[F("message")] = F("Authentication successfull!"); + response->setLength(); request->send(response); } \ No newline at end of file diff --git a/webapp/package.json b/webapp/package.json index 4cc42eb..b57fad8 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -14,6 +14,7 @@ "@popperjs/core": "^2.11.6", "bootstrap": "^5.2.2", "bootstrap-icons-vue": "^1.8.1", + "mitt": "^3.0.0", "spark-md5": "^3.0.2", "vue": "^3.2.41", "vue-router": "^4.1.6" diff --git a/webapp/src/components/NavBar.vue b/webapp/src/components/NavBar.vue index ee4fa25..e66760a 100644 --- a/webapp/src/components/NavBar.vue +++ b/webapp/src/components/NavBar.vue @@ -73,19 +73,41 @@ +
\ No newline at end of file diff --git a/webapp/src/views/SecurityAdminView.vue b/webapp/src/views/SecurityAdminView.vue index f687817..d557bd8 100644 --- a/webapp/src/views/SecurityAdminView.vue +++ b/webapp/src/views/SecurityAdminView.vue @@ -26,8 +26,8 @@