diff --git a/README.md b/README.md index 8edd8d5..dc27cfc 100644 --- a/README.md +++ b/README.md @@ -274,8 +274,9 @@ In a browser navigate to the IP address assigned to the ESP32. #### Credentials -- User: Pick a username to enable HTTP Basic authentication for the Web Configuration, Set to "#" to disable authentication. -- Password/Retype password: Pick a password to enable HTTP Basic authentication for the Web Configuration. +- User: Pick a username to enable HTTP authentication for the Web Configuration, Set to "#" to disable authentication. +- Password/Retype password: Pick a password to enable HTTP authentication for the Web Configuration. +- Use Digest Authentication (more secure): Enable to use HTTP Digest Authentication instead of HTTP Basic Authentication. Digest authentication is more secure, especially over unencrypted (HTTP) connections. #### Nuki Lock PIN / Nuki Opener PIN diff --git a/src/PreferencesKeys.h b/src/PreferencesKeys.h index f1b961a..e172907 100644 --- a/src/PreferencesKeys.h +++ b/src/PreferencesKeys.h @@ -67,6 +67,7 @@ #define preference_debug_hex_data (char*)"dbgHexData" #define preference_debug_command (char*)"dbgCommand" #define preference_connect_mode (char*)"nukiConnMode" +#define preference_http_auth_type (char*)"httpdAuthType" // CHANGE DOES NOT REQUIRE REBOOT TO TAKE EFFECT #define preference_find_best_rssi (char*)"nwbestrssi" @@ -222,8 +223,9 @@ inline void initPreferences(Preferences* preferences) preferences->putBool(preference_debug_hex_data, false); preferences->putBool(preference_debug_command, false); preferences->putBool(preference_connect_mode, true); - + preferences->putBool(preference_http_auth_type, false); preferences->putBool(preference_retain_gpio, false); + #ifndef CONFIG_IDF_TARGET_ESP32H2 WiFi.begin(); @@ -370,7 +372,7 @@ private: preference_opener_continuous_mode, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_lock_max_timecontrol_entry_count, preference_opener_max_timecontrol_entry_count, preference_enable_bootloop_reset, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_mqtt_hass_discovery, preference_mqtt_hass_cu_url, preference_buffer_size, preference_ip_dhcp_enabled, preference_ip_address, - preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server, preference_network_hardware, + preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server, preference_network_hardware, preference_http_auth_type, preference_rssi_publish_interval, preference_hostname, preference_network_timeout, preference_restart_on_disconnect, preference_restart_ble_beacon_lost, preference_query_interval_lockstate, preference_timecontrol_topic_per_entry, preference_keypad_topic_per_entry, preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_keypad_control_enabled, @@ -402,7 +404,7 @@ private: preference_publish_authdata, preference_publish_debug_info, preference_official_hybrid_enabled, preference_mqtt_hass_enabled, preference_retain_gpio, preference_official_hybrid_actions, preference_official_hybrid_retry, preference_conf_info_enabled, preference_disable_non_json, preference_update_from_mqtt, preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_webserial_enabled, preference_hass_device_discovery, - preference_ntw_reconfigure, preference_keypad_check_code_enabled, preference_disable_network_not_connected, preference_find_best_rssi, + preference_ntw_reconfigure, preference_keypad_check_code_enabled, preference_disable_network_not_connected, preference_find_best_rssi, preference_http_auth_type, preference_debug_connect, preference_debug_communication, preference_debug_readable_data, preference_debug_hex_data, preference_debug_command, preference_connect_mode, preference_lock_force_id, preference_lock_force_doorsensor, preference_lock_force_keypad, preference_opener_force_id, preference_opener_force_keypad }; diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index ebf4d31..ef6ad57 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -75,64 +75,80 @@ WebCfgServer::WebCfgServer(NukiNetwork* network, Preferences* preferences, bool void WebCfgServer::initialize() { - _psychicServer->on("/", HTTP_GET, [&](PsychicRequest *request) + _psychicServer->onOpen([&](PsychicClient* client) { Log->printf("[http] connection #%u connected from %s\n", client->socket(), client->localIP().toString().c_str()); }); + _psychicServer->onClose([&](PsychicClient* client) { Log->printf("[http] connection #%u closed from %s\n", client->socket(), client->localIP().toString().c_str()); }); + + HTTPAuthMethod auth_type = BASIC_AUTH; + if (_preferences->getBool(preference_http_auth_type, false)) + { + auth_type = DIGEST_AUTH; + } + + _psychicServer->on("/", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) { if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) { - return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); } + +#ifndef CONFIG_IDF_TARGET_ESP32H2 if(!_network->isApOpen()) { -#ifndef NUKI_HUB_UPDATER - return buildHtml(request); -#else - return buildOtaHtml(request); #endif - } +#ifndef NUKI_HUB_UPDATER + return buildHtml(request, resp); +#else + return buildOtaHtml(request, resp); +#endif #ifndef CONFIG_IDF_TARGET_ESP32H2 + } else { - return buildWifiConnectHtml(request); + return buildWifiConnectHtml(request, resp); } #endif }); - _psychicServer->on("/style.css", HTTP_GET, [&](PsychicRequest *request) + _psychicServer->on("/style.css", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) { if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) { - return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); } - return sendCss(request); + + return sendCss(request, resp); }); - _psychicServer->on("/favicon.ico", HTTP_GET, [&](PsychicRequest *request) + _psychicServer->on("/favicon.ico", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) { if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) { - return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); } - return sendFavicon(request); + + return sendFavicon(request, resp); }); if(_network->isApOpen()) { #ifndef CONFIG_IDF_TARGET_ESP32H2 - _psychicServer->on("/ssidlist", HTTP_GET, [&](PsychicRequest *request) + _psychicServer->on("/ssidlist", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) { if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) { - return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); } - return buildSSIDListHtml(request); + + return buildSSIDListHtml(request, resp); }); - _psychicServer->on("/savewifi", HTTP_POST, [&](PsychicRequest *request) + _psychicServer->on("/savewifi", HTTP_POST, [&](PsychicRequest *request, PsychicResponse* resp) { if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) { - return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); } + String message = ""; - bool connected = processWiFi(request, message); - esp_err_t res = buildConfirmHtml(request, message, 10, true); + bool connected = processWiFi(request, resp, message); + esp_err_t res = buildConfirmHtml(request, resp, message, 10, true); if(connected) { @@ -142,11 +158,11 @@ void WebCfgServer::initialize() } return res; }); - _psychicServer->on("/reboot", HTTP_GET, [&](PsychicRequest *request) + _psychicServer->on("/reboot", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) { if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) { - return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); } String value = ""; @@ -160,14 +176,14 @@ void WebCfgServer::initialize() } else { - return buildConfirmHtml(request, "No confirm code set.", 3, true); + return buildConfirmHtml(request, resp, "No confirm code set.", 3, true); } if(value != _confirmCode) { - return request->redirect("/"); + return resp->redirect("/"); } - esp_err_t res = buildConfirmHtml(request, "Rebooting...", 2, true); + esp_err_t res = buildConfirmHtml(request, resp, "Rebooting...", 2, true); waitAndProcess(true, 1000); restartEsp(RestartReason::RequestedViaWebServer); return res; @@ -176,11 +192,11 @@ void WebCfgServer::initialize() } else { - _psychicServer->on("/get", HTTP_GET, [&](PsychicRequest *request) + _psychicServer->on("/get", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) { if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) { - return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); } String value = ""; @@ -206,14 +222,14 @@ void WebCfgServer::initialize() } else { - return buildConfirmHtml(request, "No confirm code set.", 3, true); + return buildConfirmHtml(request, resp, "No confirm code set.", 3, true); } if(value != _confirmCode) { - return request->redirect("/"); + return resp->redirect("/"); } - esp_err_t res = buildConfirmHtml(request, "Rebooting...", 2, true); + esp_err_t res = buildConfirmHtml(request, resp, "Rebooting...", 2, true); waitAndProcess(true, 1000); restartEsp(RestartReason::RequestedViaWebServer); return res; @@ -221,78 +237,78 @@ void WebCfgServer::initialize() #ifndef NUKI_HUB_UPDATER else if (value == "info") { - return buildInfoHtml(request); + return buildInfoHtml(request, resp); } else if (value == "debugon") { _preferences->putBool(preference_publish_debug_info, true); - return buildConfirmHtml(request, "Debug On", 3, true); + return buildConfirmHtml(request, resp, "Debug On", 3, true); } else if (value == "debugoff") { _preferences->putBool(preference_publish_debug_info, false); - return buildConfirmHtml(request, "Debug Off", 3, true); + return buildConfirmHtml(request, resp, "Debug Off", 3, true); } else if (value == "export") { - return sendSettings(request); + return sendSettings(request, resp); } else if (value == "impexpcfg") { - return buildImportExportHtml(request); + return buildImportExportHtml(request, resp); } else if (value == "status") { - return buildStatusHtml(request); + return buildStatusHtml(request, resp); } else if (value == "acclvl") { - return buildAccLvlHtml(request); + return buildAccLvlHtml(request, resp); } else if (value == "custntw") { - return buildCustomNetworkConfigHtml(request); + return buildCustomNetworkConfigHtml(request, resp); } else if (value == "advanced") { - return buildAdvancedConfigHtml(request); + return buildAdvancedConfigHtml(request, resp); } else if (value == "cred") { - return buildCredHtml(request); + return buildCredHtml(request, resp); } else if (value == "ntwconfig") { - return buildNetworkConfigHtml(request); + return buildNetworkConfigHtml(request, resp); } else if (value == "mqttconfig") { - return buildMqttConfigHtml(request); + return buildMqttConfigHtml(request, resp); } else if (value == "mqttcaconfig") { - return buildMqttSSLConfigHtml(request, 0); + return buildMqttSSLConfigHtml(request, resp, 0); } else if (value == "mqttcrtconfig") { - return buildMqttSSLConfigHtml(request, 1); + return buildMqttSSLConfigHtml(request, resp, 1); } else if (value == "mqttkeyconfig") { - return buildMqttSSLConfigHtml(request, 2); + return buildMqttSSLConfigHtml(request, resp, 2); } else if (value == "nukicfg") { - return buildNukiConfigHtml(request); + return buildNukiConfigHtml(request, resp); } else if (value == "gpiocfg") { - return buildGpioConfigHtml(request); + return buildGpioConfigHtml(request, resp); } #ifndef CONFIG_IDF_TARGET_ESP32H2 else if (value == "wifi") { - return buildConfigureWifiHtml(request); + return buildConfigureWifiHtml(request, resp); } else if (value == "wifimanager") { @@ -307,17 +323,17 @@ void WebCfgServer::initialize() } else { - return buildConfirmHtml(request, "No confirm code set.", 3, true); + return buildConfirmHtml(request, resp, "No confirm code set.", 3, true); } if(value != _confirmCode) { - return request->redirect("/"); + return resp->redirect("/"); } if(!_allowRestartToPortal) { - return buildConfirmHtml(request, "Can't reset WiFi when network device is Ethernet", 3, true); + return buildConfirmHtml(request, resp, "Can't reset WiFi when network device is Ethernet", 3, true); } - esp_err_t res = buildConfirmHtml(request, "Restarting. Connect to ESP access point (\"NukiHub\" with password \"NukiHubESP32\") to reconfigure Wi-Fi.", 0); + esp_err_t res = buildConfirmHtml(request, resp, "Restarting. Connect to ESP access point (\"NukiHub\" with password \"NukiHubESP32\") to reconfigure Wi-Fi.", 0); waitAndProcess(false, 1000); _network->reconfigureDevice(); return res; @@ -326,11 +342,11 @@ void WebCfgServer::initialize() #endif else if (value == "ota") { - return buildOtaHtml(request); + return buildOtaHtml(request, resp); } else if (value == "otadebug") { - return buildOtaHtml(request, true); + return buildOtaHtml(request, resp, true); } else if (value == "reboottoota") { @@ -345,14 +361,14 @@ void WebCfgServer::initialize() } else { - return buildConfirmHtml(request, "No confirm code set.", 3, true); + return buildConfirmHtml(request, resp, "No confirm code set.", 3, true); } if(value != _confirmCode) { - return request->redirect("/"); + return resp->redirect("/"); } - esp_err_t res = buildConfirmHtml(request, "Rebooting to other partition...", 2, true); + esp_err_t res = buildConfirmHtml(request, resp, "Rebooting to other partition...", 2, true); waitAndProcess(true, 1000); esp_ota_set_boot_partition(esp_ota_get_next_update_partition(NULL)); restartEsp(RestartReason::OTAReboot); @@ -361,34 +377,36 @@ void WebCfgServer::initialize() else if (value == "autoupdate") { #ifndef NUKI_HUB_UPDATER - return processUpdate(request); + return processUpdate(request, resp); #else - return request->redirect("/"); + return resp->redirect("/"); #endif } else { + #ifndef CONFIG_IDF_TARGET_ESP32H2 if(!_network->isApOpen()) { + #endif #ifndef NUKI_HUB_UPDATER - return buildHtml(request); + return buildHtml(request, resp); #else - return buildOtaHtml(request); + return buildOtaHtml(request, resp); #endif - } #ifndef CONFIG_IDF_TARGET_ESP32H2 + } else { - return buildWifiConnectHtml(request); + return buildWifiConnectHtml(request, resp); } #endif } }); - _psychicServer->on("/post", HTTP_POST, [&](PsychicRequest *request) + _psychicServer->on("/post", HTTP_POST, [&](PsychicRequest *request, PsychicResponse* resp) { if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) { - return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); } String value = ""; @@ -405,20 +423,20 @@ void WebCfgServer::initialize() if (value == "savecfg") { String message = ""; - bool restart = processArgs(request, message); + bool restart = processArgs(request, resp, message); if(request->hasParam("mqttssl")) { - return buildConfirmHtml(request, message, 3, true, "/get?page=mqttconfig"); + return buildConfirmHtml(request, resp, message, 3, true, "/get?page=mqttconfig"); } else { - return buildConfirmHtml(request, message, 3, true); + return buildConfirmHtml(request, resp, message, 3, true); } } else if (value == "savegpiocfg") { - processGpioArgs(request); - esp_err_t res = buildConfirmHtml(request, "Saving GPIO configuration. Restarting.", 3, true); + processGpioArgs(request, resp); + esp_err_t res = buildConfirmHtml(request, resp, "Saving GPIO configuration. Restarting.", 3, true); Log->println(F("Restarting")); waitAndProcess(true, 1000); restartEsp(RestartReason::GpioConfigurationUpdated); @@ -426,69 +444,74 @@ void WebCfgServer::initialize() } else if (value == "unpairlock") { - return processUnpair(request, false); + return processUnpair(request, resp, false); } else if (value == "unpairopener") { - return processUnpair(request, true); + return processUnpair(request, resp, true); } else if (value == "factoryreset") { - return processFactoryReset(request); + return processFactoryReset(request, resp); } else if (value == "import") { String message = ""; - bool restart = processImport(request, message); - return buildConfirmHtml(request, message, 3, true); + bool restart = processImport(request, resp, message); + return buildConfirmHtml(request, resp, message, 3, true); } else #else if (1 == 1) #endif { + #ifndef CONFIG_IDF_TARGET_ESP32H2 if(!_network->isApOpen()) { + #endif #ifndef NUKI_HUB_UPDATER - return buildHtml(request); + return buildHtml(request, resp); #else - return buildOtaHtml(request); + return buildOtaHtml(request, resp); #endif - } #ifndef CONFIG_IDF_TARGET_ESP32H2 + } else { - return buildWifiConnectHtml(request); + return buildWifiConnectHtml(request, resp); } #endif } }); PsychicUploadHandler *updateHandler = new PsychicUploadHandler(); - updateHandler->onUpload([&](PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool final) + updateHandler->onUpload([&](PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool last) { if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) { - return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); } - return handleOtaUpload(request, filename, index, data, len, final); - } - ); + + return handleOtaUpload(request, filename, index, data, len, last); + }); - updateHandler->onRequest([&](PsychicRequest *request) + updateHandler->onRequest([&](PsychicRequest* request, PsychicResponse* resp) { if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) { - return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); } - + String result; if (!Update.hasError()) { Log->print("Update code or data OK Update.errorString() "); Log->println(Update.errorString()); result = "Update OK."; - esp_err_t res = request->reply(200,"text/html",result.c_str()); + resp->setCode(200); + resp->setContentType("text/html"); + resp->setContent(result.c_str()); + esp_err_t res = resp->send(); restartEsp(RestartReason::OTACompleted); return res; } @@ -497,7 +520,10 @@ void WebCfgServer::initialize() result = " Update.errorString() " + String(Update.errorString()); Log->print("ERROR : error "); Log->println(result.c_str()); - esp_err_t res = request->reply(500, "text/html", result.c_str()); + resp->setCode(500); + resp->setContentType("text/html"); + resp->setContent(result.c_str()); + esp_err_t res = resp->send(); restartEsp(RestartReason::OTAAborted); return res; } @@ -531,12 +557,12 @@ void WebCfgServer::printCheckBox(PsychicStreamResponse *response, const char *to } #ifndef CONFIG_IDF_TARGET_ESP32H2 -esp_err_t WebCfgServer::buildSSIDListHtml(PsychicRequest *request) +esp_err_t WebCfgServer::buildSSIDListHtml(PsychicRequest *request, PsychicResponse* resp) { _network->scan(true, false); createSsidList(); - PsychicStreamResponse response(request, "text/html"); + PsychicStreamResponse response(resp, "text/html"); response.beginSend(); for (int i = 0; i < _ssidList.size(); i++) @@ -585,10 +611,10 @@ void WebCfgServer::createSsidList() } } -esp_err_t WebCfgServer::buildWifiConnectHtml(PsychicRequest *request) +esp_err_t WebCfgServer::buildWifiConnectHtml(PsychicRequest *request, PsychicResponse* resp) { String header = ""; - PsychicStreamResponse response(request, "text/html"); + PsychicStreamResponse response(resp, "text/html"); response.beginSend(); buildHtmlHeader(&response, header); response.print("