Skip to content

Commit 99344bc

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/multisig-support
2 parents 8a85336 + 09dba26 commit 99344bc

File tree

6 files changed

+202
-34
lines changed

6 files changed

+202
-34
lines changed

fplus-http-server/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ edition = "2021"
99
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1010

1111
[dependencies]
12+
futures-util = "0.3"
1213
actix-web = "4.3.1"
14+
actix-utils = "3.0.1"
1315
serde = { version = "1.0.164", features = ["derive", "std",
1416
"serde_derive", "alloc", "rc"] }
1517
tokio = { version = "1.29.1", features = ["full"] }
@@ -26,4 +28,3 @@ anyhow = "1.0.75"
2628
async-trait = "0.1.73"
2729
uuidv4 = "1.0.0"
2830
log = "0.4.20"
29-

fplus-http-server/src/main.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
use std::env;
2-
3-
use actix_web::middleware::Logger;
4-
use actix_web::{App, HttpServer};
51
use env_logger;
62
use log::info;
7-
use fplus_database;
3+
mod middleware;
4+
use middleware::verifier_auth::VerifierAuth;
85
pub(crate) mod router;
6+
use std::env;
7+
8+
use actix_web::{
9+
App,
10+
HttpServer,
11+
web,
12+
middleware::Logger,
13+
};
914

1015
#[tokio::main]
1116
async fn main() -> std::io::Result<()> {
@@ -27,9 +32,13 @@ async fn main() -> std::io::Result<()> {
2732
.wrap(cors)
2833
.service(router::health)
2934
.service(router::application::create)
30-
.service(router::application::trigger)
31-
.service(router::application::propose)
32-
.service(router::application::approve)
35+
.service(
36+
web::scope("/api")
37+
.wrap(VerifierAuth) // Apply GitHubAuth to all routes under "/api"
38+
.service(router::application::trigger)
39+
.service(router::application::propose)
40+
.service(router::application::approve)
41+
)
3342
.service(router::application::merged)
3443
.service(router::application::active)
3544
.service(router::application::all_applications)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod verifier_auth;
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
use actix_web::{
2+
dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
3+
Error, web,
4+
};
5+
use futures_util::future::{LocalBoxFuture, ready, Ready};
6+
use reqwest::Client;
7+
use serde::Deserialize;
8+
9+
// Import any other modules that you reference in this file
10+
use fplus_database::database::allocators::get_allocator;
11+
#[derive(Deserialize, Debug)]
12+
struct RepoQuery {
13+
owner: String,
14+
repo: String,
15+
github_username: String
16+
}
17+
18+
pub struct VerifierAuth;
19+
20+
impl<S, B> Transform<S, ServiceRequest> for VerifierAuth
21+
where
22+
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
23+
S::Future: 'static,
24+
B: 'static,
25+
{
26+
type Response = ServiceResponse<B>;
27+
type Error = Error;
28+
type InitError = ();
29+
type Transform = VerifierAuthMiddleware<S>;
30+
type Future = Ready<Result<Self::Transform, Self::InitError>>;
31+
32+
fn new_transform(&self, service: S) -> Self::Future {
33+
ready(Ok(VerifierAuthMiddleware { service }))
34+
}
35+
}
36+
37+
pub struct VerifierAuthMiddleware<S> {
38+
service: S,
39+
}
40+
41+
impl<S, B> Service<ServiceRequest> for VerifierAuthMiddleware<S>
42+
where
43+
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
44+
S::Future: 'static,
45+
B: 'static,
46+
{
47+
type Response = ServiceResponse<B>;
48+
type Error = Error;
49+
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
50+
51+
forward_ready!(service);
52+
53+
fn call(&self, req: ServiceRequest) -> Self::Future {
54+
let query_string = req.query_string();
55+
let query: Result<web::Query<RepoQuery>, _> = web::Query::from_query(query_string);
56+
let RepoQuery { owner, repo, github_username } = match query {
57+
Ok(q) => q.into_inner(),
58+
Err(_) => {
59+
return Box::pin(async {
60+
return Err(actix_web::error::ErrorBadRequest("Wrong query string format"));
61+
});
62+
}
63+
};
64+
65+
let auth_header_value = req.headers().get("Authorization")
66+
.and_then(|hv| hv.to_str().ok())
67+
.filter(|hv| hv.starts_with("Bearer "))
68+
.map(|hv| hv["Bearer ".len()..].to_string());
69+
let fut = self.service.call(req);
70+
71+
Box::pin(async move {
72+
let mut user_handle = String::new();
73+
74+
if let Some(token) = auth_header_value {
75+
// Make the asynchronous HTTP request here
76+
let client = Client::new();
77+
let user_info_result = client.get("https://api.github.com/user")
78+
.header("Authorization", format!("Bearer {}", token))
79+
.header("User-Agent", "Actix-web")
80+
.send()
81+
.await;
82+
83+
match user_info_result {
84+
Ok(response) => {
85+
//Raise an actix test error
86+
if response.status().is_success() {
87+
let user_info = response
88+
.json::<serde_json::Value>()
89+
.await
90+
.expect("Failed to parse JSON");
91+
92+
if let Some(login) = user_info.get("login").and_then(|v| v.as_str()) {
93+
user_handle = login.to_string();
94+
} else {
95+
println!("GitHub handle information not found.");
96+
return Err(actix_web::error::ErrorInternalServerError("GitHub handle information not found."))
97+
}
98+
} else {
99+
println!("Failed to get GitHub user info");
100+
return Err(actix_web::error::ErrorUnauthorized("Failed to get GitHub user info."))
101+
}
102+
},
103+
Err(e) => {
104+
println!("Request error: {:?}", e);
105+
return Err(actix_web::error::ErrorBadRequest(e))
106+
}
107+
}
108+
}
109+
110+
if github_username != user_handle {
111+
return Err(actix_web::error::ErrorBadRequest("Sent GitHub handle different than auth token owner."))
112+
}
113+
114+
match get_allocator(&owner, &repo).await {
115+
Ok(allocator) => {
116+
if let Some(allocator) = &allocator {
117+
if let Some(verifiers) = &allocator.verifiers_gh_handles {
118+
let verifier_handles: Vec<String> = verifiers.split(',')
119+
.map(|s| s.trim().to_lowercase())
120+
.collect();
121+
if verifier_handles.contains(&user_handle.to_lowercase()) {
122+
println!("{} is a verifier.", user_handle);
123+
} else {
124+
println!("The user is not a verifier.");
125+
return Err(actix_web::error::ErrorUnauthorized("The user is not a verifier."))
126+
}
127+
}
128+
}
129+
},
130+
Err(e) => {
131+
println!("Failed to get allocator: {:?}", e);
132+
}
133+
}
134+
135+
let res = fut.await?;
136+
return Ok(res)
137+
})
138+
}
139+
}
140+

fplus-http-server/src/router/application.rs

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use actix_web::{get, post, web, HttpResponse, Responder};
22
use fplus_lib::core::{
3-
CompleteGovernanceReviewInfo, CompleteNewApplicationProposalInfo, CreateApplicationInfo,
4-
LDNApplication, RefillInfo, DcReachedInfo, ValidationPullRequestData, GithubQueryParams, ApplicationQueryParams
3+
application::file::VerifierInput, ApplicationQueryParams, CompleteGovernanceReviewInfo, CompleteNewApplicationProposalInfo, CreateApplicationInfo, DcReachedInfo, GithubQueryParams, LDNApplication, RefillInfo, ValidationPullRequestData, VerifierActionsQueryParams
54
};
65

76
#[post("/application")]
@@ -27,22 +26,21 @@ pub async fn single(query: web::Query<ApplicationQueryParams>) -> impl Responder
2726
};
2827
}
2928

30-
#[post("/application/{id}/trigger")]
29+
#[post("/application/trigger")]
3130
pub async fn trigger(
32-
id: web::Path<String>,
31+
query: web::Query<ApplicationQueryParams>,
3332
info: web::Json<CompleteGovernanceReviewInfo>,
3433
) -> impl Responder {
35-
36-
let CompleteGovernanceReviewInfo { actor, owner, repo } = info.into_inner();
37-
let ldn_application = match LDNApplication::load(id.into_inner(), owner.clone(), repo.clone()).await {
34+
let CompleteGovernanceReviewInfo { actor} = info.into_inner();
35+
let ldn_application = match LDNApplication::load(query.id.clone(), query.owner.clone(), query.repo.clone()).await {
3836
Ok(app) => app,
3937
Err(e) => {
4038
return HttpResponse::BadRequest().body(e.to_string());
4139
}
4240
};
4341
dbg!(&ldn_application);
4442
match ldn_application
45-
.complete_governance_review(actor, owner, repo)
43+
.complete_governance_review(actor, query.owner.clone(), query.repo.clone())
4644
.await
4745
{
4846
Ok(app) => HttpResponse::Ok().body(serde_json::to_string_pretty(&app).unwrap()),
@@ -53,25 +51,29 @@ pub async fn trigger(
5351
}
5452
}
5553

56-
#[post("/application/{id}/propose")]
54+
#[post("/application/propose")]
5755
pub async fn propose(
58-
id: web::Path<String>,
5956
info: web::Json<CompleteNewApplicationProposalInfo>,
57+
query: web::Query<VerifierActionsQueryParams>,
6058
) -> impl Responder {
6159
let CompleteNewApplicationProposalInfo {
6260
signer,
6361
request_id,
64-
owner,
65-
repo
6662
} = info.into_inner();
67-
let ldn_application = match LDNApplication::load(id.into_inner(), owner.clone(), repo.clone()).await {
63+
let ldn_application = match LDNApplication::load(query.id.clone(), query.owner.clone(), query.repo.clone()).await {
6864
Ok(app) => app,
6965
Err(e) => {
7066
return HttpResponse::BadRequest().body(e.to_string());
7167
}
7268
};
69+
let updated_signer = VerifierInput {
70+
github_username: query.github_username.clone(), // Use the provided `github_username` parameter
71+
signing_address: signer.signing_address,
72+
created_at: signer.created_at,
73+
message_cid: signer.message_cid,
74+
};
7375
match ldn_application
74-
.complete_new_application_proposal(signer, request_id, owner, repo)
76+
.complete_new_application_proposal(updated_signer, request_id, query.owner.clone(), query.repo.clone())
7577
.await
7678
{
7779
Ok(app) => HttpResponse::Ok().body(serde_json::to_string_pretty(&app).unwrap()),
@@ -81,25 +83,29 @@ pub async fn propose(
8183
}
8284
}
8385

84-
#[post("/application/{id}/approve")]
86+
#[post("/application/approve")]
8587
pub async fn approve(
86-
id: web::Path<String>,
88+
query: web::Query<VerifierActionsQueryParams>,
8789
info: web::Json<CompleteNewApplicationProposalInfo>,
8890
) -> impl Responder {
8991
let CompleteNewApplicationProposalInfo {
9092
signer,
9193
request_id,
92-
owner,
93-
repo
9494
} = info.into_inner();
95-
let ldn_application = match LDNApplication::load(id.into_inner(), owner.clone(), repo.clone()).await {
95+
let ldn_application = match LDNApplication::load(query.id.clone(), query.owner.clone(), query.repo.clone()).await {
9696
Ok(app) => app,
9797
Err(e) => {
9898
return HttpResponse::BadRequest().body(e.to_string());
9999
}
100100
};
101+
let updated_signer = VerifierInput {
102+
github_username: query.github_username.clone(), // Use the provided `github_username` parameter
103+
signing_address: signer.signing_address,
104+
created_at: signer.created_at,
105+
message_cid: signer.message_cid,
106+
};
101107
match ldn_application
102-
.complete_new_application_approval(signer, request_id, owner, repo)
108+
.complete_new_application_approval(updated_signer, request_id, query.owner.clone(), query.repo.clone())
103109
.await
104110
{
105111
Ok(app) => HttpResponse::Ok().body(serde_json::to_string_pretty(&app).unwrap()),

fplus-lib/src/core/mod.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,17 @@ pub struct CreateApplicationInfo {
4444
#[derive(Deserialize, Serialize, Debug)]
4545
pub struct VerifierList(pub Vec<String>);
4646

47+
#[derive(Deserialize, Serialize, Debug)]
48+
pub struct ApplicationProposalApprovalSignerInfo {
49+
pub signing_address: String,
50+
pub created_at: String,
51+
pub message_cid: String,
52+
}
53+
4754
#[derive(Deserialize, Serialize, Debug)]
4855
pub struct CompleteNewApplicationProposalInfo {
49-
pub signer: VerifierInput,
56+
pub signer: ApplicationProposalApprovalSignerInfo,
5057
pub request_id: String,
51-
pub owner: String,
52-
pub repo: String,
5358
}
5459

5560
#[derive(Debug)]
@@ -64,8 +69,6 @@ pub struct LDNApplication {
6469
#[derive(Debug, Serialize, Deserialize)]
6570
pub struct CompleteGovernanceReviewInfo {
6671
pub actor: String,
67-
pub owner: String,
68-
pub repo: String,
6972
}
7073

7174
#[derive(Deserialize, Debug)]
@@ -133,6 +136,14 @@ pub struct ApplicationQueryParams {
133136
pub repo: String,
134137
}
135138

139+
#[derive(Deserialize)]
140+
pub struct VerifierActionsQueryParams {
141+
pub github_username: String,
142+
pub id: String,
143+
pub owner: String,
144+
pub repo: String,
145+
}
146+
136147
#[derive(Debug, Clone)]
137148
pub struct ApplicationFileWithDate {
138149
pub application_file: ApplicationFile,

0 commit comments

Comments
 (0)