HTTP Form auth

This commit is contained in:
iranl
2025-01-16 22:28:50 +01:00
parent cd30612de0
commit c7c21bff81
8 changed files with 393 additions and 109 deletions

View File

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

View File

@@ -120,4 +120,5 @@ CONFIG_ESP_WIFI_IRAM_OPT=n
CONFIG_ESP_WIFI_RX_IRAM_OPT=n
CONFIG_MBEDTLS_DYNAMIC_BUFFER=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_INT (uint32_t)908
#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_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_battery, 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_communication, false);
@@ -233,7 +234,6 @@ 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);
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_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_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_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
@@ -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_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_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 =
{

View File

@@ -56,8 +56,12 @@ WebCfgServer::WebCfgServer(NukiNetwork* network, Preferences* preferences, bool
str = _preferences->getString(preference_cred_password, "");
const char *pass = str.c_str();
memcpy(&_credPassword, pass, str.length());
}
if (_preferences->getInt(preference_http_auth_type, 0) == 2)
{
loadSessions();
}
}
_confirmCode = generateConfirmCode();
#ifndef NUKI_HUB_UPDATER
@@ -76,41 +80,115 @@ WebCfgServer::WebCfgServer(NukiNetwork* network, Preferences* preferences, bool
#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()); });
_psychicServer->onClose([&](PsychicClient* client) { Log->printf("[http] connection #%u closed from %s\n", client->socket(), client->localIP().toString().c_str()); });
if (request->hasCookie("sessionId")) {
String cookie = request->getCookie("sessionId");
HTTPAuthMethod auth_type = BASIC_AUTH;
if (_preferences->getBool(preference_http_auth_type, false))
if (_httpSessions[cookie].is<JsonVariant>())
{
struct timeval time;
gettimeofday(&time, NULL);
int64_t time_us = (int64_t)time.tv_sec * 1000000L + (int64_t)time.tv_usec;
if (_httpSessions[cookie].as<signed long long>() > time_us)
{
return true;
}
else
{
Log->println("Cookie found, but not valid anymore");
}
}
}
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
{
auth_type = DIGEST_AUTH;
Log->print("No session cookie found");
}
_psychicServer->on("/", HTTP_GET, [&](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.");
}
return buildConfirmHtml(request, resp, "Logging out", 3, true);
}
#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
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
{
return buildWifiConnectHtml(request, resp);
File file = SPIFFS.open("/sessions.json", "r");
if (!file || file.isDirectory()) {
Log->println("sessions.json not found");
}
else
{
deserializeJson(_httpSessions, file);
}
file.close();
}
#endif
});
}
}
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)
{
return sendCss(request, resp);
@@ -129,9 +207,23 @@ void WebCfgServer::initialize()
});
_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 = "";
@@ -148,9 +240,23 @@ void WebCfgServer::initialize()
});
_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 = "";
@@ -169,6 +275,8 @@ void WebCfgServer::initialize()
if(value != _confirmCode)
{
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/");
}
esp_err_t res = buildConfirmHtml(request, resp, "Rebooting...", 2, true);
@@ -191,16 +299,44 @@ void WebCfgServer::initialize()
value = p->value();
}
}
if (value != "status")
int authReq = doAuthentication(request);
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 = "";
if(request->hasParam("CONFIRMTOKEN"))
@@ -218,6 +354,8 @@ void WebCfgServer::initialize()
if(value != _confirmCode)
{
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/");
}
esp_err_t res = buildConfirmHtml(request, resp, "Rebooting...", 2, true);
@@ -250,18 +388,7 @@ void WebCfgServer::initialize()
}
else if (value == "status")
{
if(request->hasParam("token"))
{
const PsychicWebParameter* p2 = request->getParam("token");
if(p2->value().toInt() == _randomInt)
{
return buildStatusHtml(request, resp);
}
}
resp->setCode(200);
resp->setContentType("text/html");
resp->setContent("");
return resp->send();
return buildStatusHtml(request, resp);
}
else if (value == "acclvl")
{
@@ -337,6 +464,8 @@ void WebCfgServer::initialize()
}
if(value != _confirmCode)
{
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/");
}
if(!_allowRestartToPortal)
@@ -376,6 +505,8 @@ void WebCfgServer::initialize()
if(value != _confirmCode)
{
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/");
}
esp_err_t res = buildConfirmHtml(request, resp, "Rebooting to other partition...", 2, true);
@@ -389,36 +520,21 @@ void WebCfgServer::initialize()
#ifndef NUKI_HUB_UPDATER
return processUpdate(request, resp);
#else
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/");
#endif
}
else
{
#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
Log->println("Page not found, loading index");
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/");
}
});
_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 = "";
if(request->hasParam("page"))
{
@@ -429,8 +545,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
if (value == "savecfg")
else if (value == "savecfg")
{
String message = "";
bool restart = processArgs(request, resp, message);
@@ -474,10 +628,8 @@ void WebCfgServer::initialize()
bool restart = processImport(request, resp, message);
return buildConfirmHtml(request, resp, message, 3, true);
}
else
#else
if (1 == 1)
#endif
else
{
#ifndef CONFIG_IDF_TARGET_ESP32H2
if(!_network->isApOpen())
@@ -501,19 +653,44 @@ void WebCfgServer::initialize()
PsychicUploadHandler *updateHandler = new PsychicUploadHandler();
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(auth_type, "Nuki Hub", "You must log in.");
}
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:
return ESP_FAIL;
case 4:
default:
break;
}
return handleOtaUpload(request, filename, index, data, len, last);
});
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;
@@ -546,6 +723,47 @@ void WebCfgServer::initialize()
_psychicServer->on("/uploadota", HTTP_POST, updateHandler);
//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)
@@ -1266,6 +1484,54 @@ void WebCfgServer::printInputField(PsychicStreamResponse *response,
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
esp_err_t WebCfgServer::sendSettings(PsychicRequest *request, PsychicResponse* resp)
{
@@ -2788,9 +3054,9 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S
}
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->println(key);
configChanged = true;
@@ -3914,8 +4180,7 @@ esp_err_t WebCfgServer::buildCustomNetworkConfigHtml(PsychicRequest *request, Ps
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&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>";
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>";
PsychicStreamResponse response(resp, "text/html");
response.beginSend();
buildHtmlHeader(&response, header);
@@ -4008,6 +4273,10 @@ esp_err_t WebCfgServer::buildHtml(PsychicRequest *request, PsychicResponse* resp
#endif
String rebooturl = "/get?page=reboot&CONFIRMTOKEN=" + _confirmCode;
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>");
return response.endSend();
}
@@ -4024,7 +4293,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, "CREDPASS", "Password", "*", 30, "id=\"inputpass\"", true, 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("<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>");
@@ -4964,7 +5239,7 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse*
response.print("\nWeb configurator password: ");
response.print(_preferences->getString(preference_cred_password, "").length() > 0 ? "***" : "Not set");
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(_preferences->getBool(preference_webserver_enabled, true) ? "Yes" : "No");
response.print("\nHTTP SSL: ");

View File

@@ -100,7 +100,14 @@ private:
std::vector<int> _rssiList;
String generateConfirmCode();
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 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);
@@ -128,6 +135,6 @@ private:
bool _allowRestartToPortal = false;
uint8_t _partitionType = 0;
size_t _otaContentLen = 0;
uint32_t _randomInt = 0;
String _hostname;
JsonDocument _httpSessions;
};

View File

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