diff options
| author | nippy <you@example.com> | 2026-04-18 13:49:56 +0200 |
|---|---|---|
| committer | nippy <you@example.com> | 2026-04-18 13:49:56 +0200 |
| commit | 5d94c0a7d44a2255b81815a52a7056a94a39842d (patch) | |
| tree | 759bdea9645c6a62f9e1e4c001f7d81cccd120d2 /raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Lock/Pam.qml | |
| parent | e79cdf210b267f21a186255ce1a4d50029439d54 (diff) | |
| download | RaveOS-PKGBUILD-5d94c0a7d44a2255b81815a52a7056a94a39842d.tar.gz RaveOS-PKGBUILD-5d94c0a7d44a2255b81815a52a7056a94a39842d.zip | |
update Raveos themes
Diffstat (limited to 'raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Lock/Pam.qml')
| -rw-r--r-- | raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Lock/Pam.qml | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Lock/Pam.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Lock/Pam.qml new file mode 100644 index 0000000..f5e7859 --- /dev/null +++ b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Lock/Pam.qml @@ -0,0 +1,405 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import Quickshell +import Quickshell.Io +import Quickshell.Services.Pam +import qs.Common + +Scope { + id: root + + property bool lockSecured: false + property bool unlockInProgress: false + + readonly property alias passwd: passwd + readonly property alias fprint: fprint + readonly property alias u2f: u2f + property string lockMessage + property string state + property string fprintState + property string u2fState + property bool u2fPending: false + property string buffer + + signal flashMsg + signal unlockRequested + + function resetAuthFlows(): void { + passwd.abort(); + fprint.abort(); + u2f.abort(); + errorRetry.running = false; + u2fErrorRetry.running = false; + u2fPendingTimeout.running = false; + passwdActiveTimeout.running = false; + unlockRequestTimeout.running = false; + root.u2fPending = false; + root.u2fState = ""; + root.unlockInProgress = false; + } + + function recoverFromAuthStall(newState: string): void { + resetAuthFlows(); + root.state = newState; + flashMsg(); + stateReset.restart(); + fprint.checkAvail(); + u2f.checkAvail(); + } + + function completeUnlock(): void { + if (!root.unlockInProgress) { + root.unlockInProgress = true; + passwd.abort(); + fprint.abort(); + u2f.abort(); + errorRetry.running = false; + u2fErrorRetry.running = false; + u2fPendingTimeout.running = false; + root.u2fPending = false; + root.u2fState = ""; + unlockRequestTimeout.restart(); + unlockRequested(); + } + } + + function proceedAfterPrimaryAuth(): void { + if (SettingsData.enableU2f && SettingsData.u2fMode === "and" && u2f.available) { + u2f.startForSecondFactor(); + } else { + completeUnlock(); + } + } + + function cancelU2fPending(): void { + if (!root.u2fPending) + return; + u2f.abort(); + u2fErrorRetry.running = false; + u2fPendingTimeout.running = false; + root.u2fPending = false; + root.u2fState = ""; + fprint.checkAvail(); + } + + FileView { + id: dankshellConfigWatcher + + path: "/etc/pam.d/dankshell" + printErrors: false + } + + FileView { + id: nixosMarker + + path: "/etc/NIXOS" + printErrors: false + } + + FileView { + id: u2fConfigWatcher + + path: "/etc/pam.d/dankshell-u2f" + printErrors: false + } + + // Detects Nix-installed DMS on non-NixOS systems + readonly property bool runningFromNixStore: Quickshell.shellDir.startsWith("/nix/store/") + + PamContext { + id: passwd + + config: dankshellConfigWatcher.loaded ? "dankshell" : "login" + configDirectory: (dankshellConfigWatcher.loaded || nixosMarker.loaded || root.runningFromNixStore) ? "/etc/pam.d" : Quickshell.shellDir + "/assets/pam" + + onMessageChanged: { + if (message.startsWith("The account is locked")) { + root.lockMessage = message; + } else if (root.lockMessage && message.endsWith(" left to unlock)")) { + root.lockMessage += "\n" + message; + } else if (root.lockMessage && message && message.length > 0) { + root.lockMessage = ""; + } + } + + onResponseRequiredChanged: { + if (!responseRequired) + return; + + respond(root.buffer); + } + + onCompleted: res => { + if (res === PamResult.Success) { + if (!root.unlockInProgress) { + fprint.abort(); + root.proceedAfterPrimaryAuth(); + } + return; + } + + unlockRequestTimeout.running = false; + root.unlockInProgress = false; + root.u2fPending = false; + root.u2fState = ""; + u2fPendingTimeout.running = false; + u2f.abort(); + + if (res === PamResult.Error) + root.state = "error"; + else if (res === PamResult.MaxTries) + root.state = "max"; + else if (res === PamResult.Failed) + root.state = "fail"; + + root.flashMsg(); + stateReset.restart(); + } + } + + Connections { + target: passwd + + function onActiveChanged() { + if (passwd.active) { + passwdActiveTimeout.restart(); + } else { + passwdActiveTimeout.running = false; + } + } + } + + PamContext { + id: fprint + + property bool available: SettingsData.lockFingerprintReady + property int tries + property int errorTries + + function checkAvail(): void { + if (!available || !SettingsData.enableFprint || !root.lockSecured) { + abort(); + return; + } + + tries = 0; + errorTries = 0; + start(); + } + + config: "fprint" + configDirectory: Quickshell.shellDir + "/assets/pam" + + onCompleted: res => { + if (!available) + return; + + if (res === PamResult.Success) { + if (!root.unlockInProgress) { + passwd.abort(); + root.proceedAfterPrimaryAuth(); + } + return; + } + + if (res === PamResult.Error) { + root.fprintState = "error"; + errorTries++; + if (errorTries < 5) { + abort(); + errorRetry.restart(); + } + } else if (res === PamResult.MaxTries) { + tries++; + if (tries < SettingsData.maxFprintTries) { + root.fprintState = "fail"; + start(); + } else { + root.fprintState = "max"; + abort(); + } + } + + root.flashMsg(); + fprintStateReset.start(); + } + } + + PamContext { + id: u2f + + property bool available: SettingsData.lockU2fReady + + function checkAvail(): void { + if (!available || !SettingsData.enableU2f || !root.lockSecured) { + abort(); + return; + } + + if (SettingsData.u2fMode === "or") { + start(); + } + } + + function startForSecondFactor(): void { + if (!available || !SettingsData.enableU2f) { + root.completeUnlock(); + return; + } + abort(); + root.u2fPending = true; + root.u2fState = ""; + u2fPendingTimeout.restart(); + start(); + } + + config: u2fConfigWatcher.loaded ? "dankshell-u2f" : "u2f" + configDirectory: u2fConfigWatcher.loaded ? "/etc/pam.d" : Quickshell.shellDir + "/assets/pam" + + onMessageChanged: { + if (message.toLowerCase().includes("touch")) + root.u2fState = "waiting"; + } + + onCompleted: res => { + if (!available || root.unlockInProgress) + return; + + if (res === PamResult.Success) { + root.completeUnlock(); + return; + } + + if (res === PamResult.Error || res === PamResult.MaxTries || res === PamResult.Failed) { + abort(); + + if (root.u2fPending) { + if (root.u2fState === "waiting") { + // AND mode: device was found but auth failed → back to password + root.u2fPending = false; + root.u2fState = ""; + fprint.checkAvail(); + } else { + // AND mode: no device found → keep pending, show "Insert...", retry + root.u2fState = "insert"; + u2fErrorRetry.restart(); + } + } else { + // OR mode: prompt to insert key, silently retry + root.u2fState = "insert"; + u2fErrorRetry.restart(); + } + } + } + } + + Timer { + id: errorRetry + + interval: 800 + onTriggered: fprint.start() + } + + Timer { + id: u2fErrorRetry + + interval: 800 + onTriggered: u2f.start() + } + + Timer { + id: u2fPendingTimeout + + interval: 30000 + onTriggered: root.cancelU2fPending() + } + + Timer { + id: passwdActiveTimeout + + interval: 15000 + onTriggered: { + if (passwd.active) + root.recoverFromAuthStall("error"); + } + } + + Timer { + id: unlockRequestTimeout + + interval: 8000 + onTriggered: { + if (root.unlockInProgress) + root.recoverFromAuthStall("error"); + } + } + + Timer { + id: stateReset + + interval: 4000 + onTriggered: { + if (root.state !== "max") + root.state = ""; + } + } + + Timer { + id: fprintStateReset + + interval: 4000 + onTriggered: { + root.fprintState = ""; + fprint.errorTries = 0; + } + } + + onLockSecuredChanged: { + if (lockSecured) { + SettingsData.refreshAuthAvailability(); + root.state = ""; + root.fprintState = ""; + root.u2fState = ""; + root.u2fPending = false; + root.lockMessage = ""; + root.resetAuthFlows(); + fprint.checkAvail(); + u2f.checkAvail(); + } else { + root.resetAuthFlows(); + } + } + + Connections { + target: SettingsData + + function onEnableFprintChanged(): void { + fprint.checkAvail(); + } + + function onLockFingerprintReadyChanged(): void { + fprint.checkAvail(); + } + + function onEnableU2fChanged(): void { + u2f.checkAvail(); + } + + function onLockU2fReadyChanged(): void { + u2f.checkAvail(); + } + + function onU2fModeChanged(): void { + if (root.lockSecured) { + u2f.abort(); + u2fErrorRetry.running = false; + u2fPendingTimeout.running = false; + unlockRequestTimeout.running = false; + root.u2fPending = false; + root.u2fState = ""; + u2f.checkAvail(); + } + } + } +} |