Skip to content

Commit

Permalink
Remove the need to send a JWT when calling request_payment, cancel_pa…
Browse files Browse the repository at this point in the history
…yment and mark_invoice_as_received
  • Loading branch information
ofux committed Jan 4, 2024
1 parent 4655664 commit 83bde1c
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 110 deletions.
1 change: 0 additions & 1 deletion api/src/presentation/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
22 changes: 3 additions & 19 deletions api/src/presentation/http/routes/payment/cancel.rs
Original file line number Diff line number Diff line change
@@ -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")]
Expand All @@ -17,25 +16,10 @@ pub struct Response {
pub async fn cancel_payment(
_api_key: ApiKey,
payment_id: Uuid,
claims: Option<Claims>,
role: Role,
usecase: application::payment::cancel::Usecase,
payment_repository: &State<AggregateRepository<Payment>>,
) -> Result<Json<Response>, 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")
Expand Down
43 changes: 7 additions & 36 deletions api/src/presentation/http/routes/payment/request.rs
Original file line number Diff line number Diff line change
@@ -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")]
Expand All @@ -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<u32>,
Expand All @@ -31,38 +30,22 @@ pub struct Request {
pub async fn request_payment(
_api_key: ApiKey,
request: Json<Request>,
claims: Claims,
role: Role,
payment_repository: &State<AggregateRepository<Payment>>,
request_payment_usecase: application::payment::request::Usecase,
) -> Result<Json<Response>, HttpApiProblem> {
let Request {
project_id,
recipient_id,
requestor_id,

Check warning on line 38 in api/src/presentation/http/routes/payment/request.rs

View check run for this annotation

Codecov / codecov/patch

api/src/presentation/http/routes/payment/request.rs#L38

Added line #L38 was not covered by tests
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,

Check warning on line 48 in api/src/presentation/http/routes/payment/request.rs

View check run for this annotation

Codecov / codecov/patch

api/src/presentation/http/routes/payment/request.rs#L48

Added line #L48 was not covered by tests
recipient_id,
amount,
currency,
Expand Down Expand Up @@ -98,22 +81,10 @@ pub struct InvoiceReceivedRequest {
pub async fn mark_invoice_as_received(
_api_key: ApiKey,
request: Json<InvoiceReceivedRequest>,
role: Role,
payment_repository: &State<AggregateRepository<Payment>>,
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)
Expand Down
6 changes: 4 additions & 2 deletions api/tests/context/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>) -> String {
let now = SystemTime::now()
Expand All @@ -27,10 +29,10 @@ pub fn jwt(project_leaded_id: Option<String>) -> 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"
Expand Down
66 changes: 14 additions & 52 deletions api/tests/payment_it.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use uuid::Uuid;

use crate::context::{
docker,
utils::{api_key_header, jwt},
utils::{api_key_header, USER_ID},
Context,
};

Expand All @@ -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");
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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": {
Expand All @@ -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;
Expand Down Expand Up @@ -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": {
Expand All @@ -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;
Expand Down Expand Up @@ -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": {
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 83bde1c

Please sign in to comment.