Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Endpoint 205 host/kyc and 206 host/hosting_criteria #27

Merged
merged 6 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ This API is relying on an authentication mechanism [hp-admin-crypto](https://git

This API is mounted on HPOS at v2 path of API, so all the calls should be of a format `/api/v2/<path>`, e.g. to get all hosted happs with usage calculated over last 7 days you would call `/api/v2/hosted_happs/?usage_interval=604800`.

## Integration Tests

```
RUST_LOG=hpos-api-rust=trace,integration=trace cargo test -- --nocapture --test-threads=1
```

### Endpoints

#### GET `/hosted_happs/?quantity=<quantity>&usage_interval=<usage_interval>`
Expand Down
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ pub async fn rocket() -> Rocket<Build> {
earnings, // done
invoices, // done
redeemable_histogram, // done
kyc_level, // TODO!!
hosting_criteria, // TODO!!

kyc_level, // done
hosting_criteria, // done
redemptions // done

],
)
.mount(
Expand Down
34 changes: 34 additions & 0 deletions src/routes/host/auth_payload.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use std::time::{SystemTime, UNIX_EPOCH};

// Define the struct
pub struct auth_payload {
pub email: String,
pub timestamp: u64,
pub pub_key: String,
}

impl auth_payload {
pub fn new(email: String, pub_key: String) -> Self {
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs();

auth_payload {
email,
timestamp,
pub_key,
}
}

// Method to convert the struct into bytes
pub fn into_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();

bytes.extend(self.email.as_bytes());
bytes.extend(&self.timestamp.to_be_bytes());
bytes.extend(self.pub_key.as_bytes());

bytes
}
}
12 changes: 12 additions & 0 deletions src/routes/host/holo_client_auth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#[derive(Debug, Serialize)]
pub struct holo_client_auth {
id: String,
email: String,
accessToken: String,
permissions: String,
profileImage: String,
displayName: String,
kyc: String,
jurisdiction: String,
publicKey: String,
}
156 changes: 148 additions & 8 deletions src/routes/host/hosting_criteria.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,163 @@
use anyhow::{anyhow, Context, Result};

use rocket::{
http::Status,
serde::json::Json,
serde::{json::Json, json::serde_json, Deserialize, Serialize},
{get, State},
};

use crate::hpos::WsMutex;
use holochain_types::prelude::AgentPubKey;
use crate::{common::hbs::call_hbs, hpos::WsMutex, hpos::Ws};
use hpos_config_core::*;
use hpos_config_seed_bundle_explorer::unlock;

use std::{collections::HashMap, env, fs::File, path::PathBuf, sync::Arc};
use std::time::{SystemTime, UNIX_EPOCH};

#[derive(Serialize, Deserialize, Clone)]
#[serde(crate = "rocket::serde")]
#[serde(rename_all = "camelCase")]
pub struct HoloClientAuth {
id: String,
email: String,
access_token: String,
permissions: String,
profile_image: String,
display_name: String,
kyc: String,
jurisdiction: String,
public_key: String,
}

#[derive(Serialize, Deserialize, Clone)]
#[serde(crate = "rocket::serde")]
pub struct HostingCriteriaResponse {
id: String,
kyc: String,
jurisdiction: String
}

#[derive(Serialize, Deserialize, Clone)]
#[serde(crate = "rocket::serde")]
pub struct AuthPayload {
pub email: String,
pub timestamp: u64,
pub pub_key: String,
}

impl AuthPayload {
pub fn new(email: String, pub_key: String) -> Self {
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs();

AuthPayload {
email,
timestamp,
pub_key,
}
}

// Method to convert the struct into bytes
pub fn into_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();

bytes.extend(self.email.as_bytes());
bytes.extend(&self.timestamp.to_be_bytes());
bytes.extend(self.pub_key.as_bytes());

bytes
}
}

async fn from_config() -> Result<(String, String, String)> {
let config_path =
env::var("HPOS_CONFIG_PATH").context("Cannot read HPOS_CONFIG_PATH from env var")?;

let password = env::var("DEVICE_SEED_DEFAULT_PASSWORD")
.context("Cannot read bundle password from env var")?;

let config_file =
File::open(&config_path).context(format!("Failed to open config file {}", config_path))?;

/// ???
match serde_json::from_reader(config_file)? {
Config::V2 { device_bundle, settings, .. } => {
// take in password
let public = unlock(&device_bundle, Some(password))
.await
.context(format!(
"unable to unlock the device bundle from {}",
&config_path
))?
.verifying_key();
Ok((
public_key::to_holochain_encoded_agent_key(&public),
device_bundle,
settings.admin.email
))
}
_ => Err(anyhow!("Unsupported version of hpos config")),
}
}

async fn get_holo_client_auth(payload: AuthPayload) -> Result<HoloClientAuth> {
Ok(call_hbs("/auth/api/v1/holo-client".to_owned(), payload).await?)
}

/// Returns the hosting criteria of the holoport admin user as a json object
/// {
/// "id": "string",
/// "kyc": "string",
/// "jurisdiction": "string"
/// }
#[get("/hosting_criteria")]
pub async fn hosting_criteria(wsm: &State<WsMutex>) -> Result<Json<()>, (Status, String)> {
pub async fn hosting_criteria(wsm: &State<WsMutex>) -> Result<Json<(HostingCriteriaResponse)>, (Status, String)> {
let mut ws = wsm.lock().await;

Ok(Json(()))
let hosting_criteria_response = handle_hosting_criteria(&mut ws).await.map_err(|e| {
(Status::InternalServerError, e.to_string())
})?;

Ok(Json(hosting_criteria_response))
}

/// ???
async fn handle_hosting_criteria(ws: &mut Ws) -> Result<HostingCriteriaResponse> {
let (agent_string, _device_bundle, email) = from_config()
.await
.unwrap();

let payload = AuthPayload::new(email, agent_string);

let auth_result = get_holo_client_auth(payload).await?;

Ok(HostingCriteriaResponse {
id: auth_result.id,
kyc: auth_result.kyc,
jurisdiction: auth_result.jurisdiction
})
}

/// Returns the kyc level of the holoport admin user as a string
#[get("/kyc_level")]
pub async fn kyc_level(wsm: &State<WsMutex>) -> Result<Json<()>, (Status, String)> {
pub async fn kyc_level(wsm: &State<WsMutex>) -> Result<String, (Status, String)> {
let mut ws = wsm.lock().await;

Ok(Json(()))
let kyc_level = handle_kyc_level(&mut ws).await.map_err(|e| {
(Status::InternalServerError, e.to_string())
})?;

Ok(kyc_level)
}

async fn handle_kyc_level(ws: &mut Ws) -> Result<String> {
let (agent_string, _device_bundle, email) = from_config()
.await
.unwrap();

let payload = AuthPayload::new(email, agent_string);

let auth_result = get_holo_client_auth(payload).await?;

Ok(auth_result.kyc)
}
16 changes: 15 additions & 1 deletion tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,20 @@ async fn install_components() {
debug!("body: {:#?}", response_body);
assert_eq!(response_body, "{\"earnings\":{\"last30days\":\"0\",\"last7days\":\"0\",\"lastday\":\"0\"},\"holofuel\":{\"redeemable\":\"0\",\"balance\":\"0\",\"available\":\"0\"},\"recentPayments\":[]}");

// get kyc_level
let path = format!("/host/kyc_level");
info!("calling {}", &path);
let response = client.get(path).dispatch().await;
debug!("status: {}", response.status());
assert_eq!(response.status(), Status::Ok);

// get hosting_criteria
let path = format!("/host/hosting_criteria");
info!("calling {}", &path);
let response = client.get(path).dispatch().await;
debug!("status: {}", response.status());
assert_eq!(response.status(), Status::Ok);

// get invoices report
let path = format!("/host/invoices");
info!("calling {}", &path);
Expand All @@ -264,7 +278,7 @@ async fn install_components() {
debug!("body: {:#?}", response_body);
assert_eq!(response_body, "[]");

// get invoices report
// get redemptions
let path = format!("/host/redemptions");
info!("calling {}", &path);
let response = client.get(path).dispatch().await;
Expand Down
3 changes: 3 additions & 0 deletions tests/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ impl Test {
format!("{}/resources/test/config.yaml", &manifets_path),
);

const HBS_BASE_PATH: &str = "https://hbs.dev.holotest.net";
env::set_var("HBS_URL", HBS_BASE_PATH);

// Get device_bundle from hpos-config and pass it to setup_environment so that lair
// can import a keypar for an agent from hpos-config
let (agent_string, device_bundle) = from_config(hpos_config_path.into(), PASSWORD.into())
Expand Down