Bypass MFA

This commit is contained in:
iranl
2025-02-11 22:37:00 +01:00
parent 747688c4cc
commit b2fd9d9349
10 changed files with 254 additions and 46 deletions

View File

@@ -5,7 +5,7 @@
#define NUKI_HUB_VERSION "9.09" #define NUKI_HUB_VERSION "9.09"
#define NUKI_HUB_VERSION_INT (uint32_t)909 #define NUKI_HUB_VERSION_INT (uint32_t)909
#define NUKI_HUB_BUILD "unknownbuildnr" #define NUKI_HUB_BUILD "unknownbuildnr"
#define NUKI_HUB_DATE "2025-02-11" #define NUKI_HUB_DATE "2025-02-13"
#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

@@ -39,6 +39,8 @@ void ImportExport::readSettings()
_totpKey = _preferences->getString(preference_totp_secret, ""); _totpKey = _preferences->getString(preference_totp_secret, "");
_totpEnabled = _totpKey.length() > 0; _totpEnabled = _totpKey.length() > 0;
_bypassKey = _preferences->getString(preference_bypass_secret, "");
_bypassEnabled = _bypassKey.length() > 0;
} }
bool ImportExport::getDuoEnabled() bool ImportExport::getDuoEnabled()
@@ -51,6 +53,11 @@ bool ImportExport::getTOTPEnabled()
return _totpEnabled; return _totpEnabled;
} }
bool ImportExport::getBypassEnabled()
{
return _bypassEnabled;
}
bool ImportExport::getBypassGPIOEnabled() bool ImportExport::getBypassGPIOEnabled()
{ {
return _bypassGPIO; return _bypassGPIO;
@@ -303,6 +310,30 @@ bool ImportExport::checkTOTP(String* totpKey)
return false; return false;
} }
bool ImportExport::checkBypass(String bypass)
{
if(_bypassEnabled)
{
if((pow(_invalidCount2, 5) + _lastCodeCheck2) > espMillis())
{
_lastCodeCheck2 = espMillis();
return false;
}
_lastCodeCheck2 = espMillis();
if(bypass == _bypassKey)
{
_invalidCount2 = 0;
Log->println("Successful Bypass MFA Auth");
return true;
}
_invalidCount2++;
Log->println("Failed Bypass MFA Auth");
}
return false;
}
void ImportExport::exportHttpsJson(JsonDocument &json) void ImportExport::exportHttpsJson(JsonDocument &json)
{ {
if (!SPIFFS.begin(true)) { if (!SPIFFS.begin(true)) {
@@ -442,6 +473,10 @@ void ImportExport::exportNukiHubJson(JsonDocument &json, bool redacted, bool pai
{ {
continue; continue;
} }
if(strcmp(key, preference_bypass_secret) == 0)
{
continue;
}
if(!redacted) if(std::find(redactedPrefs.begin(), redactedPrefs.end(), key) != redactedPrefs.end()) if(!redacted) if(std::find(redactedPrefs.begin(), redactedPrefs.end(), key) != redactedPrefs.end())
{ {
continue; continue;

View File

@@ -16,7 +16,9 @@ public:
int checkDuoApprove(); int checkDuoApprove();
bool startDuoAuth(char* pushType = (char*)""); bool startDuoAuth(char* pushType = (char*)"");
bool getTOTPEnabled(); bool getTOTPEnabled();
bool getBypassEnabled();
bool checkTOTP(String* totpKey); bool checkTOTP(String* totpKey);
bool checkBypass(String bypass);
bool getDuoEnabled(); bool getDuoEnabled();
bool getBypassGPIOEnabled(); bool getBypassGPIOEnabled();
int getBypassGPIOHigh(); int getBypassGPIOHigh();
@@ -27,13 +29,17 @@ public:
JsonDocument _duoSessions; JsonDocument _duoSessions;
JsonDocument _totpSessions; JsonDocument _totpSessions;
JsonDocument _sessionsOpts; JsonDocument _sessionsOpts;
JsonDocument _bypassSessions;
int64_t _lastCodeCheck = 0; int64_t _lastCodeCheck = 0;
int64_t _lastCodeCheck2 = 0;
int _invalidCount = 0; int _invalidCount = 0;
int _invalidCount2 = 0;
private: private:
void saveSessions(); void saveSessions();
Preferences* _preferences; Preferences* _preferences;
struct tm timeinfo; struct tm timeinfo;
bool _totpEnabled = false; bool _totpEnabled = false;
bool _bypassEnabled = false;
bool _duoActiveRequest; bool _duoActiveRequest;
bool _duoEnabled = false; bool _duoEnabled = false;
bool _bypassGPIO = false; bool _bypassGPIO = false;
@@ -48,5 +54,6 @@ private:
String _duoCheckId; String _duoCheckId;
String _duoCheckIP; String _duoCheckIP;
String _totpKey; String _totpKey;
String _bypassKey;
}; };

View File

@@ -390,6 +390,11 @@ bool NukiNetwork::update()
_importExport->_invalidCount--; _importExport->_invalidCount--;
} }
if(_importExport->getBypassEnabled() && _importExport->_invalidCount2 > 0 && (ts - (120000 * _importExport->_invalidCount2)) > _importExport->_lastCodeCheck2)
{
_importExport->_invalidCount2--;
}
if(disableNetwork || !_mqttEnabled || _device->isApOpen()) if(disableNetwork || !_mqttEnabled || _device->isApOpen())
{ {
return false; return false;
@@ -1114,15 +1119,19 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action, "--", true); publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action, "--", true);
return; return;
} }
else if (_importExport->startDuoAuth((char*)"Approve Nuki Hub setting change")) else
{ {
bool duoRes = _importExport->startDuoAuth((char*)"Approve Nuki Hub setting change");
int duoResult = 2; int duoResult = 2;
while (duoResult == 2) if (duoRes)
{ {
duoResult = _importExport->checkDuoApprove(); while (duoResult == 2)
delay(2000); {
esp_task_wdt_reset(); duoResult = _importExport->checkDuoApprove();
delay(2000);
esp_task_wdt_reset();
}
} }
if (duoResult != 1) if (duoResult != 1)

View File

@@ -96,6 +96,7 @@
#define preference_publish_config (char*)"nhPubConfig" #define preference_publish_config (char*)"nhPubConfig"
#define preference_config_from_mqtt (char*)"nhCntrlEnabled" #define preference_config_from_mqtt (char*)"nhCntrlEnabled"
#define preference_totp_secret (char*)"totpsecret" #define preference_totp_secret (char*)"totpsecret"
#define preference_bypass_secret (char*)"bypassecret"
// CHANGE DOES NOT REQUIRE REBOOT TO TAKE EFFECT // CHANGE DOES NOT REQUIRE REBOOT TO TAKE EFFECT
#define preference_find_best_rssi (char*)"nwbestrssi" #define preference_find_best_rssi (char*)"nwbestrssi"
@@ -535,13 +536,13 @@ private:
preference_cred_duo_host, preference_cred_duo_ikey, preference_cred_duo_skey, preference_cred_duo_user, preference_cred_duo_enabled, preference_https_fqdn, preference_bypass_proxy, preference_cred_duo_host, preference_cred_duo_ikey, preference_cred_duo_skey, preference_cred_duo_user, preference_cred_duo_enabled, preference_https_fqdn, preference_bypass_proxy,
preference_cred_session_lifetime, preference_cred_session_lifetime_remember, preference_cred_session_lifetime_duo, preference_cred_session_lifetime_duo_remember, preference_cred_session_lifetime, preference_cred_session_lifetime_remember, preference_cred_session_lifetime_duo, preference_cred_session_lifetime_duo_remember,
preference_cred_duo_approval, preference_cred_bypass_boot_btn_enabled, preference_cred_bypass_gpio_high, preference_cred_bypass_gpio_low, preference_publish_config, preference_cred_duo_approval, preference_cred_bypass_boot_btn_enabled, preference_cred_bypass_gpio_high, preference_cred_bypass_gpio_low, preference_publish_config,
preference_config_from_mqtt, preference_totp_secret, preference_cred_session_lifetime_totp, preference_cred_session_lifetime_totp_remember preference_config_from_mqtt, preference_totp_secret, preference_cred_session_lifetime_totp, preference_cred_session_lifetime_totp_remember, preference_bypass_secret
}; };
std::vector<char*> _redact = std::vector<char*> _redact =
{ {
preference_mqtt_user, preference_mqtt_password, preference_cred_user, preference_cred_password, preference_nuki_id_lock, preference_nuki_id_opener, preference_wifi_pass, preference_mqtt_user, preference_mqtt_password, preference_cred_user, preference_cred_password, preference_nuki_id_lock, preference_nuki_id_opener, preference_wifi_pass,
preference_lock_gemini_pin, preference_cred_duo_host, preference_cred_duo_ikey, preference_cred_duo_skey, preference_cred_duo_user, preference_bypass_proxy, preference_lock_gemini_pin, preference_cred_duo_host, preference_cred_duo_ikey, preference_cred_duo_skey, preference_cred_duo_user, preference_bypass_proxy,
preference_totp_secret preference_totp_secret, preference_bypass_secret
}; };
std::vector<char*> _boolPrefs = std::vector<char*> _boolPrefs =
{ {

View File

@@ -102,18 +102,22 @@ bool WebCfgServer::isAuthenticated(PsychicRequest *request, int type)
{ {
cookieKey = "totpId"; cookieKey = "totpId";
} }
else if (type == 3)
{
cookieKey = "bypassId";
}
if (request->hasCookie(cookieKey.c_str())) if (request->hasCookie(cookieKey.c_str()))
{ {
String cookie = request->getCookie(cookieKey.c_str()); String cookie = request->getCookie(cookieKey.c_str());
if ((type == 0 && _httpSessions[cookie].is<JsonVariant>()) || (type == 1 && _importExport->_duoSessions[cookie].is<JsonVariant>()) || (type == 2 && _importExport->_totpSessions[cookie].is<JsonVariant>())) if ((type == 0 && _httpSessions[cookie].is<JsonVariant>()) || (type == 1 && _importExport->_duoSessions[cookie].is<JsonVariant>()) || (type == 2 && _importExport->_totpSessions[cookie].is<JsonVariant>()) || (type == 3 && _importExport->_bypassSessions[cookie].is<JsonVariant>()))
{ {
struct timeval time; struct timeval time;
gettimeofday(&time, NULL); gettimeofday(&time, NULL);
int64_t time_us = (int64_t)time.tv_sec * 1000000L + (int64_t)time.tv_usec; int64_t time_us = (int64_t)time.tv_sec * 1000000L + (int64_t)time.tv_usec;
if ((type == 0 && _httpSessions[cookie].as<signed long long>() > time_us) || (type == 1 && _importExport->_duoSessions[cookie].as<signed long long>() > time_us) || (type == 2 && _importExport->_totpSessions[cookie].as<signed long long>() > time_us)) if ((type == 0 && _httpSessions[cookie].as<signed long long>() > time_us) || (type == 1 && _importExport->_duoSessions[cookie].as<signed long long>() > time_us) || (type == 2 && _importExport->_totpSessions[cookie].as<signed long long>() > time_us) || (type == 3 && _importExport->_bypassSessions[cookie].as<signed long long>() > time_us))
{ {
return true; return true;
} }
@@ -194,6 +198,23 @@ esp_err_t WebCfgServer::logoutSession(PsychicRequest *request, PsychicResponse*
} }
} }
if (_importExport->getBypassEnabled())
{
if (!_isSSL)
{
resp->setCookie("bypassId", "", 0, "HttpOnly");
}
else
{
resp->setCookie("bypassId", "", 0, "Secure; HttpOnly");
}
if (request->hasCookie("bypassId")) {
String cookie2 = request->getCookie("bypassId");
_importExport->_bypassSessions.remove(cookie2);
}
}
return buildConfirmHtml(request, resp, "Logging out", 3, true); return buildConfirmHtml(request, resp, "Logging out", 3, true);
} }
@@ -369,6 +390,11 @@ int WebCfgServer::doAuthentication(PsychicRequest *request)
_importExport->_sessionsOpts[request->client()->localIP().toString() + "totp"] = true; _importExport->_sessionsOpts[request->client()->localIP().toString() + "totp"] = true;
return 4; return 4;
} }
else if(!timeSynced && _importExport->getBypassEnabled() && isAuthenticated(request, 3))
{
_importExport->_sessionsOpts[request->client()->localIP().toString() + "totp"] = false;
return 4;
}
Log->println("Authentication Failed"); Log->println("Authentication Failed");
@@ -507,7 +533,7 @@ void WebCfgServer::initialize()
} }
int authReq = doAuthentication(request); int authReq = doAuthentication(request);
if (value != "status" && value != "login" && value != "duocheck") if (value != "status" && value != "login" && value != "duocheck" && value != "bypass")
{ {
switch (authReq) switch (authReq)
{ {
@@ -559,6 +585,15 @@ void WebCfgServer::initialize()
{ {
return buildTOTPHtml(request, resp, 0); return buildTOTPHtml(request, resp, 0);
} }
else if (value == "bypass")
{
return buildBypassHtml(request, resp);
}
else if (value == "newbypass" && _newBypass)
{
_newBypass = false;
return buildConfirmHtml(request, resp, "Logged in using Bypass. New bypass: " + _preferences->getString(preference_bypass_secret, ""), 3, false);
}
else if (value == "logout") else if (value == "logout")
{ {
return logoutSession(request, resp); return logoutSession(request, resp);
@@ -816,7 +851,7 @@ void WebCfgServer::initialize()
} }
} }
if (value != "login" && value != "totp") if (value != "login" && value != "totp" && value != "bypass")
{ {
int authReq = doAuthentication(request); int authReq = doAuthentication(request);
@@ -866,6 +901,11 @@ void WebCfgServer::initialize()
} }
} }
} }
else if(!timeSynced && _importExport->getBypassEnabled() && isAuthenticated(request, 3))
{
_importExport->_sessionsOpts[request->client()->localIP().toString() + "approve"] = false;
approved = true;
}
if (!approved) if (!approved)
{ {
@@ -926,6 +966,23 @@ void WebCfgServer::initialize()
return resp->redirect("/get?page=totp"); return resp->redirect("/get?page=totp");
} }
} }
else if (value == "bypass")
{
bool loggedIn = processBypass(request, resp);
if (loggedIn)
{
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
_newBypass = true;
return resp->redirect("/get?page=newbypass");
}
else
{
resp->setCode(302);
resp->addHeader("Cache-Control", "no-cache");
return resp->redirect("/");
}
}
#ifndef NUKI_HUB_UPDATER #ifndef NUKI_HUB_UPDATER
else if (value == "savecfg") else if (value == "savecfg")
{ {
@@ -1882,12 +1939,12 @@ esp_err_t WebCfgServer::buildTOTPHtml(PsychicRequest *request, PsychicResponse*
{ {
if (!timeSynced) if (!timeSynced)
{ {
return buildConfirmHtml(request, resp, "NTP time not synced yet, TOTP not available, please wait for NTP to sync", 3, true); return buildConfirmHtml(request, resp, "NTP time not synced yet, TOTP not available, please wait for NTP to sync or use <a href=\"/get?page=bypass\">one-time bypass</a>", 3, true);
} }
if((pow(_importExport->_invalidCount, 5) + _importExport->_lastCodeCheck) > espMillis()) if((pow(_importExport->_invalidCount, 5) + _importExport->_lastCodeCheck) > espMillis())
{ {
return buildConfirmHtml(request, resp, "Too many invalid TOTP tries, please wait before retrying", 3, true); return buildConfirmHtml(request, resp, "Too many invalid TOTP tries, please wait before retrying or use <a href=\"/get?page=bypass\">one-time bypass</a>", 3, true);
} }
PsychicStreamResponse response(resp, "text/html"); PsychicStreamResponse response(resp, "text/html");
@@ -1943,6 +2000,32 @@ esp_err_t WebCfgServer::buildTOTPHtml(PsychicRequest *request, PsychicResponse*
return response.endSend(); return response.endSend();
} }
esp_err_t WebCfgServer::buildBypassHtml(PsychicRequest *request, PsychicResponse* resp)
{
if (timeSynced)
{
return buildConfirmHtml(request, resp, "One-time bypass is only available if NTP time is not synced</a>", 3, true);
}
if((pow(_importExport->_invalidCount2, 5) + _importExport->_lastCodeCheck2) > espMillis())
{
return buildConfirmHtml(request, resp, "Too many invalid bypass tries, please wait before retrying", 3, true);
}
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("</head><body><center><h2>Nuki Hub One-time Bypass</h2>");
response.print("<form action=\"/post?page=bypass\" method=\"post\">");
response.print("<div class=\"container\">");
response.print("<label for=\"bypass\"><b>Bypass code</b></label><input type=\"text\" placeholder=\"Enter bypass code\" name=\"bypass\">");
response.print("<button type=\"submit\" ");
response.print(">Login</button></div>");
response.print("</form></center></body></html>");
return response.endSend();
}
esp_err_t WebCfgServer::buildDuoCheckHtml(PsychicRequest *request, PsychicResponse* resp) esp_err_t WebCfgServer::buildDuoCheckHtml(PsychicRequest *request, PsychicResponse* resp)
{ {
char valueStr[2]; char valueStr[2];
@@ -1986,7 +2069,7 @@ esp_err_t WebCfgServer::buildDuoHtml(PsychicRequest *request, PsychicResponse* r
{ {
if (!timeSynced) if (!timeSynced)
{ {
return buildConfirmHtml(request, resp, "NTP time not synced yet, Duo not available, please wait for NTP to sync", 3, true); return buildConfirmHtml(request, resp, "NTP time not synced yet, Duo not available, please wait for NTP to sync or use <a href=\"/get?page=bypass\">one-time bypass</a>", 3, true);
} }
String duoText; String duoText;
@@ -2130,6 +2213,52 @@ bool WebCfgServer::processLogin(PsychicRequest *request, PsychicResponse* resp)
return false; return false;
} }
bool WebCfgServer::processBypass(PsychicRequest *request, PsychicResponse* resp)
{
if(!timeSynced && request->hasParam("bypass"))
{
const PsychicWebParameter* pass = request->getParam("bypass");
if(pass->value() != "")
{
String bypass = pass->value();
if (_importExport->checkBypass(bypass))
{
char buffer[33];
int i;
for (i = 0; i < 4; i++) {
sprintf(buffer + (i * 8), "%08lx", (unsigned long int)esp_random());
}
if (!_isSSL)
{
resp->setCookie("bypassId", buffer, 3600, "HttpOnly");
}
else
{
resp->setCookie("bypassId", buffer, 3600, "Secure; HttpOnly");
}
struct timeval time;
gettimeofday(&time, NULL);
int64_t time_us = (int64_t)time.tv_sec * 1000000L + (int64_t)time.tv_usec;
_importExport->_bypassSessions[buffer] = time_us + ((int64_t)3600*1000000L);
char randomstr2[33];
randomSeed(analogRead(0));
char chars[] = {'1', '2', '3','4', '5', '6','7', '8', '9', '0', 'A', 'B', 'C', 'D','E', 'F', 'G','H', 'I', 'J','K', 'L', 'M', 'N', 'O','P', 'Q','R', 'S', 'T','U', 'V', 'W','X', 'Y', 'Z'};
for(int i = 0;i < 32; i++){
randomstr2[i] = chars[random(36)];
}
randomstr2[32] = '\0';
_preferences->putString(preference_bypass_secret, randomstr2);
return true;
}
}
}
return false;
}
bool WebCfgServer::processTOTP(PsychicRequest *request, PsychicResponse* resp) bool WebCfgServer::processTOTP(PsychicRequest *request, PsychicResponse* resp)
{ {
if(timeSynced && request->hasParam("totpkey")) if(timeSynced && request->hasParam("totpkey"))
@@ -4151,6 +4280,19 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S
} }
} }
} }
else if(key == "CREDBYPASS")
{
if(value != "*")
{
if(_preferences->getString(preference_bypass_secret, "") != value)
{
_preferences->putString(preference_bypass_secret, value);
Log->print("Setting changed: ");
Log->println(key);
configChanged = true;
}
}
}
else if(key == "NUKIPIN" && _nuki != nullptr) else if(key == "NUKIPIN" && _nuki != nullptr)
{ {
if(value == "#") if(value == "#")
@@ -4708,6 +4850,13 @@ esp_err_t WebCfgServer::buildCredHtml(PsychicRequest *request, PsychicResponse*
randomstr[i] = chars[random(32)]; randomstr[i] = chars[random(32)];
} }
randomstr[16] = '\0'; randomstr[16] = '\0';
char randomstr2[33];
randomSeed(analogRead(0));
char chars2[] = {'1', '2', '3','4', '5', '6','7', '8', '9', '0', 'A', 'B', 'C', 'D','E', 'F', 'G','H', 'I', 'J','K', 'L', 'M', 'N', 'O','P', 'Q','R', 'S', 'T','U', 'V', 'W','X', 'Y', 'Z'};
for(int i = 0;i < 32; i++){
randomstr2[i] = chars2[random(36)];
}
randomstr2[32] = '\0';
PsychicStreamResponse response(resp, "text/html"); PsychicStreamResponse response(resp, "text/html");
response.beginSend(); response.beginSend();
@@ -4738,6 +4887,10 @@ esp_err_t WebCfgServer::buildCredHtml(PsychicRequest *request, PsychicResponse*
response.print("<tr id=\"totpgentr\" ><td><input type=\"button\" id=\"totpgen\" onclick=\"document.getElementsByName('CREDTOTP')[0].type='text'; document.getElementsByName('CREDTOTP')[0].value='"); response.print("<tr id=\"totpgentr\" ><td><input type=\"button\" id=\"totpgen\" onclick=\"document.getElementsByName('CREDTOTP')[0].type='text'; document.getElementsByName('CREDTOTP')[0].value='");
response.print(randomstr); response.print(randomstr);
response.print("'; document.getElementById('totpgentr').style.display='none';\" value=\"Generate new TOTP key\"></td></tr>"); response.print("'; document.getElementById('totpgentr').style.display='none';\" value=\"Generate new TOTP key\"></td></tr>");
printInputField(&response, "CREDBYPASS", "One-time MFA Bypass", "*", 32, "", true, false);
response.print("<tr id=\"bypassgentr\" ><td><input type=\"button\" id=\"bypassgen\" onclick=\"document.getElementsByName('CREDBYPASS')[0].type='text'; document.getElementsByName('CREDBYPASS')[0].value='");
response.print(randomstr2);
response.print("'; document.getElementById('bypassgentr').style.display='none';\" value=\"Generate new Bypass\"></td></tr>");
printInputField(&response, "CREDLFTM", "Session validity (in seconds)", _preferences->getInt(preference_cred_session_lifetime, 3600), 12, ""); printInputField(&response, "CREDLFTM", "Session validity (in seconds)", _preferences->getInt(preference_cred_session_lifetime, 3600), 12, "");
printInputField(&response, "CREDLFTMRMBR", "Session validity remember (in hours)", _preferences->getInt(preference_cred_session_lifetime_remember, 720), 12, ""); printInputField(&response, "CREDLFTMRMBR", "Session validity remember (in hours)", _preferences->getInt(preference_cred_session_lifetime_remember, 720), 12, "");
printInputField(&response, "CREDDUOLFTM", "Duo Session validity (in seconds)", _preferences->getInt(preference_cred_session_lifetime_duo, 3600), 12, ""); printInputField(&response, "CREDDUOLFTM", "Duo Session validity (in seconds)", _preferences->getInt(preference_cred_session_lifetime_duo, 3600), 12, "");

View File

@@ -111,9 +111,11 @@ private:
bool isAuthenticated(PsychicRequest *request, int type = 0); bool isAuthenticated(PsychicRequest *request, int type = 0);
bool processLogin(PsychicRequest *request, PsychicResponse* resp); bool processLogin(PsychicRequest *request, PsychicResponse* resp);
bool processTOTP(PsychicRequest *request, PsychicResponse* resp); bool processTOTP(PsychicRequest *request, PsychicResponse* resp);
bool processBypass(PsychicRequest *request, PsychicResponse* resp);
int doAuthentication(PsychicRequest *request); int doAuthentication(PsychicRequest *request);
esp_err_t buildCoredumpHtml(PsychicRequest *request, PsychicResponse* resp); esp_err_t buildCoredumpHtml(PsychicRequest *request, PsychicResponse* resp);
esp_err_t buildLoginHtml(PsychicRequest *request, PsychicResponse* resp); esp_err_t buildLoginHtml(PsychicRequest *request, PsychicResponse* resp);
esp_err_t buildBypassHtml(PsychicRequest *request, PsychicResponse* resp);
esp_err_t buildTOTPHtml(PsychicRequest *request, PsychicResponse* resp, int type); esp_err_t buildTOTPHtml(PsychicRequest *request, PsychicResponse* resp, int type);
esp_err_t buildDuoHtml(PsychicRequest *request, PsychicResponse* resp, int type); esp_err_t buildDuoHtml(PsychicRequest *request, PsychicResponse* resp, int type);
esp_err_t buildDuoCheckHtml(PsychicRequest *request, PsychicResponse* resp); esp_err_t buildDuoCheckHtml(PsychicRequest *request, PsychicResponse* resp);
@@ -150,6 +152,7 @@ private:
JsonDocument _httpSessions; JsonDocument _httpSessions;
bool _duoEnabled = false; bool _duoEnabled = false;
bool _bypassGPIO = false; bool _bypassGPIO = false;
bool _newBypass = false;
int _bypassGPIOHigh = -1; int _bypassGPIOHigh = -1;
int _bypassGPIOLow = -1; int _bypassGPIOLow = -1;
}; };