|
|
@@ -1,9 +1,23 @@
|
|
|
+// We want Scalars to be lowercase letters, and Points and credentials
|
|
|
+// to be capital letters
|
|
|
+#![allow(non_snake_case)]
|
|
|
+
|
|
|
use crate::lox_context::LoxServerContext;
|
|
|
use bytes::Bytes;
|
|
|
+#[cfg(test)]
|
|
|
+use curve25519_dalek::scalar::Scalar;
|
|
|
use http_body_util::{combinators::BoxBody, BodyExt, Full};
|
|
|
use hyper::{body::Body, header::HeaderValue, Method, Request, Response, StatusCode};
|
|
|
+#[cfg(test)]
|
|
|
+use lox_extensions::{
|
|
|
+ bridge_table::{self, BridgeLine, MAX_BRIDGES_PER_BUCKET},
|
|
|
+ report_table::ReportStatus,
|
|
|
+};
|
|
|
use std::{convert::Infallible, fmt::Debug};
|
|
|
|
|
|
+#[cfg(test)]
|
|
|
+use std::time::SystemTime;
|
|
|
+
|
|
|
fn full<T: Into<Bytes>>(chunk: T) -> BoxBody<Bytes, Infallible> {
|
|
|
Full::new(chunk.into()).boxed()
|
|
|
}
|
|
|
@@ -82,6 +96,22 @@ where
|
|
|
let bytes = req.into_body().collect().await.unwrap().to_bytes();
|
|
|
cloned_context.verify_and_send_update_invite(bytes)
|
|
|
}),
|
|
|
+ (&Method::POST, "/reportinit") => Ok::<_, Infallible>({
|
|
|
+ let bytes = req.into_body().collect().await.unwrap().to_bytes();
|
|
|
+ cloned_context.verify_and_send_report_init(bytes)
|
|
|
+ }),
|
|
|
+ (&Method::POST, "/reportstatus") => Ok::<_, Infallible>({
|
|
|
+ let bytes = req.into_body().collect().await.unwrap().to_bytes();
|
|
|
+ cloned_context.verify_and_send_report_status(bytes)
|
|
|
+ }),
|
|
|
+ (&Method::POST, "/reportsubmit") => Ok::<_, Infallible>({
|
|
|
+ let bytes = req.into_body().collect().await.unwrap().to_bytes();
|
|
|
+ cloned_context.verify_and_send_report_submit(bytes)
|
|
|
+ }),
|
|
|
+ (&Method::POST, "/reportresolve") => Ok::<_, Infallible>({
|
|
|
+ let bytes = req.into_body().collect().await.unwrap().to_bytes();
|
|
|
+ cloned_context.verify_and_send_report_resolve(bytes)
|
|
|
+ }),
|
|
|
_ => {
|
|
|
// Return 404 not found response.
|
|
|
cloned_context.metrics.invalid_endpoint_request_count.inc();
|
|
|
@@ -110,8 +140,10 @@ mod tests {
|
|
|
lox_creds::{BucketReachability, Invitation, Lox, Migration},
|
|
|
proto::{
|
|
|
blockage_migration, check_blockage, issue_invite, level_up, migration, open_invite,
|
|
|
- redeem_invite, trust_promotion, update_cred, update_invite,
|
|
|
+ redeem_invite, report_init, report_resolve, report_status, report_submit,
|
|
|
+ trust_promotion, update_cred, update_invite,
|
|
|
},
|
|
|
+ report_table::CountryCode,
|
|
|
};
|
|
|
use serde_json::Error;
|
|
|
use std::convert::Infallible;
|
|
|
@@ -138,6 +170,19 @@ mod tests {
|
|
|
) -> Request<B>;
|
|
|
fn updatecred(&self, request: lox_utils::UpdateCredReq) -> Request<B>;
|
|
|
fn updateinvite(&self, request: lox_utils::UpdateInviteReq) -> Request<B>;
|
|
|
+ fn reportinit(
|
|
|
+ &self,
|
|
|
+ request: report_init::report_init::Request,
|
|
|
+ cc: CountryCode,
|
|
|
+ D: G,
|
|
|
+ ) -> Request<B>;
|
|
|
+ fn reportstatus(&self, request: report_status::report_status::Request) -> Request<B>;
|
|
|
+ fn reportsubmit(
|
|
|
+ &self,
|
|
|
+ request: report_submit::report_submit::Request,
|
|
|
+ bridges_being_reported: [bool; MAX_BRIDGES_PER_BUCKET],
|
|
|
+ ) -> Request<B>;
|
|
|
+ fn reportresolve(&self, request: report_resolve::report_resolve::Request) -> Request<B>;
|
|
|
}
|
|
|
|
|
|
struct LoxClientMock {}
|
|
|
@@ -286,6 +331,61 @@ mod tests {
|
|
|
.body(full(req_str))
|
|
|
.unwrap()
|
|
|
}
|
|
|
+
|
|
|
+ fn reportinit(
|
|
|
+ &self,
|
|
|
+ request: report_init::report_init::Request,
|
|
|
+ cc: CountryCode,
|
|
|
+ D: G,
|
|
|
+ ) -> Request<BoxedBody> {
|
|
|
+ let req_str = serde_json::to_string(&(request, cc, D)).unwrap();
|
|
|
+ Request::builder()
|
|
|
+ .header("Content-Type", "application/json")
|
|
|
+ .method("POST")
|
|
|
+ .uri("http://localhost/reportinit")
|
|
|
+ .body(full(req_str))
|
|
|
+ .unwrap()
|
|
|
+ }
|
|
|
+
|
|
|
+ fn reportstatus(
|
|
|
+ &self,
|
|
|
+ request: report_status::report_status::Request,
|
|
|
+ ) -> Request<BoxedBody> {
|
|
|
+ let req_str = serde_json::to_string(&request).unwrap();
|
|
|
+ Request::builder()
|
|
|
+ .header("Content-Type", "application/json")
|
|
|
+ .method("POST")
|
|
|
+ .uri("http://localhost/reportstatus")
|
|
|
+ .body(full(req_str))
|
|
|
+ .unwrap()
|
|
|
+ }
|
|
|
+
|
|
|
+ fn reportsubmit(
|
|
|
+ &self,
|
|
|
+ request: report_submit::report_submit::Request,
|
|
|
+ bridges_being_reported: [bool; MAX_BRIDGES_PER_BUCKET],
|
|
|
+ ) -> Request<BoxedBody> {
|
|
|
+ let req_str = serde_json::to_string(&(request, bridges_being_reported)).unwrap();
|
|
|
+ Request::builder()
|
|
|
+ .header("Content-Type", "application/json")
|
|
|
+ .method("POST")
|
|
|
+ .uri("http://localhost/reportsubmit")
|
|
|
+ .body(full(req_str))
|
|
|
+ .unwrap()
|
|
|
+ }
|
|
|
+
|
|
|
+ fn reportresolve(
|
|
|
+ &self,
|
|
|
+ request: report_resolve::report_resolve::Request,
|
|
|
+ ) -> Request<BoxedBody> {
|
|
|
+ let req_str = serde_json::to_string(&request).unwrap();
|
|
|
+ Request::builder()
|
|
|
+ .header("Content-Type", "application/json")
|
|
|
+ .method("POST")
|
|
|
+ .uri("http://localhost/reportresolve")
|
|
|
+ .body(full(req_str))
|
|
|
+ .unwrap()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
async fn body_to_string(res: Response<BoxBody<Bytes, Infallible>>) -> String {
|
|
|
@@ -1006,6 +1106,178 @@ mod tests {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+ async fn get_bucket(
|
|
|
+ context: LoxServerContext,
|
|
|
+ cred: Lox,
|
|
|
+ ) -> [BridgeLine; MAX_BRIDGES_PER_BUCKET] {
|
|
|
+ let (id, key) = bridge_table::from_scalar(cred.bucket.unwrap()).unwrap();
|
|
|
+ let encbuckets = context.ba.lock().unwrap().enc_bridge_table().clone();
|
|
|
+ let reach_pub = context.ba.lock().unwrap().reachability_pub.clone();
|
|
|
+ bridge_table::BridgeTable::decrypt_bucket(
|
|
|
+ id,
|
|
|
+ &key,
|
|
|
+ encbuckets.get(&id).unwrap(),
|
|
|
+ &reach_pub,
|
|
|
+ )
|
|
|
+ .unwrap()
|
|
|
+ .0
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn get_report_init(
|
|
|
+ context: LoxServerContext,
|
|
|
+ cred: Lox,
|
|
|
+ cc: CountryCode,
|
|
|
+ ) -> Result<Lox, CMZError> {
|
|
|
+ let rng = &mut rand::thread_rng();
|
|
|
+ let lc = LoxClientMock {};
|
|
|
+ let ((request, state), cc, D) = report_init::request(rng, cred, cc).unwrap();
|
|
|
+ let report_init_request = lc.reportinit(request, cc, D);
|
|
|
+ let report_init_response = handle(context, report_init_request).await.unwrap();
|
|
|
+ assert_eq!(report_init_response.status(), StatusCode::OK);
|
|
|
+ let report_init_resp = body_to_string(report_init_response).await;
|
|
|
+ let report_init_response_obj = serde_json::from_str(&report_init_resp).unwrap();
|
|
|
+ report_init::handle_response(state, report_init_response_obj)
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn get_report_status(
|
|
|
+ context: LoxServerContext,
|
|
|
+ cred: Lox,
|
|
|
+ ) -> Result<(Lox, [ReportStatus; MAX_BRIDGES_PER_BUCKET]), CMZError> {
|
|
|
+ let rng = &mut rand::thread_rng();
|
|
|
+ let lc = LoxClientMock {};
|
|
|
+ let (request, state) = report_status::request(rng, cred).unwrap();
|
|
|
+ let report_status_request = lc.reportstatus(request);
|
|
|
+ let report_status_response = handle(context, report_status_request).await.unwrap();
|
|
|
+ assert_eq!(report_status_response.status(), StatusCode::OK);
|
|
|
+ let report_status_resp = body_to_string(report_status_response).await;
|
|
|
+ let (report_status_response_obj, report_statuses) =
|
|
|
+ serde_json::from_str(&report_status_resp).unwrap();
|
|
|
+ report_status::handle_response(state, report_status_response_obj, report_statuses)
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn get_report_submit(
|
|
|
+ context: LoxServerContext,
|
|
|
+ cred: Lox,
|
|
|
+ new_false_reports: u32,
|
|
|
+ bridges_being_reported: [bool; MAX_BRIDGES_PER_BUCKET],
|
|
|
+ ) -> Result<Lox, CMZError> {
|
|
|
+ let rng = &mut rand::thread_rng();
|
|
|
+ let lc = LoxClientMock {};
|
|
|
+ let ((request, state), bridges_being_reported) =
|
|
|
+ report_submit::request(rng, cred, new_false_reports, bridges_being_reported).unwrap();
|
|
|
+ let report_submit_request = lc.reportsubmit(request, bridges_being_reported);
|
|
|
+ let report_submit_response = handle(context, report_submit_request).await.unwrap();
|
|
|
+ assert_eq!(report_submit_response.status(), StatusCode::OK);
|
|
|
+ let report_submit_resp = body_to_string(report_submit_response).await;
|
|
|
+ let report_submit_response_obj = serde_json::from_str(&report_submit_resp).unwrap();
|
|
|
+ report_submit::handle_response(state, report_submit_response_obj)
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn get_report_resolve(
|
|
|
+ context: LoxServerContext,
|
|
|
+ cred: Lox,
|
|
|
+ new_false_reports: u32,
|
|
|
+ ) -> Result<Lox, CMZError> {
|
|
|
+ let rng = &mut rand::thread_rng();
|
|
|
+ let lc = LoxClientMock {};
|
|
|
+ let (request, state) = report_resolve::request(rng, cred, new_false_reports).unwrap();
|
|
|
+ let report_resolve_request = lc.reportresolve(request);
|
|
|
+ let report_resolve_response = handle(context, report_resolve_request).await.unwrap();
|
|
|
+ assert_eq!(report_resolve_response.status(), StatusCode::OK);
|
|
|
+ let report_resolve_resp = body_to_string(report_resolve_response).await;
|
|
|
+ let report_resolve_response_obj = serde_json::from_str(&report_resolve_resp).unwrap();
|
|
|
+ report_resolve::handle_response(state, report_resolve_response_obj)
|
|
|
+ }
|
|
|
+
|
|
|
+ #[tokio::test]
|
|
|
+ async fn test_handle_report_protocols() {
|
|
|
+ let th = TestHarness::new();
|
|
|
+ // Get level 0 credential
|
|
|
+ let invite_response = get_invite(th.context.clone()).await.unwrap();
|
|
|
+ let token = get_token(invite_response).await;
|
|
|
+ let pubkeys_obj = get_pubkeys(th.context.clone()).await.unwrap();
|
|
|
+ let lox_cred = get_open_invite(th.context.clone(), token, pubkeys_obj.lox_pub.clone())
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+
|
|
|
+ // Get bucket info
|
|
|
+ let bucket = get_bucket(th.context.clone(), lox_cred.clone()).await;
|
|
|
+
|
|
|
+ // Report init
|
|
|
+ let lox_cred = get_report_init(th.context.clone(), lox_cred.clone(), CountryCode::RU)
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ let pending1 = lox_cred.pending.unwrap();
|
|
|
+ assert!(pending1 != Scalar::ZERO);
|
|
|
+
|
|
|
+ // Get report status
|
|
|
+ let (lox_cred, report_statuses) = get_report_status(th.context.clone(), lox_cred)
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ assert_eq!(report_statuses, [ReportStatus::NoReport; 3]);
|
|
|
+
|
|
|
+ // Submit a report
|
|
|
+ let lox_cred = get_report_submit(th.context.clone(), lox_cred, 0, [true, false, false])
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+
|
|
|
+ // Check status
|
|
|
+ let (lox_cred, report_statuses) = get_report_status(th.context.clone(), lox_cred)
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ assert_eq!(
|
|
|
+ report_statuses,
|
|
|
+ [
|
|
|
+ ReportStatus::Pending,
|
|
|
+ ReportStatus::NoReport,
|
|
|
+ ReportStatus::NoReport
|
|
|
+ ]
|
|
|
+ );
|
|
|
+
|
|
|
+ // Scan returns bridge is not blocked
|
|
|
+ th.context.ba.lock().unwrap().process_scan_result(
|
|
|
+ bucket[0].get_hashed_fingerprint(),
|
|
|
+ CountryCode::RU,
|
|
|
+ ReportStatus::NotBlocked,
|
|
|
+ &mut th.context.db.lock().unwrap(),
|
|
|
+ );
|
|
|
+
|
|
|
+ // Check status
|
|
|
+ let (lox_cred, report_statuses) = get_report_status(th.context.clone(), lox_cred)
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ assert_eq!(
|
|
|
+ report_statuses,
|
|
|
+ [
|
|
|
+ ReportStatus::NotBlocked,
|
|
|
+ ReportStatus::NoReport,
|
|
|
+ ReportStatus::NoReport
|
|
|
+ ]
|
|
|
+ );
|
|
|
+
|
|
|
+ // Submit a new report, adding 1 to our false_reports count
|
|
|
+ let lox_cred = get_report_submit(th.context.clone(), lox_cred, 1, [true, false, false])
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ assert_eq!(lox_cred.false_reports.unwrap(), Scalar::ONE);
|
|
|
+
|
|
|
+ // Let report time out
|
|
|
+ th.context
|
|
|
+ .ba
|
|
|
+ .lock()
|
|
|
+ .unwrap()
|
|
|
+ .mark_old_reports_simulated_time(
|
|
|
+ &bucket[0].get_hashed_fingerprint(),
|
|
|
+ CountryCode::RU,
|
|
|
+ SystemTime::now(),
|
|
|
+ );
|
|
|
+
|
|
|
+ // Resolve
|
|
|
+ let _lox_cred = get_report_resolve(th.context.clone(), lox_cred, 0)
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ }
|
|
|
+
|
|
|
fn empty() -> BoxBody<Bytes, Infallible> {
|
|
|
Empty::<Bytes>::new()
|
|
|
.map_err(|never| match never {})
|