fix: greetd-Session nach Auth-Fehler sauber canceln

Nach fehlgeschlagenem Login (falsches Passwort) wurde die greetd-Session
nicht gecancelt — beim nächsten Versuch kam "a session is already being
configured". Jetzt wird cancel_session gesendet nach Auth-Fehler, und
bei create_session-Fehler wird einmal cancel + retry versucht.

Außerdem: GTK-Theme-Name und PKGBUILD-pkgver aktualisiert.
This commit is contained in:
nevaforget 2026-03-26 15:26:12 +01:00
parent 357d2459cf
commit 3dfa596f9a
4 changed files with 66 additions and 7 deletions

View File

@ -5,4 +5,4 @@
# Absolute path to wallpaper image
background = "/usr/share/backgrounds/wallpaper.jpg"
# GTK theme for the greeter UI
gtk-theme = "Catppuccin-Mocha-Standard-Blue-Dark"
gtk-theme = "catppuccin-mocha-lavender-standard+default"

View File

@ -4,7 +4,7 @@
# Maintainer: Dominik Kressler
pkgname=moongreet-git
pkgver=0.1.0.r6.gba4f30f
pkgver=0.1.0.r7.g357d245
pkgrel=1
pkgdesc="A greetd greeter for Wayland, built with Python + GTK4 + gtk4-layer-shell"
arch=('any')

View File

@ -444,12 +444,15 @@ class GreeterWindow(Gtk.ApplicationWindow):
with self._greetd_sock_lock:
self._greetd_sock = sock
# Step 1: Create session
# Step 1: Create session — if a stale session exists, cancel it and retry
response = create_session(sock, user.username)
if response.get("type") == "error":
GLib.idle_add(self._on_login_error, response, self._strings.auth_failed)
return
cancel_session(sock)
response = create_session(sock, user.username)
if response.get("type") == "error":
GLib.idle_add(self._on_login_error, response, self._strings.auth_failed)
return
# Step 2: Send password if auth message received
if response.get("type") == "auth_message":
@ -458,6 +461,7 @@ class GreeterWindow(Gtk.ApplicationWindow):
if response.get("type") == "error":
self._failed_attempts[user.username] = self._failed_attempts.get(user.username, 0) + 1
warning = faillock_warning(self._failed_attempts[user.username], self._strings)
cancel_session(sock)
GLib.idle_add(self._on_login_auth_error, response, warning)
return

View File

@ -112,12 +112,13 @@ class TestLoginFlow:
assert mock.received[1] == {"type": "post_auth_message_response", "response": "geheim"}
assert mock.received[2] == {"type": "start_session", "cmd": ["Hyprland"]}
def test_wrong_password(self, tmp_path: Path) -> None:
"""Simulate a failed login due to wrong password."""
def test_wrong_password_sends_cancel(self, tmp_path: Path) -> None:
"""After a failed login, cancel_session must be sent to free the greetd session."""
sock_path = tmp_path / "greetd.sock"
mock = MockGreetd(sock_path)
mock.expect({"type": "auth_message", "auth_message_type": "secret", "auth_message": "Password:"})
mock.expect({"type": "error", "error_type": "auth_error", "description": "Authentication failed"})
mock.expect({"type": "success"}) # Response to cancel_session
mock.start()
try:
@ -131,10 +132,64 @@ class TestLoginFlow:
assert response["type"] == "error"
assert response["description"] == "Authentication failed"
# The greeter must cancel the session after auth failure
response = cancel_session(sock)
assert response["type"] == "success"
sock.close()
finally:
mock.stop()
assert mock.received[2] == {"type": "cancel_session"}
def test_stale_session_cancel_and_retry(self, tmp_path: Path) -> None:
"""When create_session fails due to a stale session, cancel and retry."""
sock_path = tmp_path / "greetd.sock"
mock = MockGreetd(sock_path)
# First create_session → error (stale session)
mock.expect({"type": "error", "error_type": "error", "description": "a session is already being configured"})
# cancel_session → success
mock.expect({"type": "success"})
# Second create_session → auth_message (retry succeeds)
mock.expect({"type": "auth_message", "auth_message_type": "secret", "auth_message": "Password:"})
# post_auth_response → success
mock.expect({"type": "success"})
# start_session → success
mock.expect({"type": "success"})
mock.start()
try:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(str(sock_path))
# Step 1: Create session fails
response = create_session(sock, "dominik")
assert response["type"] == "error"
# Step 2: Cancel stale session
response = cancel_session(sock)
assert response["type"] == "success"
# Step 3: Retry create session
response = create_session(sock, "dominik")
assert response["type"] == "auth_message"
# Step 4: Send password
response = post_auth_response(sock, "geheim")
assert response["type"] == "success"
# Step 5: Start session
response = start_session(sock, ["niri-session"])
assert response["type"] == "success"
sock.close()
finally:
mock.stop()
assert mock.received[0] == {"type": "create_session", "username": "dominik"}
assert mock.received[1] == {"type": "cancel_session"}
assert mock.received[2] == {"type": "create_session", "username": "dominik"}
def test_multi_stage_auth_sends_cancel(self, tmp_path: Path) -> None:
"""When greetd sends a second auth_message after password, cancel the session."""
sock_path = tmp_path / "greetd.sock"