1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-08-08 06:28:27 -04:00

fix(greeter): reimplement regression to hide debug logs during login

- Provide more status feedback
- Allow 2 fprint tries within 10 seconds for DMS-managed PAM
- Updated dms greeter docs

Note: distro-managed PAM takes precedence and may use different limits.
DMS auth changes remain conditional on greetd being installed.

Fixes #2853
Port 1.5

(cherry picked from commit 5b41d699fa)
This commit is contained in:
purian23
2026-07-14 19:41:36 -04:00
committed by dms-ci[bot]
parent deb0138198
commit 6b70491ba9
8 changed files with 89 additions and 33 deletions
+1 -1
View File
@@ -849,7 +849,7 @@ func syncGreeterPamConfigWithDeps(logFunc func(string), sudoPassword string, set
if wantFprint || wantU2f { if wantFprint || wantU2f {
blockLines := []string{GreeterPamManagedBlockStart} blockLines := []string{GreeterPamManagedBlockStart}
if wantFprint { if wantFprint {
blockLines = append(blockLines, "auth sufficient pam_fprintd.so max-tries=1 timeout=5") blockLines = append(blockLines, "auth sufficient pam_fprintd.so max-tries=2 timeout=10")
} }
if wantU2f { if wantU2f {
blockLines = append(blockLines, "auth sufficient pam_u2f.so cue nouserok timeout=10") blockLines = append(blockLines, "auth sufficient pam_u2f.so cue nouserok timeout=10")
+2 -2
View File
@@ -723,7 +723,7 @@ func TestSyncGreeterPamConfigWithDeps(t *testing.T) {
got := readFileString(t, env.greetdPath) got := readFileString(t, env.greetdPath)
for _, want := range []string{ for _, want := range []string{
GreeterPamManagedBlockStart, GreeterPamManagedBlockStart,
"auth sufficient pam_fprintd.so max-tries=1 timeout=5", "auth sufficient pam_fprintd.so max-tries=2 timeout=10",
"auth sufficient pam_u2f.so cue nouserok timeout=10", "auth sufficient pam_u2f.so cue nouserok timeout=10",
GreeterPamManagedBlockEnd, GreeterPamManagedBlockEnd,
} { } {
@@ -840,7 +840,7 @@ func TestSyncAuthConfigWithDeps(t *testing.T) {
} }
greetd := readFileString(t, env.greetdPath) greetd := readFileString(t, env.greetdPath)
if !strings.Contains(greetd, "auth sufficient pam_fprintd.so max-tries=1 timeout=5") { if !strings.Contains(greetd, "auth sufficient pam_fprintd.so max-tries=2 timeout=10") {
t.Fatalf("expected greetd PAM to receive fingerprint auth block:\n%s", greetd) t.Fatalf("expected greetd PAM to receive fingerprint auth block:\n%s", greetd)
} }
if strings.Contains(greetd, "auth sufficient pam_u2f.so cue nouserok timeout=10") { if strings.Contains(greetd, "auth sufficient pam_u2f.so cue nouserok timeout=10") {
+1 -1
View File
@@ -476,7 +476,7 @@ Singleton {
if (exitCode === 0) if (exitCode === 0)
return "missing_enrollment"; return "missing_enrollment";
if (exitCode === 127 || (output || "").includes("__missing_command__")) if (exitCode === 127 || (output || "").includes("__missing_command__"))
return "probe_failed"; return "missing_pam_support";
return pamFprintDetected ? "probe_failed" : "missing_pam_support"; return pamFprintDetected ? "probe_failed" : "missing_pam_support";
} }
+2 -4
View File
@@ -153,12 +153,10 @@ Singleton {
blockWrites: true blockWrites: true
atomicWrites: false atomicWrites: false
watchChanges: false watchChanges: false
printErrors: true printErrors: false
onLoaded: { onLoaded: {
parseSessionConfig(sessionConfigFileView.text()); parseSessionConfig(sessionConfigFileView.text());
} }
onLoadFailed: error => { onLoadFailed: {}
log.warn("Could not load greeter session config from", root.sessionConfigPath, "error:", error);
}
} }
} }
+2 -3
View File
@@ -219,12 +219,11 @@ Singleton {
blockWrites: true blockWrites: true
atomicWrites: false atomicWrites: false
watchChanges: false watchChanges: false
printErrors: true printErrors: false
onLoaded: { onLoaded: {
parseSettings(settingsFile.text()); parseSettings(settingsFile.text());
} }
onLoadFailed: error => { onLoadFailed: {
log.warn("Failed to load greetd settings:", error);
root.parseSettings(""); root.parseSettings("");
} }
} }
+29 -18
View File
@@ -51,6 +51,7 @@ Item {
property int passwordFailureCount: 0 property int passwordFailureCount: 0
property int passwordAttemptLimitHint: 0 property int passwordAttemptLimitHint: 0
property string authFeedbackMessage: "" property string authFeedbackMessage: ""
property string authSuccessMessage: ""
property string greetdPamText: "" property string greetdPamText: ""
property string systemAuthPamText: "" property string systemAuthPamText: ""
property string commonAuthPamText: "" property string commonAuthPamText: ""
@@ -67,11 +68,23 @@ Item {
property bool fprintdProbeComplete: false property bool fprintdProbeComplete: false
property bool fprintdHasDevice: false property bool fprintdHasDevice: false
property bool autoLoginOnSuccess: false property bool autoLoginOnSuccess: false
readonly property bool greeterPamStackHasFprint: greeterPamStackHasModule("pam_fprintd")
// Falls back to PAM-only detection until the fprintd D-Bus probe completes. // Falls back to PAM-only detection until the fprintd D-Bus probe completes.
readonly property bool greeterPamHasFprint: greeterPamStackHasModule("pam_fprintd") && (!fprintdProbeComplete || fprintdHasDevice) readonly property bool greeterPamHasFprint: greeterPamStackHasFprint && (!fprintdProbeComplete || fprintdHasDevice)
readonly property bool greeterPamHasU2f: greeterPamStackHasModule("pam_u2f") readonly property bool greeterPamHasU2f: greeterPamStackHasModule("pam_u2f")
readonly property bool greeterExternalAuthAvailable: (greeterPamHasFprint && GreetdSettings.greeterEnableFprint) || (greeterPamHasU2f && GreetdSettings.greeterEnableU2f) readonly property bool greeterExternalAuthAvailable: (greeterPamHasFprint && GreetdSettings.greeterEnableFprint) || (greeterPamHasU2f && GreetdSettings.greeterEnableU2f)
readonly property bool greeterPamHasExternalAuth: greeterPamHasFprint || greeterPamHasU2f readonly property bool greeterPamHasExternalAuth: greeterPamStackHasFprint || greeterPamHasU2f
readonly property bool externalAuthInProgress: awaitingExternalAuth || (Greetd.state !== GreetdState.Inactive && passwordSubmitRequested && greeterPamHasExternalAuth && !pendingPasswordResponse)
readonly property string externalAuthStatusMessage: {
if (!externalAuthInProgress)
return "";
if (greeterPamStackHasFprint && greeterPamHasU2f)
return I18n.tr("Awaiting fingerprint or security key authentication");
if (greeterPamStackHasFprint)
return I18n.tr("Awaiting fingerprint authentication");
return I18n.tr("Awaiting security key authentication");
}
readonly property string authDisplayMessage: authFeedbackMessage || authSuccessMessage || externalAuthStatusMessage
readonly property bool autoLoginAvailable: GreetdSettings.rememberLastUser && GreetdSettings.rememberLastSession readonly property bool autoLoginAvailable: GreetdSettings.rememberLastUser && GreetdSettings.rememberLastSession
readonly property bool multipleUsersAvailable: GreeterUsersService.loaded && GreeterUsersService.users.length > 1 readonly property bool multipleUsersAvailable: GreeterUsersService.loaded && GreeterUsersService.users.length > 1
// Single-user systems get the picker too when auto-login is available, so the // Single-user systems get the picker too when auto-login is available, so the
@@ -238,7 +251,7 @@ Item {
function isLikelyLockoutMessage(message) { function isLikelyLockoutMessage(message) {
const lower = (message || "").toLowerCase(); const lower = (message || "").toLowerCase();
return lower.includes("account is locked") || lower.includes("too many") || lower.includes("maximum number of") || lower.includes("auth_err"); return lower.includes("account is locked") || lower.includes("too many") || lower.includes("maximum number of");
} }
function currentAuthMessage() { function currentAuthMessage() {
@@ -251,11 +264,11 @@ Item {
const attempt = Math.max(1, Math.min(passwordFailureCount, passwordAttemptLimitHint)); const attempt = Math.max(1, Math.min(passwordFailureCount, passwordAttemptLimitHint));
const remaining = Math.max(passwordAttemptLimitHint - attempt, 0); const remaining = Math.max(passwordAttemptLimitHint - attempt, 0);
if (remaining > 0) { if (remaining > 0) {
return I18n.tr("Incorrect password - attempt %1 of %2 (lockout may follow)").arg(attempt).arg(passwordAttemptLimitHint); return I18n.tr("Authentication failed - attempt %1 of %2").arg(attempt).arg(passwordAttemptLimitHint);
} }
return I18n.tr("Incorrect password - next failures may trigger account lockout"); return I18n.tr("Authentication failed - lockout can occur");
} }
return I18n.tr("Incorrect password"); return I18n.tr("Authentication failed - try again");
} }
return ""; return "";
} }
@@ -263,6 +276,7 @@ Item {
function clearAuthFeedback() { function clearAuthFeedback() {
GreeterState.pamState = ""; GreeterState.pamState = "";
authFeedbackMessage = ""; authFeedbackMessage = "";
authSuccessMessage = "";
} }
function resetPasswordSessionTransition(clearSubmitRequest) { function resetPasswordSessionTransition(clearSubmitRequest) {
@@ -619,8 +633,8 @@ Item {
pendingPasswordResponse = false; pendingPasswordResponse = false;
passwordSubmitRequested = submitPassword; passwordSubmitRequested = submitPassword;
awaitingExternalAuth = !submitPassword && !hasPasswordBuffer && root.greeterExternalAuthAvailable; awaitingExternalAuth = !submitPassword && !hasPasswordBuffer && root.greeterExternalAuthAvailable;
// Use greeterExternalAuthAvailable so systems with pam_fprintd but no hardware don't incur the 30 s wait. // Let the effective PAM stack finish external authentication.
const waitingOnPamExternalBeforePassword = submitPassword && root.greeterExternalAuthAvailable; const waitingOnPamExternalBeforePassword = submitPassword && root.greeterPamHasExternalAuth;
authTimeout.interval = (awaitingExternalAuth || waitingOnPamExternalBeforePassword) ? externalAuthTimeoutMs : defaultAuthTimeoutMs; authTimeout.interval = (awaitingExternalAuth || waitingOnPamExternalBeforePassword) ? externalAuthTimeoutMs : defaultAuthTimeoutMs;
authTimeout.restart(); authTimeout.restart();
Greetd.createSession(GreeterState.username); Greetd.createSession(GreeterState.username);
@@ -1406,16 +1420,16 @@ Item {
StyledText { StyledText {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: root.authFeedbackMessage !== "" ? 38 : 0 Layout.preferredHeight: root.authDisplayMessage !== "" ? 38 : 0
Layout.topMargin: -Theme.spacingS Layout.topMargin: -Theme.spacingS
Layout.bottomMargin: -Theme.spacingS Layout.bottomMargin: -Theme.spacingS
text: root.authFeedbackMessage text: root.authDisplayMessage
color: Theme.error color: root.authFeedbackMessage !== "" ? Theme.error : (root.authSuccessMessage !== "" ? Theme.success : Theme.surfaceVariantText)
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
maximumLineCount: 2 maximumLineCount: 2
opacity: root.authFeedbackMessage !== "" ? 1 : 0 opacity: root.authDisplayMessage !== "" ? 1 : 0
Behavior on opacity { Behavior on opacity {
NumberAnimation { NumberAnimation {
@@ -1975,6 +1989,7 @@ Item {
authTimeout.stop(); authTimeout.stop();
passwordFailureCount = 0; passwordFailureCount = 0;
clearAuthFeedback(); clearAuthFeedback();
authSuccessMessage = I18n.tr("Authenticated - starting session");
const sessionCmd = GreeterState.selectedSession || GreeterState.sessionExecs[GreeterState.currentSessionIndex]; const sessionCmd = GreeterState.selectedSession || GreeterState.sessionExecs[GreeterState.currentSessionIndex];
const sessionPath = GreeterState.selectedSessionPath || GreeterState.sessionPaths[GreeterState.currentSessionIndex]; const sessionPath = GreeterState.selectedSessionPath || GreeterState.sessionPaths[GreeterState.currentSessionIndex];
const sessionDesktopId = GreeterState.selectedSessionDesktopId || GreeterState.sessionDesktopIds[GreeterState.currentSessionIndex] || desktopIdFromPath(sessionPath); const sessionDesktopId = GreeterState.selectedSessionDesktopId || GreeterState.sessionDesktopIds[GreeterState.currentSessionIndex] || desktopIdFromPath(sessionPath);
@@ -2055,12 +2070,8 @@ Item {
pendingLaunchCommand = ""; pendingLaunchCommand = "";
pendingLaunchEnv = []; pendingLaunchEnv = [];
const sessionArgs = sessionCommand.trim().split(/\s+/); const sessionArgs = sessionCommand.trim().split(/\s+/);
const needsVoidDbusSession = Quickshell.env("DMS_VOID") === "1" const needsVoidDbusSession = Quickshell.env("DMS_VOID") === "1" && !Quickshell.env("DBUS_SESSION_BUS_ADDRESS") && sessionArgs[0] !== "dbus-run-session";
&& !Quickshell.env("DBUS_SESSION_BUS_ADDRESS") const launchArgs = needsVoidDbusSession ? ["dbus-run-session"].concat(sessionArgs) : sessionArgs;
&& sessionArgs[0] !== "dbus-run-session";
const launchArgs = needsVoidDbusSession
? ["dbus-run-session"].concat(sessionArgs)
: sessionArgs;
Greetd.launch(launchArgs, launchEnv); Greetd.launch(launchArgs, launchEnv);
} }
} }
+34 -2
View File
@@ -51,12 +51,44 @@ dms greeter enable
dms greeter sync dms greeter sync
``` ```
Fingerprint authentication is optional. Install your distribution's
fingerprint service/client and PAM integration before enabling it. Package
names vary by distribution; on Fedora the native stack is:
```bash
sudo dnf install fprintd fprintd-pam
fprintd-enroll
```
The distro `fprintd` package normally pulls in `libfprint` automatically. After
enrollment, enable fingerprint login in **Settings → Greeter**. The toggle
applies the shared PAM configuration automatically, so no additional sync is
needed after initial greeter setup. Use `dms auth sync` only to repair or apply
authentication manually without syncing the rest of the greeter.
greetd tries fingerprint and password sequentially. DMS-managed PAM allows two
scans for up to ten seconds before password fallback. Existing distro PAM
policy takes precedence, so fallback timing can differ.
Verify enrollment with `fprintd-list "$USER"`; pass a token such as
`fprintd-enroll -f right-thumb` to enroll a specific finger. Security keys
similarly require `pam_u2f` setup and key registration before enabling the
login toggle.
If `fprintd-enroll` reports **No devices available**, check the [libfprint
supported devices list](https://fprint.freedesktop.org/supported-devices.html).
Some Validity/Synaptics readers are not supported by the native stack,
regardless of distribution; the
[open-fprintd](https://github.com/uunicorn/open-fprintd) service with the
[python-validity](https://github.com/uunicorn/python-validity) driver may work
instead. Follow the driver's installation and firmware instructions, and do
not run the stock `fprintd` daemon alongside its replacement.
#### Syncing themes (Optional) #### Syncing themes (Optional)
To sync your wallpaper and theme with the greeter login screen, follow the manual setup below: To sync your wallpaper and theme with the greeter login screen, follow the manual setup below:
<details> ##### Manual theme syncing
<summary>Manual theme syncing</summary>
```bash ```bash
# Add yourself to greeter group # Add yourself to greeter group
+18 -2
View File
@@ -88,6 +88,19 @@ is_hyprland_lua_config() {
return 1 return 1
} }
clear_vt() {
local clear_sequence=$'\033[2J\033[H\033[3J\033[?25l'
# Clear retained console text on the controlling VT.
if printf '%s' "$clear_sequence" >/dev/tty 2>/dev/null; then
return
fi
if [[ -t 1 ]]; then
printf '%s' "$clear_sequence"
fi
}
exec_compositor() { exec_compositor() {
local log_tag="$1" local log_tag="$1"
shift shift
@@ -96,11 +109,14 @@ exec_compositor() {
exec "$@" exec "$@"
fi fi
clear_vt
if command -v systemd-cat >/dev/null 2>&1; then if command -v systemd-cat >/dev/null 2>&1; then
exec "$@" > >(systemd-cat -t "dms-greeter/$log_tag" -p info) 2>&1 exec "$@" > >(systemd-cat -t "dms-greeter/$log_tag" -p info 2>/dev/null) 2>&1
fi fi
exec "$@" # Preserve diagnostics on systems without journald.
exec "$@" >>"$CACHE_DIR/$log_tag.log" 2>&1
} }
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do