feat(dashboard): drop token auth, trust oauth2-proxy X-Forwarded-User
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
We mock the Gitea HTTP layer (urllib.request.urlopen) so the test never
|
||||
touches the network. The dashboard handler is responsible for:
|
||||
* auth + CSRF
|
||||
* auth (X-Forwarded-User set by oauth2-proxy) + CSRF
|
||||
* read .pr-review.json via GET (404 → start from {})
|
||||
* validate model against cost_model.PRICES
|
||||
* PUT the updated file back, with sha + base64 content
|
||||
@@ -35,12 +35,12 @@ def _free_port() -> int:
|
||||
return port
|
||||
|
||||
|
||||
def _post(port: int, path: str, body: bytes, *, cookie: str | None = None) -> tuple[int, dict, bytes]:
|
||||
def _post(port: int, path: str, body: bytes, *, headers: dict | None = None) -> tuple[int, dict, bytes]:
|
||||
conn = http.client.HTTPConnection("127.0.0.1", port, timeout=5)
|
||||
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
||||
if cookie:
|
||||
headers["Cookie"] = cookie
|
||||
conn.request("POST", path, body=body, headers=headers)
|
||||
hdrs = {"Content-Type": "application/x-www-form-urlencoded"}
|
||||
if headers:
|
||||
hdrs.update(headers)
|
||||
conn.request("POST", path, body=body, headers=hdrs)
|
||||
r = conn.getresponse()
|
||||
body_b = r.read()
|
||||
h = dict(r.getheaders())
|
||||
@@ -68,7 +68,6 @@ class TestDashboardEdit(unittest.TestCase):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.db = f"{self.tmp.name}/f.db"
|
||||
os.environ["PRAGENT_FEEDBACK_DB"] = self.db
|
||||
os.environ["PRAGENT_DASHBOARD_TOKEN"] = "secret-edit-token"
|
||||
os.environ["PRAGENT_BOT_TOKEN"] = "bot-token"
|
||||
# Seed a row so the repo page is meaningful.
|
||||
conn = feedback.init(self.db)
|
||||
@@ -82,7 +81,10 @@ class TestDashboardEdit(unittest.TestCase):
|
||||
self.srv = ThreadingHTTPServer(("127.0.0.1", self.port), dash.Handler)
|
||||
self.thread = threading.Thread(target=self.srv.serve_forever, daemon=True)
|
||||
self.thread.start()
|
||||
self.cookie = "pragent_dash=secret-edit-token"
|
||||
# Pull the per-process CSRF secret from the rendered repo page — the
|
||||
# edit form embeds the same token as a hidden input.
|
||||
self.csrf = dash._CSRF_SECRET
|
||||
self.auth_hdr = {"X-Forwarded-User": "marcos@example.com"}
|
||||
|
||||
# Records of HTTP calls made by the handler.
|
||||
self.calls: list[tuple[str, str, dict | None, bytes | None]] = []
|
||||
@@ -91,7 +93,7 @@ class TestDashboardEdit(unittest.TestCase):
|
||||
self.srv.shutdown()
|
||||
self.srv.server_close()
|
||||
self.thread.join(timeout=2)
|
||||
for k in ("PRAGENT_FEEDBACK_DB", "PRAGENT_DASHBOARD_TOKEN", "PRAGENT_BOT_TOKEN"):
|
||||
for k in ("PRAGENT_FEEDBACK_DB", "PRAGENT_BOT_TOKEN"):
|
||||
os.environ.pop(k, None)
|
||||
self.tmp.cleanup()
|
||||
|
||||
@@ -114,12 +116,12 @@ class TestDashboardEdit(unittest.TestCase):
|
||||
|
||||
def test_edit_updates_static_message_and_model(self):
|
||||
form = (
|
||||
b"_csrf=secret-edit-token"
|
||||
b"&static_message=Hello%20world"
|
||||
b"&model=claude-sonnet-5"
|
||||
)
|
||||
f"_csrf={self.csrf}"
|
||||
f"&static_message=Hello%20world"
|
||||
f"&model=claude-sonnet-5"
|
||||
).encode()
|
||||
with patch.object(dash.urllib.request, "urlopen", side_effect=self._urlopen):
|
||||
status, h, _b = _post(self.port, "/r/o/r/edit", form, cookie=self.cookie)
|
||||
status, h, _b = _post(self.port, "/r/o/r/edit", form, headers=self.auth_hdr)
|
||||
self.assertEqual(status, 302)
|
||||
self.assertEqual(h.get("Location"), "/r/o/r")
|
||||
|
||||
@@ -139,24 +141,24 @@ class TestDashboardEdit(unittest.TestCase):
|
||||
def test_edit_strips_static_message_to_400(self):
|
||||
long_msg = "x" * 600
|
||||
form = (
|
||||
f"_csrf=secret-edit-token"
|
||||
f"_csrf={self.csrf}"
|
||||
f"&static_message={long_msg}"
|
||||
f"&model=claude-haiku-4-5"
|
||||
).encode()
|
||||
with patch.object(dash.urllib.request, "urlopen", side_effect=self._urlopen):
|
||||
_post(self.port, "/r/o/r/edit", form, cookie=self.cookie)
|
||||
_post(self.port, "/r/o/r/edit", form, headers=self.auth_hdr)
|
||||
put = next(c for c in self.calls if c[0] == "PUT")
|
||||
cfg = json.loads(base64.b64decode(json.loads(put[3])["content"]))
|
||||
self.assertEqual(len(cfg["static_message"]), 400)
|
||||
|
||||
def test_edit_rejects_unknown_model_with_flash(self):
|
||||
form = (
|
||||
b"_csrf=secret-edit-token"
|
||||
b"&static_message=hi"
|
||||
b"&model=does-not-exist"
|
||||
)
|
||||
f"_csrf={self.csrf}"
|
||||
f"&static_message=hi"
|
||||
f"&model=does-not-exist"
|
||||
).encode()
|
||||
with patch.object(dash.urllib.request, "urlopen", side_effect=self._urlopen):
|
||||
status, h, _b = _post(self.port, "/r/o/r/edit", form, cookie=self.cookie)
|
||||
status, h, _b = _post(self.port, "/r/o/r/edit", form, headers=self.auth_hdr)
|
||||
self.assertEqual(status, 302)
|
||||
self.assertIn("flash=", h.get("Location", ""))
|
||||
# No PUT should have been issued.
|
||||
@@ -164,16 +166,18 @@ class TestDashboardEdit(unittest.TestCase):
|
||||
self.assertEqual(put_calls, [])
|
||||
|
||||
def test_edit_requires_auth(self):
|
||||
form = b"_csrf=secret-edit-token&static_message=x&model=claude-haiku-4-5"
|
||||
form = f"_csrf={self.csrf}&static_message=x&model=claude-haiku-4-5".encode()
|
||||
with patch.object(dash.urllib.request, "urlopen", side_effect=self._urlopen):
|
||||
status, h, _b = _post(self.port, "/r/o/r/edit", form)
|
||||
self.assertEqual(status, 302)
|
||||
self.assertEqual(h.get("Location"), "/login")
|
||||
self.assertEqual(status, 401)
|
||||
self.assertEqual(h.get("WWW-Authenticate"), 'Basic realm="pragent-dashboard"')
|
||||
# No Gitea calls at all — auth gate fires first.
|
||||
self.assertEqual(self.calls, [])
|
||||
|
||||
def test_edit_csrf_mismatch_redirects_without_save(self):
|
||||
form = b"_csrf=wrong&static_message=x&model=claude-haiku-4-5"
|
||||
form = f"_csrf=wrong&static_message=x&model=claude-haiku-4-5".encode()
|
||||
with patch.object(dash.urllib.request, "urlopen", side_effect=self._urlopen):
|
||||
status, h, _b = _post(self.port, "/r/o/r/edit", form, cookie=self.cookie)
|
||||
status, h, _b = _post(self.port, "/r/o/r/edit", form, headers=self.auth_hdr)
|
||||
self.assertEqual(status, 302)
|
||||
self.assertEqual(h.get("Location"), "/r/o/r")
|
||||
put_calls = [c for c in self.calls if c[0] == "PUT"]
|
||||
@@ -192,9 +196,9 @@ class TestDashboardEdit(unittest.TestCase):
|
||||
return _FakeResp(201, b"{}")
|
||||
return _FakeResp(404, b"")
|
||||
|
||||
form = b"_csrf=secret-edit-token&static_message=hi&model=claude-haiku-4-5"
|
||||
form = f"_csrf={self.csrf}&static_message=hi&model=claude-haiku-4-5".encode()
|
||||
with patch.object(dash.urllib.request, "urlopen", side_effect=_route):
|
||||
status, h, _b = _post(self.port, "/r/o/r/edit", form, cookie=self.cookie)
|
||||
status, h, _b = _post(self.port, "/r/o/r/edit", form, headers=self.auth_hdr)
|
||||
self.assertEqual(status, 302)
|
||||
put = next(c for c in self.calls if c[0] == "PUT")
|
||||
payload = json.loads(put[3])
|
||||
@@ -202,4 +206,4 @@ class TestDashboardEdit(unittest.TestCase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user