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

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
This commit is contained in:
purian23
2026-07-17 17:27:45 -04:00
parent 2986e354e8
commit 3938e60ce4
7 changed files with 75 additions and 38 deletions
@@ -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)
}
}
}
+7 -1
View File
@@ -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"] 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() { function detectAuthCapabilities() {
// FileView cannot watch paths that do not exist yet, so reload the U2F PAM
dankshellU2fPamWatcher.reload();
u2fKeysWatcher.reload();
if (forcedFprintAvailable === null) { if (forcedFprintAvailable === null) {
fingerprintProbeFinalized = false; fingerprintProbeFinalized = false;
Proc.runCommand("fprint-probe", _fprintProbeCommand, (output, exitCode) => { Proc.runCommand("fprint-probe", _fprintProbeCommand, (output, exitCode) => {
@@ -600,7 +604,7 @@ Singleton {
let details = out; let details = out;
if (err !== "") if (err !== "")
details = details !== "" ? details + "\n\nstderr:\n" + err : "stderr:\n" + 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.detectAuthCapabilities();
root.finishAuthApply(); root.finishAuthApply();
return; return;
@@ -723,6 +727,7 @@ Singleton {
FileView { FileView {
id: dankshellU2fPamWatcher id: dankshellU2fPamWatcher
path: "/etc/pam.d/dankshell-u2f" path: "/etc/pam.d/dankshell-u2f"
watchChanges: true
printErrors: false printErrors: false
onLoaded: root.dankshellU2fPamText = text() onLoaded: root.dankshellU2fPamText = text()
onLoadFailed: root.dankshellU2fPamText = "" onLoadFailed: root.dankshellU2fPamText = ""
@@ -737,6 +742,7 @@ Singleton {
FileView { FileView {
id: u2fKeysWatcher id: u2fKeysWatcher
path: root.u2fKeysPath path: root.u2fKeysPath
watchChanges: true
printErrors: false printErrors: false
onLoaded: root.u2fKeysText = text() onLoaded: root.u2fKeysText = text()
onLoadFailed: root.u2fKeysText = "" onLoadFailed: root.u2fKeysText = ""
+3 -3
View File
@@ -73,14 +73,14 @@ Item {
readonly property bool greeterPamHasFprint: greeterPamStackHasFprint && (!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: greeterPamStackHasFprint || greeterPamHasU2f readonly property bool greeterPamHasExternalAuth: greeterPamHasFprint || greeterPamHasU2f
readonly property bool externalAuthInProgress: awaitingExternalAuth || (Greetd.state !== GreetdState.Inactive && passwordSubmitRequested && greeterPamHasExternalAuth && !pendingPasswordResponse) readonly property bool externalAuthInProgress: awaitingExternalAuth || (Greetd.state !== GreetdState.Inactive && passwordSubmitRequested && greeterPamHasExternalAuth && !pendingPasswordResponse)
readonly property string externalAuthStatusMessage: { readonly property string externalAuthStatusMessage: {
if (!externalAuthInProgress) if (!externalAuthInProgress)
return ""; return "";
if (greeterPamStackHasFprint && greeterPamHasU2f) if (greeterPamHasFprint && greeterPamHasU2f)
return I18n.tr("Awaiting fingerprint or security key authentication"); return I18n.tr("Awaiting fingerprint or security key authentication");
if (greeterPamStackHasFprint) if (greeterPamHasFprint)
return I18n.tr("Awaiting fingerprint authentication"); return I18n.tr("Awaiting fingerprint authentication");
return I18n.tr("Awaiting security key authentication"); return I18n.tr("Awaiting security key authentication");
} }
+4
View File
@@ -113,6 +113,7 @@ Scope {
id: u2fConfigWatcher id: u2fConfigWatcher
path: "/etc/pam.d/dankshell-u2f" path: "/etc/pam.d/dankshell-u2f"
watchChanges: true
printErrors: false printErrors: false
} }
@@ -488,6 +489,9 @@ Scope {
root.resetAuthFlows(); root.resetAuthFlows();
if (!SettingsData.lockPamExternallyManaged && !dankshellConfigWatcher.loaded && !userPamWatcher.loaded) if (!SettingsData.lockPamExternallyManaged && !dankshellConfigWatcher.loaded && !userPamWatcher.loaded)
ensureUserPamConfig(); 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(); fprint.checkAvail();
u2f.checkAvail(); u2f.checkAvail();
} }
+15 -15
View File
@@ -19,39 +19,39 @@ Item {
function greeterFingerprintDescription() { function greeterFingerprintDescription() {
if (SettingsData.greeterPamExternallyManaged) 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") if (SettingsData.greeterFingerprintSource === "pam")
return I18n.tr("PAM already provides fingerprint auth. Enable this to show it at login.", "greeter fingerprint login setting"); return I18n.tr("PAM already provides fingerprint auth. Enable this to show it at login.", "greeter fingerprint login setting");
switch (SettingsData.greeterFingerprintReason) { switch (SettingsData.greeterFingerprintReason) {
case "ready": 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": 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"); 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": 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": case "missing_pam_support":
return I18n.tr("Not available — install fprintd and pam_fprintd, or configure greetd PAM.", "greeter fingerprint login setting"); return I18n.tr("Not available — install fprintd and pam_fprintd, or configure greetd PAM.", "greeter fingerprint login setting");
default: 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() { function greeterU2fDescription() {
if (SettingsData.greeterPamExternallyManaged) 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") 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"); return I18n.tr("PAM already provides security-key auth. Enable this to show it at login.", "greeter security key login setting");
switch (SettingsData.greeterU2fReason) { switch (SettingsData.greeterU2fReason) {
case "ready": 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": 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"); 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": case "missing_pam_support":
return I18n.tr("Not available — install or configure pam_u2f, or configure greetd PAM.", "greeter security key login setting"); return I18n.tr("Not available — install or configure pam_u2f, or configure greetd PAM.", "greeter security key login setting");
default: 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" settingKey: "greeterStatus"
StyledText { 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 font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
width: parent.width width: parent.width
@@ -474,7 +474,7 @@ Item {
settingKey: "greeterAuth" settingKey: "greeterAuth"
StyledText { 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 font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
width: parent.width width: parent.width
@@ -486,7 +486,7 @@ Item {
settingKey: "greeterPamExternallyManaged" settingKey: "greeterPamExternallyManaged"
tags: ["greeter", "pam", "managed", "external", "greetd", "auth"] tags: ["greeter", "pam", "managed", "external", "greetd", "auth"]
text: I18n.tr("Use system PAM authentication", "system PAM policy toggle") 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 checked: SettingsData.greeterPamExternallyManaged
onToggled: checked => SettingsData.set("greeterPamExternallyManaged", checked) onToggled: checked => SettingsData.set("greeterPamExternallyManaged", checked)
} }
@@ -553,7 +553,7 @@ Item {
settingKey: "greeterLockDateFormat" settingKey: "greeterLockDateFormat"
tags: ["greeter", "date", "format"] tags: ["greeter", "date", "format"]
text: I18n.tr("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) options: root._lockDateFormatPresets.map(p => p.label)
currentValue: { currentValue: {
var current = (SettingsData.greeterLockDateFormat !== undefined && SettingsData.greeterLockDateFormat !== "") ? SettingsData.greeterLockDateFormat : SettingsData.lockDateFormat || ""; var current = (SettingsData.greeterLockDateFormat !== undefined && SettingsData.greeterLockDateFormat !== "") ? SettingsData.greeterLockDateFormat : SettingsData.lockDateFormat || "";
@@ -577,7 +577,7 @@ Item {
} }
StyledText { 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 font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
width: parent.width width: parent.width
@@ -605,7 +605,7 @@ Item {
settingKey: "greeterBehavior" settingKey: "greeterBehavior"
StyledText { 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 font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
width: parent.width width: parent.width
@@ -649,7 +649,7 @@ Item {
settingKey: "greeterDeps" settingKey: "greeterDeps"
StyledText { 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 font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
width: parent.width width: parent.width
@@ -658,7 +658,7 @@ Item {
} }
StyledText { StyledText {
text: I18n.tr("Installation and PAM setup: see the ") + "<a href=\"https://danklinux.com/docs/dankgreeter/installation\" style=\"text-decoration:none; color:" + Theme.primary + ";\">DankGreeter docs</a> " + I18n.tr("or run ") + "'dms greeter install'." text: I18n.tr("Installation and PAM setup are documented in the ") + "<a href=\"https://danklinux.com/docs/dankgreeter/installation\" style=\"text-decoration:none; color:" + Theme.primary + ";\">DankGreeter docs.</a> "
textFormat: Text.RichText textFormat: Text.RichText
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
+12 -12
View File
@@ -96,28 +96,28 @@ Item {
function lockFingerprintDescription() { function lockFingerprintDescription() {
switch (SettingsData.lockFingerprintReason) { switch (SettingsData.lockFingerprintReason) {
case "ready": 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": 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"); 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": 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": 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: 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() { function lockU2fDescription() {
switch (SettingsData.lockU2fReason) { switch (SettingsData.lockU2fReason) {
case "ready": 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": 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"); 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": 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: 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 : []; const warns = Array.isArray(data.warnings) ? data.warnings : [];
root.authValidateOk = true; root.authValidateOk = true;
root.authValidateWarn = warns.length > 0; 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 : []; const warns = Array.isArray(data.warnings) ? data.warnings : [];
root.u2fValidateOk = true; root.u2fValidateOk = true;
root.u2fValidateWarn = warns.length > 0; 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(); root.refreshAuthDetection();
} }
} }
@@ -400,7 +400,7 @@ Item {
settingKey: "lockAuthSource" settingKey: "lockAuthSource"
StyledText { StyledText {
text: I18n.tr("Authentication changes apply automatically.") text: I18n.tr("Authentication changes apply automatically")
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
width: parent.width width: parent.width
@@ -484,7 +484,7 @@ Item {
settingKey: "enableFprint" settingKey: "enableFprint"
tags: ["lock", "screen", "fingerprint", "authentication", "biometric", "fprint"] tags: ["lock", "screen", "fingerprint", "authentication", "biometric", "fprint"]
text: I18n.tr("Enable fingerprint authentication") 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 descriptionColor: root.lockFprintControlledByPrimary || SettingsData.lockFingerprintReason === "ready" ? Theme.surfaceVariantText : Theme.warning
checked: SettingsData.enableFprint || root.primaryPamHasFprint checked: SettingsData.enableFprint || root.primaryPamHasFprint
enabled: root.lockFprintToggleAvailable && !root.lockFprintControlledByPrimary enabled: root.lockFprintToggleAvailable && !root.lockFprintControlledByPrimary
@@ -495,7 +495,7 @@ Item {
settingKey: "enableU2f" settingKey: "enableU2f"
tags: ["lock", "screen", "u2f", "yubikey", "security", "key", "fido", "authentication", "hardware"] 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") 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 descriptionColor: root.lockU2fControlledByPrimary || SettingsData.lockU2fReason === "ready" ? Theme.surfaceVariantText : Theme.warning
checked: SettingsData.enableU2f || root.primaryPamHasU2f checked: SettingsData.enableU2f || root.primaryPamHasU2f
enabled: root.lockU2fToggleAvailable && !root.lockU2fControlledByPrimary enabled: root.lockU2fToggleAvailable && !root.lockU2fControlledByPrimary
@@ -4776,7 +4776,7 @@
"security", "security",
"source" "source"
], ],
"description": "Managed by the primary PAM source." "description": "Managed by the primary PAM source"
}, },
{ {
"section": "loginctlLockIntegration", "section": "loginctlLockIntegration",
@@ -4823,7 +4823,7 @@
"u2f", "u2f",
"yubikey" "yubikey"
], ],
"description": "Managed by the primary PAM source." "description": "Managed by the primary PAM source"
}, },
{ {
"section": "lockLayout", "section": "lockLayout",
@@ -7808,11 +7808,12 @@
"managed", "managed",
"pam", "pam",
"removes", "removes",
"services",
"stops", "stops",
"writing" "write"
], ],
"icon": "fingerprint", "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", "section": "greeterAutoLogin",
@@ -7880,7 +7881,7 @@
"login", "login",
"screen" "screen"
], ],
"description": "Greeter only — format for the date on the login screen" "description": "Format the date on the login screen"
}, },
{ {
"section": "greeterDeps", "section": "greeterDeps",
@@ -8026,11 +8027,12 @@
"managed", "managed",
"pam", "pam",
"removes", "removes",
"services",
"stops", "stops",
"system", "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", "section": "muxType",