Merge remote-tracking branch 'refs/remotes/origin/main'

This commit is contained in:
Alex Emmet 2026-08-28 13:25:15 +02:00
commit 4caa6bb3e9
No known key found for this signature in database
33 changed files with 2028 additions and 445 deletions

View file

@ -2,7 +2,6 @@ use crate::auth::auth_user::AuthUser;
use crate::communities::community::Community;
use crate::communities::interactables::interactable::Interactable;
use crate::users::user_manager::get_user;
use iota_util::mtp_compat::CommunicationValueCompat;
use aes_gcm::{Aes256Gcm, KeyInit, Nonce, aead::Aead};
use base64::{Engine as _, engine::general_purpose::STANDARD};
use futures::SinkExt;
@ -22,6 +21,20 @@ use tungstenite::Message;
use tungstenite::Utf8Bytes;
use uuid::Uuid;
use x448::PublicKey;
trait CommunicationResponseExt {
fn with_request_id(self, request: &CommunicationValue) -> Self;
}
impl CommunicationResponseExt for CommunicationValue {
fn with_request_id(mut self, request: &CommunicationValue) -> Self {
self = self.without_id();
if let Some(id) = request.id() {
self = self.with_id(id);
}
self
}
}
pub struct CommunityConnection {
pub sender: Arc<RwLock<SplitSink<WebSocketStream<TokioIo<Upgraded>>, Message>>>,
pub receiver: Arc<RwLock<SplitStream<WebSocketStream<TokioIo<Upgraded>>>>>,
@ -118,7 +131,7 @@ impl CommunityConnection {
.unwrap_or(0);
let Some(user) = get_user(user_id) else {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInvalidUserId)
self.send_error_response(&cv, CommunicationType::ErrorInvalidUserId)
.await;
return;
};
@ -148,7 +161,7 @@ impl CommunityConnection {
let user_public_key_bytes = match STANDARD.decode(&user.public_key) {
Ok(bytes) => bytes,
Err(_) => {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInvalidUserId)
self.send_error_response(&cv, CommunicationType::ErrorInvalidUserId)
.await;
return;
}
@ -157,14 +170,14 @@ impl CommunityConnection {
let user_pub_key: PublicKey = match PublicKey::from_bytes(&user_public_key_bytes) {
Some(key) => key,
__ => {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInvalidUserId)
self.send_error_response(&cv, CommunicationType::ErrorInvalidUserId)
.await;
return;
}
};
let Some(community) = self.community.read().await.clone() else {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInternal)
self.send_error_response(&cv, CommunicationType::ErrorInternal)
.await;
return;
};
@ -175,7 +188,7 @@ impl CommunityConnection {
let shared_secret = match community_private_key.to_diffie_hellman(&user_pub_key) {
Some(secret) => secret,
_ => {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInternal)
self.send_error_response(&cv, CommunicationType::ErrorInternal)
.await;
return;
}
@ -197,7 +210,7 @@ impl CommunityConnection {
let encrypted_challenge = match cipher.encrypt(nonce, challenge_str.as_bytes()) {
Ok(data) => data,
Err(_) => {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInternal)
self.send_error_response(&cv, CommunicationType::ErrorInternal)
.await;
return;
}
@ -212,7 +225,7 @@ impl CommunityConnection {
STANDARD.encode(community_public_key.as_bytes()),
)
.add_data_str(DataType::Challenge, STANDARD.encode(&encrypted_out))
.with_id(cv.get_id());
.with_request_id(&cv);
self.send_message(&response).await;
}
@ -220,7 +233,7 @@ impl CommunityConnection {
let client_challenge_response_b64 = match cv.get_data(DataType::Challenge) {
Some(data) => data.to_string(),
_ => {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInvalidData)
self.send_error_response(&cv, CommunicationType::ErrorInvalidData)
.await;
return;
}
@ -229,38 +242,38 @@ impl CommunityConnection {
let challenge_response_bytes = match STANDARD.decode(&client_challenge_response_b64) {
Ok(bytes) => bytes,
Err(_) => {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInvalidData)
self.send_error_response(&cv, CommunicationType::ErrorInvalidData)
.await;
return;
}
};
if challenge_response_bytes.len() < 12 {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInvalidData)
self.send_error_response(&cv, CommunicationType::ErrorInvalidData)
.await;
return;
}
let Some(user) = self.auth.read().await.clone() else {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInternal)
self.send_error_response(&cv, CommunicationType::ErrorInternal)
.await;
return;
};
let Some(user_pub_bytes) = STANDARD.decode(&user.public_key).ok() else {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInvalidData)
self.send_error_response(&cv, CommunicationType::ErrorInvalidData)
.await;
return;
};
let Some(user_pub_key) = PublicKey::from_bytes(&user_pub_bytes) else {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInvalidPublicKey)
self.send_error_response(&cv, CommunicationType::ErrorInvalidPublicKey)
.await;
return;
};
let Some(community) = self.community.read().await.clone() else {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInternal)
self.send_error_response(&cv, CommunicationType::ErrorInternal)
.await;
return;
};
@ -270,7 +283,7 @@ impl CommunityConnection {
let shared_secret = match community_private_key.to_diffie_hellman(&user_pub_key) {
Some(secret) => secret,
_ => {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInternal)
self.send_error_response(&cv, CommunicationType::ErrorInternal)
.await;
return;
}
@ -294,7 +307,7 @@ impl CommunityConnection {
let decrypted_bytes = match cipher.decrypt(nonce, ciphertext) {
Ok(pt) => pt,
Err(_) => {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInvalidChallenge)
self.send_error_response(&cv, CommunicationType::ErrorInvalidChallenge)
.await;
return;
}
@ -303,7 +316,7 @@ impl CommunityConnection {
let client_response = match String::from_utf8(decrypted_bytes) {
Ok(str) => str,
Err(_) => {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInvalidData)
self.send_error_response(&cv, CommunicationType::ErrorInvalidData)
.await;
return;
}
@ -312,7 +325,7 @@ impl CommunityConnection {
let expected_challenge = self.challenge.read().await.clone();
if client_response != expected_challenge {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInvalidChallenge)
self.send_error_response(&cv, CommunicationType::ErrorInvalidChallenge)
.await;
self.close().await;
return;
@ -324,7 +337,7 @@ impl CommunityConnection {
}
let Some(community) = self.community.read().await.clone() else {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInternal)
self.send_error_response(&cv, CommunicationType::ErrorInternal)
.await;
return;
};
@ -332,7 +345,7 @@ impl CommunityConnection {
let user_id = self.get_user_id().await;
if user_id == 0 {
self.send_error_response(&cv.get_id(), CommunicationType::ErrorInvalidUserId)
self.send_error_response(&cv, CommunicationType::ErrorInvalidUserId)
.await;
return;
}
@ -351,13 +364,17 @@ impl CommunityConnection {
}
c
})
.with_id(cv.get_id());
.with_request_id(&cv);
self.send_message(&response).await;
}
async fn send_error_response(&self, message_id: &Uuid, error_type: CommunicationType) {
let error = CommunicationValue::new(error_type).with_id(*message_id);
async fn send_error_response(
&self,
request: &CommunicationValue,
error_type: CommunicationType,
) {
let error = CommunicationValue::new(error_type).with_request_id(request);
self.send_message(&error).await;
}
pub async fn close(&self) {

View file

@ -8,7 +8,7 @@ use crate::{
};
use async_trait::async_trait;
use json::{JsonValue, array, object};
use iota_util::mtp_compat::{CommunicationValueCompat, OptionalDataValueExt};
use iota_util::mtp_compat::{OptionalDataValueExt, RequiredCommunicationFields};
use std::fs;
use std::path::Path;
use std::sync::Arc;
@ -31,6 +31,10 @@ impl TextChat {
}
}
pub fn add_message(&self, send_time: u128, sender: i64, message: &str) {
let Ok(send_time) = i64::try_from(send_time) else {
log!("Message timestamp exceeds local storage range");
return;
};
let user_dir = &format!(
"communities/{}/interactables/{}/{}",
self.get_community().get_name(),
@ -74,7 +78,7 @@ impl TextChat {
}
let json_obj = object! {
"timestamp" => send_time as i64,
"timestamp" => send_time,
"content" => message,
"sender" => sender.to_string(),
};
@ -202,7 +206,7 @@ impl Interactable for TextChat {
let mut payload = JsonValue::new_object();
payload["messages"] = messages;
return CommunicationValue::new(CommunicationType::Function)
.with_id(cv.get_id())
.with_request_id(&cv)
.add_data_str(DataType::Name, self.name.clone())
.add_data_str(DataType::Path, self.path.clone())
.add_data_str(DataType::Result, "message_chunk".to_string())
@ -210,19 +214,28 @@ impl Interactable for TextChat {
}
if cv.get_data(DataType::Function).unwrap().as_str().unwrap() == "send_message" {
let message = payload["message"].as_str().unwrap();
let sender = match cv.require_sender() {
Ok(sender) => sender,
Err(_) => return CommunicationValue::new(CommunicationType::ErrorInvalidData)
.with_request_id(&cv),
};
let milliseconds_timestamp: u128 = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis();
self.add_message(milliseconds_timestamp, cv.get_sender(), message);
let Ok(sender) = i64::try_from(sender) else {
return CommunicationValue::new(CommunicationType::ErrorInvalidData)
.with_request_id(&cv);
};
self.add_message(milliseconds_timestamp, sender, message);
let mut distribution_payload = JsonValue::new_object();
distribution_payload["message"] = JsonValue::String(message.to_string());
distribution_payload["sender_id"] = JsonValue::String(cv.get_sender().to_string());
distribution_payload["sender_id"] = JsonValue::String(sender.to_string());
distribution_payload["send_time"] =
JsonValue::String(milliseconds_timestamp.to_string());
let distribution = CommunicationValue::new(CommunicationType::Update)
.with_id(cv.get_id())
.with_request_id(&cv)
.add_data_str(DataType::Name, self.name.clone())
.add_data_str(DataType::Path, self.path.clone())
.add_data_str(DataType::Result, "message_live".to_string())
@ -238,13 +251,13 @@ impl Interactable for TextChat {
}
}
return CommunicationValue::new(CommunicationType::Function)
.with_id(cv.get_id())
.with_request_id(&cv)
.add_data_str(DataType::Name, self.name.clone())
.add_data_str(DataType::Path, self.path.clone())
.add_data_str(DataType::Result, "message_received".to_string())
.add_data(DataType::Payload, JsonValue::new_object());
}
CommunicationValue::new(CommunicationType::ErrorInternal).with_id(cv.get_id())
CommunicationValue::new(CommunicationType::ErrorInternal).with_request_id(&cv)
}
fn to_json(&self) -> JsonValue {
JsonValue::new_object()

View file

@ -1,7 +1,7 @@
use crate::communities::{community::Community, interactables::interactable::Interactable};
use async_trait::async_trait;
use json::JsonValue;
use iota_util::mtp_compat::{CommunicationValueCompat, OptionalDataValueExt};
use iota_util::mtp_compat::OptionalDataValueExt;
use std::sync::Arc;
use std::{any::Any, sync::RwLock};
use uuid::Uuid;
@ -131,7 +131,7 @@ impl Interactable for VoiceChat {
response_payload["send_time"] = JsonValue::String(send_time.to_string());
return CommunicationValue::new(CommunicationType::Function)
.with_id(cv.get_id())
.with_request_id(&cv)
.add_data_str(DataType::Name, self.name.clone())
.add_data_str(DataType::Path, self.path.clone())
.add_data_str(DataType::Result, "getting_call".to_string())
@ -159,13 +159,13 @@ impl Interactable for VoiceChat {
response_payload["streaming"] = JsonValue::Boolean(streaming);
return CommunicationValue::new(CommunicationType::Update)
.with_id(cv.get_id())
.with_request_id(&cv)
.add_data_str(DataType::Name, self.name.clone())
.add_data_str(DataType::Path, self.path.clone())
.add_data_str(DataType::Result, "user_changed".to_string())
.add_data(DataType::Payload, response_payload);
}
CommunicationValue::new(CommunicationType::ErrorInternal).with_id(cv.get_id())
CommunicationValue::new(CommunicationType::ErrorInternal).with_request_id(&cv)
}
fn to_json(&self) -> JsonValue {