From 83bde1cf7c8f5f85db19e6231ee16b5df2920a2b Mon Sep 17 00:00:00 2001 From: Olivier Fuxet Date: Thu, 4 Jan 2024 14:16:08 +0100 Subject: [PATCH] Remove the need to send a JWT when calling request_payment, cancel_payment and mark_invoice_as_received --- api/src/presentation/http/mod.rs | 1 - .../http/routes/payment/cancel.rs | 22 +------ .../http/routes/payment/request.rs | 43 ++---------- api/tests/context/utils.rs | 6 +- api/tests/payment_it.rs | 66 ++++--------------- 5 files changed, 28 insertions(+), 110 deletions(-) diff --git a/api/src/presentation/http/mod.rs b/api/src/presentation/http/mod.rs index fb52142816..071126009b 100644 --- a/api/src/presentation/http/mod.rs +++ b/api/src/presentation/http/mod.rs @@ -117,7 +117,6 @@ pub fn serve( .mount( "/api", routes![ - routes::users::profile_picture, routes::users::update_user_profile, routes::users::update_user_payout_info, routes::users::search_users, diff --git a/api/src/presentation/http/routes/payment/cancel.rs b/api/src/presentation/http/routes/payment/cancel.rs index 00b16cb180..abfc93953c 100644 --- a/api/src/presentation/http/routes/payment/cancel.rs +++ b/api/src/presentation/http/routes/payment/cancel.rs @@ -1,11 +1,10 @@ -use domain::{AggregateRepository, Payment}; use http_api_problem::{HttpApiProblem, StatusCode}; -use presentation::http::guards::{ApiKey, Claims, Role}; -use rocket::{serde::json::Json, State}; +use presentation::http::guards::ApiKey; +use rocket::serde::json::Json; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use crate::{application, domain::permissions::IntoPermission}; +use crate::application; #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -17,25 +16,10 @@ pub struct Response { pub async fn cancel_payment( _api_key: ApiKey, payment_id: Uuid, - claims: Option, - role: Role, usecase: application::payment::cancel::Usecase, - payment_repository: &State>, ) -> Result, HttpApiProblem> { let payment_id = payment_id.into(); - if !role - .to_permissions((*payment_repository).clone()) - .can_cancel_payment(&payment_id) - { - return Err(HttpApiProblem::new(StatusCode::UNAUTHORIZED) - .title("Unauthorized operation on project") - .detail(format!( - "User {} needs project lead role to cancel a payment request", - claims.map(|c| c.user_id).unwrap_or_default(), - ))); - } - let command_id = usecase.cancel(&payment_id).await.map_err(|e| { HttpApiProblem::new(StatusCode::INTERNAL_SERVER_ERROR) .title("Unable to process cancel_payment request") diff --git a/api/src/presentation/http/routes/payment/request.rs b/api/src/presentation/http/routes/payment/request.rs index 9a2261bf24..baf4bb0dc5 100644 --- a/api/src/presentation/http/routes/payment/request.rs +++ b/api/src/presentation/http/routes/payment/request.rs @@ -1,13 +1,11 @@ -use domain::{ - AggregateRepository, CommandId, Currency, GithubUserId, Payment, PaymentId, ProjectId, -}; +use domain::{CommandId, Currency, GithubUserId, PaymentId, ProjectId, UserId}; use http_api_problem::{HttpApiProblem, StatusCode}; -use presentation::http::guards::{ApiKey, Claims, Role}; -use rocket::{serde::json::Json, State}; +use presentation::http::guards::ApiKey; +use rocket::serde::json::Json; use rust_decimal::Decimal; use serde::{Deserialize, Serialize}; -use crate::{application, domain::permissions::IntoPermission, presentation::http::dto}; +use crate::{application, presentation::http::dto}; #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -21,6 +19,7 @@ pub struct Response { pub struct Request { project_id: ProjectId, recipient_id: GithubUserId, + requestor_id: UserId, amount: Decimal, currency: &'static Currency, hours_worked: Option, @@ -31,38 +30,22 @@ pub struct Request { pub async fn request_payment( _api_key: ApiKey, request: Json, - claims: Claims, - role: Role, - payment_repository: &State>, request_payment_usecase: application::payment::request::Usecase, ) -> Result, HttpApiProblem> { let Request { project_id, recipient_id, + requestor_id, amount, currency, hours_worked, reason, } = request.into_inner(); - let caller_id = claims.user_id; - - if !role - .to_permissions((*payment_repository).clone()) - .can_spend_budget_of_project(&project_id) - { - return Err(HttpApiProblem::new(StatusCode::UNAUTHORIZED) - .title("Unauthorized operation on project") - .detail(format!( - "User {} needs project lead role to create a payment request on project {}", - caller_id, project_id - ))); - } - let (payment_id, command_id) = request_payment_usecase .request( project_id, - caller_id, + requestor_id, recipient_id, amount, currency, @@ -98,22 +81,10 @@ pub struct InvoiceReceivedRequest { pub async fn mark_invoice_as_received( _api_key: ApiKey, request: Json, - role: Role, - payment_repository: &State>, invoice_payment_usecase: application::payment::invoice::Usecase, ) -> Result<(), HttpApiProblem> { let InvoiceReceivedRequest { payments } = request.into_inner(); - let caller_permissions = role.to_permissions((*payment_repository).clone()); - - if payments - .iter() - .any(|payment_id| !caller_permissions.can_mark_invoice_as_received_for_payment(payment_id)) - { - return Err(HttpApiProblem::new(StatusCode::UNAUTHORIZED) - .title("Only recipient can mark invoice as received")); - } - invoice_payment_usecase.mark_invoice_as_received(payments).await.map_err(|e| { { HttpApiProblem::new(StatusCode::INTERNAL_SERVER_ERROR) diff --git a/api/tests/context/utils.rs b/api/tests/context/utils.rs index 6753d26094..13bb4f8a2f 100644 --- a/api/tests/context/utils.rs +++ b/api/tests/context/utils.rs @@ -5,6 +5,8 @@ use rocket::{http::Header, serde::json::json}; use super::API_KEY; +pub const USER_ID: &str = "9b7effeb-963f-4ac4-be74-d735501925ed"; + #[allow(unused)] pub fn jwt(project_leaded_id: Option) -> String { let now = SystemTime::now() @@ -27,10 +29,10 @@ pub fn jwt(project_leaded_id: Option) -> String { "registered_user" ], "x-hasura-default-role": "registered_user", - "x-hasura-user-id": "9b7effeb-963f-4ac4-be74-d735501925ed", + "x-hasura-user-id": USER_ID, "x-hasura-user-is-anonymous": "false" }, - "sub": "9b7effeb-963f-4ac4-be74-d735501925ed", + "sub": USER_ID, "iat": now, "exp": now + 1000, "iss": "hasura-auth-unit-tests" diff --git a/api/tests/payment_it.rs b/api/tests/payment_it.rs index e1074ed67c..4bc2f1f1d8 100644 --- a/api/tests/payment_it.rs +++ b/api/tests/payment_it.rs @@ -26,7 +26,7 @@ use uuid::Uuid; use crate::context::{ docker, - utils::{api_key_header, jwt}, + utils::{api_key_header, USER_ID}, Context, }; @@ -52,16 +52,16 @@ pub async fn payment_processing(docker: &'static Cli) { test.project_lead_can_request_payments_in_usdc() .await .expect("project_lead_can_request_payments_in_usdc"); - test.anyone_cannot_request_payments() + test.cannot_request_payments_without_api_key() .await - .expect("anyone_cannot_request_payments"); + .expect("cannot_request_payments_without_api_key"); test.project_lead_can_cancel_payments() .await .expect("project_lead_can_cancel_payments"); test.admin_can_cancel_payments().await.expect("admin_can_cancel_payments"); - test.anyone_cannot_cancel_payments() + test.cannot_cancel_payments_without_api_key() .await - .expect("anyone_cannot_cancel_payments"); + .expect("cannot_cancel_payments_without_api_key"); test.admin_can_add_a_sepa_receipt().await.expect("admin_can_add_a_sepa_receipt"); test.admin_can_add_an_eth_receipt().await.expect("admin_can_add_an_eth_receipt"); @@ -118,6 +118,7 @@ impl<'a> Test<'a> { let request = json!({ "projectId": project_id, "recipientId": 595505, + "requestorId": USER_ID, "amount": 10, "currency": "USD", "hoursWorked": 1, @@ -138,11 +139,6 @@ impl<'a> Test<'a> { .post("/api/payments") .header(ContentType::JSON) .header(api_key_header()) - .header(Header::new("x-hasura-role", "registered_user")) - .header(Header::new( - "Authorization", - format!("Bearer {}", jwt(Some(project_id.to_string()))), - )) .body(request.to_string()) .dispatch() .await; @@ -242,6 +238,7 @@ impl<'a> Test<'a> { let request = json!({ "projectId": project_id, "recipientId": 595505, + "requestorId": USER_ID, "amount": 0.00001, "currency": "ETH", "reason": { @@ -261,11 +258,6 @@ impl<'a> Test<'a> { .post("/api/payments") .header(ContentType::JSON) .header(api_key_header()) - .header(Header::new("x-hasura-role", "registered_user")) - .header(Header::new( - "Authorization", - format!("Bearer {}", jwt(Some(project_id.to_string()))), - )) .body(request.to_string()) .dispatch() .await; @@ -365,6 +357,7 @@ impl<'a> Test<'a> { let request = json!({ "projectId": project_id, "recipientId": 595505, + "requestorId": USER_ID, "amount": 100.52, "currency": "LORDS", "reason": { @@ -384,11 +377,6 @@ impl<'a> Test<'a> { .post("/api/payments") .header(ContentType::JSON) .header(api_key_header()) - .header(Header::new("x-hasura-role", "registered_user")) - .header(Header::new( - "Authorization", - format!("Bearer {}", jwt(Some(project_id.to_string()))), - )) .body(request.to_string()) .dispatch() .await; @@ -485,6 +473,7 @@ impl<'a> Test<'a> { let request = json!({ "projectId": project_id, "recipientId": 595505, + "requestorId": USER_ID, "amount": 100.52, "currency": "USDC", "reason": { @@ -504,11 +493,6 @@ impl<'a> Test<'a> { .post("/api/payments") .header(ContentType::JSON) .header(api_key_header()) - .header(Header::new("x-hasura-role", "registered_user")) - .header(Header::new( - "Authorization", - format!("Bearer {}", jwt(Some(project_id.to_string()))), - )) .body(request.to_string()) .dispatch() .await; @@ -570,8 +554,8 @@ impl<'a> Test<'a> { Ok(()) } - async fn anyone_cannot_request_payments(&mut self) -> Result<()> { - info!("anyone_cannot_request_payments"); + async fn cannot_request_payments_without_api_key(&mut self) -> Result<()> { + info!("cannot_request_payments_without_api_key"); // Given let project_id = ProjectId::new(); @@ -604,6 +588,7 @@ impl<'a> Test<'a> { let request = json!({ "projectId": project_id, "recipientId": 595505, + "requestorId": USER_ID, "amount": 10, "currency": "USD", "hoursWorked": 1, @@ -623,12 +608,6 @@ impl<'a> Test<'a> { .http_client .post("/api/payments") .header(ContentType::JSON) - .header(api_key_header()) - .header(Header::new("x-hasura-role", "registered_user")) - .header(Header::new( - "Authorization", - format!("Bearer {}", jwt(None)), - )) .body(request.to_string()) .dispatch() .await; @@ -694,11 +673,6 @@ impl<'a> Test<'a> { .delete(format!("/api/payments/{payment_id}")) .header(ContentType::JSON) .header(api_key_header()) - .header(Header::new("x-hasura-role", "registered_user")) - .header(Header::new( - "Authorization", - format!("Bearer {}", jwt(Some(project_id.to_string()))), - )) .dispatch() .await; @@ -776,7 +750,6 @@ impl<'a> Test<'a> { .delete(format!("/api/payments/{payment_id}")) .header(ContentType::JSON) .header(api_key_header()) - .header(Header::new("x-hasura-role", "admin")) .dispatch() .await; @@ -804,8 +777,8 @@ impl<'a> Test<'a> { Ok(()) } - async fn anyone_cannot_cancel_payments(&mut self) -> Result<()> { - info!("anyone_cannot_cancel_payments"); + async fn cannot_cancel_payments_without_api_key(&mut self) -> Result<()> { + info!("cannot_cancel_payments_without_api_key"); // Given let project_id = ProjectId::new(); @@ -853,12 +826,6 @@ impl<'a> Test<'a> { .http_client .delete(format!("/api/payments/{payment_id}")) .header(ContentType::JSON) - .header(api_key_header()) - .header(Header::new("x-hasura-role", "registered_user")) - .header(Header::new( - "Authorization", - format!("Bearer {}", jwt(None)), - )) .dispatch() .await; @@ -1469,11 +1436,6 @@ impl<'a> Test<'a> { .put("/api/payments/invoiceReceivedAt") .header(ContentType::JSON) .header(api_key_header()) - .header(Header::new("x-hasura-role", "registered_user")) - .header(Header::new( - "Authorization", - format!("Bearer {}", jwt(None)), - )) .body(request.to_string()) .dispatch() .await;