1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#!/usr/bin/env -S uv run python
from __future__ import annotations
__VERSION__ = "1"
import os
import sys
from pathlib import Path
from pygittools.hooks import HookResult, RefUpdate, parse_ref_updates
from pygitweb.auth_config import DEFAULT_AUTH_CONFIG_PATH, AuthConfig
from pygitweb.permissions import PermissionsMap, has_branch_write_permission
def _normalize_env(name: str) -> str | None:
value = os.environ.get(name)
if value is None:
return None
stripped = value.strip()
return stripped or None
def _load_permissions() -> PermissionsMap | None:
config_path_env = _normalize_env("PYGITWEB_AUTH_CONFIG")
path = Path(config_path_env).expanduser() if config_path_env is not None else DEFAULT_AUTH_CONFIG_PATH
try:
text = path.read_text(encoding="utf-8")
except OSError:
return None
try:
config = AuthConfig.model_validate_json(text)
except Exception:
return None
return config.oauth_permissions
def _check_updates(
principal: str,
project: str,
updates: list[RefUpdate],
permissions: PermissionsMap,
) -> HookResult:
for _, _, ref_name in updates:
if has_branch_write_permission(permissions, principal, project, ref_name):
continue
sys.stderr.write(
f"Push rejected: {principal!r} lacks branch write permission for project {project!r} ref {ref_name!r}.\n",
)
return HookResult.FAILURE
return HookResult.SUCCESS
def main() -> int:
principal = _normalize_env("PYGITWEB_PRINCIPAL")
project = _normalize_env("PYGITWEB_PROJECT")
if principal is None or project is None:
return int(HookResult.SUCCESS.value)
permissions = _load_permissions()
if permissions is None:
return int(HookResult.SUCCESS.value)
updates = parse_ref_updates(sys.stdin)
result = _check_updates(principal, project, updates, permissions)
return int(result.value)
if __name__ == "__main__":
raise SystemExit(main())