diff --git a/Cargo.lock b/Cargo.lock index b3d2fd9..ff02ab7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -627,6 +627,20 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "data-encoding" version = "2.9.0" @@ -1014,6 +1028,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.5" @@ -1404,6 +1424,7 @@ dependencies = [ "cmake", "color-eyre", "crossterm 0.29.0", + "dashmap", "der", "futures", "futures-util", diff --git a/Cargo.toml b/Cargo.toml index 5586b07..cccbf3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,3 +55,4 @@ ratatui_input = "0.1.3" tui-textarea = "0.7.0" tui-input = "0.14.0" pnet = "0.35.0" +dashmap = "6.1.0" diff --git a/src/auth/auth_connector.rs b/src/auth/auth_connector.rs deleted file mode 100644 index 24de38b..0000000 --- a/src/auth/auth_connector.rs +++ /dev/null @@ -1,158 +0,0 @@ -use crate::CONFIG; -use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; -use crate::users::user_profile::UserProfile; -use json::JsonValue; -use json::number::Number; -use reqwest::header::CONTENT_TYPE; -use reqwest::{Client, Response}; -use std::time::Duration; -#[derive(Debug, Clone)] -pub struct AuthUser { - pub created_at: i64, - pub username: String, - pub display: String, - pub avatar: String, - pub about: String, - pub status: String, - pub public_key: String, - pub sub_level: i32, - pub sub_end: i32, -} - -fn client() -> Client { - Client::builder() - .connect_timeout(Duration::from_secs(100)) - .timeout(Duration::from_secs(150)) - .build() - .unwrap() -} - -pub async fn unregister_user(user_id: i64, reset_token: &str) -> Option { - let url = format!("https:/auth.tensamin.net/api/delete/{}", user_id); - let client = client(); - - let mut payload = JsonValue::new_object(); - payload["reset_token"] = reset_token.into(); - - let res = client - .post(&url) - .header(CONTENT_TYPE, "application/json") - .body(payload.dump()) - .send() - .await - .ok()?; - let json = res.text().await.ok()?; - let cv = CommunicationValue::from_json(&json); - Option::from(cv.is_type(CommunicationType::success)) -} - -pub async fn get_user(user_id: i64) -> Option { - let url = format!("https://auth.tensamin.net/api/get/{}", user_id); - let client = client(); - let res = client.get(&url).send().await.ok()?; - let json = res.text().await.ok()?; - - let cv = CommunicationValue::from_json(&json); - if cv.comm_type != CommunicationType::success { - return None; - } - - Some(AuthUser { - created_at: cv - .get_data(DataTypes::created_at) - .unwrap() - .to_string() - .parse::() - .unwrap_or(-1), - username: cv.get_data(DataTypes::username).unwrap().to_string(), - display: cv.get_data(DataTypes::display).unwrap().to_string(), - avatar: cv.get_data(DataTypes::avatar).unwrap().to_string(), - about: cv.get_data(DataTypes::about).unwrap().to_string(), - status: cv.get_data(DataTypes::status).unwrap().to_string(), - public_key: cv.get_data(DataTypes::public_key).unwrap().to_string(), - sub_level: cv - .get_data(DataTypes::sub_level) - .unwrap() - .to_string() - .parse::() - .unwrap_or(-1), - sub_end: cv - .get_data(DataTypes::sub_end) - .unwrap() - .to_string() - .parse::() - .unwrap_or(-1), - }) -} - -pub async fn get_register() -> Option { - let url = "https://auth.tensamin.net/api/register/init".to_string(); - let client = client(); - let res = client.get(&url).send().await.ok()?; - let json = res.text().await.ok()?; - - let cv = CommunicationValue::from_json(&json); - cv.get_data(DataTypes::user_id) - .unwrap_or(&json::JsonValue::Number(Number::from(0))) - .as_i64() -} - -pub async fn complete_register(user_profile: &UserProfile, iota_id: &str) -> bool { - let url = "https://auth.tensamin.net/api/register/complete"; - let client = client(); - - let mut payload = JsonValue::new_object(); - payload["id"] = user_profile.user_id.into(); - payload["public_key"] = user_profile.public_key.clone().into(); - payload["private_key_hash"] = user_profile.private_key_hash.clone().into(); - payload["username"] = user_profile.username.clone().into(); - payload["iota_id"] = iota_id.into(); - payload["reset_token"] = user_profile.reset_token.clone().into(); - - let res = client - .post(url) - .header(CONTENT_TYPE, "application/json") - .body(payload.to_string()) - .send() - .await - .unwrap(); - - let body = res.text().await.unwrap(); - - CommunicationValue::from_json(&body).is_type(CommunicationType::success) -} - -pub async fn migrate_user(user_profile: &mut UserProfile) -> bool { - let url = format!( - "https://auth.tensamin.net/api/change/iota-id/{}", - user_profile.user_id - ); - let client = client(); - - let mut payload = JsonValue::new_object(); - payload["iota_id"] = JsonValue::String(CONFIG.read().await.get_iota_id().to_string()); - payload["reset_token"] = user_profile.reset_token.clone().into(); - payload["new_token"] = user_profile.randomize_reset_token().into(); - - let res = client - .post(url) - .header(CONTENT_TYPE, "application/json") - .body(payload.dump()) - .send() - .await; - - match res { - Ok(resp) => handle_response(resp).await, - Err(_) => false, - } -} - -async fn handle_response(resp: Response) -> bool { - match resp.text().await { - Ok(text) => { - let cv = CommunicationValue::from_json(&text.to_string()); - cv.comm_type == CommunicationType::success - } - Err(_) => false, - } -} diff --git a/src/auth/auth_user.rs b/src/auth/auth_user.rs new file mode 100644 index 0000000..8f763b6 --- /dev/null +++ b/src/auth/auth_user.rs @@ -0,0 +1,18 @@ +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct AuthUser { + pub id: i64, + pub username: String, + pub password_hash: String, + pub public_key: String, +} + +impl AuthUser { + pub fn new(id: i64, username: String, password_hash: String, public_key: String) -> Self { + AuthUser { + id, + username, + password_hash, + public_key, + } + } +} diff --git a/src/auth/local_auth.rs b/src/auth/local_auth.rs index b5fbdf3..112ee2b 100644 --- a/src/auth/local_auth.rs +++ b/src/auth/local_auth.rs @@ -1,7 +1,7 @@ use json::JsonValue; use crate::util::file_util::load_file; - +// NOT USED AT MOMENT pub fn is_private_key_valid(user_id: &i64, key_hash: &str) -> bool { let file_contents = load_file("", "users.json"); diff --git a/src/auth/mod.rs b/src/auth/mod.rs index ba5d612..73d6442 100644 --- a/src/auth/mod.rs +++ b/src/auth/mod.rs @@ -1,3 +1,2 @@ -pub mod auth_connector; -pub mod crypto_helper; +pub mod auth_user; pub mod local_auth; diff --git a/src/communities/community_connection.rs b/src/communities/community_connection.rs index 798b909..1ec919f 100644 --- a/src/communities/community_connection.rs +++ b/src/communities/community_connection.rs @@ -1,8 +1,8 @@ -use crate::auth::auth_connector::AuthUser; -use crate::auth::auth_connector::get_user; +use crate::auth::auth_user::AuthUser; use crate::communities::community::Community; use crate::communities::interactables::interactable::Interactable; use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; +use crate::users::user_manager::get_user; use aes_gcm::{Aes256Gcm, KeyInit, Nonce, aead::Aead}; use base64::{Engine as _, engine::general_purpose::STANDARD}; use futures::SinkExt; @@ -121,7 +121,7 @@ impl CommunityConnection { .as_i64() .unwrap_or(0); - let Some(user) = get_user(user_id).await else { + let Some(user) = get_user(user_id) else { self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_user_id) .await; return; @@ -129,7 +129,7 @@ impl CommunityConnection { { let mut auth_guard = self.auth.write().await; - *auth_guard = Some(user.clone()); + //*auth_guard = Some(user.clone()); let mut user_id_guard = self.user_id.write().await; *user_id_guard = user_id; diff --git a/src/data/communication.rs b/src/data/communication.rs index e21dcae..de68e6b 100644 --- a/src/data/communication.rs +++ b/src/data/communication.rs @@ -10,6 +10,7 @@ pub enum DataTypes { error_type, accepted_ids, uuid, + register_id, settings, settings_name, chat_partner_id, @@ -81,6 +82,8 @@ pub enum DataTypes { online_status, omikron_id, omikron_connections, + reset_token, + new_token, } impl DataTypes { @@ -90,6 +93,7 @@ impl DataTypes { match normalized.as_str() { "errortype" => DataTypes::error_type, "chatpartnerid" => DataTypes::chat_partner_id, + "registerid" => DataTypes::register_id, "uuid" => DataTypes::uuid, "settings" => DataTypes::settings, "settingsname" => DataTypes::settings_name, @@ -161,6 +165,8 @@ impl DataTypes { "onlinestatus" => DataTypes::online_status, "omikronid" => DataTypes::omikron_id, "omikronconnections" => DataTypes::omikron_connections, + "resettoken" => DataTypes::reset_token, + "newtoken" => DataTypes::new_token, _ => DataTypes::error_type, // fallback if unknown } } @@ -170,6 +176,8 @@ impl DataTypes { #[allow(non_camel_case_types, dead_code)] pub enum CommunicationType { error, + error_internal, + error_invalid_data, error_invalid_user_id, error_invalid_omikron_id, error_not_found, @@ -206,6 +214,8 @@ pub enum CommunicationType { register_response, identification, identification_response, + register_iota, + register_iota_success, ping, pong, add_chat, @@ -244,6 +254,12 @@ pub enum CommunicationType { change_user_data, change_iota_data, + get_register, + complete_register_user, + complete_register_iota, + delete_user, + delete_iota, + start_register, complete_register, } @@ -262,6 +278,8 @@ impl CommunicationType { "function" => CommunicationType::function, "update" => CommunicationType::update, "createuser" => CommunicationType::create_user, + "errorinternal" => CommunicationType::error_internal, + "errorinvaliddata" => CommunicationType::error_invalid_data, "errorinvaliduserid" => CommunicationType::error_invalid_user_id, "errorinvalidomikronid" => CommunicationType::error_invalid_omikron_id, "errornotfound" => CommunicationType::error_not_found, @@ -298,6 +316,8 @@ impl CommunicationType { "registerresponse" => CommunicationType::register_response, "identification" => CommunicationType::identification, "identificationresponse" => CommunicationType::identification_response, + "registeriota" => CommunicationType::register_iota, + "registeriotasuccess" => CommunicationType::register_iota_success, "ping" => CommunicationType::ping, "pong" => CommunicationType::pong, "addchat" => CommunicationType::add_chat, @@ -326,6 +346,12 @@ impl CommunicationType { "changeuserdata" => CommunicationType::change_user_data, "changeiotadata" => CommunicationType::change_iota_data, + "getregister" => CommunicationType::get_register, + "completeregisteruser" => CommunicationType::complete_register_user, + "completeregisteriota" => CommunicationType::complete_register_iota, + "deleteuser" => CommunicationType::delete_user, + "deleteiota" => CommunicationType::delete_iota, + "startregister" => CommunicationType::start_register, "completeregister" => CommunicationType::complete_register, diff --git a/src/main.rs b/src/main.rs index 795407d..90a1b66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,18 +71,6 @@ async fn main() { // BASIC CONFIGURATION &CONFIG.write().await.load(); - if !CONFIG.read().await.config.has_key("iota_id") { - CONFIG.write().await.change( - "iota_id", - JsonValue::Number(Number::from( - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or(Duration::from_millis(0)) - .as_millis() as i64, - )), - ); - CONFIG.write().await.update(); - } // USER MANAGEMENT if let Err(_) = user_manager::load_users().await { @@ -153,21 +141,7 @@ async fn main() { break; } let omikron: Arc = Arc::new(OmikronConnection::new()); - omikron.connect().await; - omikron - .send_message( - CommunicationValue::new(CommunicationType::identification) - .add_data(DataTypes::user_ids, JsonValue::String(sb.to_string())) - .add_data( - DataTypes::iota_id, - JsonValue::Number(Number::from(CONFIG.read().await.get_iota_id())), - ) - .to_json() - .to_string() - .as_mut() - .to_string(), - ) - .await; + omikron.connect(sb.clone()).await; let mut omikron_connection = OMIKRON_CONNECTION.write().await; *omikron_connection = Some(omikron.clone()); log_message_trans("setup_completed"); diff --git a/src/omikron/omikron_connection.rs b/src/omikron/omikron_connection.rs index f697951..5889759 100644 --- a/src/omikron/omikron_connection.rs +++ b/src/omikron/omikron_connection.rs @@ -1,28 +1,35 @@ use crate::auth::local_auth; -use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; -use crate::gui::log_panel::{log_cv, log_message, log_message_format}; +use crate::gui::log_panel::{log_cv, log_message_format}; +use crate::{ + data::communication::{CommunicationType, CommunicationValue, DataTypes}, + gui::log_panel::{log_message, log_message_trans}, + util::{config_util::CONFIG, crypto_helper}, +}; +use json::JsonValue; + use crate::users::contact::Contact; use crate::users::user_community_util::UserCommunityUtil; -use crate::util::chat_files; -use crate::util::chats_util::{get_user, get_users, mod_user}; +use crate::util::chats_util::{get_user, mod_user}; use crate::util::file_util::{get_children, load_file, save_file}; +use crate::util::{chat_files, chats_util}; use crate::{ACTIVE_TASKS, SHUTDOWN}; +use dashmap::DashMap; use futures::Stream; use futures::stream::{SplitSink, SplitStream}; use futures_util::sink::Sink; use futures_util::{SinkExt, StreamExt}; use hyper::upgrade::Upgraded; use hyper_util::rt::TokioIo; -use json::JsonValue; use json::number::Number; use std::collections::HashMap; use std::sync::{Arc, LazyLock}; use std::time::{SystemTime, UNIX_EPOCH}; -use tokio::sync::{Mutex, RwLock}; +use tokio::sync::{Mutex, RwLock, mpsc}; use tokio::time::{Duration, Instant, sleep}; use tokio_tungstenite::{connect_async, tungstenite::protocol::Message}; use tungstenite::Utf8Bytes; use uuid::Uuid; + pub static OMIKRON_CONNECTION: LazyLock>>>> = LazyLock::new(|| Arc::new(RwLock::new(None))); @@ -39,7 +46,7 @@ pub struct OmikronConnection { pub user_id: Arc>, pub(crate) writer: Arc + Send + Unpin>>>>, - waiting: Arc>>>, + waiting: Arc>>, pingpong: Arc>>>, pub last_ping: Arc>, pub message_send_times: Arc>>, @@ -52,7 +59,7 @@ impl OmikronConnection { variant: Arc::new(RwLock::new(ConnectionVariant::Omikron)), user_id: Arc::new(RwLock::new(0)), writer: Arc::new(Mutex::new(None)), - waiting: Arc::new(Mutex::new(HashMap::new())), + waiting: Arc::new(DashMap::new()), pingpong: Arc::new(Mutex::new(None)), last_ping: Arc::new(Mutex::new(-1)), message_send_times: Arc::new(Mutex::new(HashMap::new())), @@ -68,7 +75,7 @@ impl OmikronConnection { user_id: Arc::new(RwLock::new(0)), writer: Arc::new(Mutex::new(Some(Box::new(writer) as Box + Send + Unpin>))), - waiting: Arc::new(Mutex::new(HashMap::new())), + waiting: Arc::new(DashMap::new()), pingpong: Arc::new(Mutex::new(None)), last_ping: Arc::new(Mutex::new(-1)), message_send_times: Arc::new(Mutex::new(HashMap::new())), @@ -85,54 +92,103 @@ impl OmikronConnection { *self.is_connected.lock().await } /// Connect loop with retry - pub async fn connect(self: &Arc) { - loop { - if *SHUTDOWN.read().await { - break; + pub async fn connect(self: &Arc, user_ids: String) { + if self.is_connected().await { + return; + } + + let conf = CONFIG.read().await; + let iota_id = conf.get_iota_id(); + let public_key = conf.get_public_key(); + let private_key = conf.get_private_key(); + + if iota_id == 0 || public_key.is_none() || private_key.is_none() { + // Registration flow + drop(conf); // release read lock + log_message_trans("iota_register_new"); + let key_pair = crypto_helper::generate_keypair(); + let public_key_base64 = crypto_helper::public_key_to_base64(&key_pair.public); + let private_key_base64 = crypto_helper::secret_key_to_base64(&key_pair.secret); + + let mut conf_write = CONFIG.write().await; + conf_write.change("public_key", JsonValue::String(public_key_base64.clone())); + conf_write.change("private_key", JsonValue::String(private_key_base64)); + conf_write.update(); + drop(conf_write); + + if self.connect_internal().await { + // a new helper function to just connect + self.send_message( + CommunicationValue::new(CommunicationType::register_iota) + .add_data(DataTypes::public_key, JsonValue::String(public_key_base64)) + .to_json() + .to_string(), + ) + .await; } - match connect_async("wss://app.tensamin.net/ws/iota/").await { - Ok((ws_stream, _)) => { - let (write_half, read_half) = ws_stream.split(); - *self.writer.lock().await = Some(Box::new(write_half)); - let boxed_reader: Box< - dyn Stream> + Send + Unpin, - > = Box::new(read_half); - self.clone().spawn_listener(boxed_reader).await; - let cloned_self = self.clone(); - - { - ACTIVE_TASKS.lock().unwrap().push("PingPong".to_string()); - } - let handle = tokio::spawn(async move { - loop { - if *SHUTDOWN.read().await { - break; - } - if *cloned_self.is_connected.lock().await == false { - break; - } - cloned_self.send_ping().await; - sleep(Duration::from_secs(1)).await; - } - }); - { - ACTIVE_TASKS - .lock() - .unwrap() - .retain(|t| !t.eq(&"PingPong".to_string())); - } - - *self.is_connected.lock().await = true; - *self.pingpong.lock().await = Some(handle); - break; - } - Err(_) => { - *self.is_connected.lock().await = false; - sleep(Duration::from_secs(2)).await; - } + } else { + // Login flow + if self.connect_internal().await { + self.send_message( + CommunicationValue::new(CommunicationType::identification) + .add_data( + DataTypes::iota_id, + JsonValue::Number(json::number::Number::from(iota_id)), + ) + .to_json() + .to_string(), + ) + .await; } } } + + async fn connect_internal(self: &Arc) -> bool { + if self.is_connected().await { + return true; + } + log_message_trans("omikron_connecting"); + + // connect to omikron + let conf = CONFIG.read().await; + let addr = conf + .get("omikron_addr") + .as_str() + .unwrap_or("wss://app.tensamin.net/ws/iota/"); + let stream_res = connect_async(addr).await; + if let Err(e) = stream_res { + log_message(format!("omikron_connection_error {}", e.to_string())); + return false; + } + let (stream, _) = stream_res.unwrap(); + log_message_trans("omikron_connection_success"); + + let (write_half, read_half) = stream.split(); + + *self.writer.lock().await = Some(Box::new(write_half)); + let boxed_reader: Box< + dyn Stream> + Send + Unpin, + > = Box::new(read_half); + self.spawn_listener(boxed_reader).await; + + let mut is_connected = self.is_connected.lock().await; + *is_connected = true; + drop(is_connected); + + let sel_arc_clone = self.clone(); + tokio::spawn(async move { + loop { + if !sel_arc_clone.is_connected().await { + break; + } + sel_arc_clone.send_ping().await; + sleep(Duration::from_secs(10)).await; + } + }); + + true + } + pub async fn send_message(&self, msg: String) { Self::send_message_static(&self.writer, Arc::clone(&self.is_connected), msg).await; } @@ -186,7 +242,7 @@ impl OmikronConnection { } pub fn handle_message( msg: Result, - waiting: Arc>>>, + waiting: Arc>>, writer: Arc< Mutex< Option + Send + Unpin + 'static>>, @@ -205,11 +261,86 @@ impl OmikronConnection { return; } Ok(Message::Text(text)) => { - let mut cv = CommunicationValue::from_json(&text); + let cv = CommunicationValue::from_json(&text); if cv.is_type(CommunicationType::pong) { sel.handle_pong(&cv, true).await; return; } + if cv.is_type(CommunicationType::challenge) { + let conf = CONFIG.read().await; + let private_key = conf.get_private_key().unwrap(); + drop(conf); + + let omikron_public_key = cv + .get_data(DataTypes::public_key) + .unwrap() + .as_str() + .unwrap(); + let encrypted_challenge = + cv.get_data(DataTypes::challenge).unwrap().as_str().unwrap(); + + let solved_challenge = crypto_helper::decrypt( + &private_key, + omikron_public_key, + encrypted_challenge, + ); + + if let Ok(decrypted) = solved_challenge { + let response = + CommunicationValue::new(CommunicationType::challenge_response) + .with_id(cv.get_id()) + .add_data(DataTypes::challenge, JsonValue::String(decrypted)); + + sel_arc.send_message(response.to_json().to_string()).await; + } else { + log_message("Failed to decrypt challenge"); + } + + return; + } + if cv.is_type(CommunicationType::register_iota_success) { + let iota_id = cv + .get_data(DataTypes::iota_id) + .unwrap() + .as_i64() + .unwrap_or(0); + if iota_id != 0 { + let mut conf = CONFIG.write().await; + conf.change("iota_id", JsonValue::Number(iota_id.into())); + conf.update(); + log_message(format!("Iota registered with ID: {}", iota_id)); + + // Now, proceed to login + let login_message = + CommunicationValue::new(CommunicationType::identification) + .add_data( + DataTypes::iota_id, + JsonValue::Number(json::number::Number::from(iota_id)), + ) + .to_json() + .to_string(); + + let sel_arc_clone = sel_arc.clone(); + tokio::spawn(async move { + sel_arc_clone.send_message(login_message).await; + }); + } else { + log_message("Iota registration failed."); + } + return; + } + if cv.is_type(CommunicationType::identification_response) { + if let Some(accepted) = cv.get_data(DataTypes::accepted) { + if accepted.as_str().unwrap_or("0") != "0" { + log_message(format!("Omikron connected: {}", accepted.to_string())); + } else { + log_message("omikron_connection_failed"); + // Maybe add a retry logic here or close the app + } + } + return; + } + let com = variant.read().await.clone(); if com == ConnectionVariant::ClientUnauthenticated { if cv.is_type(CommunicationType::identification) { @@ -288,8 +419,8 @@ impl OmikronConnection { // Direct messages // // ************************************************ // log_cv(&cv); - if let Some(x) = waiting.lock().await.remove(&cv.get_id()) { - x(cv); + if let Some((_, y)) = waiting.remove(&cv.get_id()) { + y(cv); return; } if cv.is_type(CommunicationType::message_other_iota) { @@ -433,7 +564,7 @@ impl OmikronConnection { if cv.is_type(CommunicationType::get_chats) { let user_id = cv.get_sender(); - let users = get_users(user_id); + let users = chats_util::get_users(user_id); let resp = CommunicationValue::new(CommunicationType::get_chats) .with_id(cv.get_id()) .with_receiver(user_id) @@ -641,4 +772,41 @@ impl OmikronConnection { *connected.lock().await = false; } } + pub async fn await_response( + self: Arc, + cv: &CommunicationValue, + timeout_duration: Option, + ) -> Result { + let (tx, mut rx) = mpsc::channel(1); + let msg_id = cv.get_id(); + + let task_tx = tx.clone(); + self.waiting.insert( + msg_id, + Box::new(move |response_cv| { + let inner_tx = task_tx.clone(); + tokio::spawn(async move { + if let Err(e) = inner_tx.send(response_cv).await { + log_message(format!("Failed to send response back to awaiter: {}", &e)); + } + }); + }), + ); + + self.send_message(cv.to_json().to_string()).await; + + let timeout = timeout_duration.unwrap_or(Duration::from_secs(10)); + + match tokio::time::timeout(timeout, rx.recv()).await { + Ok(Some(response_cv)) => Ok(response_cv), + Ok(None) => Err("Failed to receive response, channel was closed.".to_string()), + Err(_) => { + self.waiting.remove(&msg_id); + Err(format!( + "Request timed out after {} seconds.", + timeout.as_secs() + )) + } + } + } } diff --git a/src/server/api.rs b/src/server/api.rs index 257b377..c9637a0 100644 --- a/src/server/api.rs +++ b/src/server/api.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use crate::auth::auth_connector::unregister_user; use crate::communities::community::Community; use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; use crate::gui::log_panel::log_message; @@ -108,11 +107,12 @@ pub async fn handle( "{\"type\":\"error\"}".to_string() } else { let uuid = body.unwrap()["uuid"].as_i64().unwrap_or(0); - unregister_user( + // TODO MOVE TO OMIKRON CONNECTION + /*unregister_user( uuid, &user_manager::get_user(uuid).unwrap().reset_token, ) - .await; + .await;*/ user_manager::remove_user(uuid); user_manager::save_users(); "{}".to_string() diff --git a/src/users/user_manager.rs b/src/users/user_manager.rs index b714638..f3df7a5 100644 --- a/src/users/user_manager.rs +++ b/src/users/user_manager.rs @@ -1,19 +1,23 @@ -use crate::auth::{auth_connector, crypto_helper}; +use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; use crate::gui::log_panel::log_message; +use crate::omikron::omikron_connection::{OMIKRON_CONNECTION, OmikronConnection}; use crate::users::user_profile::UserProfile; use crate::util::config_util::CONFIG; +use crate::util::crypto_helper::{self, public_key_to_base64}; use crate::util::file_util::{load_file, save_file}; use crate::{RELOAD, SHUTDOWN}; use base64::{Engine as _, engine::general_purpose::STANDARD}; use hex::{self}; use json::JsonValue; +use json::number::Number; use once_cell::sync::Lazy; use rand::Rng; use rand_core::OsRng; use rand_core::RngCore; use sha2::{Digest, Sha256}; use std::io::{self}; -use std::sync::Mutex; +use std::sync::{Arc, Mutex}; +use std::time::Duration; use x448::{PublicKey, Secret}; static USERS: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); @@ -45,7 +49,25 @@ pub async fn load_from_tu(username: &str) -> Result<(), ()> { Ok(()) } pub async fn create_user(username: &str) -> (Option, Option) { - let user_id = auth_connector::get_register().await.unwrap(); + let omikron_con: Arc = + OMIKRON_CONNECTION.read().await.as_ref().unwrap().clone(); + let register_cv = if let Ok(register_cv) = omikron_con + .clone() + .await_response( + &CommunicationValue::new(CommunicationType::get_register), + Some(Duration::from_secs(20)), + ) + .await + { + register_cv + } else { + return (None, None); + }; + let user_id = register_cv + .get_data(DataTypes::register_id) + .unwrap_or(&JsonValue::Null) + .as_i64() + .unwrap_or(0); let mut buf = [0u8; 56]; let mut rng = OsRng; rng.fill_bytes(&mut buf); @@ -67,10 +89,31 @@ pub async fn create_user(username: &str) -> (Option, Option None, STANDARD.encode(&public_key.as_bytes()), private_key_hash, - reset_token, + reset_token.clone(), ); - auth_connector::complete_register(&up, &CONFIG.read().await.get_iota_id().to_string()).await; + let cv = CommunicationValue::new(CommunicationType::complete_register_user) + .add_data(DataTypes::user_id, JsonValue::Number(Number::from(user_id))) + .add_data(DataTypes::username, JsonValue::String(username.to_string())) + .add_data( + DataTypes::public_key, + JsonValue::String(public_key_to_base64(&public_key)), + ) + .add_data(DataTypes::iota_id, JsonValue::Number(Number::from(user_id))) + .add_data(DataTypes::reset_token, JsonValue::String(reset_token)); + + let response_cv = omikron_con + .await_response(&cv, Some(Duration::from_secs(20))) + .await; + if let Ok(resp) = response_cv { + if !resp.is_type(CommunicationType::success) { + return (None, None); + } + } else { + return (None, None); + } + // auth_connector::complete_register(&up, &CONFIG.read().await.get_iota_id().to_string()).await; + // *SHUTDOWN.write().await = true; *RELOAD.write().await = true; log_message("Created User"); diff --git a/src/users/user_profile.rs b/src/users/user_profile.rs index 49acfa8..aef1d85 100644 --- a/src/users/user_profile.rs +++ b/src/users/user_profile.rs @@ -1,6 +1,5 @@ use std::time::{SystemTime, UNIX_EPOCH}; -use crate::auth::auth_connector; use crate::gui::log_panel::log_message; use crate::users::user_manager; use crate::util::file_util::{has_file, load_file, used_dir_space}; @@ -95,7 +94,8 @@ impl UserProfile { reset_token, }; - if j.has_key("migrate") + // TODO: Migrate to Omikron / Wss + /* if j.has_key("migrate") || j.has_key("migrating") || j.has_key("changing") || j.has_key("move") @@ -105,7 +105,7 @@ impl UserProfile { log_message(format!("[INFO] Migration triggered for {}", up.username)); user_manager::set_unique(true); } - } + } */ Some(up) } diff --git a/src/util/config_util.rs b/src/util/config_util.rs index 4b09921..6330b59 100644 --- a/src/util/config_util.rs +++ b/src/util/config_util.rs @@ -35,6 +35,14 @@ impl ConfigUtil { self.config["port"].as_u16().unwrap_or(1984) } + pub fn get_public_key(&self) -> Option { + self.config["public_key"].as_str().map(String::from) + } + + pub fn get_private_key(&self) -> Option { + self.config["private_key"].as_str().map(String::from) + } + pub fn get(&self, key: &str) -> &JsonValue { &self.config[key] } diff --git a/src/auth/crypto_helper.rs b/src/util/crypto_helper.rs similarity index 99% rename from src/auth/crypto_helper.rs rename to src/util/crypto_helper.rs index 4f7d17a..17296ac 100644 --- a/src/auth/crypto_helper.rs +++ b/src/util/crypto_helper.rs @@ -7,7 +7,7 @@ use rand_core::RngCore; use sha2::{Digest, Sha256}; use x448::{PublicKey, Secret, SharedSecret}; -/// Errors for crypto operations +/// Errors for crypto opertions #[derive(Debug)] pub enum CryptoError { Base64Decode(base64::DecodeError), diff --git a/src/util/mod.rs b/src/util/mod.rs index b326b92..8130294 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,4 +1,5 @@ pub mod chat_files; pub mod chats_util; pub mod config_util; +pub mod crypto_helper; pub mod file_util;