fix: Audit-Findings — Login-Thread, Traversable-Assets, Session-Exec
- Login-IPC in Background-Thread ausgelagert, UI friert nicht mehr ein bei langsamem PAM/greetd (PERF-2/BUG-4) - importlib.resources korrekt via as_file()/read_text() statt Path(str()) — funktioniert in ZIP-Wheels (BUG-1) - is_absolute()-Check für Session-Exec entfernt, greetd löst PATH selbst auf — relative Executables wie 'sway' blockieren nicht mehr (LOGIC-1) - ValueError (json.JSONDecodeError) im except abgefangen (BUG-2)
This commit is contained in:
parent
9a964aaecb
commit
65d3ba64f9
@ -6,7 +6,8 @@ import shlex
|
||||
import socket
|
||||
import stat
|
||||
import subprocess
|
||||
from importlib.resources import files
|
||||
import threading
|
||||
from importlib.resources import as_file, files
|
||||
from pathlib import Path
|
||||
|
||||
import gi
|
||||
@ -72,7 +73,9 @@ class GreeterWindow(Gtk.ApplicationWindow):
|
||||
# Background wallpaper (blurred and darkened)
|
||||
bg_path = self._config.background
|
||||
if not bg_path or not bg_path.exists():
|
||||
bg_path = Path(str(DEFAULT_WALLPAPER_PATH))
|
||||
# Extract package wallpaper to a real filesystem path (works in ZIP wheels too)
|
||||
self._wallpaper_ctx = as_file(DEFAULT_WALLPAPER_PATH)
|
||||
bg_path = self._wallpaper_ctx.__enter__()
|
||||
if bg_path.exists():
|
||||
background = Gtk.Picture()
|
||||
background.set_filename(str(bg_path))
|
||||
@ -249,10 +252,10 @@ class GreeterWindow(Gtk.ApplicationWindow):
|
||||
)
|
||||
if avatar_path and avatar_path.exists():
|
||||
self._set_avatar_from_file(avatar_path, user.username)
|
||||
elif DEFAULT_AVATAR_PATH.exists():
|
||||
self._set_default_avatar()
|
||||
else:
|
||||
self._avatar_image.set_from_icon_name("avatar-default-symbolic")
|
||||
# Default avatar — _set_default_avatar uses Traversable.read_text()
|
||||
# which works in ZIP wheels too, no exists() check needed
|
||||
self._set_default_avatar()
|
||||
|
||||
# Apply user's GTK theme if available
|
||||
self._apply_user_theme(user)
|
||||
@ -379,6 +382,11 @@ class GreeterWindow(Gtk.ApplicationWindow):
|
||||
pass
|
||||
self._greetd_sock = None
|
||||
|
||||
def _set_login_sensitive(self, sensitive: bool) -> None:
|
||||
"""Enable or disable login controls during authentication."""
|
||||
self._password_entry.set_sensitive(sensitive)
|
||||
self._session_dropdown.set_sensitive(sensitive)
|
||||
|
||||
def _attempt_login(self, user: User, password: str, session: Session) -> None:
|
||||
"""Attempt to authenticate and start a session via greetd IPC."""
|
||||
sock_path = os.environ.get("GREETD_SOCK")
|
||||
@ -389,6 +397,17 @@ class GreeterWindow(Gtk.ApplicationWindow):
|
||||
if not self._validate_greetd_sock(sock_path):
|
||||
return
|
||||
|
||||
# Disable UI while authenticating — the IPC runs in a background thread
|
||||
self._set_login_sensitive(False)
|
||||
thread = threading.Thread(
|
||||
target=self._login_worker,
|
||||
args=(user, password, session, sock_path),
|
||||
daemon=True,
|
||||
)
|
||||
thread.start()
|
||||
|
||||
def _login_worker(self, user: User, password: str, session: Session, sock_path: str) -> None:
|
||||
"""Run greetd IPC in a background thread to avoid blocking the GTK main loop."""
|
||||
try:
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
sock.settimeout(10.0)
|
||||
@ -399,8 +418,7 @@ class GreeterWindow(Gtk.ApplicationWindow):
|
||||
response = create_session(sock, user.username)
|
||||
|
||||
if response.get("type") == "error":
|
||||
self._show_greetd_error(response, self._strings.auth_failed)
|
||||
self._close_greetd_sock()
|
||||
GLib.idle_add(self._on_login_error, response, self._strings.auth_failed)
|
||||
return
|
||||
|
||||
# Step 2: Send password if auth message received
|
||||
@ -409,47 +427,59 @@ class GreeterWindow(Gtk.ApplicationWindow):
|
||||
|
||||
if response.get("type") == "error":
|
||||
self._failed_attempts[user.username] = self._failed_attempts.get(user.username, 0) + 1
|
||||
self._show_greetd_error(response, self._strings.wrong_password)
|
||||
warning = faillock_warning(self._failed_attempts[user.username], self._strings)
|
||||
if warning:
|
||||
current = self._error_label.get_text()
|
||||
self._error_label.set_text(f"{current}\n{warning}")
|
||||
self._close_greetd_sock()
|
||||
GLib.idle_add(self._on_login_auth_error, response, warning)
|
||||
return
|
||||
|
||||
if response.get("type") == "auth_message":
|
||||
# Multi-stage auth (e.g. TOTP) is not supported
|
||||
cancel_session(sock)
|
||||
self._close_greetd_sock()
|
||||
self._show_error(self._strings.multi_stage_unsupported)
|
||||
GLib.idle_add(self._on_login_error, None, self._strings.multi_stage_unsupported)
|
||||
return
|
||||
|
||||
# Step 3: Start session
|
||||
if response.get("type") == "success":
|
||||
cmd = shlex.split(session.exec_cmd)
|
||||
if not cmd or not Path(cmd[0]).is_absolute():
|
||||
self._show_error(self._strings.invalid_session_command)
|
||||
if not cmd:
|
||||
cancel_session(sock)
|
||||
self._close_greetd_sock()
|
||||
GLib.idle_add(self._on_login_error, None, self._strings.invalid_session_command)
|
||||
return
|
||||
response = start_session(sock, cmd)
|
||||
|
||||
if response.get("type") == "success":
|
||||
self._save_last_user(user.username)
|
||||
self._close_greetd_sock()
|
||||
self.get_application().quit()
|
||||
GLib.idle_add(self.get_application().quit)
|
||||
return
|
||||
else:
|
||||
self._show_greetd_error(response, self._strings.session_start_failed)
|
||||
GLib.idle_add(self._on_login_error, response, self._strings.session_start_failed)
|
||||
|
||||
self._close_greetd_sock()
|
||||
|
||||
except ConnectionError as e:
|
||||
self._close_greetd_sock()
|
||||
self._show_error(self._strings.connection_error.format(error=e))
|
||||
except OSError as e:
|
||||
GLib.idle_add(self._on_login_error, None, self._strings.connection_error.format(error=e))
|
||||
except (OSError, ValueError) as e:
|
||||
self._close_greetd_sock()
|
||||
self._show_error(self._strings.socket_error.format(error=e))
|
||||
GLib.idle_add(self._on_login_error, None, self._strings.socket_error.format(error=e))
|
||||
|
||||
def _on_login_error(self, response: dict | None, message: str) -> None:
|
||||
"""Handle login error on the GTK main thread."""
|
||||
if response:
|
||||
self._show_greetd_error(response, message)
|
||||
else:
|
||||
self._show_error(message)
|
||||
self._close_greetd_sock()
|
||||
self._set_login_sensitive(True)
|
||||
|
||||
def _on_login_auth_error(self, response: dict, warning: str | None) -> None:
|
||||
"""Handle authentication failure with optional faillock warning on the GTK main thread."""
|
||||
self._show_greetd_error(response, self._strings.wrong_password)
|
||||
if warning:
|
||||
current = self._error_label.get_text()
|
||||
self._error_label.set_text(f"{current}\n{warning}")
|
||||
self._close_greetd_sock()
|
||||
self._set_login_sensitive(True)
|
||||
|
||||
def _cancel_pending_session(self) -> None:
|
||||
"""Cancel any in-progress greetd session."""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user