From 3dfa596f9ac8996e58a14c9875144ef7d0ac6c71 Mon Sep 17 00:00:00 2001 From: nevaforget Date: Thu, 26 Mar 2026 15:26:12 +0100 Subject: [PATCH] fix: greetd-Session nach Auth-Fehler sauber canceln MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- config/moongreet.toml | 2 +- pkg/PKGBUILD | 2 +- src/moongreet/greeter.py | 10 +++++-- tests/test_integration.py | 59 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 66 insertions(+), 7 deletions(-) diff --git a/config/moongreet.toml b/config/moongreet.toml index 7259477..01557e0 100644 --- a/config/moongreet.toml +++ b/config/moongreet.toml @@ -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" diff --git a/pkg/PKGBUILD b/pkg/PKGBUILD index a339437..ad1788d 100644 --- a/pkg/PKGBUILD +++ b/pkg/PKGBUILD @@ -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') diff --git a/src/moongreet/greeter.py b/src/moongreet/greeter.py index 3722721..4ddd496 100644 --- a/src/moongreet/greeter.py +++ b/src/moongreet/greeter.py @@ -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 diff --git a/tests/test_integration.py b/tests/test_integration.py index cd394fc..c251ea8 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -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"