From 5ce68b58f39269387b9e90de51e6c4088d901b23 Mon Sep 17 00:00:00 2001 From: TheGeneralist <180094941+thegeneralist01@users.noreply.github.com> Date: Mon, 29 Jun 2026 20:13:31 +0200 Subject: [PATCH] feat(server): require auth on list_entries and search_entries_handler Both handlers accepted AuthUser but served guests. auth.require_auth()? added as first line closes the gap. Existing unauthenticated tests updated with session cookies. --- crates/archivr-server/src/routes.rs | 105 ++++++++++++++++++---------- 1 file changed, 68 insertions(+), 37 deletions(-) diff --git a/crates/archivr-server/src/routes.rs b/crates/archivr-server/src/routes.rs index 69b058c..18f12ff 100644 --- a/crates/archivr-server/src/routes.rs +++ b/crates/archivr-server/src/routes.rs @@ -285,6 +285,7 @@ async fn list_entries( auth: AuthUser, Path(archive_id): Path, ) -> Result>, ApiError> { + auth.require_auth()?; let mounted = mounted_archive(&state, &archive_id)?; let conn = database::open_or_initialize(&mounted.archive_path)?; let caller_bits = auth_to_caller_bits(&auth); @@ -297,6 +298,7 @@ async fn search_entries_handler( Path(archive_id): Path, Query(params): Query, ) -> Result>, ApiError> { + auth.require_auth()?; let mounted = mounted_archive(&state, &archive_id)?; let conn = database::open_or_initialize(&mounted.archive_path)?; let raw = params.q.as_deref().unwrap_or(""); @@ -1379,17 +1381,14 @@ mod tests { #[tokio::test] async fn missing_archive_returns_404() { - let (test_app, _dir) = make_test_app(); - let response = test_app - .oneshot( - Request::builder() - .uri("/api/archives/missing/entries") - .body(Body::empty()) - .unwrap(), - ) - .await - .unwrap(); - + let dir = tempfile::tempdir().unwrap(); + let auth_path = dir.path().join("auth.sqlite"); + { let conn = archivr_core::database::open_auth_db(&auth_path).unwrap(); archivr_core::database::create_owner(&conn, "testowner", "dummy").unwrap(); } + let session_cookie = make_test_session(&auth_path); + let registry = ServerRegistry { archives: vec![], bind: None, auth_db_path: None }; + let response = app(registry, auth_path) + .oneshot(Request::builder().uri("/api/archives/missing/entries").header("cookie", &session_cookie).body(Body::empty()).unwrap()) + .await.unwrap(); assert_eq!(response.status(), StatusCode::NOT_FOUND); } @@ -1585,16 +1584,14 @@ mod tests { #[tokio::test] async fn search_missing_archive_returns_404() { - let (test_app, _dir) = make_test_app(); - let response = test_app - .oneshot( - Request::builder() - .uri("/api/archives/nope/entries/search?q=anything") - .body(Body::empty()) - .unwrap(), - ) - .await - .unwrap(); + let dir = tempfile::tempdir().unwrap(); + let auth_path = dir.path().join("auth.sqlite"); + { let conn = archivr_core::database::open_auth_db(&auth_path).unwrap(); archivr_core::database::create_owner(&conn, "testowner", "dummy").unwrap(); } + let session_cookie = make_test_session(&auth_path); + let registry = ServerRegistry { archives: vec![], bind: None, auth_db_path: None }; + let response = app(registry, auth_path) + .oneshot(Request::builder().uri("/api/archives/nope/entries/search?q=anything").header("cookie", &session_cookie).body(Body::empty()).unwrap()) + .await.unwrap(); assert_eq!(response.status(), StatusCode::NOT_FOUND); } @@ -1602,15 +1599,10 @@ mod tests { async fn search_empty_q_returns_ok() { let dir = tempfile::tempdir().unwrap(); let (registry, _, auth_path) = make_test_registry(&dir); + let session_cookie = make_test_session(&auth_path); let response = app(registry, auth_path) - .oneshot( - Request::builder() - .uri("/api/archives/test/entries/search") - .body(Body::empty()) - .unwrap(), - ) - .await - .unwrap(); + .oneshot(Request::builder().uri("/api/archives/test/entries/search").header("cookie", &session_cookie).body(Body::empty()).unwrap()) + .await.unwrap(); assert_eq!(response.status(), StatusCode::OK); } @@ -1618,15 +1610,10 @@ mod tests { async fn search_unknown_prefix_returns_400() { let dir = tempfile::tempdir().unwrap(); let (registry, _, auth_path) = make_test_registry(&dir); + let session_cookie = make_test_session(&auth_path); let response = app(registry, auth_path) - .oneshot( - Request::builder() - .uri("/api/archives/test/entries/search?q=unknownprefix%3Aval") - .body(Body::empty()) - .unwrap(), - ) - .await - .unwrap(); + .oneshot(Request::builder().uri("/api/archives/test/entries/search?q=unknownprefix%3Aval").header("cookie", &session_cookie).body(Body::empty()).unwrap()) + .await.unwrap(); assert_eq!(response.status(), StatusCode::BAD_REQUEST); } @@ -2673,4 +2660,48 @@ mod tests { assert_eq!(response.status(), StatusCode::OK); } + // ── Task 2: list_entries / search_entries auth enforcement ─────────────── + + #[tokio::test] + async fn list_entries_requires_auth() { + let dir = tempfile::tempdir().unwrap(); + let (registry, _, auth_path) = make_test_registry(&dir); + let response = app(registry, auth_path) + .oneshot(Request::builder().uri("/api/archives/test/entries").body(Body::empty()).unwrap()) + .await.unwrap(); + assert_eq!(response.status(), StatusCode::UNAUTHORIZED); + } + + #[tokio::test] + async fn list_entries_with_auth_returns_ok() { + let dir = tempfile::tempdir().unwrap(); + let (registry, _, auth_path) = make_test_registry(&dir); + let session_cookie = make_test_session(&auth_path); + let response = app(registry, auth_path) + .oneshot(Request::builder().uri("/api/archives/test/entries").header("cookie", &session_cookie).body(Body::empty()).unwrap()) + .await.unwrap(); + assert_eq!(response.status(), StatusCode::OK); + } + + #[tokio::test] + async fn search_entries_requires_auth() { + let dir = tempfile::tempdir().unwrap(); + let (registry, _, auth_path) = make_test_registry(&dir); + let response = app(registry, auth_path) + .oneshot(Request::builder().uri("/api/archives/test/entries/search").body(Body::empty()).unwrap()) + .await.unwrap(); + assert_eq!(response.status(), StatusCode::UNAUTHORIZED); + } + + #[tokio::test] + async fn search_entries_with_auth_returns_ok() { + let dir = tempfile::tempdir().unwrap(); + let (registry, _, auth_path) = make_test_registry(&dir); + let session_cookie = make_test_session(&auth_path); + let response = app(registry, auth_path) + .oneshot(Request::builder().uri("/api/archives/test/entries/search").header("cookie", &session_cookie).body(Body::empty()).unwrap()) + .await.unwrap(); + assert_eq!(response.status(), StatusCode::OK); + } + }