1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-15 18:22:08 -04:00

fix(lock/greeter): sync auth files with master

This commit is contained in:
purian23
2026-03-26 11:37:17 -04:00
parent 799773c62b
commit 519a8357a1
8 changed files with 71 additions and 42 deletions

View File

@@ -1,7 +1,5 @@
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Services.Greetd
import qs.Common
import qs.Modules.Greetd import qs.Modules.Greetd
Scope { Scope {

View File

@@ -225,7 +225,13 @@ Item {
} }
StyledText { StyledText {
text: root.errorCount > 0 ? I18n.tr("%1 issue(s) found", "greeter doctor page error count").arg(root.errorCount) : I18n.tr("All checks passed", "greeter doctor page success") text: {
if (root.errorCount === 0)
return I18n.tr("All checks passed", "greeter doctor page success");
return root.errorCount === 1
? I18n.tr("%1 issue found", "greeter doctor page error count").arg(root.errorCount)
: I18n.tr("%1 issues found", "greeter doctor page error count").arg(root.errorCount);
}
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: root.errorCount > 0 ? Theme.error : Theme.surfaceVariantText color: root.errorCount > 0 ? Theme.error : Theme.surfaceVariantText
} }

View File

@@ -230,19 +230,19 @@ Item {
function currentAuthMessage() { function currentAuthMessage() {
if (GreeterState.pamState === "error") if (GreeterState.pamState === "error")
return "Authentication error - try again"; return I18n.tr("Authentication error - try again");
if (GreeterState.pamState === "max") if (GreeterState.pamState === "max")
return "Too many failed attempts - account may be locked"; return I18n.tr("Too many failed attempts - account may be locked");
if (GreeterState.pamState === "fail") { if (GreeterState.pamState === "fail") {
if (passwordAttemptLimitHint > 0) { if (passwordAttemptLimitHint > 0) {
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 "Incorrect password - attempt " + attempt + " of " + passwordAttemptLimitHint + " (lockout may follow)"; return I18n.tr("Incorrect password - attempt %1 of %2 (lockout may follow)").arg(attempt).arg(passwordAttemptLimitHint);
} }
return "Incorrect password - next failures may trigger account lockout"; return I18n.tr("Incorrect password - next failures may trigger account lockout");
} }
return "Incorrect password"; return I18n.tr("Incorrect password");
} }
return ""; return "";
} }
@@ -767,7 +767,7 @@ Item {
property string fullTimeStr: { property string fullTimeStr: {
const format = GreetdSettings.getEffectiveTimeFormat(); const format = GreetdSettings.getEffectiveTimeFormat();
return systemClock.date.toLocaleTimeString(Qt.locale(), format); return systemClock.date.toLocaleTimeString(I18n.locale(), format);
} }
property var timeParts: fullTimeStr.split(':') property var timeParts: fullTimeStr.split(':')
property string hours: timeParts[0] || "" property string hours: timeParts[0] || ""
@@ -876,7 +876,7 @@ Item {
anchors.top: clockContainer.bottom anchors.top: clockContainer.bottom
anchors.topMargin: 4 anchors.topMargin: 4
text: { text: {
return systemClock.date.toLocaleDateString(Qt.locale(), GreetdSettings.getEffectiveLockDateFormat()); return systemClock.date.toLocaleDateString(I18n.locale(), GreetdSettings.getEffectiveLockDateFormat());
} }
font.pixelSize: Theme.fontSizeXLarge font.pixelSize: Theme.fontSizeXLarge
color: "white" color: "white"
@@ -1012,15 +1012,15 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
text: { text: {
if (GreeterState.unlocking) { if (GreeterState.unlocking) {
return "Logging in..."; return I18n.tr("Logging in...");
} }
if (Greetd.state !== GreetdState.Inactive && !awaitingExternalAuth && !pendingPasswordResponse) { if (Greetd.state !== GreetdState.Inactive && !awaitingExternalAuth && !pendingPasswordResponse) {
return "Authenticating..."; return I18n.tr("Authenticating...");
} }
if (GreeterState.showPasswordInput) { if (GreeterState.showPasswordInput) {
return "Password..."; return I18n.tr("Password...");
} }
return "Username..."; return I18n.tr("Username...");
} }
color: (GreeterState.unlocking || (Greetd.state !== GreetdState.Inactive && !awaitingExternalAuth && !pendingPasswordResponse)) ? Theme.primary : Theme.outline color: (GreeterState.unlocking || (Greetd.state !== GreetdState.Inactive && !awaitingExternalAuth && !pendingPasswordResponse)) ? Theme.primary : Theme.outline
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium

View File

@@ -6,7 +6,7 @@ A greeter for [greetd](https://github.com/kennylevinsen/greetd) that follows the
- **Multi user**: Login with any system user - **Multi user**: Login with any system user
- **dms sync**: Sync settings with dms for consistent styling between shell and greeter - **dms sync**: Sync settings with dms for consistent styling between shell and greeter
- **Multiple compositors**: Supports niri, Hyprland, Sway, or mangowc. - **Multiple compositors**: The `dms-greeter` wrapper supports niri, Hyprland, sway, scroll, miracle-wm, labwc, and mangowc.
- **Custom PAM**: Supports custom PAM configuration in `/etc/pam.d/greetd` - **Custom PAM**: Supports custom PAM configuration in `/etc/pam.d/greetd`
- **Session Memory**: Remembers last selected session and user - **Session Memory**: Remembers last selected session and user
- Can be disabled via `settings.json` keys: `greeterRememberLastSession` and `greeterRememberLastUser` - Can be disabled via `settings.json` keys: `greeterRememberLastSession` and `greeterRememberLastUser`
@@ -152,8 +152,8 @@ sudo chmod +x /usr/local/bin/dms-greeter
5. Create greeter cache directory with proper permissions: 5. Create greeter cache directory with proper permissions:
```bash ```bash
sudo mkdir -p /var/cache/dms-greeter sudo mkdir -p /var/cache/dms-greeter
sudo chown greeter:greeter /var/cache/dms-greeter sudo chown <greeter-user>:<greeter-group> /var/cache/dms-greeter
sudo chmod 750 /var/cache/dms-greeter sudo chmod 2770 /var/cache/dms-greeter
``` ```
6. Edit or create `/etc/greetd/config.toml`: 6. Edit or create `/etc/greetd/config.toml`:
@@ -163,7 +163,7 @@ vt = 1
[default_session] [default_session]
user = "greeter" user = "greeter"
# Change compositor to sway, hyprland, or mangowc if preferred # Change compositor to another wrapper-supported compositor if preferred
command = "/usr/local/bin/dms-greeter --command niri" command = "/usr/local/bin/dms-greeter --command niri"
``` ```
@@ -238,9 +238,12 @@ DMS_RUN_GREETER=1 qs -p /path/to/dms
#### Compositor #### Compositor
You can configure compositor specific settings such as outputs/displays the same as you would in niri or Hyprland. For current wrapper-based installs, the `dms-greeter` wrapper supports niri, hyprland, sway, scroll, miracle-wm, labwc, and mangowc.
Simply edit `/etc/greetd/dms-niri.kdl` or `/etc/greetd/dms-hypr.conf` to change compositor settings for the greeter Only niri currently has a generated greeter config path managed by `dms greeter sync`.
- niri: `dms greeter sync` writes the generated greeter config to `/etc/greetd/niri/config.kdl`. Add local manual tweaks in `/etc/greetd/niri_overrides.kdl`.
- Other wrapper-supported compositors use the wrapper-generated config by default. If you need a custom compositor config, add `-C /path/to/config` to the `dms-greeter` command in `/etc/greetd/config.toml`.
#### Personalization #### Personalization
@@ -271,4 +274,4 @@ sudo ln -sf ~/.cache/DankMaterialShell/dms-colors.json /var/cache/dms-greeter/co
**Advanced:** You can override the configuration path with the `DMS_GREET_CFG_DIR` environment variable or the `--cache-dir` flag when using `dms-greeter`. The default is `/var/cache/dms-greeter`. **Advanced:** You can override the configuration path with the `DMS_GREET_CFG_DIR` environment variable or the `--cache-dir` flag when using `dms-greeter`. The default is `/var/cache/dms-greeter`.
The cache directory should be owned by `greeter:greeter` with `770` permissions. The cache directory should be owned by `<greeter-user>:<greeter-group>` with `2770` permissions. If the greeter user is not available yet, DMS falls back to `root:<greeter-group>`.

View File

@@ -214,18 +214,6 @@ export XDG_STATE_HOME="$CACHE_DIR/.local/state"
export XDG_DATA_HOME="$CACHE_DIR/.local/share" export XDG_DATA_HOME="$CACHE_DIR/.local/share"
export XDG_CACHE_HOME="$CACHE_DIR/.cache" export XDG_CACHE_HOME="$CACHE_DIR/.cache"
# Propagate correct XDG dirs into the systemd user session so socket-activated
# services (e.g. wireplumber) don't inherit HOME=/ from /etc/passwd.
if command -v systemctl >/dev/null 2>&1; then
systemctl --user set-environment \
HOME="$CACHE_DIR" \
XDG_STATE_HOME="$CACHE_DIR/.local/state" \
XDG_DATA_HOME="$CACHE_DIR/.local/share" \
XDG_CACHE_HOME="$CACHE_DIR/.cache" 2>/dev/null || true
if systemctl --user is-active --quiet wireplumber.service 2>/dev/null; then
systemctl --user restart wireplumber.service 2>/dev/null || true
fi
fi
# Keep greeter VT clean by default; callers can override via env or --debug. # Keep greeter VT clean by default; callers can override via env or --debug.
if [[ -z "${RUST_LOG:-}" ]]; then if [[ -z "${RUST_LOG:-}" ]]; then

View File

@@ -365,9 +365,9 @@ Item {
visible: SettingsData.lockScreenShowDate visible: SettingsData.lockScreenShowDate
text: { text: {
if (SettingsData.lockDateFormat && SettingsData.lockDateFormat.length > 0) { if (SettingsData.lockDateFormat && SettingsData.lockDateFormat.length > 0) {
return systemClock.date.toLocaleDateString(Qt.locale(), SettingsData.lockDateFormat); return systemClock.date.toLocaleDateString(I18n.locale(), SettingsData.lockDateFormat);
} }
return systemClock.date.toLocaleDateString(Qt.locale(), Locale.LongFormat); return systemClock.date.toLocaleDateString(I18n.locale(), Locale.LongFormat);
} }
font.pixelSize: Theme.fontSizeXLarge font.pixelSize: Theme.fontSizeXLarge
color: "white" color: "white"
@@ -719,14 +719,24 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
name: { name: {
if (pam.u2fPending)
return "passkey";
if (pam.fprint.tries >= SettingsData.maxFprintTries) if (pam.fprint.tries >= SettingsData.maxFprintTries)
return "fingerprint_off"; return "fingerprint_off";
if (pam.fprint.active) if (pam.fprint.active)
return "fingerprint"; return "fingerprint";
if (pam.u2f.active)
return "passkey";
return "lock"; return "lock";
} }
size: 20 size: 20
color: pam.fprint.tries >= SettingsData.maxFprintTries ? Theme.error : (passwordField.activeFocus ? Theme.primary : Theme.surfaceVariantText) color: {
if (pam.fprint.tries >= SettingsData.maxFprintTries)
return Theme.error;
if (pam.u2fState !== "")
return Theme.tertiary;
return passwordField.activeFocus ? Theme.primary : Theme.surfaceVariantText;
}
opacity: pam.passwd.active ? 0 : 1 opacity: pam.passwd.active ? 0 : 1
Behavior on opacity { Behavior on opacity {
@@ -792,6 +802,11 @@ Item {
} }
if (event.key === Qt.Key_Escape) { if (event.key === Qt.Key_Escape) {
if (pam.u2fPending) {
pam.cancelU2fPending();
event.accepted = true;
return;
}
clear(); clear();
} }
@@ -856,6 +871,11 @@ Item {
if (root.unlocking) { if (root.unlocking) {
return "Unlocking..."; return "Unlocking...";
} }
if (pam.u2fPending) {
if (pam.u2fState === "insert")
return "Insert your security key...";
return "Touch your security key...";
}
if (pam.passwd.active) { if (pam.passwd.active) {
return "Authenticating..."; return "Authenticating...";
} }
@@ -930,7 +950,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
iconName: "keyboard" iconName: "keyboard"
buttonSize: 32 buttonSize: 32
visible: !demoMode && !pam.passwd.active && !root.unlocking visible: !demoMode && !pam.passwd.active && !root.unlocking && !pam.u2fPending
enabled: visible enabled: visible
onClicked: { onClicked: {
if (keyboardController.isKeyboardActive) { if (keyboardController.isKeyboardActive) {
@@ -1031,7 +1051,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
iconName: "keyboard_return" iconName: "keyboard_return"
buttonSize: 36 buttonSize: 36
visible: (demoMode || (!pam.passwd.active && !root.unlocking)) visible: (demoMode || (!pam.passwd.active && !root.unlocking && !pam.u2fPending))
enabled: !demoMode enabled: !demoMode
onClicked: { onClicked: {
if (!demoMode && !root.unlocking && !pam.u2fPending) { if (!demoMode && !root.unlocking && !pam.u2fPending) {
@@ -1637,6 +1657,14 @@ Item {
root.passwordBuffer = ""; root.passwordBuffer = "";
} }
} }
onU2fPendingChanged: {
if (u2fPending) {
passwordField.text = "";
root.passwordBuffer = "";
if (keyboardController.isKeyboardActive)
keyboardController.hide();
}
}
} }
Connections { Connections {

View File

@@ -14,9 +14,12 @@ Scope {
readonly property alias passwd: passwd readonly property alias passwd: passwd
readonly property alias fprint: fprint readonly property alias fprint: fprint
readonly property alias u2f: u2f
property string lockMessage property string lockMessage
property string state property string state
property string fprintState property string fprintState
property string u2fState
property bool u2fPending: false
property string buffer property string buffer
signal flashMsg signal flashMsg
@@ -127,9 +130,8 @@ Scope {
onCompleted: res => { onCompleted: res => {
if (res === PamResult.Success) { if (res === PamResult.Success) {
if (!root.unlockInProgress) { if (!root.unlockInProgress) {
root.unlockInProgress = true;
fprint.abort(); fprint.abort();
root.unlockRequested(); root.proceedAfterPrimaryAuth();
} }
return; return;
} }
@@ -192,9 +194,8 @@ Scope {
if (res === PamResult.Success) { if (res === PamResult.Success) {
if (!root.unlockInProgress) { if (!root.unlockInProgress) {
root.unlockInProgress = true;
passwd.abort(); passwd.abort();
root.unlockRequested(); root.proceedAfterPrimaryAuth();
} }
return; return;
} }
@@ -356,6 +357,8 @@ Scope {
SettingsData.refreshAuthAvailability(); SettingsData.refreshAuthAvailability();
root.state = ""; root.state = "";
root.fprintState = ""; root.fprintState = "";
root.u2fState = "";
root.u2fPending = false;
root.lockMessage = ""; root.lockMessage = "";
root.resetAuthFlows(); root.resetAuthFlows();
fprint.checkAvail(); fprint.checkAvail();

View File

@@ -0,0 +1,3 @@
#%PAM-1.0
auth required pam_u2f.so cue nouserok timeout=10