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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
|
"""
Auth-flow integration tests for genenetwork3 protected endpoints.
Tests that GN3's `@require_token` decorator correctly rejects requests
that carry no bearer token or a malformed/invalid token. These tests do
not require database state — the rejection happens before any DB access.
Endpoints under test are in `gn3/api/case_attributes.py`:
POST /api/case-attribute/<inbredset_id>/edit
POST /api/case-attribute/<inbredset_id>/approve/<change_id>
POST /api/case-attribute/<inbredset_id>/reject/<change_id>
Via the nginx proxy the external URL prefix is `/api3/`, so the full CD
URL for the first endpoint is:
https://cd.genenetwork.org/api3/case-attribute/1/edit
Run with:
pytest -m "gn3 and auth_flow"
"""
import pytest
pytestmark = [pytest.mark.gn3, pytest.mark.auth_flow]
# Arbitrary but valid-looking id. The auth rejection happens before any
# DB lookup so the exact value does not matter.
_INBREDSET_ID = 1
_CHANGE_ID = 1
_INVALID_TOKEN = "Bearer this-is-not-a-valid-jwt"
# ---------------------------------------------------------------------------
# POST /case-attribute/<id>/edit — token enforcement
# ---------------------------------------------------------------------------
def test_edit_no_token_returns_400(gn3_url, http):
resp = http.post(
f"{gn3_url}/case-attribute/{_INBREDSET_ID}/edit",
json={},
timeout=30,
)
assert resp.status_code == 400, (
f"Expected 400 when no token supplied, got {resp.status_code}: {resp.text}"
)
def test_edit_no_token_error_is_token_validation_error(gn3_url, http):
resp = http.post(
f"{gn3_url}/case-attribute/{_INBREDSET_ID}/edit",
json={},
timeout=30,
)
assert resp.json().get("error") == "TokenValidationError", (
f"Expected error='TokenValidationError', got: {resp.json()}"
)
def test_edit_invalid_token_returns_400(gn3_url, http):
resp = http.post(
f"{gn3_url}/case-attribute/{_INBREDSET_ID}/edit",
json={},
headers={"Authorization": _INVALID_TOKEN},
timeout=30,
)
assert resp.status_code == 400, (
f"Expected 400 for invalid token, got {resp.status_code}: {resp.text}"
)
def test_edit_invalid_token_error_is_token_validation_error(gn3_url, http):
resp = http.post(
f"{gn3_url}/case-attribute/{_INBREDSET_ID}/edit",
json={},
headers={"Authorization": _INVALID_TOKEN},
timeout=30,
)
assert resp.json().get("error") == "TokenValidationError", (
f"Expected error='TokenValidationError', got: {resp.json()}"
)
# ---------------------------------------------------------------------------
# POST /case-attribute/<id>/approve/<change_id> — token enforcement
# ---------------------------------------------------------------------------
def test_approve_no_token_returns_400(gn3_url, http):
resp = http.post(
f"{gn3_url}/case-attribute/{_INBREDSET_ID}/approve/{_CHANGE_ID}",
json={},
timeout=30,
)
assert resp.status_code == 400, (
f"Expected 400 when no token supplied, got {resp.status_code}: {resp.text}"
)
def test_approve_no_token_error_is_token_validation_error(gn3_url, http):
resp = http.post(
f"{gn3_url}/case-attribute/{_INBREDSET_ID}/approve/{_CHANGE_ID}",
json={},
timeout=30,
)
assert resp.json().get("error") == "TokenValidationError", (
f"Expected error='TokenValidationError', got: {resp.json()}"
)
def test_approve_invalid_token_returns_400(gn3_url, http):
resp = http.post(
f"{gn3_url}/case-attribute/{_INBREDSET_ID}/approve/{_CHANGE_ID}",
json={},
headers={"Authorization": _INVALID_TOKEN},
timeout=30,
)
assert resp.status_code == 400, (
f"Expected 400 for invalid token, got {resp.status_code}: {resp.text}"
)
def test_approve_invalid_token_error_is_token_validation_error(gn3_url, http):
resp = http.post(
f"{gn3_url}/case-attribute/{_INBREDSET_ID}/approve/{_CHANGE_ID}",
json={},
headers={"Authorization": _INVALID_TOKEN},
timeout=30,
)
assert resp.json().get("error") == "TokenValidationError", (
f"Expected error='TokenValidationError', got: {resp.json()}"
)
# ---------------------------------------------------------------------------
# POST /case-attribute/<id>/reject/<change_id> — token enforcement
# ---------------------------------------------------------------------------
def test_reject_no_token_returns_400(gn3_url, http):
resp = http.post(
f"{gn3_url}/case-attribute/{_INBREDSET_ID}/reject/{_CHANGE_ID}",
json={},
timeout=30,
)
assert resp.status_code == 400, (
f"Expected 400 when no token supplied, got {resp.status_code}: {resp.text}"
)
def test_reject_no_token_error_is_token_validation_error(gn3_url, http):
resp = http.post(
f"{gn3_url}/case-attribute/{_INBREDSET_ID}/reject/{_CHANGE_ID}",
json={},
timeout=30,
)
assert resp.json().get("error") == "TokenValidationError", (
f"Expected error='TokenValidationError', got: {resp.json()}"
)
def test_reject_invalid_token_returns_400(gn3_url, http):
resp = http.post(
f"{gn3_url}/case-attribute/{_INBREDSET_ID}/reject/{_CHANGE_ID}",
json={},
headers={"Authorization": _INVALID_TOKEN},
timeout=30,
)
assert resp.status_code == 400, (
f"Expected 400 for invalid token, got {resp.status_code}: {resp.text}"
)
def test_reject_invalid_token_error_is_token_validation_error(gn3_url, http):
resp = http.post(
f"{gn3_url}/case-attribute/{_INBREDSET_ID}/reject/{_CHANGE_ID}",
json={},
headers={"Authorization": _INVALID_TOKEN},
timeout=30,
)
assert resp.json().get("error") == "TokenValidationError", (
f"Expected error='TokenValidationError', got: {resp.json()}"
)
def test_edit_valid_token_is_not_rejected_by_auth(gn3_url, http, access_token):
"""Valid token reaches the endpoint handler; response is not TokenValidationError.
Requires live auth credentials (set by the CI test-session lifecycle).
"""
resp = http.post(
f"{gn3_url}/case-attribute/{_INBREDSET_ID}/edit",
json={"edit-data": []},
headers={"Authorization": f"Bearer {access_token}"},
timeout=30,
)
assert resp.json().get("error") != "TokenValidationError", (
f"Valid token should not be rejected by @require_token, got: {resp.json()}"
)
|