1use crate::{
2 auth,
3 error::Error,
4 request::{ApiResponse, DEFAULT_API_HOST, DEFAULT_USER_AGENT, HTTP_CLIENT},
5};
6use reqwest::header::{AUTHORIZATION, CONTENT_TYPE, USER_AGENT};
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Serialize, Deserialize)]
10pub struct SafeUser {
11 #[serde(rename = "app_id")]
12 pub user_id: String,
13 #[serde(rename = "session_id")]
14 pub session_id: String,
15 #[serde(rename = "session_private_key")]
16 pub session_private_key: String,
17 #[serde(rename = "server_public_key")]
18 pub server_public_key: String,
19 #[serde(rename = "spend_private_key")]
20 pub spend_private_key: String,
21 #[serde(skip)]
22 pub is_spend_private_sum: bool,
23}
24
25#[derive(Debug, Serialize, Deserialize)]
26pub struct GhostKeys {
27 #[serde(rename = "type")]
28 pub key_type: String,
29 pub mask: String,
30 pub keys: Vec<String>,
31}
32
33#[derive(Debug, Serialize, Deserialize)]
34pub struct GhostKeyRequest {
35 pub receivers: Vec<String>,
36 pub index: u32,
37 pub hint: String,
38}
39
40impl SafeUser {
41 pub fn new(
42 user_id: String,
43 session_id: String,
44 session_private_key: String,
45 server_public_key: String,
46 spend_private_key: String,
47 ) -> Self {
48 Self {
49 user_id,
50 session_id,
51 session_private_key,
52 server_public_key,
53 spend_private_key,
54 is_spend_private_sum: false,
55 }
56 }
57
58 pub fn new_from_file(path: &str) -> Result<Self, Error> {
59 let file = std::fs::read(path).unwrap();
60 let safe_user: SafeUser = serde_json::from_slice(&file).unwrap();
61 Ok(safe_user)
62 }
63
64 pub fn new_from_env() -> Result<Self, Error> {
65 Self::new_from_env_str("")
66 }
67
68 pub fn new_from_env_str(env: &str) -> Result<Self, Error> {
69 let env = if env.is_empty() {
70 "TEST_KEYSTORE_PATH"
71 } else {
72 env
73 };
74 let env = std::env::var(env).unwrap();
75 let path = std::path::Path::new(&env);
76 Self::new_from_file(path.to_str().unwrap())
77 }
78}
79
80impl GhostKeys {
81 pub fn keys_slice(&self) -> Vec<crypto::Key> {
82 self.keys
83 .iter()
84 .map(|k| crypto::Key::from_string(k).expect("Invalid key format"))
85 .collect()
86 }
87}
88
89pub async fn request_safe_ghost_keys(
90 gkr: &[GhostKeyRequest],
91 user: &SafeUser,
92) -> Result<Vec<GhostKeys>, Error> {
93 let data = serde_json::to_vec(gkr)?;
94 let path = "/safe/keys";
95
96 let token = auth::sign_authentication_token(
97 reqwest::Method::POST.as_str(),
98 path,
99 &String::from_utf8_lossy(&data),
100 user,
101 )?;
102
103 let response = HTTP_CLIENT
104 .post(DEFAULT_API_HOST.to_string() + path)
105 .header(AUTHORIZATION, format!("Bearer {}", token))
106 .header(CONTENT_TYPE, "application/json")
107 .header(USER_AGENT, DEFAULT_USER_AGENT)
108 .json(&gkr)
109 .send()
110 .await?;
111
112 let body = response.bytes().await?;
113
114 let parsed: ApiResponse<Vec<GhostKeys>> = serde_json::from_slice(&body)?;
115
116 if let Some(api_error) = parsed.error {
117 return Err(Error::Api(api_error));
118 }
119
120 parsed
121 .data
122 .ok_or_else(|| Error::DataNotFound("API response did not contain user data".to_string()))
123}
124
125#[derive(Debug, Deserialize)]
126pub struct OAuthError {
127 pub code: i32,
128 pub description: String,
129}
130
131pub mod crypto {
132 use serde::{Deserialize, Serialize};
133
134 #[derive(Debug, Clone, Serialize, Deserialize)]
135 pub struct Key {
136 pub value: String,
137 }
138
139 impl Key {
140 pub fn from_string(s: &str) -> Result<Self, Box<dyn std::error::Error>> {
141 Ok(Self {
142 value: s.to_string(),
143 })
144 }
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use std::env;
151
152 use super::*;
153
154 #[tokio::test]
155 async fn test_new_from_env() {
156 if env::var("TEST_KEYSTORE_PATH").is_err() {
157 println!("TEST_KEYSTORE_PATH is not set");
158 return;
159 }
160 let safe_user = SafeUser::new_from_env().expect("Failed to init user from env");
161 println!("{:?}", safe_user);
162 }
163}