Fixes Patches Creation & More

Working with files / Paths fixed

Websocket PingPongs (There is no data behind this just PingPong)

Storing User Profiles correctly

Added necessary dependencies and initial code support for: - X.509
certificate handling via x509/pkcs8 crates - Key generation and crypto
operations via x448/sha2 - Structured hex encoding and base64 encoding
This commit is contained in:
Alex Emmet 2025-09-23 20:26:48 +00:00
commit 0d07ddf851
23 changed files with 887 additions and 266 deletions

View file

@ -14,11 +14,11 @@ pub struct Contact {
#[derive(Debug, Clone)]
pub enum UserStatus {
Online,
DoNotDisturb,
Do_Not_Disturb,
WC,
Away,
UserOffline,
IotaOffline,
User_Offline,
Iota_Offline,
}
impl Default for Contact {
@ -31,7 +31,7 @@ impl Default for Contact {
user_id: None,
user_name: None,
last_message_at: Some(now),
user_status: UserStatus::UserOffline,
user_status: UserStatus::User_Offline,
about: None,
}
}
@ -43,7 +43,7 @@ impl Contact {
user_id: Some(user_id),
user_name: None,
last_message_at: Some(last_message_at),
user_status: UserStatus::UserOffline,
user_status: UserStatus::User_Offline,
about: None,
}
}
@ -53,7 +53,7 @@ impl Contact {
user_id: Some(user_id),
user_name: None,
last_message_at: None,
user_status: UserStatus::UserOffline,
user_status: UserStatus::User_Offline,
about: None,
}
}
@ -64,13 +64,13 @@ impl Contact {
pub fn to_json(&self) -> JsonValue {
let mut obj = JsonValue::new_object();
if let Some(id) = &self.user_id {
obj["userID"] = JsonValue::from(id.to_string());
obj["user_id"] = JsonValue::from(id.to_string());
}
if let Some(name) = &self.user_name {
obj["userName"] = JsonValue::from(name.as_str());
obj["user_name"] = JsonValue::from(name.as_str());
}
if let Some(ts) = &self.last_message_at {
obj["lastMessageAt"] = JsonValue::from(ts.to_string());
obj["last_message_at"] = JsonValue::from(ts.to_string());
}
obj
}
@ -81,29 +81,27 @@ impl Contact {
}
pub fn from_json(o: &JsonValue) -> Contact {
let user_id = o["userID"]
.as_str()
.and_then(|s| Uuid::parse_str(s).ok());
let user_id = o["user_id"].as_str().and_then(|s| Uuid::parse_str(s).ok());
let user_name = o["userName"].as_str().map(|s| s.to_string());
let user_name = o["user_name"].as_str().map(|s| s.to_string());
let last_message_at = o["lastMessageAt"].as_i64();
let last_message_at = o["last_message_at"].as_i64();
Contact {
user_id,
user_name,
last_message_at,
user_status: UserStatus::UserOffline, // default
user_status: UserStatus::User_Offline, // default
about: None,
}
}
pub fn info(&self) -> JsonValue {
let mut obj = self.to_json();
if let Some(id) = &self.user_id {
obj["userID"] = JsonValue::from(id.to_string());
obj["user_id"] = JsonValue::from(id.to_string());
}
if let Some(name) = &self.user_name {
obj["userName"] = JsonValue::from(name.as_str());
obj["user_name"] = JsonValue::from(name.as_str());
}
obj
}

View file

@ -1,5 +1,5 @@
pub mod contact;
pub mod user_community_util;
pub mod user_manager;
pub mod user_profile_full;
pub mod user_profile;
pub mod user_community_util;
pub mod user_profile_full;

View file

@ -6,12 +6,7 @@ use uuid::Uuid;
pub struct UserCommunityUtil;
impl UserCommunityUtil {
pub fn add_community(
storage_owner: Uuid,
address: String,
title: String,
position: String,
) {
pub fn add_community(storage_owner: Uuid, address: String, title: String, position: String) {
let path = format!("users/{}/communities.json", storage_owner);
let mut communities: JsonValue = Self::load_array(&path);
@ -58,7 +53,7 @@ impl UserCommunityUtil {
if let Some(parent) = Path::new(path).parent() {
let _ = fs::create_dir_all(parent);
}
let _ = fs::write(path, arr.pretty(3));
}
}

View file

@ -1,16 +1,26 @@
use std::fs;
use std::sync::Mutex;
use std::io;
use std::path::Path;
use uuid::Uuid;
use rand::Rng;
use rand::rngs::OsRng;
use base64::{engine::general_purpose, Engine as _};
use json::{JsonValue};
use once_cell::sync::Lazy;
use crate::auth::auth_connector::AuthConnector;
use crate::users::user_profile::UserProfile;
use crate::users::user_profile_full::UserProfileFull;
use crate::util::config_util::CONFIG;
use crate::util::file_util::{load_file, save_file};
use base64::{Engine as _, engine::general_purpose};
use der::DerOrd;
use hex;
use json::JsonValue;
use once_cell::sync::Lazy;
use pkcs8::EncodePublicKey;
use pkcs8::spki::SubjectPublicKeyInfoOwned;
use pkcs8::{ObjectIdentifier, PrivateKeyInfo, SubjectPublicKeyInfo, der::Encode};
use rand::Rng;
use rand_core::OsRng;
use rand_core::RngCore;
use rustls::pki_types::SubjectPublicKeyInfoDer;
use sha2::{Digest, Sha256};
use std::io;
use std::sync::Mutex;
use uuid::Uuid;
use x448::{PublicKey, Secret};
use x509::AlgorithmIdentifier;
pub struct UserManager;
@ -18,14 +28,22 @@ static USERS: Lazy<Mutex<Vec<UserProfile>>> = Lazy::new(|| Mutex::new(Vec::new()
static UNIQUE: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false));
impl UserManager {
pub fn create_user(username: &str) -> Option<UserProfileFull> {
// Stub: normally AuthConnector.getRegister() returns a UUID
let user_id = Uuid::new_v4();
pub async fn create_user(username: &str) -> Option<UserProfileFull> {
let user_id = AuthConnector::get_register().await.unwrap();
let mut buf = [0u8; 56];
let mut rng = OsRng;
rng.fill_bytes(&mut buf);
let private_key = Secret::from_bytes(&buf).unwrap();
let public_key = PublicKey::from(&private_key);
// Stubbed: CryptoHelper.generateKeyPair()
let public_key = general_purpose::STANDARD.encode(b"dummy-public");
let private_key = general_purpose::STANDARD.encode(b"dummy-private");
let private_key_hash = format!("hash-{}", &private_key);
let mut hasher = Sha256::new();
hasher.update(
&general_purpose::STANDARD
.encode(&private_key.as_bytes())
.as_bytes(),
);
let result = hasher.finalize();
let private_key_hash = hex::encode(result);
let mut bytes = [0u8; 192];
OsRng.fill(bytes.as_mut());
@ -35,12 +53,27 @@ impl UserManager {
user_id,
username.to_string(),
None,
public_key,
general_purpose::STANDARD.encode(&public_key.as_bytes()),
private_key_hash,
reset_token,
);
let up_full = UserProfileFull { user_profile: up.clone(), private_key };
let up_full = UserProfileFull {
user_profile: up.clone(),
private_key: general_purpose::STANDARD.encode(&private_key.as_bytes()),
};
AuthConnector::complete_register(&up, &CONFIG.lock().unwrap().get_iota_id().to_string())
.await;
save_file(
"",
&format!("{}.tu", username),
&format!(
"{}::{}",
user_id,
general_purpose::STANDARD.encode(&private_key.as_bytes())
),
);
USERS.lock().unwrap().push(up);
Self::save_users().ok();
@ -48,7 +81,12 @@ impl UserManager {
}
pub fn get_user(user_id: Uuid) -> Option<UserProfile> {
USERS.lock().unwrap().iter().cloned().find(|u| u.user_id == user_id)
USERS
.lock()
.unwrap()
.iter()
.cloned()
.find(|u| u.user_id == user_id)
}
pub fn get_users() -> Vec<UserProfile> {

View file

@ -1,15 +1,15 @@
use std::fs;
use std::sync::Mutex;
use std::collections::HashMap;
use std::io;
use std::path::Path;
use uuid::Uuid;
use crate::auth::auth_connector::AuthConnector;
use crate::users::user_manager::UserManager;
use base64::{Engine as _, engine::general_purpose};
use json::{JsonValue, object, stringify};
use rand::Rng;
use rand::rngs::OsRng;
use base64::{engine::general_purpose, Engine as _};
use json::{JsonValue, object, stringify};
use crate::users::user_manager::UserManager;
use crate::auth::auth_connector::AuthConnector;
use std::collections::HashMap;
use std::fs;
use std::io;
use std::path::Path;
use std::sync::Mutex;
use uuid::Uuid;
// --- UserProfile ---
#[derive(Clone, Debug)]
@ -43,34 +43,42 @@ impl UserProfile {
pub fn to_json(&self) -> JsonValue {
let mut obj = object! {
"UUID" => self.user_id.to_string(),
"uuid" => self.user_id.to_string(),
"username" => self.username.clone(),
"publicKey" => self.public_key.clone(),
"privateKeyHash" => self.private_key_hash.clone(),
"resetToken" => self.reset_token.clone()
"public_key" => self.public_key.clone(),
"private_key_hash" => self.private_key_hash.clone(),
"reset_token" => self.reset_token.clone()
};
if let Some(d) = &self.display_name {
obj["displayName"] = d.clone().into();
obj["display_name"] = d.clone().into();
}
obj
}
pub async fn from_json(j: &JsonValue) -> Option<Self> {
let uuid = Uuid::parse_str(j["UUID"].as_str()?).ok()?;
let uuid = Uuid::parse_str(j["uuid"].as_str()?).ok()?;
let username = j["username"].as_str()?.to_string();
let public_key = j["publicKey"].as_str()?.to_string();
let private_key_hash = j["privateKeyHash"].as_str()?.to_string();
let reset_token = j["resetToken"].as_str()?.to_string();
let display_name = j["displayName"].as_str().map(|s| s.to_string());
let public_key = j["public_key"].as_str()?.to_string();
let private_key_hash = j["private_key_hash"].as_str()?.to_string();
let reset_token = j["reset_token"].as_str()?.to_string();
let display_name = j["display_name"].as_str().map(|s| s.to_string());
let mut up = UserProfile::new(uuid, username, display_name, public_key, private_key_hash, reset_token);
let mut up = UserProfile::new(
uuid,
username,
display_name,
public_key,
private_key_hash,
reset_token,
);
// Migration hook (stubbed, since AuthConnector isnt implemented here)
if j.has_key("migrate")
|| j.has_key("migrating")
|| j.has_key("changing")
|| j.has_key("move")
|| j.has_key("moving") {
if j.has_key("migrate")
|| j.has_key("migrating")
|| j.has_key("changing")
|| j.has_key("move")
|| j.has_key("moving")
{
if AuthConnector::migrate_user(&mut up, stringify!("{}", Uuid::new_v4())).await {
println!("[INFO] Migration triggered for {}", up.username);
UserManager::set_unique(true);
@ -90,6 +98,8 @@ impl UserProfile {
}
pub fn get_display_name(&self) -> String {
self.display_name.clone().unwrap_or_else(|| self.username.clone())
self.display_name
.clone()
.unwrap_or_else(|| self.username.clone())
}
}
}

View file

@ -5,4 +5,4 @@ use crate::users::user_profile::UserProfile;
pub struct UserProfileFull {
pub user_profile: UserProfile,
pub private_key: String,
}
}