From 070200bc2a7f350ad58d5f8bc3e625d39eb45214 Mon Sep 17 00:00:00 2001 From: Vasili Pascal Date: Fri, 31 Jul 2026 11:23:37 +0200 Subject: [PATCH] test(project-protection): authenticate requests so casbin doesn't 403 The 9 project_protection integration tests sent no Authorization header, so every request was anonymous -> casbin denied -> 403 (even POST /project). They never passed, which froze the Docker CICD image build (docker.yml gates on cargo test) and blocked deploying the audit feature to production. Add `Authorization: Bearer USER_A_TOKEN` before .send() on every request (the single-user mock returns test_user_id / group_user for any token, matching the create_test_project owner), mirroring the passing IDOR test. The casbin grant for PATCH /project/:id/protection already exists (20260727130000_casbin_project_protection_rule). Co-Authored-By: Claude Opus 4.8 --- tests/project_protection.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/project_protection.rs b/tests/project_protection.rs index b2f20769..cdeafa5d 100644 --- a/tests/project_protection.rs +++ b/tests/project_protection.rs @@ -15,6 +15,7 @@ async fn test_new_project_default_unprotected() { .post(format!("{}/project", &app.address)) .header("Content-Type", "application/json") .body(r#"{"custom":{"custom_stack_code":"default-unprot","web":[{"_id":"a1","code":"nginx","name":"Nginx","type":"web","restart":"always","dockerhub_name":"nginx","custom":true}]}}"#) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed"); @@ -41,6 +42,7 @@ async fn test_enable_protection_via_patch() { )) .header("Content-Type", "application/json") .body(r#"{"is_protected": true}"#) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed"); @@ -65,6 +67,7 @@ async fn test_delete_protected_project_returns_403() { )) .header("Content-Type", "application/json") .body(r#"{"is_protected": true}"#) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed"); @@ -73,6 +76,7 @@ async fn test_delete_protected_project_returns_403() { // Try to delete — should be blocked let resp = client .delete(format!("{}/project/{}", &app.address, project_id)) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed"); @@ -99,6 +103,7 @@ async fn test_delete_unprotected_project_succeeds() { let resp = client .delete(format!("{}/project/{}", &app.address, project_id)) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed"); @@ -125,6 +130,7 @@ async fn test_disable_protection_requires_name() { )) .header("Content-Type", "application/json") .body(r#"{"is_protected": true}"#) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed"); @@ -138,6 +144,7 @@ async fn test_disable_protection_requires_name() { )) .header("Content-Type", "application/json") .body(r#"{"is_protected": false}"#) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed"); @@ -164,6 +171,7 @@ async fn test_disable_protection_wrong_name_rejected() { )) .header("Content-Type", "application/json") .body(r#"{"is_protected": true}"#) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed"); @@ -177,6 +185,7 @@ async fn test_disable_protection_wrong_name_rejected() { )) .header("Content-Type", "application/json") .body(r#"{"is_protected": false, "confirmation_name": "wrong-name"}"#) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed"); @@ -199,6 +208,7 @@ async fn test_disable_protection_correct_name_succeeds() { )) .header("Content-Type", "application/json") .body(r#"{"is_protected": true}"#) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed"); @@ -212,6 +222,7 @@ async fn test_disable_protection_correct_name_succeeds() { )) .header("Content-Type", "application/json") .body(r#"{"is_protected": false, "confirmation_name": "Test Project"}"#) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed"); @@ -222,6 +233,7 @@ async fn test_disable_protection_correct_name_succeeds() { // Now delete should succeed let resp = client .delete(format!("{}/project/{}", &app.address, project_id)) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed"); @@ -257,6 +269,7 @@ async fn test_protection_shows_deployment_and_server_counts() { )) .header("Content-Type", "application/json") .body(r#"{"is_protected": true}"#) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed"); @@ -265,6 +278,7 @@ async fn test_protection_shows_deployment_and_server_counts() { // Try to delete — should be blocked with counts let resp = client .delete(format!("{}/project/{}", &app.address, project_id)) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed"); @@ -317,6 +331,7 @@ async fn test_project_list_includes_is_protected() { )) .header("Content-Type", "application/json") .body(r#"{"is_protected": true}"#) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed"); @@ -325,6 +340,7 @@ async fn test_project_list_includes_is_protected() { // List projects — should include is_protected let resp = client .get(format!("{}/project", &app.address)) + .header("Authorization", format!("Bearer {}", common::USER_A_TOKEN)) .send() .await .expect("request failed");