[Add] basic split
This commit is contained in:
parent
a0ff6b1082
commit
3cdf7c62d5
77 changed files with 506 additions and 648 deletions
61
iota-storage/src/users/contact.rs
Normal file
61
iota-storage/src/users/contact.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
use json::{self, JsonValue, number::Number};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Contact {
|
||||
pub user_id: i64,
|
||||
pub user_name: Option<String>,
|
||||
pub last_message_at: Option<i64>,
|
||||
}
|
||||
|
||||
impl Default for Contact {
|
||||
fn default() -> Self {
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis() as i64;
|
||||
Contact {
|
||||
user_id: 0,
|
||||
user_name: None,
|
||||
last_message_at: Some(now),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Contact {
|
||||
pub fn new(user_id: i64) -> Self {
|
||||
Contact {
|
||||
user_id: user_id,
|
||||
user_name: None,
|
||||
last_message_at: None,
|
||||
}
|
||||
}
|
||||
pub fn set_last_message_at(&mut self, p0: i64) {
|
||||
self.last_message_at = Option::from(p0);
|
||||
}
|
||||
|
||||
pub fn to_json(&self) -> JsonValue {
|
||||
let mut obj = JsonValue::new_object();
|
||||
obj["user_id"] = JsonValue::Number(Number::from(self.user_id));
|
||||
if let Some(name) = &self.user_name {
|
||||
obj["user_name"] = JsonValue::from(name.as_str());
|
||||
}
|
||||
if let Some(ts) = &self.last_message_at {
|
||||
obj["last_message_at"] = JsonValue::Number(Number::from(*ts));
|
||||
}
|
||||
obj
|
||||
}
|
||||
pub fn from_json(o: &JsonValue) -> Contact {
|
||||
let user_id = o["user_id"].as_i64().unwrap_or(0);
|
||||
|
||||
let user_name = o["user_name"].as_str().map(|s| s.to_string());
|
||||
|
||||
let last_message_at = o["last_message_at"].as_i64();
|
||||
|
||||
Contact {
|
||||
user_id,
|
||||
user_name,
|
||||
last_message_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
4
iota-storage/src/users/mod.rs
Normal file
4
iota-storage/src/users/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pub mod contact;
|
||||
pub mod user_community_util;
|
||||
pub mod user_manager;
|
||||
pub mod user_profile;
|
||||
67
iota-storage/src/users/user_community_util.rs
Normal file
67
iota-storage/src/users/user_community_util.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
use crate::util::file_util::save_file;
|
||||
use json::{self, Array, JsonValue};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
pub struct UserCommunityUtil;
|
||||
|
||||
impl UserCommunityUtil {
|
||||
pub fn add_community(storage_owner: i64, address: String, title: String, position: String) {
|
||||
let file_path = format!("users/{}/", storage_owner);
|
||||
let mut communities = Self::load_array(&file_path);
|
||||
|
||||
let mut community = JsonValue::new_object();
|
||||
community["title"] = JsonValue::String(title);
|
||||
community["address"] = JsonValue::String(address);
|
||||
community["position"] = JsonValue::String(position);
|
||||
|
||||
communities.push(community);
|
||||
|
||||
save_file(
|
||||
&file_path,
|
||||
"communities.json",
|
||||
&JsonValue::Array(communities).to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn remove_community(storage_owner: i64, community_address: String) {
|
||||
let file_path = format!("users/{}/", storage_owner);
|
||||
let communities = Self::load_array(&file_path);
|
||||
|
||||
let filtered: Array = communities
|
||||
.iter()
|
||||
.filter(|entry| entry["address"].as_str() != Some(&community_address))
|
||||
.cloned()
|
||||
.collect();
|
||||
save_file(
|
||||
&file_path,
|
||||
"communities.json",
|
||||
&JsonValue::Array(filtered).to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn get_communities(storage_owner: i64) -> Array {
|
||||
let file_path = format!("users/{}/communities.json", storage_owner);
|
||||
Self::load_array(&file_path)
|
||||
}
|
||||
|
||||
fn load_array(file_path: &str) -> Array {
|
||||
if !Path::new(file_path).exists() {
|
||||
return Array::new();
|
||||
}
|
||||
|
||||
match fs::read_to_string(file_path) {
|
||||
Ok(content) => {
|
||||
let parsed = json::parse(&content);
|
||||
match parsed {
|
||||
Ok(JsonValue::Array(arr)) => arr,
|
||||
_ => Array::new(),
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Failed to read file {}: {}", file_path, err);
|
||||
Array::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
197
iota-storage/src/users/user_manager.rs
Normal file
197
iota-storage/src/users/user_manager.rs
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
use crate::omikron::omikron_connection::OMIKRON_CONNECTION;
|
||||
use crate::users::user_profile::UserProfile;
|
||||
use crate::util::crypto_helper::{self, public_key_to_base64};
|
||||
use crate::util::file_util::{load_file, save_file};
|
||||
use crate::util::logger::PrintType;
|
||||
use crate::{RELOAD, SHUTDOWN};
|
||||
use crate::{log, log_cv};
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
||||
use hex::{self};
|
||||
use json::JsonValue;
|
||||
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::time::Duration;
|
||||
use ttp_core::{CommunicationType, CommunicationValue, DataTypes, DataValue};
|
||||
use x448::{PublicKey, Secret};
|
||||
|
||||
static USERS: Lazy<Mutex<Vec<UserProfile>>> = Lazy::new(|| Mutex::new(Vec::new()));
|
||||
static UNIQUE: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false));
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn load_from_tu(username: &str) -> Result<(), ()> {
|
||||
let file_content = load_file("", &format!("{}.tu", username));
|
||||
let segments = file_content.split("::").collect::<Vec<&str>>();
|
||||
let uuid = segments[0].parse::<i64>().unwrap_or(0);
|
||||
let b64_private_key = segments[1];
|
||||
|
||||
let secret: Secret = crypto_helper::load_secret_key(b64_private_key).unwrap();
|
||||
let public_key = PublicKey::from(&secret);
|
||||
|
||||
let mut bytes = [0u8; 192];
|
||||
OsRng.fill(bytes.as_mut());
|
||||
let reset_token = STANDARD.encode(&bytes);
|
||||
|
||||
let user_profile = UserProfile::new(
|
||||
uuid,
|
||||
username.to_string(),
|
||||
Some(username.to_string()),
|
||||
crypto_helper::public_key_to_base64(&public_key),
|
||||
crypto_helper::hex_hash(b64_private_key),
|
||||
reset_token,
|
||||
);
|
||||
USERS.lock().unwrap().push(user_profile);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn create_user(username: &str) -> (Option<UserProfile>, Option<String>) {
|
||||
let register_cv = CommunicationValue::new(CommunicationType::get_register);
|
||||
|
||||
let conn = OMIKRON_CONNECTION.clone();
|
||||
|
||||
let response_cv = match conn
|
||||
.await_response(®ister_cv, Some(Duration::from_secs(20)))
|
||||
.await
|
||||
{
|
||||
Ok(cv) => cv,
|
||||
Err(_) => return (None, None),
|
||||
};
|
||||
log_cv!(PrintType::Omega, response_cv);
|
||||
|
||||
let user_id = match response_cv.get_data(DataTypes::user_id).as_number() {
|
||||
Some(id) => id,
|
||||
None => return (None, None),
|
||||
};
|
||||
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);
|
||||
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(&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());
|
||||
let reset_token = STANDARD.encode(&bytes);
|
||||
|
||||
let up = UserProfile::new(
|
||||
user_id,
|
||||
username.to_string(),
|
||||
None,
|
||||
STANDARD.encode(&public_key.as_bytes()),
|
||||
private_key_hash,
|
||||
reset_token.clone(),
|
||||
);
|
||||
|
||||
let cv = CommunicationValue::new(CommunicationType::complete_register_user)
|
||||
.add_data(DataTypes::user_id, DataValue::Number(user_id))
|
||||
.add_data(DataTypes::username, DataValue::Str(username.to_string()))
|
||||
.add_data(
|
||||
DataTypes::public_key,
|
||||
DataValue::Str(public_key_to_base64(&public_key)),
|
||||
)
|
||||
.add_data(DataTypes::iota_id, DataValue::Number(user_id))
|
||||
.add_data(DataTypes::reset_token, DataValue::Str(reset_token));
|
||||
|
||||
let response_cv = conn
|
||||
.await_response(&cv, Some(Duration::from_secs(20)))
|
||||
.await;
|
||||
|
||||
if let Ok(resp) = response_cv {
|
||||
log_cv!(PrintType::Omega, resp);
|
||||
if !resp.is_type(CommunicationType::success) {
|
||||
return (None, None);
|
||||
}
|
||||
} else {
|
||||
return (None, None);
|
||||
}
|
||||
*SHUTDOWN.write().await = true;
|
||||
*RELOAD.write().await = true;
|
||||
log!("Created User");
|
||||
save_file(
|
||||
"",
|
||||
&format!("{}.tu", username),
|
||||
&format!("{}::{}", user_id, STANDARD.encode(&private_key.as_bytes())),
|
||||
);
|
||||
|
||||
USERS.lock().unwrap().push(up.clone());
|
||||
save_users();
|
||||
(Some(up), Some(STANDARD.encode(&private_key.as_bytes())))
|
||||
}
|
||||
|
||||
pub fn get_user_by_username(username: &str) -> Option<UserProfile> {
|
||||
USERS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.cloned()
|
||||
.find(|u| u.username == username)
|
||||
}
|
||||
|
||||
pub fn get_user(user_id: i64) -> Option<UserProfile> {
|
||||
USERS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.cloned()
|
||||
.find(|u| u.user_id == user_id)
|
||||
}
|
||||
|
||||
pub fn get_users() -> Vec<UserProfile> {
|
||||
USERS.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn remove_user(user_id: i64) {
|
||||
let mut users = USERS.lock().unwrap();
|
||||
users.retain(|u| u.user_id != user_id);
|
||||
*UNIQUE.lock().unwrap() = true;
|
||||
}
|
||||
|
||||
pub fn save_users() {
|
||||
*UNIQUE.lock().unwrap() = false;
|
||||
let users = USERS.lock().unwrap();
|
||||
let arr: Vec<JsonValue> = users.iter().map(|u| u.to_json()).collect();
|
||||
let json_str = JsonValue::Array(arr).dump();
|
||||
|
||||
save_file("", "users.json", &json_str);
|
||||
}
|
||||
|
||||
pub fn clear() {
|
||||
let mut users = USERS.lock().unwrap();
|
||||
users.clear();
|
||||
*UNIQUE.lock().unwrap() = true;
|
||||
}
|
||||
|
||||
pub async fn load_users() -> io::Result<()> {
|
||||
let content = load_file("", "users.json");
|
||||
if content.trim().is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let parsed =
|
||||
json::parse(&content).map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
|
||||
if let JsonValue::Array(arr) = parsed {
|
||||
let mut users = USERS.lock().unwrap();
|
||||
for j in arr.iter() {
|
||||
if let Some(up) = UserProfile::from_json(j).await {
|
||||
users.push(up);
|
||||
}
|
||||
}
|
||||
}
|
||||
if *UNIQUE.lock().unwrap() {
|
||||
save_users();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn set_unique(val: bool) {
|
||||
*UNIQUE.lock().unwrap() = val;
|
||||
}
|
||||
126
iota-storage/src/users/user_profile.rs
Normal file
126
iota-storage/src/users/user_profile.rs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use crate::util::file_util::{has_file, load_file, used_dir_space};
|
||||
use base64::{Engine as _, engine::general_purpose};
|
||||
use json::{JsonValue, object};
|
||||
use rand::Rng;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
// --- UserProfile ---
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UserProfile {
|
||||
pub user_id: i64,
|
||||
pub username: String,
|
||||
pub public_key: String,
|
||||
pub private_key_hash: String,
|
||||
pub reset_token: String,
|
||||
pub created_at: i64,
|
||||
pub display_name: Option<String>,
|
||||
}
|
||||
|
||||
impl UserProfile {
|
||||
pub fn new(
|
||||
user_id: i64,
|
||||
username: String,
|
||||
display_name: Option<String>,
|
||||
public_key: String,
|
||||
private_key_hash: String,
|
||||
reset_token: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
user_id,
|
||||
username,
|
||||
display_name,
|
||||
public_key,
|
||||
private_key_hash,
|
||||
created_at: SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis() as i64,
|
||||
reset_token,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_json(&self) -> JsonValue {
|
||||
let mut obj = object! {
|
||||
"uuid" => self.user_id,
|
||||
"username" => self.username.clone(),
|
||||
"public_key" => self.public_key.clone(),
|
||||
"private_key_hash" => self.private_key_hash.clone(),
|
||||
"created_at" => self.created_at,
|
||||
"reset_token" => self.reset_token.clone()
|
||||
};
|
||||
if let Some(d) = &self.display_name {
|
||||
obj["display_name"] = d.clone().into();
|
||||
}
|
||||
obj
|
||||
}
|
||||
pub fn frontend(&self) -> JsonValue {
|
||||
let mut obj = object! {
|
||||
"uuid" => self.user_id,
|
||||
"username" => self.username.clone(),
|
||||
"public_key" => self.public_key.clone(),
|
||||
"private_key_hash" => self.private_key_hash.clone(),
|
||||
"created_at" => self.created_at,
|
||||
"storage" => used_dir_space(&format!("users/{}", self.user_id.to_string())),
|
||||
};
|
||||
if let Some(d) = &self.display_name {
|
||||
obj["display_name"] = d.clone().into();
|
||||
}
|
||||
if has_file("", &format!("{}.tu", self.username.clone())) {
|
||||
obj["tu"] = load_file("", &format!("{}.tu", self.username.clone())).into();
|
||||
}
|
||||
|
||||
obj
|
||||
}
|
||||
pub async fn from_json(j: &JsonValue) -> Option<Self> {
|
||||
let user_id = j["uuid"].as_i64()?;
|
||||
let username = j["username"].as_str()?.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 created_at = j["created_at"].as_i64()?;
|
||||
let display_name = j["display_name"].as_str().map(|s| s.to_string());
|
||||
|
||||
let up = UserProfile {
|
||||
user_id,
|
||||
username,
|
||||
display_name,
|
||||
public_key,
|
||||
private_key_hash,
|
||||
created_at,
|
||||
reset_token,
|
||||
};
|
||||
|
||||
// TODO: Migrate to Omikron / Wss
|
||||
/* if j.has_key("migrate")
|
||||
|| j.has_key("migrating")
|
||||
|| j.has_key("changing")
|
||||
|| j.has_key("move")
|
||||
|| j.has_key("moving")
|
||||
{
|
||||
if auth_connector::migrate_user(&mut up).await {
|
||||
log_message(format!("[INFO] Migration triggered for {}", up.username));
|
||||
user_manager::set_unique(true);
|
||||
}
|
||||
} */
|
||||
|
||||
Some(up)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn randomize_reset_token(&mut self) -> String {
|
||||
let mut bytes = [0u8; 192];
|
||||
OsRng.fill(bytes.as_mut());
|
||||
let new_token = general_purpose::STANDARD.encode(&bytes);
|
||||
self.reset_token = new_token.clone();
|
||||
new_token
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_display_name(&self) -> String {
|
||||
self.display_name
|
||||
.clone()
|
||||
.unwrap_or_else(|| self.username.clone())
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue