#!/usr/bin/env python3 """Atomically migrate a Blueforce device identity to a new dealer number.""" from __future__ import annotations import os import shutil import subprocess import sys from pathlib import Path IDENTITY_FILE = Path(os.environ.get("BF_IDENTITY_FILE", "/etc/blueforce/dealer-id")) AUDIT_FILE = Path(os.environ.get("BF_IDENTITY_AUDIT", "/var/log/blueforce-identity.log")) def run(*args: str) -> None: subprocess.run(args, check=True, text=True, capture_output=True) def valid(value: str) -> bool: return len(value) == 8 and value.isdigit() def audit(message: str) -> None: AUDIT_FILE.parent.mkdir(parents=True, exist_ok=True) with AUDIT_FILE.open("a", encoding="utf-8") as handle: handle.write(message + "\n") AUDIT_FILE.chmod(0o600) def main() -> int: if os.geteuid() != 0: print("HATA: bayi kimliği geçişi için sudo bf gerekir.", file=sys.stderr) return 3 if len(sys.argv) != 2 or not valid(sys.argv[1]): print("Kullanım: sudo bf-reidentity YYYYYYYY", file=sys.stderr) return 2 new_id = sys.argv[1] try: old_id = IDENTITY_FILE.read_text(encoding="utf-8").strip() except OSError as exc: print(f"HATA: mevcut bayi kimliği okunamadı: {exc}", file=sys.stderr) return 1 if not valid(old_id) or old_id == new_id: print("HATA: yeni kimlik mevcut kimlikten farklı ve 8 haneli olmalıdır.", file=sys.stderr) return 2 old_user = f"bf-{old_id}" new_user = f"bf-{new_id}" if subprocess.run(("id", "-u", new_user), capture_output=True).returncode == 0: print(f"HATA: hedef kullanıcı zaten var: {new_user}", file=sys.stderr) return 1 if subprocess.run(("id", "-u", old_user), capture_output=True).returncode != 0: print(f"HATA: mevcut kullanıcı bulunamadı: {old_user}", file=sys.stderr) return 1 old_group = old_user old_home = Path(f"/home/{old_user}") new_home = Path(f"/home/{new_user}") old_hostname = subprocess.run(("hostname",), check=True, capture_output=True, text=True).stdout.strip() renamed = False group_renamed = False moved_sudo = moved_ssh = False new_sudo = Path(f"/etc/sudoers.d/{new_user}") old_sudo = Path(f"/etc/sudoers.d/{old_user}") new_ssh = Path(f"/etc/ssh/sshd_config.d/99-{new_user}.conf") old_ssh = Path(f"/etc/ssh/sshd_config.d/99-{old_user}.conf") try: audit(f"identity-change requested old={old_id} new={new_id}") run("usermod", "-l", new_user, old_user) renamed = True if subprocess.run(("getent", "group", old_group), capture_output=True).returncode == 0: run("groupmod", "-n", new_user, old_group) group_renamed = True run("usermod", "-d", f"/home/{new_user}", "-m", new_user) if old_sudo.exists(): shutil.move(old_sudo, new_sudo) moved_sudo = True text = new_sudo.read_text(encoding="utf-8").replace(old_user, new_user) new_sudo.write_text(text, encoding="utf-8") run("visudo", "-cf", str(new_sudo)) if old_ssh.exists(): shutil.move(old_ssh, new_ssh) moved_ssh = True text = new_ssh.read_text(encoding="utf-8").replace(f"AllowUsers {old_user}", f"AllowUsers {new_user}") new_ssh.write_text(text, encoding="utf-8") run("sshd", "-t") tmp = IDENTITY_FILE.with_suffix(".tmp") tmp.write_text(new_id + "\n", encoding="utf-8") tmp.chmod(0o600) os.replace(tmp, IDENTITY_FILE) run("hostnamectl", "set-hostname", new_user) audit(f"identity-change completed old={old_id} new={new_id} user={new_user}") print(f"OK: {old_id} -> {new_id} ({new_user})") return 0 except Exception as exc: try: if IDENTITY_FILE.read_text(encoding="utf-8").strip() == new_id: IDENTITY_FILE.write_text(old_id + "\n", encoding="utf-8") IDENTITY_FILE.chmod(0o600) if moved_ssh and new_ssh.exists(): shutil.move(new_ssh, old_ssh) if moved_sudo and new_sudo.exists(): shutil.move(new_sudo, old_sudo) if renamed: run("usermod", "-d", str(old_home), "-m", new_user) if group_renamed: run("groupmod", "-n", old_group, new_user) if renamed: run("usermod", "-l", old_user, new_user) if old_hostname: run("hostnamectl", "set-hostname", old_hostname) except Exception: pass audit(f"identity-change failed old={old_id} new={new_id}: {exc}") print(f"HATA: kimlik geçişi geri alınmaya çalışıldı: {exc}", file=sys.stderr) return 1 if __name__ == "__main__": raise SystemExit(main())