From 3938e60ce44cdce2af494daf531f49fea802f3ef Mon Sep 17 00:00:00 2001 From: purian23 Date: Fri, 17 Jul 2026 17:27:45 -0400 Subject: [PATCH] refactor(auth): relay on distro system auth over DMS managed sessions - Gate greeter external-auth status fprint availability only on confirmed setups - Reload dankshell-U2F/U2F-Key watchers live Related: #2874 Port 1.5 --- core/internal/qmlchecks/greeter_auth_test.go | 25 ++++++++++++++++ quickshell/Common/settings/Processes.qml | 8 ++++- quickshell/Modules/Greetd/GreeterContent.qml | 6 ++-- quickshell/Modules/Lock/Pam.qml | 4 +++ quickshell/Modules/Settings/GreeterTab.qml | 30 +++++++++---------- quickshell/Modules/Settings/LockScreenTab.qml | 24 +++++++-------- .../translations/settings_search_index.json | 16 +++++----- 7 files changed, 75 insertions(+), 38 deletions(-) create mode 100644 core/internal/qmlchecks/greeter_auth_test.go diff --git a/core/internal/qmlchecks/greeter_auth_test.go b/core/internal/qmlchecks/greeter_auth_test.go new file mode 100644 index 000000000..98047a9ea --- /dev/null +++ b/core/internal/qmlchecks/greeter_auth_test.go @@ -0,0 +1,25 @@ +package qmlchecks + +import ( + "os" + "strings" + "testing" +) + +func TestGreeterExternalAuthStatusUsesEffectiveFingerprintAvailability(t *testing.T) { + data, err := os.ReadFile("../../../quickshell/Modules/Greetd/GreeterContent.qml") + if err != nil { + t.Fatalf("read greeter QML: %v", err) + } + + content := string(data) + for _, required := range []string{ + "readonly property bool greeterPamHasExternalAuth: greeterPamHasFprint || greeterPamHasU2f", + "if (greeterPamHasFprint && greeterPamHasU2f)", + "if (greeterPamHasFprint)", + } { + if !strings.Contains(content, required) { + t.Fatalf("greeter external-auth status must contain %q", required) + } + } +} diff --git a/quickshell/Common/settings/Processes.qml b/quickshell/Common/settings/Processes.qml index 732d42db1..bdc9fd9ec 100644 --- a/quickshell/Common/settings/Processes.qml +++ b/quickshell/Common/settings/Processes.qml @@ -247,6 +247,10 @@ Singleton { readonly property var _pamProbeCommand: ["sh", "-c", "for module in pam_fprintd.so pam_u2f.so; do found=false; for dir in /usr/lib64/security /usr/lib/security /lib/security /lib/x86_64-linux-gnu/security /usr/lib/x86_64-linux-gnu/security /usr/lib/aarch64-linux-gnu/security /run/current-system/sw/lib/security; do if [ -f \"$dir/$module\" ]; then found=true; break; fi; done; printf '%s:%s\\n' \"$module\" \"$found\"; done"] function detectAuthCapabilities() { + // FileView cannot watch paths that do not exist yet, so reload the U2F PAM + dankshellU2fPamWatcher.reload(); + u2fKeysWatcher.reload(); + if (forcedFprintAvailable === null) { fingerprintProbeFinalized = false; Proc.runCommand("fprint-probe", _fprintProbeCommand, (output, exitCode) => { @@ -600,7 +604,7 @@ Singleton { let details = out; if (err !== "") details = details !== "" ? details + "\n\nstderr:\n" + err : "stderr:\n" + err; - ToastService.showInfo(I18n.tr("Authentication changes applied."), details, "", "auth-sync"); + ToastService.showInfo(I18n.tr("Authentication changes applied"), details, "", "auth-sync"); root.detectAuthCapabilities(); root.finishAuthApply(); return; @@ -723,6 +727,7 @@ Singleton { FileView { id: dankshellU2fPamWatcher path: "/etc/pam.d/dankshell-u2f" + watchChanges: true printErrors: false onLoaded: root.dankshellU2fPamText = text() onLoadFailed: root.dankshellU2fPamText = "" @@ -737,6 +742,7 @@ Singleton { FileView { id: u2fKeysWatcher path: root.u2fKeysPath + watchChanges: true printErrors: false onLoaded: root.u2fKeysText = text() onLoadFailed: root.u2fKeysText = "" diff --git a/quickshell/Modules/Greetd/GreeterContent.qml b/quickshell/Modules/Greetd/GreeterContent.qml index 87973289f..81566fd51 100644 --- a/quickshell/Modules/Greetd/GreeterContent.qml +++ b/quickshell/Modules/Greetd/GreeterContent.qml @@ -73,14 +73,14 @@ Item { readonly property bool greeterPamHasFprint: greeterPamStackHasFprint && (!fprintdProbeComplete || fprintdHasDevice) readonly property bool greeterPamHasU2f: greeterPamStackHasModule("pam_u2f") readonly property bool greeterExternalAuthAvailable: (greeterPamHasFprint && GreetdSettings.greeterEnableFprint) || (greeterPamHasU2f && GreetdSettings.greeterEnableU2f) - readonly property bool greeterPamHasExternalAuth: greeterPamStackHasFprint || greeterPamHasU2f + readonly property bool greeterPamHasExternalAuth: greeterPamHasFprint || greeterPamHasU2f readonly property bool externalAuthInProgress: awaitingExternalAuth || (Greetd.state !== GreetdState.Inactive && passwordSubmitRequested && greeterPamHasExternalAuth && !pendingPasswordResponse) readonly property string externalAuthStatusMessage: { if (!externalAuthInProgress) return ""; - if (greeterPamStackHasFprint && greeterPamHasU2f) + if (greeterPamHasFprint && greeterPamHasU2f) return I18n.tr("Awaiting fingerprint or security key authentication"); - if (greeterPamStackHasFprint) + if (greeterPamHasFprint) return I18n.tr("Awaiting fingerprint authentication"); return I18n.tr("Awaiting security key authentication"); } diff --git a/quickshell/Modules/Lock/Pam.qml b/quickshell/Modules/Lock/Pam.qml index 8938a5d81..84f8f096b 100644 --- a/quickshell/Modules/Lock/Pam.qml +++ b/quickshell/Modules/Lock/Pam.qml @@ -113,6 +113,7 @@ Scope { id: u2fConfigWatcher path: "/etc/pam.d/dankshell-u2f" + watchChanges: true printErrors: false } @@ -488,6 +489,9 @@ Scope { root.resetAuthFlows(); if (!SettingsData.lockPamExternallyManaged && !dankshellConfigWatcher.loaded && !userPamWatcher.loaded) ensureUserPamConfig(); + // FileView cannot watch a path that does not exist yet; re-read so a + // dedicated service created after startup is used on the next lock. + u2fConfigWatcher.reload(); fprint.checkAvail(); u2f.checkAvail(); } diff --git a/quickshell/Modules/Settings/GreeterTab.qml b/quickshell/Modules/Settings/GreeterTab.qml index b3f22156b..397f40834 100644 --- a/quickshell/Modules/Settings/GreeterTab.qml +++ b/quickshell/Modules/Settings/GreeterTab.qml @@ -19,39 +19,39 @@ Item { function greeterFingerprintDescription() { if (SettingsData.greeterPamExternallyManaged) - return I18n.tr("Managed by the primary PAM source.", "factor managed by PAM source status"); + return I18n.tr("Managed by the primary PAM source", "factor managed by PAM source status"); if (SettingsData.greeterFingerprintSource === "pam") return I18n.tr("PAM already provides fingerprint auth. Enable this to show it at login.", "greeter fingerprint login setting"); switch (SettingsData.greeterFingerprintReason) { case "ready": - return I18n.tr("Authentication changes apply automatically.", "greeter auth setting description"); + return I18n.tr("Authentication changes apply automatically", "greeter auth setting description"); case "missing_enrollment": return I18n.tr("Fingerprint reader detected, but no prints are enrolled yet. You can enable this now and run Sync later.", "greeter fingerprint login setting"); case "missing_reader": - return I18n.tr("No fingerprint reader detected.", "fingerprint setting status"); + return I18n.tr("No fingerprint reader detected", "fingerprint setting status"); case "missing_pam_support": return I18n.tr("Not available — install fprintd and pam_fprintd, or configure greetd PAM.", "greeter fingerprint login setting"); default: - return I18n.tr("Fingerprint availability could not be confirmed.", "fingerprint setting status"); + return I18n.tr("Fingerprint availability could not be confirmed", "fingerprint setting status"); } } function greeterU2fDescription() { if (SettingsData.greeterPamExternallyManaged) - return I18n.tr("Managed by the primary PAM source.", "factor managed by PAM source status"); + return I18n.tr("Managed by the primary PAM source", "factor managed by PAM source status"); if (SettingsData.greeterU2fSource === "pam") return I18n.tr("PAM already provides security-key auth. Enable this to show it at login.", "greeter security key login setting"); switch (SettingsData.greeterU2fReason) { case "ready": - return I18n.tr("Authentication changes apply automatically.", "greeter auth setting description"); + return I18n.tr("Authentication changes apply automatically", "greeter auth setting description"); case "missing_key_registration": return I18n.tr("Security-key support was detected, but no registered key was found yet. You can enable this now and register one later.", "security key setting status"); case "missing_pam_support": return I18n.tr("Not available — install or configure pam_u2f, or configure greetd PAM.", "greeter security key login setting"); default: - return I18n.tr("Security-key availability could not be confirmed.", "security key setting status"); + return I18n.tr("Security-key availability could not be confirmed", "security key setting status"); } } @@ -400,7 +400,7 @@ Item { settingKey: "greeterStatus" StyledText { - text: I18n.tr("Sync applies your theme and settings to the login screen. Other users should run dms greeter sync --profile instead of a full sync. Authentication changes apply automatically.") + text: I18n.tr("Sync applies your theme and settings to the login screen. Shared users should run dms greeter sync --profile instead of a primary user sync.") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText width: parent.width @@ -474,7 +474,7 @@ Item { settingKey: "greeterAuth" StyledText { - text: I18n.tr("Enable fingerprint or security key for DMS Greeter. Authentication changes apply automatically.") + text: I18n.tr("Enable fingerprint or security key for DMS Greeter") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText width: parent.width @@ -486,7 +486,7 @@ Item { settingKey: "greeterPamExternallyManaged" tags: ["greeter", "pam", "managed", "external", "greetd", "auth"] text: I18n.tr("Use system PAM authentication", "system PAM policy toggle") - description: I18n.tr("DMS removes its managed block from /etc/pam.d/greetd and stops writing to it", "greeter system PAM toggle description") + description: I18n.tr("DMS removes its managed block from /etc/pam.d/greetd and stops write services", "greeter system PAM toggle description") checked: SettingsData.greeterPamExternallyManaged onToggled: checked => SettingsData.set("greeterPamExternallyManaged", checked) } @@ -553,7 +553,7 @@ Item { settingKey: "greeterLockDateFormat" tags: ["greeter", "date", "format"] text: I18n.tr("Date Format") - description: I18n.tr("Greeter only — format for the date on the login screen") + description: I18n.tr("Format the date on the login screen") options: root._lockDateFormatPresets.map(p => p.label) currentValue: { var current = (SettingsData.greeterLockDateFormat !== undefined && SettingsData.greeterLockDateFormat !== "") ? SettingsData.greeterLockDateFormat : SettingsData.lockDateFormat || ""; @@ -577,7 +577,7 @@ Item { } StyledText { - text: I18n.tr("Use a custom image for the login screen, or leave empty to use your desktop wallpaper.") + text: I18n.tr("Use a custom image for the login screen, or leave empty to use desktop wallpaper") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText width: parent.width @@ -605,7 +605,7 @@ Item { settingKey: "greeterBehavior" StyledText { - text: I18n.tr("Convenience options for the login screen. Sync to apply.") + text: I18n.tr("Convenience options for the login screen") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText width: parent.width @@ -649,7 +649,7 @@ Item { settingKey: "greeterDeps" StyledText { - text: I18n.tr("Requires greetd, dms-greeter, and your user in the greeter group (plus fprintd/pam_fprintd for fingerprint, pam_u2f for security keys). Auth changes apply automatically and may open a terminal for sudo.") + text: I18n.tr("Requires greetd, dms-greeter, and your user in the greeter group (plus fprintd/pam_fprintd for fingerprint, pam_u2f for security keys).") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText width: parent.width @@ -658,7 +658,7 @@ Item { } StyledText { - text: I18n.tr("Installation and PAM setup: see the ") + "DankGreeter docs " + I18n.tr("or run ") + "'dms greeter install'." + text: I18n.tr("Installation and PAM setup are documented in the ") + "DankGreeter docs. " textFormat: Text.RichText font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText diff --git a/quickshell/Modules/Settings/LockScreenTab.qml b/quickshell/Modules/Settings/LockScreenTab.qml index 755ac0a5d..b650e6771 100644 --- a/quickshell/Modules/Settings/LockScreenTab.qml +++ b/quickshell/Modules/Settings/LockScreenTab.qml @@ -96,28 +96,28 @@ Item { function lockFingerprintDescription() { switch (SettingsData.lockFingerprintReason) { case "ready": - return I18n.tr("Use fingerprint authentication for the lock screen.", "lock screen fingerprint setting"); + return I18n.tr("Use fingerprint authentication for the lock screen", "lock screen fingerprint setting"); case "missing_enrollment": return I18n.tr("Fingerprint reader detected, but no prints are enrolled yet. You can enable this now and enroll later.", "lock screen fingerprint setting"); case "missing_reader": - return I18n.tr("No fingerprint reader detected.", "fingerprint setting status"); + return I18n.tr("No fingerprint reader detected", "fingerprint setting status"); case "missing_pam_support": - return I18n.tr("Not available — install fprintd and pam_fprintd.", "lock screen fingerprint setting"); + return I18n.tr("Not available - install fprintd and pam_fprintd", "lock screen fingerprint setting"); default: - return I18n.tr("Fingerprint availability could not be confirmed.", "fingerprint setting status"); + return I18n.tr("Fingerprint availability could not be confirmed", "fingerprint setting status"); } } function lockU2fDescription() { switch (SettingsData.lockU2fReason) { case "ready": - return I18n.tr("Use a security key for lock screen authentication.", "lock screen U2F security key setting"); + return I18n.tr("Use a security key for lock screen authentication", "lock screen U2F security key setting"); case "missing_key_registration": return I18n.tr("Security-key support was detected, but no registered key was found yet. You can enable this now and register one later.", "security key setting status"); case "missing_pam_support": - return I18n.tr("Not available — install or configure pam_u2f.", "lock screen security key setting"); + return I18n.tr("Not available - install or configure pam_u2f", "lock screen security key setting"); default: - return I18n.tr("Security-key availability could not be confirmed.", "security key setting status"); + return I18n.tr("Security-key availability could not be confirmed", "security key setting status"); } } @@ -207,7 +207,7 @@ Item { const warns = Array.isArray(data.warnings) ? data.warnings : []; root.authValidateOk = true; root.authValidateWarn = warns.length > 0; - root.authValidateMessage = [I18n.tr("Authentication changes applied."), ...warns].join("\n"); + root.authValidateMessage = [I18n.tr("Authentication changes applied"), ...warns].join("\n"); } } @@ -245,7 +245,7 @@ Item { const warns = Array.isArray(data.warnings) ? data.warnings : []; root.u2fValidateOk = true; root.u2fValidateWarn = warns.length > 0; - root.u2fValidateMessage = [I18n.tr("Authentication changes applied."), ...warns].join("\n"); + root.u2fValidateMessage = [I18n.tr("Authentication changes applied"), ...warns].join("\n"); root.refreshAuthDetection(); } } @@ -400,7 +400,7 @@ Item { settingKey: "lockAuthSource" StyledText { - text: I18n.tr("Authentication changes apply automatically.") + text: I18n.tr("Authentication changes apply automatically") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText width: parent.width @@ -484,7 +484,7 @@ Item { settingKey: "enableFprint" tags: ["lock", "screen", "fingerprint", "authentication", "biometric", "fprint"] text: I18n.tr("Enable fingerprint authentication") - description: root.lockFprintControlledByPrimary ? I18n.tr("Managed by the primary PAM source.", "factor managed by PAM source status") : root.lockFingerprintDescription() + description: root.lockFprintControlledByPrimary ? I18n.tr("Managed by the primary PAM source", "factor managed by PAM source status") : root.lockFingerprintDescription() descriptionColor: root.lockFprintControlledByPrimary || SettingsData.lockFingerprintReason === "ready" ? Theme.surfaceVariantText : Theme.warning checked: SettingsData.enableFprint || root.primaryPamHasFprint enabled: root.lockFprintToggleAvailable && !root.lockFprintControlledByPrimary @@ -495,7 +495,7 @@ Item { settingKey: "enableU2f" tags: ["lock", "screen", "u2f", "yubikey", "security", "key", "fido", "authentication", "hardware"] text: I18n.tr("Enable security key authentication", "Enable FIDO2/U2F hardware security key for lock screen") - description: root.lockU2fControlledByPrimary ? I18n.tr("Managed by the primary PAM source.", "factor managed by PAM source status") : root.lockU2fDescription() + description: root.lockU2fControlledByPrimary ? I18n.tr("Managed by the primary PAM source", "factor managed by PAM source status") : root.lockU2fDescription() descriptionColor: root.lockU2fControlledByPrimary || SettingsData.lockU2fReason === "ready" ? Theme.surfaceVariantText : Theme.warning checked: SettingsData.enableU2f || root.primaryPamHasU2f enabled: root.lockU2fToggleAvailable && !root.lockU2fControlledByPrimary diff --git a/quickshell/translations/settings_search_index.json b/quickshell/translations/settings_search_index.json index b6588b338..a9ba21753 100644 --- a/quickshell/translations/settings_search_index.json +++ b/quickshell/translations/settings_search_index.json @@ -4776,7 +4776,7 @@ "security", "source" ], - "description": "Managed by the primary PAM source." + "description": "Managed by the primary PAM source" }, { "section": "loginctlLockIntegration", @@ -4823,7 +4823,7 @@ "u2f", "yubikey" ], - "description": "Managed by the primary PAM source." + "description": "Managed by the primary PAM source" }, { "section": "lockLayout", @@ -7808,11 +7808,12 @@ "managed", "pam", "removes", + "services", "stops", - "writing" + "write" ], "icon": "fingerprint", - "description": "DMS removes its managed block from /etc/pam.d/greetd and stops writing to it" + "description": "DMS removes its managed block from /etc/pam.d/greetd and stops write services" }, { "section": "greeterAutoLogin", @@ -7880,7 +7881,7 @@ "login", "screen" ], - "description": "Greeter only — format for the date on the login screen" + "description": "Format the date on the login screen" }, { "section": "greeterDeps", @@ -8026,11 +8027,12 @@ "managed", "pam", "removes", + "services", "stops", "system", - "writing" + "write" ], - "description": "DMS removes its managed block from /etc/pam.d/greetd and stops writing to it" + "description": "DMS removes its managed block from /etc/pam.d/greetd and stops write services" }, { "section": "muxType",