gateway/scripts/check_no_sysadmin_role.py

108 lines
3.1 KiB
Python

"""CI-Gate: Stelle sicher, dass keine Verweise auf die abgeschaffte
``sysadmin``-Rolle bzw. die alten Helper im Codebase mehr existieren.
Verbotene Symbole nach Abschluss von ``2026-04-sysadmin-authority-split``:
- ``hasSysAdminRole`` (RequestContext property)
- ``requireSysAdminRole`` (FastAPI dependency)
- ``_hasSysAdminRole`` (Hilfs-Funktion gegen die alte Rolle)
Erlaubt sind weiterhin:
- ``isSysAdmin`` (User-Flag fuer Infrastruktur-Operator)
- ``isPlatformAdmin`` (User-Flag fuer Cross-Mandate-Governance)
- ``requireSysAdmin`` / ``requirePlatformAdmin`` (FastAPI Dependencies)
Exit-Code:
- 0 -> sauber
- 1 -> Fundstellen vorhanden (CI bricht ab)
Aufruf::
python gateway/scripts/check_no_sysadmin_role.py
"""
from __future__ import annotations
import os
import re
import sys
from pathlib import Path
from typing import Iterable, List, Tuple
_REPO_ROOT = Path(__file__).resolve().parents[2]
_FORBIDDEN_PATTERNS: Tuple[Tuple[str, str], ...] = (
(r"\bhasSysAdminRole\b", "Use ctx.isPlatformAdmin (Governance) or ctx.isSysAdmin (Infra)"),
(r"\brequireSysAdminRole\b", "Use requirePlatformAdmin (Governance) or requireSysAdmin (Infra)"),
(r"\b_hasSysAdminRole\b", "Use User.isPlatformAdmin flag check directly"),
)
_INCLUDE_SUFFIXES = {".py", ".ts", ".tsx", ".js", ".jsx"}
_EXCLUDE_DIR_NAMES = {
".git",
"node_modules",
"dist",
"build",
"__pycache__",
".venv",
"venv",
".pytest_cache",
".mypy_cache",
"wiki",
"scripts",
}
def _shouldScan(path: Path) -> bool:
if path.suffix not in _INCLUDE_SUFFIXES:
return False
parts = set(path.parts)
if parts & _EXCLUDE_DIR_NAMES:
return False
return True
def _iterFiles(root: Path) -> Iterable[Path]:
for dirpath, dirnames, filenames in os.walk(root):
dirnames[:] = [d for d in dirnames if d not in _EXCLUDE_DIR_NAMES]
for name in filenames:
full = Path(dirpath) / name
if _shouldScan(full):
yield full
def _scanFile(path: Path) -> List[Tuple[int, str, str, str]]:
findings: List[Tuple[int, str, str, str]] = []
try:
text = path.read_text(encoding="utf-8", errors="ignore")
except Exception:
return findings
for pattern, hint in _FORBIDDEN_PATTERNS:
compiled = re.compile(pattern)
for lineNo, line in enumerate(text.splitlines(), start=1):
if compiled.search(line):
findings.append((lineNo, pattern, hint, line.rstrip()))
return findings
def _main() -> int:
findings = []
for filePath in _iterFiles(_REPO_ROOT):
for entry in _scanFile(filePath):
findings.append((filePath, *entry))
if not findings:
print("[OK] No legacy sysadmin-role references found.")
return 0
print("[FAIL] Found legacy sysadmin-role references:")
for filePath, lineNo, pattern, hint, line in findings:
rel = filePath.relative_to(_REPO_ROOT)
print(f" {rel}:{lineNo}: {pattern}\n hint: {hint}\n line: {line}")
print(f"\n[FAIL] {len(findings)} forbidden reference(s).")
return 1
if __name__ == "__main__":
sys.exit(_main())