from __future__ import annotations import pwd import subprocess def user_exists( username: str, ) -> bool: try: pwd.getpwnam( username ) return True except KeyError: return False def _run( command: list[str], ) -> subprocess.CompletedProcess: return subprocess.run( command, capture_output=True, text=True, check=False, ) def is_locked( username: str, ) -> bool: if not user_exists(username): return False result = _run( [ "passwd", "-S", username, ] ) if result.returncode != 0: return False parts = result.stdout.strip().split() if len(parts) < 2: return False status = parts[1].upper() return status.startswith("L") def lock_user( username: str, ) -> None: if not user_exists(username): raise RuntimeError( f"Linux user does not exist: {username}" ) result = _run( [ "passwd", "-l", username, ] ) if result.returncode != 0: error = ( result.stderr.strip() or result.stdout.strip() or "unknown error" ) raise RuntimeError( f"Unable to lock {username}: {error}" ) def unlock_user( username: str, ) -> None: if not user_exists(username): raise RuntimeError( f"Linux user does not exist: {username}" ) result = _run( [ "passwd", "-u", username, ] ) if result.returncode != 0: error = ( result.stderr.strip() or result.stdout.strip() or "unknown error" ) raise RuntimeError( f"Unable to unlock {username}: {error}" ) def terminate_user( username: str, ) -> None: if not user_exists(username): return result = _run( [ "loginctl", "terminate-user", username, ] ) if result.returncode != 0: error = ( result.stderr.strip() or result.stdout.strip() ) if error: raise RuntimeError( f"Unable to terminate " f"{username}: {error}" ) def get_uid( username: str, ) -> int | None: try: return pwd.getpwnam( username ).pw_uid except KeyError: return None def get_home( username: str, ) -> str | None: try: return pwd.getpwnam( username ).pw_dir except KeyError: return None