Merge pull request #612 from iranl/http-form-auth

HTTP Form auth
This commit is contained in:
iranl
2025-01-17 23:28:38 +01:00
committed by GitHub
13 changed files with 418 additions and 160 deletions

View File

@@ -13,7 +13,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
board: [esp32, esp32-s3, esp32-s3-oct, esp32-c3, esp32-c6, esp32-h2, esp32-solo1] board: [esp32, esp32-s3, esp32-s3-oct, esp32-c3, esp32-c6, esp32-h2, esp32-solo1]
build: [release, debug] build: [release]
env: env:
BOARD: ${{ matrix.board }} BOARD: ${{ matrix.board }}
VARIANT: ${{ matrix.name || matrix.board }} VARIANT: ${{ matrix.name || matrix.board }}
@@ -107,24 +107,16 @@ jobs:
with: with:
path: release path: release
pattern: '*-release-assets' pattern: '*-release-assets'
- name: Download debug assets
uses: actions/download-artifact@v4
with:
path: debug
pattern: '*-debug-assets'
- name: Copy binaries to ota/beta - name: Copy binaries to ota/beta
env: env:
Version: ${{ github.run_id }}.${{ github.run_number }}.${{ github.run_attempt }} Version: ${{ github.run_id }}.${{ github.run_number }}.${{ github.run_attempt }}
run: | run: |
mkdir -p ota/beta/ mkdir -p ota/beta/
mkdir -p ota/debug/beta/
mkdir -p ota/master/ mkdir -p ota/master/
mkdir -p ota/debug/master/
mkdir -p webflash/ mkdir -p webflash/
mkdir -p resources/ mkdir -p resources/
mkdir -p src/ mkdir -p src/
cp -vf release/*/nuki_hub_*.bin ota/beta/ cp -vf release/*/nuki_hub_*.bin ota/beta/
cp -vf debug/*/nuki_hub_*.bin ota/debug/beta/
cp -vf master/resources/ota_manifest.py resources/ota_manifest.py cp -vf master/resources/ota_manifest.py resources/ota_manifest.py
cp -vf master/src/Config.h src/Config.h cp -vf master/src/Config.h src/Config.h
python3 resources/ota_manifest.py beta $Version python3 resources/ota_manifest.py beta $Version
@@ -132,14 +124,12 @@ jobs:
rm -rf .github .gitignore .gitmodules rm -rf .github .gitignore .gitmodules
touch ota/beta/empty touch ota/beta/empty
touch ota/master/empty touch ota/master/empty
touch ota/debug/beta/empty
touch ota/debug/master/empty
touch webflash/empty touch webflash/empty
- name: Commit binaries to binary - name: Commit binaries to binary
uses: stefanzweifel/git-auto-commit-action@v5 uses: stefanzweifel/git-auto-commit-action@v5
with: with:
commit_message: "Update binaries" commit_message: "Update binaries"
file_pattern: 'ota/* ota/debug/* ota/master/* ota/debug/master/* ota/beta/* ota/debug/beta/* webflash/*' file_pattern: 'ota/* ota/master/* ota/beta/* webflash/*'
branch: binary branch: binary
skip_dirty_check: true skip_dirty_check: true
skip_fetch: true skip_fetch: true

View File

@@ -19,7 +19,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
board: [esp32, esp32-s3, esp32-s3-oct, esp32-c3, esp32-c6, esp32-h2, esp32-solo1] board: [esp32, esp32-s3, esp32-s3-oct, esp32-c3, esp32-c6, esp32-h2, esp32-solo1]
build: [release, debug] build: [release]
env: env:
BOARD: ${{ matrix.board }} BOARD: ${{ matrix.board }}
VARIANT: ${{ matrix.name || matrix.board }} VARIANT: ${{ matrix.name || matrix.board }}

View File

@@ -35,7 +35,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
board: [esp32, esp32-s3, esp32-s3-oct, esp32-c3, esp32-c6, esp32-h2, esp32-solo1] board: [esp32, esp32-s3, esp32-s3-oct, esp32-c3, esp32-c6, esp32-h2, esp32-solo1]
build: [release, debug] build: [release]
env: env:
BOARD: ${{ matrix.board }} BOARD: ${{ matrix.board }}
VARIANT: ${{ matrix.name || matrix.board }} VARIANT: ${{ matrix.name || matrix.board }}
@@ -130,19 +130,12 @@ jobs:
with: with:
path: release path: release
pattern: '*-release-assets' pattern: '*-release-assets'
- name: Download debug assets
uses: actions/download-artifact@v4
with:
path: debug
pattern: '*-debug-assets'
- name: Copy binaries to ota/master - name: Copy binaries to ota/master
env: env:
Version: ${{ github.run_id }}.${{ github.run_number }}.${{ github.run_attempt }} Version: ${{ github.run_id }}.${{ github.run_number }}.${{ github.run_attempt }}
run: | run: |
mkdir -p ota/beta/ mkdir -p ota/beta/
mkdir -p ota/debug/beta/
mkdir -p ota/master/ mkdir -p ota/master/
mkdir -p ota/debug/master/
mkdir -p webflash/ mkdir -p webflash/
mkdir -p resources/ mkdir -p resources/
mkdir -p src/ mkdir -p src/
@@ -155,14 +148,12 @@ jobs:
rm -rf .github .gitignore .gitmodules rm -rf .github .gitignore .gitmodules
touch ota/beta/empty touch ota/beta/empty
touch ota/master/empty touch ota/master/empty
touch ota/debug/beta/empty
touch ota/debug/master/empty
touch webflash/empty touch webflash/empty
- name: Commit binaries to binary - name: Commit binaries to binary
uses: stefanzweifel/git-auto-commit-action@v5 uses: stefanzweifel/git-auto-commit-action@v5
with: with:
commit_message: "Update binaries" commit_message: "Update binaries"
file_pattern: 'ota/* ota/debug/* ota/master/* ota/debug/master/* ota/beta/* ota/debug/beta/* webflash/*' file_pattern: 'ota/* ota/master/* ota/beta/* webflash/*'
branch: binary branch: binary
skip_dirty_check: true skip_dirty_check: true
skip_fetch: true skip_fetch: true

View File

@@ -13,7 +13,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
board: [esp32, esp32-s3, esp32-s3-oct, esp32-c3, esp32-c6, esp32-h2, esp32-solo1] board: [esp32, esp32-s3, esp32-s3-oct, esp32-c3, esp32-c6, esp32-h2, esp32-solo1]
build: [release, debug] build: [release]
env: env:
BOARD: ${{ matrix.board }} BOARD: ${{ matrix.board }}
VARIANT: ${{ matrix.name || matrix.board }} VARIANT: ${{ matrix.name || matrix.board }}
@@ -109,11 +109,6 @@ jobs:
with: with:
path: release path: release
pattern: '*-release-assets' pattern: '*-release-assets'
- name: Download debug assets
uses: actions/download-artifact@v4
with:
path: debug
pattern: '*-debug-assets'
- name: Build zip archives - name: Build zip archives
id: zip id: zip
env: env:
@@ -171,14 +166,11 @@ jobs:
Version: ${{ github.run_id }}.${{ github.run_number }}.${{ github.run_attempt }} Version: ${{ github.run_id }}.${{ github.run_number }}.${{ github.run_attempt }}
run: | run: |
mkdir -p ota/beta/ mkdir -p ota/beta/
mkdir -p ota/debug/beta/
mkdir -p ota/master/ mkdir -p ota/master/
mkdir -p ota/debug/master/
mkdir -p webflash/ mkdir -p webflash/
mkdir -p resources/ mkdir -p resources/
mkdir -p src/ mkdir -p src/
cp -vf release/*/nuki_hub_*.bin ota/ cp -vf release/*/nuki_hub_*.bin ota/
cp -vf debug/*/nuki_hub_*.bin ota/debug/
cp -vf release/*/webflash_nuki_hub_*.bin webflash/ cp -vf release/*/webflash_nuki_hub_*.bin webflash/
cp -vf master/resources/ota_manifest.py resources/ota_manifest.py cp -vf master/resources/ota_manifest.py resources/ota_manifest.py
cp -vf master/src/Config.h src/Config.h cp -vf master/src/Config.h src/Config.h
@@ -186,18 +178,15 @@ jobs:
python3 resources/ota_manifest.py beta none python3 resources/ota_manifest.py beta none
find * -not -path "ota*" -not -path "webflash*" -delete find * -not -path "ota*" -not -path "webflash*" -delete
rm -rf ota/beta/*.bin rm -rf ota/beta/*.bin
rm -rf ota/debug/beta/*.bin
rm -rf .github .gitignore .gitmodules rm -rf .github .gitignore .gitmodules
touch ota/beta/empty touch ota/beta/empty
touch ota/master/empty touch ota/master/empty
touch ota/debug/beta/empty
touch ota/debug/master/empty
touch webflash/empty touch webflash/empty
- name: Commit binaries to binary - name: Commit binaries to binary
uses: stefanzweifel/git-auto-commit-action@v5 uses: stefanzweifel/git-auto-commit-action@v5
with: with:
commit_message: "Update binaries" commit_message: "Update binaries"
file_pattern: 'ota/* ota/debug/* ota/master/* ota/debug/master/* ota/beta/* ota/debug/beta/* webflash/*' file_pattern: 'ota/* ota/master/* ota/beta/* webflash/*'
branch: binary branch: binary
skip_dirty_check: true skip_dirty_check: true
skip_fetch: true skip_fetch: true

View File

@@ -312,7 +312,7 @@ In a browser navigate to the IP address assigned to the ESP32.
- User: Pick a username to enable HTTP authentication for the Web Configuration, Set to "#" to disable authentication. - 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. - 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. - HTTP Authentication type: Select from Basic, Digest or Form based authentication. Digest authentication is more secure than Basic or Form based authentication, especially over unencrypted (HTTP) connections. Form based authentication works best with password managers. Note: Firefox seems to have issues with basic authentication.
#### Nuki Lock PIN / Nuki Opener PIN #### Nuki Lock PIN / Nuki Opener PIN

View File

@@ -303,7 +303,7 @@ String PsychicRequest::getCookie(const char* key)
// how big is our cookie? // how big is our cookie?
size_t size; size_t size;
if (!hasCookie("counter", &size)) if (!hasCookie(key, &size))
return cookie; return cookie;
// allocate cookie buffer... keep it on the stack // allocate cookie buffer... keep it on the stack

View File

@@ -121,3 +121,4 @@ CONFIG_ESP_WIFI_RX_IRAM_OPT=n
CONFIG_MBEDTLS_DYNAMIC_BUFFER=y CONFIG_MBEDTLS_DYNAMIC_BUFFER=y
CONFIG_LWIP_DHCP_GET_NTP_SRV=y CONFIG_LWIP_DHCP_GET_NTP_SRV=y
CONFIG_LWIP_SNTP_UPDATE_DELAY=43200000 CONFIG_LWIP_SNTP_UPDATE_DELAY=43200000
CONFIG_LWIP_SNTP_MAX_SERVERS=3

View File

@@ -5,7 +5,7 @@
#define NUKI_HUB_VERSION "9.08" #define NUKI_HUB_VERSION "9.08"
#define NUKI_HUB_VERSION_INT (uint32_t)908 #define NUKI_HUB_VERSION_INT (uint32_t)908
#define NUKI_HUB_BUILD "unknownbuildnr" #define NUKI_HUB_BUILD "unknownbuildnr"
#define NUKI_HUB_DATE "2025-01-15" #define NUKI_HUB_DATE "2025-01-17"
#define GITHUB_LATEST_RELEASE_URL (char*)"https://github.com/technyon/nuki_hub/releases/latest" #define GITHUB_LATEST_RELEASE_URL (char*)"https://github.com/technyon/nuki_hub/releases/latest"
#define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json" #define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json"

View File

@@ -226,6 +226,7 @@ inline void initPreferences(Preferences* preferences)
preferences->putInt(preference_query_interval_configuration, 3600); preferences->putInt(preference_query_interval_configuration, 3600);
preferences->putInt(preference_query_interval_battery, 1800); preferences->putInt(preference_query_interval_battery, 1800);
preferences->putInt(preference_query_interval_keypad, 1800); preferences->putInt(preference_query_interval_keypad, 1800);
preferences->putInt(preference_http_auth_type, 0);
preferences->putBool(preference_debug_connect, false); preferences->putBool(preference_debug_connect, false);
preferences->putBool(preference_debug_communication, false); preferences->putBool(preference_debug_communication, false);
@@ -233,7 +234,6 @@ inline void initPreferences(Preferences* preferences)
preferences->putBool(preference_debug_hex_data, false); preferences->putBool(preference_debug_hex_data, false);
preferences->putBool(preference_debug_command, false); preferences->putBool(preference_debug_command, false);
preferences->putBool(preference_connect_mode, true); preferences->putBool(preference_connect_mode, true);
preferences->putBool(preference_http_auth_type, false);
preferences->putBool(preference_retain_gpio, false); preferences->putBool(preference_retain_gpio, false);
preferences->putBool(preference_enable_debug_mode, false); preferences->putBool(preference_enable_debug_mode, false);
@@ -508,7 +508,7 @@ private:
preference_publish_authdata, preference_publish_debug_info, preference_official_hybrid_enabled, preference_mqtt_hass_enabled, preference_retain_gpio, 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_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_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_http_auth_type, preference_ntw_reconfigure, preference_keypad_check_code_enabled, preference_disable_network_not_connected, preference_find_best_rssi,
preference_debug_connect, preference_debug_communication, preference_debug_readable_data, preference_debug_hex_data, preference_debug_command, preference_connect_mode, 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, preference_mqtt_ssl_enabled, preference_lock_force_id, preference_lock_force_doorsensor, preference_lock_force_keypad, preference_opener_force_id, preference_opener_force_keypad, preference_mqtt_ssl_enabled,
preference_hybrid_reboot_on_disconnect, preference_lock_gemini_enabled, preference_enable_debug_mode preference_hybrid_reboot_on_disconnect, preference_lock_gemini_enabled, preference_enable_debug_mode
@@ -528,7 +528,7 @@ private:
preference_task_size_network, preference_task_size_nuki, preference_authlog_max_entries, preference_keypad_max_entries, preference_timecontrol_max_entries, preference_task_size_network, preference_task_size_nuki, preference_authlog_max_entries, preference_keypad_max_entries, preference_timecontrol_max_entries,
preference_ble_tx_power, preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr, preference_ble_tx_power, preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr,
preference_network_custom_irq, preference_network_custom_rst, preference_network_custom_cs, preference_network_custom_sck, preference_network_custom_miso, preference_network_custom_irq, preference_network_custom_rst, preference_network_custom_cs, preference_network_custom_sck, preference_network_custom_miso,
preference_network_custom_mosi, preference_network_custom_pwr, preference_network_custom_mdio preference_network_custom_mosi, preference_network_custom_pwr, preference_network_custom_mdio, preference_http_auth_type
}; };
std::vector<char*> _uintPrefs = std::vector<char*> _uintPrefs =
{ {

View File

@@ -56,8 +56,12 @@ WebCfgServer::WebCfgServer(NukiNetwork* network, Preferences* preferences, bool
str = _preferences->getString(preference_cred_password, ""); str = _preferences->getString(preference_cred_password, "");
const char *pass = str.c_str(); const char *pass = str.c_str();
memcpy(&_credPassword, pass, str.length()); memcpy(&_credPassword, pass, str.length());
}
if (_preferences->getInt(preference_http_auth_type, 0) == 2)
{
loadSessions();
}
}
_confirmCode = generateConfirmCode(); _confirmCode = generateConfirmCode();
#ifndef NUKI_HUB_UPDATER #ifndef NUKI_HUB_UPDATER
@@ -76,41 +80,115 @@ WebCfgServer::WebCfgServer(NukiNetwork* network, Preferences* preferences, bool
#endif #endif
} }
void WebCfgServer::initialize() bool WebCfgServer::isAuthenticated(PsychicRequest *request)
{ {
_psychicServer->onOpen([&](PsychicClient* client) { Log->printf("[http] connection #%u connected from %s\n", client->socket(), client->localIP().toString().c_str()); }); if (request->hasCookie("sessionId")) {
_psychicServer->onClose([&](PsychicClient* client) { Log->printf("[http] connection #%u closed from %s\n", client->socket(), client->localIP().toString().c_str()); }); String cookie = request->getCookie("sessionId");
HTTPAuthMethod auth_type = BASIC_AUTH; if (_httpSessions[cookie].is<JsonVariant>())
if (_preferences->getBool(preference_http_auth_type, false))
{ {
auth_type = DIGEST_AUTH; struct timeval time;
} gettimeofday(&time, NULL);
int64_t time_us = (int64_t)time.tv_sec * 1000000L + (int64_t)time.tv_usec;
_psychicServer->on("/", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) if (_httpSessions[cookie].as<signed long long>() > time_us)
{ {
if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) return true;
{
return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in.");
}
#ifndef CONFIG_IDF_TARGET_ESP32H2
if(!_network->isApOpen())
{
#endif
#ifndef NUKI_HUB_UPDATER
return buildHtml(request, resp);
#else
return buildOtaHtml(request, resp);
#endif
#ifndef CONFIG_IDF_TARGET_ESP32H2
} }
else else
{ {
return buildWifiConnectHtml(request, resp); Log->println("Cookie found, but not valid anymore");
} }
#endif }
}); }
Log->println("Authentication Failed");
return false;
}
esp_err_t WebCfgServer::logoutSession(PsychicRequest *request, PsychicResponse* resp)
{
Log->print("Logging out");
resp->setCookie("sessionId", "", 0, "HttpOnly");
if (request->hasCookie("sessionId")) {
String cookie = request->getCookie("sessionId");
_httpSessions.remove(cookie);
saveSessions();
}
else
{
Log->print("No session cookie found");
}
return buildConfirmHtml(request, resp, "Logging out", 3, true);
}
void WebCfgServer::saveSessions()
{
if(_preferences->getBool(preference_update_time, false))
{
if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed");
}
else
{
File file = SPIFFS.open("/sessions.json", "w");
serializeJson(_httpSessions, file);
file.close();
}
}
}
void WebCfgServer::loadSessions()
{
if(_preferences->getBool(preference_update_time, false))
{
if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed");
}
else
{
File file = SPIFFS.open("/sessions.json", "r");
if (!file || file.isDirectory()) {
Log->println("sessions.json not found");
}
else
{
deserializeJson(_httpSessions, file);
}
file.close();
}
}
}
int WebCfgServer::doAuthentication(PsychicRequest *request)
{
if(strlen(_credUser) > 0 && strlen(_credPassword) > 0)
{
int savedAuthType = _preferences->getInt(preference_http_auth_type, 0);
if (savedAuthType == 2)
{
if (!isAuthenticated(request)) {
return savedAuthType;
}
}
else
{
if (!request->authenticate(_credUser, _credPassword))
{
return savedAuthType;
}
}
}
return 4;
}
void WebCfgServer::initialize()
{
//_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()); });
_psychicServer->on("/style.css", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) _psychicServer->on("/style.css", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp)
{ {
return sendCss(request, resp); return sendCss(request, resp);
@@ -129,9 +207,23 @@ void WebCfgServer::initialize()
}); });
_psychicServer->on("/savewifi", HTTP_POST, [&](PsychicRequest *request, PsychicResponse* resp) _psychicServer->on("/savewifi", HTTP_POST, [&](PsychicRequest *request, PsychicResponse* resp)
{ {
if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) int authReq = doAuthentication(request);
switch (authReq)
{ {
return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); case 0:
return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in.");
break;
case 1:
return request->requestAuthentication(DIGEST_AUTH, "Nuki Hub", "You must log in.");
break;
case 2:
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/get?page=login");
case 4:
default:
break;
} }
String message = ""; String message = "";
@@ -148,9 +240,23 @@ void WebCfgServer::initialize()
}); });
_psychicServer->on("/reboot", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) _psychicServer->on("/reboot", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp)
{ {
if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) int authReq = doAuthentication(request);
switch (authReq)
{ {
return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); case 0:
return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in.");
break;
case 1:
return request->requestAuthentication(DIGEST_AUTH, "Nuki Hub", "You must log in.");
break;
case 2:
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/get?page=login");
case 4:
default:
break;
} }
String value = ""; String value = "";
@@ -169,6 +275,8 @@ void WebCfgServer::initialize()
if(value != _confirmCode) if(value != _confirmCode)
{ {
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/"); return resp->redirect("/");
} }
esp_err_t res = buildConfirmHtml(request, resp, "Rebooting...", 2, true); esp_err_t res = buildConfirmHtml(request, resp, "Rebooting...", 2, true);
@@ -191,16 +299,44 @@ void WebCfgServer::initialize()
value = p->value(); value = p->value();
} }
} }
int authReq = doAuthentication(request);
if (value != "status") if (value != "status" && value != "login" && value != "logout")
{ {
if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) switch (authReq)
{ {
return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); case 0:
return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in.");
break;
case 1:
return request->requestAuthentication(DIGEST_AUTH, "Nuki Hub", "You must log in.");
break;
case 2:
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/get?page=login");
case 4:
default:
break;
} }
} }
else if (value == "status" && authReq != 4)
{
resp->setCode(200);
resp->setContentType("application/json");
resp->setContent("{}");
return resp->send();
}
if (value == "reboot") if (value == "login")
{
return buildLoginHtml(request, resp);
}
else if (value == "logout")
{
return logoutSession(request, resp);
}
else if (value == "reboot")
{ {
String value = ""; String value = "";
if(request->hasParam("CONFIRMTOKEN")) if(request->hasParam("CONFIRMTOKEN"))
@@ -218,6 +354,8 @@ void WebCfgServer::initialize()
if(value != _confirmCode) if(value != _confirmCode)
{ {
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/"); return resp->redirect("/");
} }
esp_err_t res = buildConfirmHtml(request, resp, "Rebooting...", 2, true); esp_err_t res = buildConfirmHtml(request, resp, "Rebooting...", 2, true);
@@ -249,20 +387,9 @@ void WebCfgServer::initialize()
return buildImportExportHtml(request, resp); return buildImportExportHtml(request, resp);
} }
else if (value == "status") else if (value == "status")
{
if(request->hasParam("token"))
{
const PsychicWebParameter* p2 = request->getParam("token");
if(p2->value().toInt() == _randomInt)
{ {
return buildStatusHtml(request, resp); return buildStatusHtml(request, resp);
} }
}
resp->setCode(200);
resp->setContentType("text/html");
resp->setContent("");
return resp->send();
}
else if (value == "acclvl") else if (value == "acclvl")
{ {
return buildAccLvlHtml(request, resp); return buildAccLvlHtml(request, resp);
@@ -337,6 +464,8 @@ void WebCfgServer::initialize()
} }
if(value != _confirmCode) if(value != _confirmCode)
{ {
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/"); return resp->redirect("/");
} }
if(!_allowRestartToPortal) if(!_allowRestartToPortal)
@@ -356,7 +485,8 @@ void WebCfgServer::initialize()
} }
else if (value == "otadebug") else if (value == "otadebug")
{ {
return buildOtaHtml(request, resp, true); return buildOtaHtml(request, resp);
//return buildOtaHtml(request, resp, true);
} }
else if (value == "reboottoota") else if (value == "reboottoota")
{ {
@@ -376,6 +506,8 @@ void WebCfgServer::initialize()
if(value != _confirmCode) if(value != _confirmCode)
{ {
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/"); return resp->redirect("/");
} }
esp_err_t res = buildConfirmHtml(request, resp, "Rebooting to other partition...", 2, true); esp_err_t res = buildConfirmHtml(request, resp, "Rebooting to other partition...", 2, true);
@@ -389,36 +521,21 @@ void WebCfgServer::initialize()
#ifndef NUKI_HUB_UPDATER #ifndef NUKI_HUB_UPDATER
return processUpdate(request, resp); return processUpdate(request, resp);
#else #else
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/"); return resp->redirect("/");
#endif #endif
} }
else else
{ {
#ifndef CONFIG_IDF_TARGET_ESP32H2 Log->println("Page not found, loading index");
if(!_network->isApOpen()) resp->setCode(302);
{ resp->addHeader("Cache-Control", "no-cache");
#endif return resp->redirect("/");
#ifndef NUKI_HUB_UPDATER
return buildHtml(request, resp);
#else
return buildOtaHtml(request, resp);
#endif
#ifndef CONFIG_IDF_TARGET_ESP32H2
}
else
{
return buildWifiConnectHtml(request, resp);
}
#endif
} }
}); });
_psychicServer->on("/post", HTTP_POST, [&](PsychicRequest *request, PsychicResponse* resp) _psychicServer->on("/post", HTTP_POST, [&](PsychicRequest *request, PsychicResponse* resp)
{ {
if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword))
{
return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in.");
}
String value = ""; String value = "";
if(request->hasParam("page")) if(request->hasParam("page"))
{ {
@@ -429,8 +546,46 @@ void WebCfgServer::initialize()
} }
} }
if (value != "login")
{
int authReq = doAuthentication(request);
switch (authReq)
{
case 0:
return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in.");
break;
case 1:
return request->requestAuthentication(DIGEST_AUTH, "Nuki Hub", "You must log in.");
break;
case 2:
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/get?page=login");
case 4:
default:
break;
}
}
if (value == "login")
{
bool loggedIn = processLogin(request, resp);
if (loggedIn)
{
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/");
}
else
{
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/get?page=login");
}
}
#ifndef NUKI_HUB_UPDATER #ifndef NUKI_HUB_UPDATER
if (value == "savecfg") else if (value == "savecfg")
{ {
String message = ""; String message = "";
bool restart = processArgs(request, resp, message); bool restart = processArgs(request, resp, message);
@@ -474,10 +629,8 @@ void WebCfgServer::initialize()
bool restart = processImport(request, resp, message); bool restart = processImport(request, resp, message);
return buildConfirmHtml(request, resp, message, 3, true); return buildConfirmHtml(request, resp, message, 3, true);
} }
else
#else
if (1 == 1)
#endif #endif
else
{ {
#ifndef CONFIG_IDF_TARGET_ESP32H2 #ifndef CONFIG_IDF_TARGET_ESP32H2
if(!_network->isApOpen()) if(!_network->isApOpen())
@@ -501,19 +654,44 @@ void WebCfgServer::initialize()
PsychicUploadHandler *updateHandler = new PsychicUploadHandler(); PsychicUploadHandler *updateHandler = new PsychicUploadHandler();
updateHandler->onUpload([&](PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool last) 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)) int authReq = doAuthentication(request);
{
return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in.");
}
switch (authReq)
{
case 0:
return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in.");
break;
case 1:
return request->requestAuthentication(DIGEST_AUTH, "Nuki Hub", "You must log in.");
break;
case 2:
return ESP_FAIL;
case 4:
default:
break;
}
return handleOtaUpload(request, filename, index, data, len, last); return handleOtaUpload(request, filename, index, data, len, last);
}); });
updateHandler->onRequest([&](PsychicRequest* request, PsychicResponse* resp) updateHandler->onRequest([&](PsychicRequest* request, PsychicResponse* resp)
{ {
if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) int authReq = doAuthentication(request);
switch (authReq)
{ {
return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); case 0:
return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in.");
break;
case 1:
return request->requestAuthentication(DIGEST_AUTH, "Nuki Hub", "You must log in.");
break;
case 2:
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/get?page=login");
case 4:
default:
break;
} }
String result; String result;
@@ -546,6 +724,47 @@ void WebCfgServer::initialize()
_psychicServer->on("/uploadota", HTTP_POST, updateHandler); _psychicServer->on("/uploadota", HTTP_POST, updateHandler);
//Update.onProgress(printProgress); //Update.onProgress(printProgress);
} }
_psychicServer->on("/", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp)
{
int authReq = doAuthentication(request);
switch (authReq)
{
case 0:
return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in.");
break;
case 1:
return request->requestAuthentication(DIGEST_AUTH, "Nuki Hub", "You must log in.");
break;
case 2:
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/get?page=login");
break;
case 4:
default:
break;
}
#ifndef CONFIG_IDF_TARGET_ESP32H2
if(!_network->isApOpen())
{
#endif
#ifndef NUKI_HUB_UPDATER
return buildHtml(request, resp);
#else
return buildOtaHtml(request, resp);
#endif
#ifndef CONFIG_IDF_TARGET_ESP32H2
}
else
{
return buildWifiConnectHtml(request, resp);
}
#endif
});
} }
void WebCfgServer::printCheckBox(PsychicStreamResponse *response, const char *token, const char *description, const bool value, const char *htmlClass) void WebCfgServer::printCheckBox(PsychicStreamResponse *response, const char *token, const char *description, const bool value, const char *htmlClass)
@@ -1266,6 +1485,54 @@ void WebCfgServer::printInputField(PsychicStreamResponse *response,
printInputField(response, token, description, valueStr, maxLength, args); printInputField(response, token, description, valueStr, maxLength, args);
} }
esp_err_t WebCfgServer::buildLoginHtml(PsychicRequest *request, PsychicResponse* resp)
{
PsychicStreamResponse response(resp, "text/html");
response.beginSend();
response.print("<html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
response.print("<style>form{border:3px solid #f1f1f1; max-width: 400px;}input[type=password],input[type=text]{width:100%;padding:12px 20px;margin:8px 0;display:inline-block;border:1px solid #ccc;box-sizing:border-box}button{background-color:#04aa6d;color:#fff;padding:14px 20px;margin:8px 0;border:none;cursor:pointer;width:100%}button:hover{opacity:.8}.container{padding:16px}span.password{float:right;padding-top:16px}@media screen and (max-width:300px){span.psw{display:block;float:none}}</style>");
response.print("<html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
response.print("</head><body><center><h2>NukiHub login</h2><form action=\"/post?page=login\" method=\"post\">");
response.print("<div class=\"container\"><label for=\"username\"><b>Username</b></label><input type=\"text\" placeholder=\"Enter Username\" name=\"username\" required>");
response.print("<label for=\"password\"><b>Password</b></label><input type=\"password\" placeholder=\"Enter Password\" name=\"password\" required>");
response.print("<button type=\"submit\">Login</button><label><input type=\"checkbox\" checked=\"checked\" name=\"remember\"> Remember me</label></div>");
response.print("</form></center></body></html>");
return response.endSend();
}
bool WebCfgServer::processLogin(PsychicRequest *request, PsychicResponse* resp)
{
if(request->hasParam("username") && request->hasParam("password"))
{
const PsychicWebParameter* user = request->getParam("username");
const PsychicWebParameter* pass = request->getParam("password");
if(user->value() != "" && pass->value() != "")
{
if (user->value() == _preferences->getString(preference_cred_user, "") && pass->value() == _preferences->getString(preference_cred_password, ""))
{
char buffer[33];
int i;
int64_t durationLength = 60*60*24*30;
for (i = 0; i < 4; i++) {
sprintf(buffer + (i * 8), "%08lx", (unsigned long int)esp_random());
}
if(!request->hasParam("remember")) {
durationLength = 60*60;
}
resp->setCookie("sessionId", buffer, durationLength, "HttpOnly");
struct timeval time;
gettimeofday(&time, NULL);
int64_t time_us = (int64_t)time.tv_sec * 1000000L + (int64_t)time.tv_usec;
_httpSessions[buffer] = time_us + (durationLength*1000000L);
saveSessions();
return true;
}
}
}
return false;
}
#ifndef NUKI_HUB_UPDATER #ifndef NUKI_HUB_UPDATER
esp_err_t WebCfgServer::sendSettings(PsychicRequest *request, PsychicResponse* resp) esp_err_t WebCfgServer::sendSettings(PsychicRequest *request, PsychicResponse* resp)
{ {
@@ -2788,9 +3055,9 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S
} }
else if(key == "CREDDIGEST") else if(key == "CREDDIGEST")
{ {
if(_preferences->getBool(preference_http_auth_type, false) != (value == "1")) if(_preferences->getInt(preference_http_auth_type, 0) != value.toInt())
{ {
_preferences->putBool(preference_http_auth_type, (value == "1")); _preferences->putInt(preference_http_auth_type, value.toInt());
Log->print(("Setting changed: ")); Log->print(("Setting changed: "));
Log->println(key); Log->println(key);
configChanged = true; configChanged = true;
@@ -3914,8 +4181,7 @@ esp_err_t WebCfgServer::buildCustomNetworkConfigHtml(PsychicRequest *request, Ps
esp_err_t WebCfgServer::buildHtml(PsychicRequest *request, PsychicResponse* resp) esp_err_t WebCfgServer::buildHtml(PsychicRequest *request, PsychicResponse* resp)
{ {
_randomInt = esp_random(); String header = (String)"<script>let intervalId; window.onload = function() { updateInfo(); intervalId = setInterval(updateInfo, 3000); }; function updateInfo() { var request = new XMLHttpRequest(); request.open('GET', '/get?page=status', true); request.onload = () => { const obj = JSON.parse(request.responseText); if (obj.stop == 1) { clearInterval(intervalId); } for (var key of Object.keys(obj)) { if(key=='ota' && document.getElementById(key) !== null) { document.getElementById(key).innerText = \"<a href='/ota'>\" + obj[key] + \"</a>\"; } else if(document.getElementById(key) !== null) { document.getElementById(key).innerText = obj[key]; } } }; request.send(); }</script>";
String header = (String)"<script>let intervalId; window.onload = function() { updateInfo(); intervalId = setInterval(updateInfo, 3000); }; function updateInfo() { var request = new XMLHttpRequest(); request.open('GET', '/get?page=status&token=" + _randomInt + "', true); request.onload = () => { const obj = JSON.parse(request.responseText); if (obj.stop == 1) { clearInterval(intervalId); } for (var key of Object.keys(obj)) { if(key=='ota' && document.getElementById(key) !== null) { document.getElementById(key).innerText = \"<a href='/ota'>\" + obj[key] + \"</a>\"; } else if(document.getElementById(key) !== null) { document.getElementById(key).innerText = obj[key]; } } }; request.send(); }</script>";
PsychicStreamResponse response(resp, "text/html"); PsychicStreamResponse response(resp, "text/html");
response.beginSend(); response.beginSend();
buildHtmlHeader(&response, header); buildHtmlHeader(&response, header);
@@ -4008,6 +4274,10 @@ esp_err_t WebCfgServer::buildHtml(PsychicRequest *request, PsychicResponse* resp
#endif #endif
String rebooturl = "/get?page=reboot&CONFIRMTOKEN=" + _confirmCode; String rebooturl = "/get?page=reboot&CONFIRMTOKEN=" + _confirmCode;
buildNavigationMenuEntry(&response, "Reboot Nuki Hub", rebooturl.c_str()); buildNavigationMenuEntry(&response, "Reboot Nuki Hub", rebooturl.c_str());
if (_preferences->getInt(preference_http_auth_type, 0) == 2)
{
buildNavigationMenuEntry(&response, "Logout", "/get?page=logout");
}
response.print("</ul></body></html>"); response.print("</ul></body></html>");
return response.endSend(); return response.endSend();
} }
@@ -4024,7 +4294,13 @@ esp_err_t WebCfgServer::buildCredHtml(PsychicRequest *request, PsychicResponse*
printInputField(&response, "CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 30, "id=\"inputuser\"", false, true); printInputField(&response, "CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 30, "id=\"inputuser\"", false, true);
printInputField(&response, "CREDPASS", "Password", "*", 30, "id=\"inputpass\"", true, true); printInputField(&response, "CREDPASS", "Password", "*", 30, "id=\"inputpass\"", true, true);
printInputField(&response, "CREDPASSRE", "Retype password", "*", 30, "id=\"inputpass2\"", true); printInputField(&response, "CREDPASSRE", "Retype password", "*", 30, "id=\"inputpass2\"", true);
printCheckBox(&response, "CREDDIGEST", "Use Digest Authentication (more secure)", _preferences->getBool(preference_http_auth_type, false), "");
std::vector<std::pair<String, String>> httpAuthOptions;
httpAuthOptions.push_back(std::make_pair("0", "Basic"));
httpAuthOptions.push_back(std::make_pair("1", "Digest"));
httpAuthOptions.push_back(std::make_pair("2", "Form"));
printDropDown(&response, "CREDDIGEST", "HTTP Authentication type", String(_preferences->getInt(preference_http_auth_type, 0)), httpAuthOptions, "");
response.print("</table>"); response.print("</table>");
response.print("<br><input type=\"submit\" name=\"submit\" value=\"Save\">"); response.print("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
response.print("</form><script>function testcreds() { var input_user = document.getElementById(\"inputuser\").value; var input_pass = document.getElementById(\"inputpass\").value; var input_pass2 = document.getElementById(\"inputpass2\").value; var pattern = /^[ -~]*$/; if(input_user == '#' || input_user == '') { return true; } if (input_pass != input_pass2) { alert('Passwords do not match'); return false;} if(!pattern.test(input_user) || !pattern.test(input_pass)) { alert('Only non unicode characters are allowed in username and password'); return false;} else { return true; } }</script>"); response.print("</form><script>function testcreds() { var input_user = document.getElementById(\"inputuser\").value; var input_pass = document.getElementById(\"inputpass\").value; var input_pass2 = document.getElementById(\"inputpass2\").value; var pattern = /^[ -~]*$/; if(input_user == '#' || input_user == '') { return true; } if (input_pass != input_pass2) { alert('Passwords do not match'); return false;} if(!pattern.test(input_user) || !pattern.test(input_pass)) { alert('Only non unicode characters are allowed in username and password'); return false;} else { return true; } }</script>");
@@ -4964,7 +5240,7 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse*
response.print("\nWeb configurator password: "); response.print("\nWeb configurator password: ");
response.print(_preferences->getString(preference_cred_password, "").length() > 0 ? "***" : "Not set"); response.print(_preferences->getString(preference_cred_password, "").length() > 0 ? "***" : "Not set");
response.print("\nWeb configurator authentication: "); response.print("\nWeb configurator authentication: ");
response.print(_preferences->getBool(preference_http_auth_type, false) ? "Digest" : "Basic"); response.print(_preferences->getInt(preference_http_auth_type, 0) == 0 ? "Basic" : _preferences->getInt(preference_http_auth_type, 0) == 1 ? "Digest" : "Form");
response.print("\nWeb configurator enabled: "); response.print("\nWeb configurator enabled: ");
response.print(_preferences->getBool(preference_webserver_enabled, true) ? "Yes" : "No"); response.print(_preferences->getBool(preference_webserver_enabled, true) ? "Yes" : "No");
response.print("\nHTTP SSL: "); response.print("\nHTTP SSL: ");
@@ -5652,6 +5928,7 @@ esp_err_t WebCfgServer::processUpdate(PsychicRequest *request, PsychicResponse*
if(request->hasParam("beta")) if(request->hasParam("beta"))
{ {
/*
if(request->hasParam("debug")) if(request->hasParam("debug"))
{ {
res = buildConfirmHtml(request, resp, "Rebooting to update Nuki Hub and Nuki Hub updater<br/>Updating to latest DEBUG BETA version", 2, true); res = buildConfirmHtml(request, resp, "Rebooting to update Nuki Hub and Nuki Hub updater<br/>Updating to latest DEBUG BETA version", 2, true);
@@ -5660,13 +5937,15 @@ esp_err_t WebCfgServer::processUpdate(PsychicRequest *request, PsychicResponse*
} }
else else
{ {
*/
res = buildConfirmHtml(request, resp, "Rebooting to update Nuki Hub and Nuki Hub updater<br/>Updating to latest BETA version", 2, true); res = buildConfirmHtml(request, resp, "Rebooting to update Nuki Hub and Nuki Hub updater<br/>Updating to latest BETA version", 2, true);
_preferences->putString(preference_ota_updater_url, GITHUB_BETA_UPDATER_BINARY_URL); _preferences->putString(preference_ota_updater_url, GITHUB_BETA_UPDATER_BINARY_URL);
_preferences->putString(preference_ota_main_url, GITHUB_BETA_RELEASE_BINARY_URL); _preferences->putString(preference_ota_main_url, GITHUB_BETA_RELEASE_BINARY_URL);
} //}
} }
else if(request->hasParam("master")) else if(request->hasParam("master"))
{ {
/*
if(request->hasParam("debug")) if(request->hasParam("debug"))
{ {
res = buildConfirmHtml(request, resp, "Rebooting to update Nuki Hub and Nuki Hub updater<br/>Updating to latest DEBUG DEVELOPMENT version", 2, true); res = buildConfirmHtml(request, resp, "Rebooting to update Nuki Hub and Nuki Hub updater<br/>Updating to latest DEBUG DEVELOPMENT version", 2, true);
@@ -5675,10 +5954,11 @@ esp_err_t WebCfgServer::processUpdate(PsychicRequest *request, PsychicResponse*
} }
else else
{ {
*/
res = buildConfirmHtml(request, resp, "Rebooting to update Nuki Hub and Nuki Hub updater<br/>Updating to latest DEVELOPMENT version", 2, true); res = buildConfirmHtml(request, resp, "Rebooting to update Nuki Hub and Nuki Hub updater<br/>Updating to latest DEVELOPMENT version", 2, true);
_preferences->putString(preference_ota_updater_url, GITHUB_MASTER_UPDATER_BINARY_URL); _preferences->putString(preference_ota_updater_url, GITHUB_MASTER_UPDATER_BINARY_URL);
_preferences->putString(preference_ota_main_url, GITHUB_MASTER_RELEASE_BINARY_URL); _preferences->putString(preference_ota_main_url, GITHUB_MASTER_RELEASE_BINARY_URL);
} //}
} }
#if defined(CONFIG_IDF_TARGET_ESP32S3) #if defined(CONFIG_IDF_TARGET_ESP32S3)
else if(request->hasParam("other")) else if(request->hasParam("other"))
@@ -5690,6 +5970,7 @@ esp_err_t WebCfgServer::processUpdate(PsychicRequest *request, PsychicResponse*
#endif #endif
else else
{ {
/*
if(request->hasParam("debug")) if(request->hasParam("debug"))
{ {
res = buildConfirmHtml(request, resp, "Rebooting to update Nuki Hub and Nuki Hub updater<br/>Updating to latest DEBUG RELEASE version", 2, true); res = buildConfirmHtml(request, resp, "Rebooting to update Nuki Hub and Nuki Hub updater<br/>Updating to latest DEBUG RELEASE version", 2, true);
@@ -5698,10 +5979,11 @@ esp_err_t WebCfgServer::processUpdate(PsychicRequest *request, PsychicResponse*
} }
else else
{ {
*/
res = buildConfirmHtml(request, resp, "Rebooting to update Nuki Hub and Nuki Hub updater<br/>Updating to latest RELEASE version", 2, true); res = buildConfirmHtml(request, resp, "Rebooting to update Nuki Hub and Nuki Hub updater<br/>Updating to latest RELEASE version", 2, true);
_preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL); _preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL);
_preferences->putString(preference_ota_main_url, GITHUB_LATEST_RELEASE_BINARY_URL); _preferences->putString(preference_ota_main_url, GITHUB_LATEST_RELEASE_BINARY_URL);
} //}
} }
waitAndProcess(true, 1000); waitAndProcess(true, 1000);
restartEsp(RestartReason::OTAReboot); restartEsp(RestartReason::OTAReboot);

View File

@@ -101,6 +101,13 @@ private:
String generateConfirmCode(); String generateConfirmCode();
String _confirmCode = "----"; String _confirmCode = "----";
void saveSessions();
void loadSessions();
esp_err_t logoutSession(PsychicRequest *request, PsychicResponse* resp);
bool isAuthenticated(PsychicRequest *request);
bool processLogin(PsychicRequest *request, PsychicResponse* resp);
int doAuthentication(PsychicRequest *request);
esp_err_t buildLoginHtml(PsychicRequest *request, PsychicResponse* resp);
esp_err_t buildSSIDListHtml(PsychicRequest *request, PsychicResponse* resp); esp_err_t buildSSIDListHtml(PsychicRequest *request, PsychicResponse* resp);
esp_err_t buildConfirmHtml(PsychicRequest *request, PsychicResponse* resp, const String &message, uint32_t redirectDelay = 5, bool redirect = false, String redirectTo = "/"); esp_err_t buildConfirmHtml(PsychicRequest *request, PsychicResponse* resp, const String &message, uint32_t redirectDelay = 5, bool redirect = false, String redirectTo = "/");
esp_err_t buildOtaHtml(PsychicRequest *request, PsychicResponse* resp, bool debug = false); esp_err_t buildOtaHtml(PsychicRequest *request, PsychicResponse* resp, bool debug = false);
@@ -128,6 +135,6 @@ private:
bool _allowRestartToPortal = false; bool _allowRestartToPortal = false;
uint8_t _partitionType = 0; uint8_t _partitionType = 0;
size_t _otaContentLen = 0; size_t _otaContentLen = 0;
uint32_t _randomInt = 0;
String _hostname; String _hostname;
JsonDocument _httpSessions;
}; };

View File

@@ -10,6 +10,7 @@
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
#include "hal/wdt_hal.h" #include "hal/wdt_hal.h"
#include "esp_chip_info.h" #include "esp_chip_info.h"
#include "esp_netif_sntp.h"
#ifdef CONFIG_SOC_SPIRAM_SUPPORTED #ifdef CONFIG_SOC_SPIRAM_SUPPORTED
#include "esp_psram.h" #include "esp_psram.h"
#include "FS.h" #include "FS.h"
@@ -29,7 +30,6 @@
#include "RestartReason.h" #include "RestartReason.h"
#include "EspMillis.h" #include "EspMillis.h"
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
#include "esp_netif_sntp.h"
/* /*
#ifdef DEBUG_NUKIHUB #ifdef DEBUG_NUKIHUB
@@ -38,8 +38,6 @@
#endif #endif
*/ */
char log_print_buffer[1024];
NukiNetworkLock* networkLock = nullptr; NukiNetworkLock* networkLock = nullptr;
NukiNetworkOpener* networkOpener = nullptr; NukiNetworkOpener* networkOpener = nullptr;
BleScanner::Scanner* bleScanner = nullptr; BleScanner::Scanner* bleScanner = nullptr;
@@ -71,6 +69,8 @@ int64_t restartTs = 10 * 60 * 1000;
#endif #endif
char log_print_buffer[1024];
PsychicHttpServer* psychicServer = nullptr; PsychicHttpServer* psychicServer = nullptr;
PsychicHttpsServer* psychicSSLServer = nullptr; PsychicHttpsServer* psychicSSLServer = nullptr;
NukiNetwork* network = nullptr; NukiNetwork* network = nullptr;
@@ -97,7 +97,6 @@ RestartReason currentRestartReason = RestartReason::NotApplicable;
TaskHandle_t otaTaskHandle = nullptr; TaskHandle_t otaTaskHandle = nullptr;
TaskHandle_t networkTaskHandle = nullptr; TaskHandle_t networkTaskHandle = nullptr;
#ifndef NUKI_HUB_UPDATER
ssize_t write_fn(void* cookie, const char* buf, ssize_t size) ssize_t write_fn(void* cookie, const char* buf, ssize_t size)
{ {
Log->write((uint8_t *)buf, (size_t)size); Log->write((uint8_t *)buf, (size_t)size);
@@ -157,7 +156,6 @@ void setReroute()
} }
} }
#endif
uint8_t checkPartition() uint8_t checkPartition()
{ {
@@ -181,6 +179,10 @@ uint8_t checkPartition()
} }
} }
void cbSyncTime(struct timeval *tv) {
Log->println("NTP time synched");
}
void networkTask(void *pvParameters) void networkTask(void *pvParameters)
{ {
int64_t networkLoopTs = 0; int64_t networkLoopTs = 0;
@@ -204,13 +206,15 @@ void networkTask(void *pvParameters)
network->update(); network->update();
bool connected = network->isConnected(); bool connected = network->isConnected();
#ifndef NUKI_HUB_UPDATER
if(connected && reroute) if(connected && reroute)
{ {
if(preferences->getBool(preference_update_time, false))
{
esp_netif_sntp_start();
}
reroute = false; reroute = false;
setReroute(); setReroute();
} }
#endif
#ifndef NUKI_HUB_UPDATER #ifndef NUKI_HUB_UPDATER
wifiConnected = network->wifiConnected(); wifiConnected = network->wifiConnected();
@@ -503,10 +507,6 @@ void setupTasks(bool ota)
} }
} }
void cbSyncTime(struct timeval *tv) {
Log->println(("NTP time synched"));
}
void setup() void setup()
{ {
//Set Log level to error for all TAGS //Set Log level to error for all TAGS
@@ -822,11 +822,9 @@ void setup()
} }
#endif #endif
*/ */
}
if(preferences->getBool(preference_update_time, false)) String timeserver = preferences->getString(preference_time_server, "pool.ntp.org");
{ esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(timeserver.c_str());
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(preferences->getString(preference_time_server, "pool.ntp.org").c_str());
config.start = false; config.start = false;
config.server_from_dhcp = true; config.server_from_dhcp = true;
config.renew_servers_after_new_IP = true; config.renew_servers_after_new_IP = true;