diff --git a/src/auth/Auth.cpp b/src/auth/Auth.cpp index d767084d..c5a8f46a 100644 --- a/src/auth/Auth.cpp +++ b/src/auth/Auth.cpp @@ -40,21 +40,8 @@ bool CAuth::checkWaiting() { return false; } -std::string CAuth::getInlineFeedback() { - std::optional firstFeedback = std::nullopt; - for (const auto& i : m_vImpls) { - const auto FEEDBACK = (m_bDisplayFailText) ? i->getLastFailText() : i->getLastPrompt(); - if (!FEEDBACK.has_value()) - continue; - - if (!firstFeedback.has_value()) - firstFeedback = FEEDBACK; - - if (i->getImplType() == m_eLastActiveImpl) - return FEEDBACK.value(); - } - - return firstFeedback.value_or("Ups, no authentication feedack"); +const std::string& CAuth::getCurrentFailText() { + return m_sCurrentFail.failText; } std::optional CAuth::getFailText(eAuthImplementations implType) { @@ -73,6 +60,10 @@ std::optional CAuth::getPrompt(eAuthImplementations implType) { return std::nullopt; } +size_t CAuth::getFailedAttempts() { + return m_sCurrentFail.failedAttempts; +} + std::shared_ptr CAuth::getImpl(eAuthImplementations implType) { for (const auto& i : m_vImpls) { if (i->getImplType() == implType) @@ -89,11 +80,10 @@ void CAuth::terminate() { } static void passwordFailCallback(std::shared_ptr self, void* data) { + g_pAuth->m_bDisplayFailText = true; + g_pHyprlock->clearPasswordBuffer(); - g_pAuth->m_iFailedAttempts++; - Debug::log(LOG, "Failed attempts: {}", g_pAuth->m_iFailedAttempts); - g_pAuth->m_bDisplayFailText = true; g_pHyprlock->enqueueForceUpdateTimers(); g_pHyprlock->renderAllOutputs(); @@ -103,14 +93,16 @@ static void passwordUnlockCallback(std::shared_ptr self, void* data) { g_pHyprlock->unlock(); } -void CAuth::enqueueFail() { +void CAuth::enqueueFail(const std::string& failText, eAuthImplementations implType) { + m_sCurrentFail.failText = failText; + m_sCurrentFail.failSource = implType; + m_sCurrentFail.failedAttempts++; + + Debug::log(LOG, "Failed attempts: {}", m_sCurrentFail.failedAttempts); + g_pHyprlock->addTimer(std::chrono::milliseconds(0), passwordFailCallback, nullptr); } void CAuth::enqueueUnlock() { g_pHyprlock->addTimer(std::chrono::milliseconds(0), passwordUnlockCallback, nullptr); } - -void CAuth::postActivity(eAuthImplementations implType) { - m_eLastActiveImpl = implType; -} diff --git a/src/auth/Auth.hpp b/src/auth/Auth.hpp index 49a618c0..8a0682f8 100644 --- a/src/auth/Auth.hpp +++ b/src/auth/Auth.hpp @@ -28,34 +28,35 @@ class CAuth { public: CAuth(); - void start(); + void start(); - void submitInput(const std::string& input); - bool checkWaiting(); + void submitInput(const std::string& input); + bool checkWaiting(); - // Used by the PasswordInput field. We are constraint to a single line for the authentication feedback there. - // Based on m_bDisplayFailText, this will return either the fail text or the prompt. - // Based on m_eLastActiveImpl, it will select the implementation. - std::string getInlineFeedback(); + const std::string& getCurrentFailText(); std::optional getFailText(eAuthImplementations implType); std::optional getPrompt(eAuthImplementations implType); + size_t getFailedAttempts(); std::shared_ptr getImpl(eAuthImplementations implType); void terminate(); - // Should only be set via the main thread - bool m_bDisplayFailText = false; - size_t m_iFailedAttempts = 0; + void enqueueUnlock(); + void enqueueFail(const std::string& failText, eAuthImplementations implType); - void enqueueUnlock(); - void enqueueFail(); - void postActivity(eAuthImplementations implType); + // Should only be set via the main thread + bool m_bDisplayFailText = false; private: + struct { + std::string failText = ""; + eAuthImplementations failSource = AUTH_IMPL_PAM; + size_t failedAttempts = 0; + } m_sCurrentFail; + std::vector> m_vImpls; - std::optional m_eLastActiveImpl = std::nullopt; }; inline std::unique_ptr g_pAuth; diff --git a/src/auth/Fingerprint.cpp b/src/auth/Fingerprint.cpp index 7eb5cd6e..5f03c39b 100644 --- a/src/auth/Fingerprint.cpp +++ b/src/auth/Fingerprint.cpp @@ -143,8 +143,7 @@ bool CFingerprint::createDeviceProxy() { bool isPresent = presentVariant.get(); if (!isPresent) return; - m_sPrompt = m_sFingerprintPresent; - m_sFailureReason = ""; + m_sPrompt = m_sFingerprintPresent; g_pHyprlock->enqueueForceUpdateTimers(); } catch (std::out_of_range& e) {} }); @@ -153,7 +152,6 @@ bool CFingerprint::createDeviceProxy() { } void CFingerprint::handleVerifyStatus(const std::string& result, bool done) { - g_pAuth->postActivity(AUTH_IMPL_FINGERPRINT); Debug::log(LOG, "fprint: handling status {}", result); auto matchResult = s_mapStringToTestType[result]; bool authenticated = false; @@ -165,60 +163,47 @@ void CFingerprint::handleVerifyStatus(const std::string& result, bool done) { case MATCH_NO_MATCH: stopVerify(); if (m_sDBUSState.retries >= 3) { - m_sPrompt = ""; m_sFailureReason = "Fingerprint auth disabled (too many failed attempts)"; } else { - done = false; + done = false; static const auto RETRYDELAY = **(Hyprlang::INT* const*)(g_pConfigManager->getValuePtr("auth:fingerprint:retry_delay")); - g_pHyprlock->addTimer( - std::chrono::milliseconds(RETRYDELAY), - [](std::shared_ptr self, void* data) { - ((CFingerprint*)data)->startVerify(true); - }, - this); + g_pHyprlock->addTimer(std::chrono::milliseconds(RETRYDELAY), [](std::shared_ptr self, void* data) { ((CFingerprint*)data)->startVerify(true); }, this); + m_sFailureReason = "Fingerprint did not match"; } break; case MATCH_UNKNOWN_ERROR: stopVerify(); - m_sPrompt = ""; m_sFailureReason = "Fingerprint auth disabled (unknown error)"; break; case MATCH_MATCHED: stopVerify(); - m_sPrompt = ""; - m_sFailureReason = ""; - authenticated = true; + authenticated = true; g_pAuth->enqueueUnlock(); break; case MATCH_RETRY: - retry = true; - m_sPrompt = "Please retry fingerprint scan"; - m_sFailureReason = ""; + retry = true; + m_sPrompt = "Please retry fingerprint scan"; break; case MATCH_SWIPE_TOO_SHORT: - retry = true; - m_sPrompt = "Swipe too short - try again"; - m_sFailureReason = ""; + retry = true; + m_sPrompt = "Swipe too short - try again"; break; case MATCH_FINGER_NOT_CENTERED: - retry = true; - m_sPrompt = "Finger not centered - try again"; - m_sFailureReason = ""; + retry = true; + m_sPrompt = "Finger not centered - try again"; break; case MATCH_REMOVE_AND_RETRY: - retry = true; - m_sPrompt = "Remove your finger and try again"; - m_sFailureReason = ""; + retry = true; + m_sPrompt = "Remove your finger and try again"; break; case MATCH_DISCONNECTED: - m_sPrompt = ""; m_sFailureReason = "Fingerprint device disconnected"; m_sDBUSState.abort = true; break; } if (!authenticated && !retry) - g_pAuth->enqueueFail(); + g_pAuth->enqueueFail(m_sFailureReason, AUTH_IMPL_FINGERPRINT); if (done || m_sDBUSState.abort) m_sDBUSState.done = true; @@ -248,20 +233,16 @@ void CFingerprint::startVerify(bool isRetry) { m_sDBUSState.device->callMethodAsync("VerifyStart").onInterface(DEVICE).withArguments(finger).uponReplyInvoke([this, isRetry](std::optional e) { if (e) { Debug::log(WARN, "fprint: could not start verifying, {}", e->what()); - if (isRetry) { - m_sPrompt = ""; + if (isRetry) m_sFailureReason = "Fingerprint auth disabled (failed to restart)"; - } + } else { Debug::log(LOG, "fprint: started verifying"); if (isRetry) { m_sDBUSState.retries++; - m_sPrompt = "Could not match fingerprint. Try again."; - m_sFailureReason = ""; - } else { - m_sPrompt = m_sFingerprintReady; - m_sFailureReason = ""; - } + m_sPrompt = "Could not match fingerprint. Try again."; + } else + m_sPrompt = m_sFingerprintReady; } g_pHyprlock->enqueueForceUpdateTimers(); }); diff --git a/src/auth/Pam.cpp b/src/auth/Pam.cpp index 2ea4b048..b7ed5bcf 100644 --- a/src/auth/Pam.cpp +++ b/src/auth/Pam.cpp @@ -96,7 +96,7 @@ void CPam::init() { return; if (!AUTHENTICATED) - g_pAuth->enqueueFail(); + g_pAuth->enqueueFail(m_sConversationState.failText, AUTH_IMPL_PAM); else { g_pAuth->enqueueUnlock(); return; @@ -124,7 +124,6 @@ bool CPam::auth() { handle = nullptr; m_sConversationState.waitingForPamAuth = false; - g_pAuth->postActivity(AUTH_IMPL_PAM); if (ret != PAM_SUCCESS) { if (!m_sConversationState.failTextFromPam) @@ -156,7 +155,6 @@ void CPam::waitForInput() { } void CPam::handleInput(const std::string& input) { - g_pAuth->postActivity(AUTH_IMPL_PAM); std::unique_lock lk(m_sConversationState.inputMutex); if (!m_sConversationState.inputRequested) diff --git a/src/renderer/widgets/IWidget.cpp b/src/renderer/widgets/IWidget.cpp index 6f2ed923..405b8027 100644 --- a/src/renderer/widgets/IWidget.cpp +++ b/src/renderer/widgets/IWidget.cpp @@ -76,7 +76,7 @@ int IWidget::roundingForBorderBox(const CBox& borderBox, int roundingConfig, int static void replaceAllAttempts(std::string& str) { - const size_t ATTEMPTS = g_pAuth->m_iFailedAttempts; + const size_t ATTEMPTS = g_pAuth->getFailedAttempts(); const std::string STR = std::to_string(ATTEMPTS); size_t pos = 0; @@ -118,16 +118,14 @@ static void replaceAllLayout(std::string& str) { } } -static bool logMissingTzOnce = true; +static bool logMissingTzOnce = true; static std::chrono::hh_mm_ss getTime() { const std::chrono::time_zone* pCurrentTz = nullptr; try { auto name = std::getenv("TZ"); if (name) pCurrentTz = std::chrono::locate_zone(name); - } catch (std::runtime_error&) { - Debug::log(WARN, "Invalid TZ value. Falling back to current timezone!"); - } + } catch (std::runtime_error&) { Debug::log(WARN, "Invalid TZ value. Falling back to current timezone!"); } if (!pCurrentTz) pCurrentTz = std::chrono::current_zone(); @@ -150,15 +148,15 @@ static std::chrono::hh_mm_ss getTime() { static std::string getTime24h() { const auto HHMMSS = getTime(); - const auto HRS = HHMMSS.hours().count(); - const auto MINS = HHMMSS.minutes().count(); + const auto HRS = HHMMSS.hours().count(); + const auto MINS = HHMMSS.minutes().count(); return (HRS < 10 ? "0" : "") + std::to_string(HRS) + ":" + (MINS < 10 ? "0" : "") + std::to_string(MINS); } static std::string getTime12h() { const auto HHMMSS = getTime(); - const auto HRS = HHMMSS.hours().count(); - const auto MINS = HHMMSS.minutes().count(); + const auto HRS = HHMMSS.hours().count(); + const auto MINS = HHMMSS.minutes().count(); return (HRS == 12 || HRS == 0 ? "12" : (HRS % 12 < 10 ? "0" : "") + std::to_string(HRS % 12)) + ":" + (MINS < 10 ? "0" : "") + std::to_string(MINS) + (HRS < 12 ? " AM" : " PM"); } @@ -190,31 +188,37 @@ IWidget::SFormatResult IWidget::formatString(std::string in) { result.updateEveryMs = result.updateEveryMs != 0 && result.updateEveryMs < 1000 ? result.updateEveryMs : 1000; } - if (in.contains("$FAIL")) { - const auto FAIL = g_pAuth->getFailText(AUTH_IMPL_PAM); - replaceInString(in, "$FAIL", FAIL.value_or("")); + if (in.contains("$ATTEMPTS")) { + replaceAllAttempts(in); result.allowForceUpdate = true; } - if (in.contains("$PROMPT")) { - const auto PROMPT = g_pAuth->getPrompt(AUTH_IMPL_PAM); - replaceInString(in, "$PROMPT", PROMPT.value_or("")); + if (in.contains("$LAYOUT")) { + replaceAllLayout(in); result.allowForceUpdate = true; } - if (in.contains("$ATTEMPTS")) { - replaceAllAttempts(in); + if (in.contains("$FAIL")) { + const auto FAIL = g_pAuth->getCurrentFailText(); + replaceInString(in, "$FAIL", FAIL); result.allowForceUpdate = true; } - if (in.contains("$LAYOUT")) { - replaceAllLayout(in); + if (in.contains("$PAMFAIL")) { + const auto FAIL = g_pAuth->getFailText(AUTH_IMPL_PAM); + replaceInString(in, "$PAMFAIL", FAIL.value_or("")); + result.allowForceUpdate = true; + } + + if (in.contains("$PAMPROMPT")) { + const auto PROMPT = g_pAuth->getPrompt(AUTH_IMPL_PAM); + replaceInString(in, "$PAMPROMPT", PROMPT.value_or("")); result.allowForceUpdate = true; } - if (in.contains("$FPRINTMESSAGE")) { - const auto FPRINTMESSAGE = g_pAuth->getFailText(AUTH_IMPL_FINGERPRINT); - replaceInString(in, "$FPRINTMESSAGE", FPRINTMESSAGE.value_or("")); + if (in.contains("$FPRINTFAIL")) { + const auto FPRINTFAIL = g_pAuth->getFailText(AUTH_IMPL_FINGERPRINT); + replaceInString(in, "$FPRINTFAIL", FPRINTFAIL.value_or("")); result.allowForceUpdate = true; } diff --git a/src/renderer/widgets/PasswordInputField.cpp b/src/renderer/widgets/PasswordInputField.cpp index 84f18384..e08fca7f 100644 --- a/src/renderer/widgets/PasswordInputField.cpp +++ b/src/renderer/widgets/PasswordInputField.cpp @@ -304,32 +304,39 @@ void CPasswordInputField::updatePlaceholder() { return; } - const auto AUTHFEEDBACK = g_pAuth->getInlineFeedback(); - const auto ALLOWCOLORSWAP = outThick == 0 && colorConfig.swapFont; - - if (!ALLOWCOLORSWAP && placeholder.lastAuthFeedback == AUTHFEEDBACK && g_pAuth->m_iFailedAttempts == placeholder.failedAttempts) + // already requested a placeholder for the current fail + if (displayFail && placeholder.failedAttempts == g_pAuth->getFailedAttempts()) return; - placeholder.failedAttempts = g_pAuth->m_iFailedAttempts; - placeholder.lastAuthFeedback = AUTHFEEDBACK; - - placeholder.asset = nullptr; + placeholder.failedAttempts = g_pAuth->getFailedAttempts(); + std::string newText; if (displayFail) { g_pHyprlock->addTimer(std::chrono::milliseconds(configFailTimeoutMs), failTimeoutCallback, nullptr); - const auto FORMATTEDFAIL = formatString(configFailText).formatted; - placeholder.currentText = FORMATTEDFAIL; - } else { - const auto FORMATTEDPLACEHOLDER = formatString(configPlaceholderText).formatted; - placeholder.currentText = FORMATTEDPLACEHOLDER; - } + newText = formatString(configFailText).formatted; + } else + newText = formatString(configPlaceholderText).formatted; + + // if the text is unchanged we don't need to do anything, unless we are swapping font color + const auto ALLOWCOLORSWAP = outThick == 0 && colorConfig.swapFont; + if (!ALLOWCOLORSWAP && newText == placeholder.currentText) + return; - placeholder.resourceID = + const auto NEWRESOURCEID = std::format("placeholder:{}{}{}{}{}{}", placeholder.currentText, (uintptr_t)this, colorState.font.r, colorState.font.g, colorState.font.b, colorState.font.a); + if (placeholder.resourceID == NEWRESOURCEID) + return; + + Debug::log(TRACE, "Updating placeholder text: {}", newText); + placeholder.currentText = newText; + placeholder.asset = nullptr; + placeholder.resourceID = NEWRESOURCEID; + if (std::find(placeholder.registeredResourceIDs.begin(), placeholder.registeredResourceIDs.end(), placeholder.resourceID) != placeholder.registeredResourceIDs.end()) return; + Debug::log(TRACE, "Requesting new placeholder asset: {}", placeholder.resourceID); placeholder.registeredResourceIDs.push_back(placeholder.resourceID); // query diff --git a/src/renderer/widgets/PasswordInputField.hpp b/src/renderer/widgets/PasswordInputField.hpp index 9a2e3731..b9d709f7 100644 --- a/src/renderer/widgets/PasswordInputField.hpp +++ b/src/renderer/widgets/PasswordInputField.hpp @@ -72,9 +72,6 @@ class CPasswordInputField : public IWidget { std::string currentText = ""; size_t failedAttempts = 0; - bool canGetNewText = true; - - std::string lastAuthFeedback; std::vector registeredResourceIDs;