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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import pytest
from pygitweb.permissions import (
PERMISSION_ADD_PROJECTS,
PERMISSION_SETTINGS,
Permission,
PermissionsMap,
parse_permission_key,
permission_key,
project_matches_scope,
)
def test_parse_global_keys() -> None:
assert parse_permission_key(PERMISSION_ADD_PROJECTS) is not None
assert parse_permission_key(PERMISSION_SETTINGS) is not None
assert parse_permission_key("pgw.unknown") is None
assert parse_permission_key("*") is None
def test_parse_scoped_key() -> None:
parsed = parse_permission_key("pgw.tasks.my/repo")
assert parsed is not None
assert parsed.permission is Permission.TASKS
assert parsed.project_scope == "my/repo"
def test_permission_key_roundtrip() -> None:
assert permission_key(Permission.READ, "foo") == "pgw.read.foo"
assert permission_key(Permission.WRITE, "foo") == "pgw.write.foo"
assert permission_key(Permission.BOARDS, "foo") == "pgw.boards.foo"
assert permission_key(Permission.MR_CREATE, "foo") == "pgw.mr.create.foo"
assert permission_key(Permission.MR_MERGE, "foo") == "pgw.mr.merge.foo"
assert permission_key(Permission.HOOKS, "foo") == "pgw.hooks.foo"
def test_parse_read_write_scoped_keys() -> None:
read = parse_permission_key("pgw.read.team/*")
assert read is not None
assert read.permission is Permission.READ
assert read.project_scope == "team/*"
write = parse_permission_key("pgw.write.private")
assert write is not None
assert write.permission is Permission.WRITE
assert write.project_scope == "private"
def test_parse_mr_scoped_keys() -> None:
create = parse_permission_key("pgw.mr.create.demo")
assert create is not None
assert create.permission is Permission.MR_CREATE
merge = parse_permission_key("pgw.mr.merge.demo")
assert merge is not None
assert merge.permission is Permission.MR_MERGE
def test_project_glob() -> None:
assert project_matches_scope("team/app", "team/*")
assert not project_matches_scope("other/app", "team/*")
assert project_matches_scope("anything", "*")
def test_superuser_grants_all() -> None:
m = PermissionsMap.model_validate({
"*": ["admin@example.com"],
"pgw.tasks.secret": ["other@example.com"],
})
assert m.has_permission("admin@example.com", Permission.TASKS, "secret")
assert m.has_permission("admin@example.com", Permission.SETTINGS)
assert m.has_permission("other@example.com", Permission.TASKS, "secret")
assert not m.has_permission("other@example.com", Permission.SETTINGS)
assert not m.has_permission("other@example.com", Permission.TASKS, "public")
def test_scoped_grant() -> None:
m = PermissionsMap.model_validate({
"*": [],
"pgw.comments.proj-a": ["reader@example.com"],
"pgw.boards.team/*": ["lead@example.com"],
})
assert m.has_permission("reader@example.com", Permission.COMMENTS, "proj-a")
assert not m.has_permission("reader@example.com", Permission.TASKS, "proj-a")
assert m.has_permission("lead@example.com", Permission.BOARDS, "team/widget")
assert not m.has_permission("lead@example.com", Permission.BOARDS, "other/widget")
def test_email_case_insensitive() -> None:
m = PermissionsMap.model_validate({"*": [], PERMISSION_SETTINGS: ["Admin@Example.COM"]})
assert m.has_permission("admin@example.com", Permission.SETTINGS)
def test_rejects_unknown_key() -> None:
with pytest.raises(ValueError, match="unknown permission key"):
PermissionsMap.model_validate({"pgw.notreal": ["a@b.com"]})
def test_ensures_wildcard_key() -> None:
m = PermissionsMap.model_validate({PERMISSION_ADD_PROJECTS: ["u@x.com"]})
assert "*" in m.root
def test_default_access_grants_read_write_to_any_authenticated_user() -> None:
m = PermissionsMap.default_access()
assert m.has_permission("alice", Permission.READ, "secret")
assert m.has_permission("bob@example.com", Permission.WRITE, "team/app")
assert not m.has_permission("", Permission.READ, "secret")
assert not m.has_permission(None, Permission.WRITE, "secret")
def test_scoped_read_write_without_wildcard_principal() -> None:
m = PermissionsMap.model_validate({
"*": [],
"pgw.read.public": ["reader@example.com"],
"pgw.write.public": ["writer@example.com"],
})
assert m.has_permission("reader@example.com", Permission.READ, "public")
assert not m.has_permission("reader@example.com", Permission.WRITE, "public")
assert m.has_permission("writer@example.com", Permission.WRITE, "public")
assert not m.has_permission("writer@example.com", Permission.READ, "private")
def test_owner_grants_read_and_write() -> None:
from unittest.mock import patch
from pygitweb.auth import can_read_project, can_write_project
from pygitweb.config import settings
from pygitweb.sessions import create_session
with (
patch.object(settings, "AUTH", True),
patch("pygitweb.auth.git_get_project_owner", return_value="owner@example.com"),
patch(
"pygitweb.auth.auth_config.oauth_permissions",
PermissionsMap.model_validate({"*": []}),
),
):
token = create_session("someone", email="owner@example.com", auth_method="local")
assert can_read_project("secret", token)
assert can_write_project("secret", token)
assert not can_read_project("secret", None)