mirror of
https://github.com/hyprwm/hyprlock.git
synced 2025-02-03 16:29:48 +01:00
auth: fixup prompt and fail substitution (#641)
BREAKING: - Removed $PROMPT variable. Either use $PAMPROMPT or $FPRINTPROMPT. - Removed $FPRINTMESSAGE. Use $FPRINTPROMPT instead. There is also $FPRINTFAIL.
This commit is contained in:
parent
023aff52ad
commit
4f964371cc
7 changed files with 101 additions and 121 deletions
|
@ -40,21 +40,8 @@ bool CAuth::checkWaiting() {
|
|||
return false;
|
||||
}
|
||||
|
||||
std::string CAuth::getInlineFeedback() {
|
||||
std::optional<std::string> 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<std::string> CAuth::getFailText(eAuthImplementations implType) {
|
||||
|
@ -73,6 +60,10 @@ std::optional<std::string> CAuth::getPrompt(eAuthImplementations implType) {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
size_t CAuth::getFailedAttempts() {
|
||||
return m_sCurrentFail.failedAttempts;
|
||||
}
|
||||
|
||||
std::shared_ptr<IAuthImplementation> 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<CTimer> self, void* data) {
|
||||
g_pHyprlock->clearPasswordBuffer();
|
||||
g_pAuth->m_iFailedAttempts++;
|
||||
Debug::log(LOG, "Failed attempts: {}", g_pAuth->m_iFailedAttempts);
|
||||
|
||||
g_pAuth->m_bDisplayFailText = true;
|
||||
|
||||
g_pHyprlock->clearPasswordBuffer();
|
||||
|
||||
g_pHyprlock->enqueueForceUpdateTimers();
|
||||
|
||||
g_pHyprlock->renderAllOutputs();
|
||||
|
@ -103,14 +93,16 @@ static void passwordUnlockCallback(std::shared_ptr<CTimer> 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;
|
||||
}
|
||||
|
|
|
@ -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<std::string> getFailText(eAuthImplementations implType);
|
||||
std::optional<std::string> getPrompt(eAuthImplementations implType);
|
||||
size_t getFailedAttempts();
|
||||
|
||||
std::shared_ptr<IAuthImplementation> 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<std::shared_ptr<IAuthImplementation>> m_vImpls;
|
||||
std::optional<eAuthImplementations> m_eLastActiveImpl = std::nullopt;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CAuth> g_pAuth;
|
||||
|
|
|
@ -143,8 +143,7 @@ bool CFingerprint::createDeviceProxy() {
|
|||
bool isPresent = presentVariant.get<bool>();
|
||||
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<CTimer> self, void* data) {
|
||||
((CFingerprint*)data)->startVerify(true);
|
||||
},
|
||||
this);
|
||||
g_pHyprlock->addTimer(std::chrono::milliseconds(RETRYDELAY), [](std::shared_ptr<CTimer> 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<sdbus::Error> 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();
|
||||
});
|
||||
|
|
|
@ -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<std::mutex> lk(m_sConversationState.inputMutex);
|
||||
|
||||
if (!m_sConversationState.inputRequested)
|
||||
|
|
|
@ -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<std::chrono::system_clock::duration> 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<std::chrono::system_clock::duration> 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,18 +188,6 @@ 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(""));
|
||||
result.allowForceUpdate = true;
|
||||
}
|
||||
|
||||
if (in.contains("$PROMPT")) {
|
||||
const auto PROMPT = g_pAuth->getPrompt(AUTH_IMPL_PAM);
|
||||
replaceInString(in, "$PROMPT", PROMPT.value_or(""));
|
||||
result.allowForceUpdate = true;
|
||||
}
|
||||
|
||||
if (in.contains("$ATTEMPTS")) {
|
||||
replaceAllAttempts(in);
|
||||
result.allowForceUpdate = true;
|
||||
|
@ -212,9 +198,27 @@ IWidget::SFormatResult IWidget::formatString(std::string in) {
|
|||
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("$FAIL")) {
|
||||
const auto FAIL = g_pAuth->getCurrentFailText();
|
||||
replaceInString(in, "$FAIL", FAIL);
|
||||
result.allowForceUpdate = true;
|
||||
}
|
||||
|
||||
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("$FPRINTFAIL")) {
|
||||
const auto FPRINTFAIL = g_pAuth->getFailText(AUTH_IMPL_FINGERPRINT);
|
||||
replaceInString(in, "$FPRINTFAIL", FPRINTFAIL.value_or(""));
|
||||
result.allowForceUpdate = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
placeholder.resourceID =
|
||||
// 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;
|
||||
|
||||
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
|
||||
|
|
|
@ -72,9 +72,6 @@ class CPasswordInputField : public IWidget {
|
|||
|
||||
std::string currentText = "";
|
||||
size_t failedAttempts = 0;
|
||||
bool canGetNewText = true;
|
||||
|
||||
std::string lastAuthFeedback;
|
||||
|
||||
std::vector<std::string> registeredResourceIDs;
|
||||
|
||||
|
|
Loading…
Reference in a new issue