Skip to content

Commit 9b3a375

Browse files
committed
sentry - routes & middlewares to axum + tests refactor
- middleware - auth - is_admin - middleware - channel - channel_load - middleware - campaign - called_by_campaign - routes - channel - payout + tests refactor - routes - channel - list tests refactor - routes - campaign - campaign update - routers - add new channels & campaign routes - primitives - sentry - ChannelPayRequest derive Clone
1 parent 94d9d12 commit 9b3a375

File tree

7 files changed

+366
-116
lines changed

7 files changed

+366
-116
lines changed

primitives/src/sentry.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -676,7 +676,7 @@ pub struct AllSpendersQuery {
676676
/// ```
677677
#[doc = include_str!("../examples/channel_pay_request.rs")]
678678
/// ```
679-
#[derive(Debug, Serialize, Deserialize)]
679+
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
680680
pub struct ChannelPayRequest {
681681
pub payouts: UnifiedMap,
682682
}

sentry/src/middleware/auth.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,27 @@ impl<C: Locked + 'static> Middleware<C> for IsAdmin {
7474
}
7575
}
7676

77+
pub async fn is_admin<C: Locked + 'static, B>(
78+
request: axum::http::Request<B>,
79+
next: Next<B>,
80+
) -> Result<axum::response::Response, ResponseError> {
81+
let auth = request
82+
.extensions()
83+
.get::<Auth>()
84+
.ok_or(ResponseError::Unauthorized)?;
85+
86+
let config = &request
87+
.extensions()
88+
.get::<Arc<Application<C>>>()
89+
.expect("Application should always be present")
90+
.config;
91+
92+
if !config.admins.contains(auth.uid.as_address()) {
93+
return Err(ResponseError::Unauthorized);
94+
}
95+
Ok(next.run(request).await)
96+
}
97+
7798
pub async fn authentication_required<C: Locked + 'static, B>(
7899
request: axum::http::Request<B>,
79100
next: Next<B>,

sentry/src/middleware/campaign.rs

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use axum::{
99
middleware::Next,
1010
};
1111
use hyper::{Body, Request};
12-
use primitives::{campaign::Campaign, CampaignId};
12+
use primitives::{campaign::Campaign, CampaignId, ChainOf};
1313

1414
#[derive(Debug)]
1515
pub struct CampaignLoad;
@@ -111,6 +111,34 @@ where
111111
Ok(next.run(request).await)
112112
}
113113

114+
pub async fn called_by_campaign<C: Locked + 'static, B>(
115+
request: axum::http::Request<B>,
116+
next: Next<B>,
117+
) -> Result<axum::response::Response, ResponseError>
118+
where
119+
B: Send,
120+
{
121+
let campaign_context = request
122+
.extensions()
123+
.get::<ChainOf<Campaign>>()
124+
.expect("We must have a campaign in extensions")
125+
.to_owned();
126+
127+
let auth = request
128+
.extensions()
129+
.get::<Auth>()
130+
.expect("request should have session")
131+
.to_owned();
132+
133+
if auth.uid.to_address() != campaign_context.context.creator {
134+
return Err(ResponseError::Forbidden(
135+
"Request not sent by campaign creator".to_string(),
136+
));
137+
}
138+
139+
Ok(next.run(request).await)
140+
}
141+
114142
#[async_trait]
115143
impl<C: Locked + 'static> Middleware<C> for CalledByCreator {
116144
async fn call<'a>(
@@ -142,17 +170,13 @@ impl<C: Locked + 'static> Middleware<C> for CalledByCreator {
142170

143171
#[cfg(test)]
144172
mod test {
145-
use adapter::Dummy;
146173
use axum::{
147-
body::{self, Body, BoxBody, Empty},
148-
http::StatusCode,
149-
middleware::from_fn,
150-
response::Response,
151-
routing::get,
152-
Extension, Router,
174+
body::Body, http::StatusCode, middleware::from_fn, routing::get, Extension, Router,
153175
};
176+
use tower::Service;
177+
178+
use adapter::Dummy;
154179
use primitives::{test_util::DUMMY_CAMPAIGN, Campaign, ChainOf};
155-
use tower::{Service, ServiceExt};
156180

157181
use crate::{
158182
db::{insert_campaign, insert_channel},
@@ -180,10 +204,6 @@ mod test {
180204
"Ok".into()
181205
}
182206

183-
async fn body_to_string(response: Response<BoxBody>) -> String {
184-
String::from_utf8(hyper::body::to_bytes(response).await.unwrap().to_vec()).unwrap()
185-
}
186-
187207
let mut router = Router::new()
188208
.route("/:id", get(handle))
189209
.layer(from_fn(campaign_load::<Dummy, _>));

sentry/src/middleware/channel.rs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
use std::sync::Arc;
2+
13
use crate::{
24
db::get_channel_by_id, middleware::Middleware, response::ResponseError,
35
routes::routers::RouteParams, Application, Auth,
46
};
57
use adapter::client::Locked;
8+
use axum::{
9+
extract::{Path, RequestParts},
10+
middleware::Next,
11+
};
612
use futures::future::{BoxFuture, FutureExt};
713
use hex::FromHex;
814
use hyper::{Body, Request};
@@ -20,12 +26,12 @@ impl<C: Locked + 'static> Middleware<C> for ChannelLoad {
2026
request: Request<Body>,
2127
application: &'a Application<C>,
2228
) -> Result<Request<Body>, ResponseError> {
23-
channel_load(request, application).await
29+
channel_load_old(request, application).await
2430
}
2531
}
2632

2733
/// channel_load & channel_if_exist
28-
fn channel_load<C: Locked>(
34+
fn channel_load_old<C: Locked>(
2935
mut req: Request<Body>,
3036
app: &Application<C>,
3137
) -> BoxFuture<'_, Result<Request<Body>, ResponseError>> {
@@ -63,3 +69,56 @@ fn channel_load<C: Locked>(
6369
}
6470
.boxed()
6571
}
72+
73+
pub async fn channel_load<C: Locked + 'static, B>(
74+
request: axum::http::Request<B>,
75+
next: Next<B>,
76+
) -> Result<axum::response::Response, ResponseError>
77+
where
78+
B: Send,
79+
{
80+
let app = request
81+
.extensions()
82+
.get::<Arc<Application<C>>>()
83+
.expect("Application should always be present")
84+
.clone();
85+
86+
// running extractors requires a `RequestParts`
87+
let mut request_parts = RequestParts::new(request);
88+
89+
let channel_id = request_parts
90+
.extract::<Path<ChannelId>>()
91+
.await
92+
.map_err(|_| ResponseError::BadRequest("Bad Channel Id".to_string()))?;
93+
94+
let channel = get_channel_by_id(&app.pool, &channel_id)
95+
.await?
96+
.ok_or(ResponseError::NotFound)?;
97+
98+
let channel_context = app
99+
.config
100+
.find_chain_of(channel.token)
101+
.ok_or_else(|| {
102+
ResponseError::FailedValidation(
103+
"Channel token is not whitelisted in this validator".into(),
104+
)
105+
})?
106+
.with_channel(channel);
107+
108+
// If this is an authenticated call
109+
// Check if the Channel context (Chain Id) aligns with the Authentication token Chain id
110+
match request_parts.extensions().get::<Auth>() {
111+
// If Chain Ids differ, the requester hasn't generated Auth token
112+
// to access the Channel in it's Chain Id.
113+
Some(auth) if auth.chain.chain_id != channel_context.chain.chain_id => {
114+
return Err(ResponseError::Forbidden("Authentication token is generated for different Chain and differs from the Channel's Chain".into()))
115+
}
116+
_ => {},
117+
}
118+
119+
request_parts.extensions_mut().insert(channel_context);
120+
121+
let request = request_parts.try_into_request().expect("Body extracted");
122+
123+
Ok(next.run(request).await)
124+
}

sentry/src/routes/campaign.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,26 @@ pub mod update_campaign {
503503

504504
use super::*;
505505

506+
pub async fn handle_route_axum<C: Locked + 'static>(
507+
Json(modify_campaign_fields): Json<ModifyCampaign>,
508+
Extension(campaign_being_mutated): Extension<ChainOf<Campaign>>,
509+
Extension(app): Extension<Arc<Application<C>>>,
510+
) -> Result<Json<Campaign>, ResponseError> {
511+
// modify Campaign
512+
let modified_campaign = modify_campaign(
513+
app.adapter.clone(),
514+
&app.pool,
515+
&app.config,
516+
&app.campaign_remaining,
517+
&campaign_being_mutated,
518+
modify_campaign_fields,
519+
)
520+
.await
521+
.map_err(|err| ResponseError::BadRequest(err.to_string()))?;
522+
523+
Ok(Json(modified_campaign))
524+
}
525+
506526
/// POST `/v5/campaign/:id` (auth required)
507527
///
508528
/// Request body (json): [`ModifyCampaign`](`primitives::sentry::campaign_modify::ModifyCampaign`)

0 commit comments

Comments
 (0)