From cc43704e7412ef82f6d41ba211b50e26307a3ddf Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Thu, 30 Oct 2025 09:21:24 +0800 Subject: Return key ID along with challenge verification result - Update challenge method to return (bool, String) tuple - Include key ID in both success and failure cases - Update tests to verify key ID matches expected value - Maintain same verification logic but provide additional context --- crates/utils/tcp_connection/src/instance_challenge.rs | 10 +++++----- .../tcp_connection/tcp_connection_test/src/test_challenge.rs | 9 ++++++--- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'crates') diff --git a/crates/utils/tcp_connection/src/instance_challenge.rs b/crates/utils/tcp_connection/src/instance_challenge.rs index c1cf46f..c4ea6a8 100644 --- a/crates/utils/tcp_connection/src/instance_challenge.rs +++ b/crates/utils/tcp_connection/src/instance_challenge.rs @@ -35,13 +35,13 @@ impl ConnectionInstance { /// * `public_key_dir` - Directory containing public key files for verification /// /// # Returns - /// * `Ok(true)` - Challenge verification successful - /// * `Ok(false)` - Challenge verification failed + /// * `Ok((true, "KeyId"))` - Challenge verification successful + /// * `Ok((false, "KeyId"))` - Challenge verification failed /// * `Err(TcpTargetError)` - Error during challenge process pub async fn challenge( &mut self, public_key_dir: impl AsRef, - ) -> Result { + ) -> Result<(bool, String), TcpTargetError> { // Generate random challenge let mut challenge = [0u8; 32]; rand::rngs::OsRng @@ -76,7 +76,7 @@ impl ConnectionInstance { // Load appropriate public key let public_key_path = public_key_dir.as_ref().join(format!("{}.pem", key_id)); if !public_key_path.exists() { - return Ok(false); + return Ok((false, key_id)); } let public_key_pem = tokio::fs::read_to_string(&public_key_path).await?; @@ -103,7 +103,7 @@ impl ConnectionInstance { false }; - Ok(verified) + Ok((verified, key_id)) } /// Accepts a challenge from the target machine to verify connection security diff --git a/crates/utils/tcp_connection/tcp_connection_test/src/test_challenge.rs b/crates/utils/tcp_connection/tcp_connection_test/src/test_challenge.rs index 2fc1a87..9327b3e 100644 --- a/crates/utils/tcp_connection/tcp_connection_test/src/test_challenge.rs +++ b/crates/utils/tcp_connection/tcp_connection_test/src/test_challenge.rs @@ -69,8 +69,9 @@ impl ServerHandle for ExampleChallengeServerHandle async fn process(mut instance: ConnectionInstance) { // Challenge with correct key let key_dir = current_dir().unwrap().join("res").join("key"); - let result = instance.challenge(key_dir).await.unwrap(); + let (result, key_id) = instance.challenge(key_dir).await.unwrap(); assert!(result); + assert_eq!(key_id, "test_key"); // Send response instance @@ -80,8 +81,9 @@ impl ServerHandle for ExampleChallengeServerHandle // Challenge again let key_dir = current_dir().unwrap().join("res").join("key"); - let result = instance.challenge(key_dir).await.unwrap(); + let (result, key_id) = instance.challenge(key_dir).await.unwrap(); assert!(!result); + assert_eq!(key_id, "test_key"); // Send response instance @@ -91,8 +93,9 @@ impl ServerHandle for ExampleChallengeServerHandle // Challenge again let key_dir = current_dir().unwrap().join("res").join("key"); - let result = instance.challenge(key_dir).await.unwrap(); + let (result, key_id) = instance.challenge(key_dir).await.unwrap(); assert!(!result); + assert_eq!(key_id, "test_key__"); // Send response instance -- cgit From 941e5d1317b5ed562df2a172de717a5a7408ee15 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Thu, 30 Oct 2025 09:23:02 +0800 Subject: Change auth_member to return MemberId on success The authentication function now returns the authenticated member's ID instead of just () when successful. This provides callers with access to the authenticated member's identity for subsequent operations. --- crates/vcs_actions/src/actions.rs | 12 ++++++------ crates/vcs_actions/src/registry/client_registry.rs | 4 +++- crates/vcs_data/src/constants.rs | 9 ++++++++- crates/vcs_data/src/data/local.rs | 1 + crates/vcs_data/src/data/sheet.rs | 18 ++++++++++++++++-- .../test_sheet_creation_management_and_persistence.rs | 6 ++---- 6 files changed, 36 insertions(+), 14 deletions(-) (limited to 'crates') diff --git a/crates/vcs_actions/src/actions.rs b/crates/vcs_actions/src/actions.rs index 858695a..795d2b0 100644 --- a/crates/vcs_actions/src/actions.rs +++ b/crates/vcs_actions/src/actions.rs @@ -5,7 +5,7 @@ use tcp_connection::{error::TcpTargetError, instance::ConnectionInstance}; use tokio::sync::Mutex; use vcs_data::{ constants::SERVER_PATH_MEMBER_PUB, - data::{local::LocalWorkspace, user::UserDirectory, vault::Vault}, + data::{local::LocalWorkspace, member::MemberId, user::UserDirectory, vault::Vault}, }; pub mod local_actions; @@ -57,11 +57,11 @@ pub fn try_get_user_directory(ctx: &ActionContext) -> Result, Ok(user_directory) } -/// Authenticate member based on whether the process is running locally or remotely +/// Authenticate member based on context and return MemberId pub async fn auth_member( ctx: &ActionContext, instance: &Arc>, -) -> Result<(), TcpTargetError> { +) -> Result { // Start Challenge (Remote) if ctx.is_proc_on_remote() { let vault = try_get_vault(ctx)?; @@ -72,7 +72,7 @@ pub async fn auth_member( .await; return match result { - Ok(pass) => { + Ok((pass, member_id)) => { if !pass { // Send false to inform the client that authentication failed instance.lock().await.write(false).await?; @@ -82,7 +82,7 @@ pub async fn auth_member( } else { // Send true to inform the client that authentication was successful instance.lock().await.write(true).await?; - Ok(()) + Ok(member_id) } } Err(e) => Err(e), @@ -106,7 +106,7 @@ pub async fn auth_member( // Read result let challenge_result = instance.lock().await.read::().await?; if challenge_result { - return Ok(()); + return Ok(member_name.clone()); } else { return Err(TcpTargetError::Authentication( "Authenticate failed.".to_string(), diff --git a/crates/vcs_actions/src/registry/client_registry.rs b/crates/vcs_actions/src/registry/client_registry.rs index 6f820e6..dcad657 100644 --- a/crates/vcs_actions/src/registry/client_registry.rs +++ b/crates/vcs_actions/src/registry/client_registry.rs @@ -52,7 +52,9 @@ async fn on_proc_begin( let local_workspace = match LocalWorkspace::init_current_dir(local_config) { Some(workspace) => workspace, None => { - return Err(TcpTargetError::NotFound("Failed to initialize local workspace.".to_string())); + return Err(TcpTargetError::NotFound( + "Failed to initialize local workspace.".to_string(), + )); } }; let local_workspace_arc = Arc::new(local_workspace); diff --git a/crates/vcs_data/src/constants.rs b/crates/vcs_data/src/constants.rs index 7514fe2..cd6eaa3 100644 --- a/crates/vcs_data/src/constants.rs +++ b/crates/vcs_data/src/constants.rs @@ -17,7 +17,7 @@ pub const SERVER_FILE_VAULT: &str = "./vault.toml"; // Server - Sheets pub const REF_SHEET_NAME: &str = "ref"; pub const SERVER_PATH_SHEETS: &str = "./sheets/"; -pub const SERVER_FILE_SHEET: &str = "./sheets/{sheet-name}.yaml"; +pub const SERVER_FILE_SHEET: &str = "./sheets/{sheet_name}.yaml"; // Server - Members pub const SERVER_PATH_MEMBERS: &str = "./members/"; @@ -35,6 +35,7 @@ pub const SERVER_FILE_VF_META: &str = "./storage/{vf_index}/{vf_id}/meta.yaml"; // Server - Service pub const SERVER_FILE_LOCKFILE: &str = "./.lock"; +// Server - Documents pub const SERVER_FILE_README: &str = "./README.md"; // ------------------------------------------------------------------------------------- @@ -45,6 +46,12 @@ pub const CLIENT_PATH_WORKSPACE_ROOT: &str = "./.jv/"; // Client - Workspace (Main) pub const CLIENT_FILE_WORKSPACE: &str = "./.jv/workspace.toml"; +// Client - Latest Information +pub const CLIENT_FILE_LATEST_INFO: &str = "./.jv/latest.json"; + +// Client - Sheets +pub const CLIENT_FILE_SHEET_COPY: &str = "./.jv/sheets/{sheet_name}.copy.json"; + // Client - Other pub const CLIENT_FILE_IGNOREFILES: &str = "IGNORE_RULES.toml"; pub const CLIENT_FILE_README: &str = "./README.md"; diff --git a/crates/vcs_data/src/data/local.rs b/crates/vcs_data/src/data/local.rs index fb43042..407b171 100644 --- a/crates/vcs_data/src/data/local.rs +++ b/crates/vcs_data/src/data/local.rs @@ -10,6 +10,7 @@ use crate::{ }; pub mod config; +pub mod latest_info; pub struct LocalWorkspace { config: Arc>, diff --git a/crates/vcs_data/src/data/sheet.rs b/crates/vcs_data/src/data/sheet.rs index f1cf67c..b558c0d 100644 --- a/crates/vcs_data/src/data/sheet.rs +++ b/crates/vcs_data/src/data/sheet.rs @@ -35,7 +35,7 @@ impl PartialEq for InputPackage { } } -const SHEET_NAME: &str = "{sheet-name}"; +const SHEET_NAME: &str = "{sheet_name}"; pub struct Sheet<'a> { /// The name of the current sheet @@ -48,7 +48,7 @@ pub struct Sheet<'a> { pub(crate) vault_reference: &'a Vault, } -#[derive(Default, Serialize, Deserialize, ConfigFile)] +#[derive(Default, Serialize, Deserialize, ConfigFile, Clone)] pub struct SheetData { /// The holder of the current sheet, who has full operation rights to the sheet mapping pub(crate) holder: MemberId, @@ -61,6 +61,10 @@ pub struct SheetData { } impl<'a> Sheet<'a> { + pub fn name(&self) -> &SheetName { + &self.name + } + /// Get the holder of this sheet pub fn holder(&self) -> &MemberId { &self.data.holder @@ -344,4 +348,14 @@ impl<'a> Sheet<'a> { common_components.into_iter().collect() } + + /// Clone the data of the sheet + pub fn clone_data(&self) -> SheetData { + self.data.clone() + } + + /// Convert the sheet into its data representation + pub fn to_data(self) -> SheetData { + self.data + } } diff --git a/crates/vcs_data/vcs_data_test/src/test_sheet_creation_management_and_persistence.rs b/crates/vcs_data/vcs_data_test/src/test_sheet_creation_management_and_persistence.rs index a8dfb89..7484e4b 100644 --- a/crates/vcs_data/vcs_data_test/src/test_sheet_creation_management_and_persistence.rs +++ b/crates/vcs_data/vcs_data_test/src/test_sheet_creation_management_and_persistence.rs @@ -42,8 +42,7 @@ async fn test_sheet_creation_management_and_persistence() -> Result<(), std::io: assert!(sheet.mapping().is_empty()); // Verify sheet file was created - const SHEET_NAME_PARAM: &str = "{sheet-name}"; - let sheet_path = dir.join(SERVER_FILE_SHEET.replace(SHEET_NAME_PARAM, &sheet_name)); + let sheet_path = dir.join(SERVER_FILE_SHEET.replace("{sheet_name}", &sheet_name)); assert!(sheet_path.exists()); // Test 2: Add input packages to the sheet @@ -296,8 +295,7 @@ async fn test_sheet_data_serialization() -> Result<(), std::io::Error> { sheet.persist().await?; // Verify the sheet file was created - const SHEET_NAME_PARAM: &str = "{sheet-name}"; - let sheet_path = dir.join(SERVER_FILE_SHEET.replace(SHEET_NAME_PARAM, &sheet_name)); + let sheet_path = dir.join(SERVER_FILE_SHEET.replace("{sheet_name}", &sheet_name)); assert!(sheet_path.exists()); // Clean up -- cgit From a2a58a568f5b6ef57d31cd72271dc9b70da156ad Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Thu, 30 Oct 2025 09:23:27 +0800 Subject: Add update_to_latest_info action This action synchronizes the latest vault information including sheets, reference sheet content, and member data between local and remote instances. --- crates/vcs_actions/src/actions/local_actions.rs | 83 ++++++++++++++++++++-- crates/vcs_actions/src/registry/client_registry.rs | 5 +- crates/vcs_actions/src/registry/server_registry.rs | 5 +- 3 files changed, 87 insertions(+), 6 deletions(-) (limited to 'crates') diff --git a/crates/vcs_actions/src/actions/local_actions.rs b/crates/vcs_actions/src/actions/local_actions.rs index 3027218..87eafb8 100644 --- a/crates/vcs_actions/src/actions/local_actions.rs +++ b/crates/vcs_actions/src/actions/local_actions.rs @@ -5,7 +5,10 @@ use cfg_file::config::ConfigFile; use log::{info, warn}; use serde::{Deserialize, Serialize}; use tcp_connection::error::TcpTargetError; -use vcs_data::data::{local::config::LocalConfig, vault::config::VaultUuid}; +use vcs_data::data::{ + local::{config::LocalConfig, latest_info::LatestInfo}, + vault::config::VaultUuid, +}; use crate::actions::{ auth_member, check_connection_instance, try_get_local_workspace, try_get_vault, @@ -26,15 +29,14 @@ pub async fn set_upstream_vault_action( ctx: ActionContext, upstream: SocketAddr, ) -> Result { - // Ensure the instance is available let instance = check_connection_instance(&ctx)?; - // Step1: Auth Member + // Auth Member if let Err(e) = auth_member(&ctx, instance).await { return Ok(SetUpstreamVaultActionResult::AuthorizeFailed(e.to_string())); } - // Step2: Direct + // Direct if ctx.is_proc_on_remote() { let vault = try_get_vault(&ctx)?; instance @@ -74,3 +76,76 @@ pub async fn set_upstream_vault_action( Err(TcpTargetError::NoResult("No result.".to_string())) } + +#[derive(Serialize, Deserialize)] +pub enum UpdateToLatestInfoResult { + Success, + + // Fail + AuthorizeFailed(String), +} + +#[action_gen] +pub async fn update_to_latest_info_action( + ctx: ActionContext, + _unused: (), +) -> Result { + let instance = check_connection_instance(&ctx)?; + + let member_id = match auth_member(&ctx, instance).await { + Ok(id) => id, + Err(e) => return Ok(UpdateToLatestInfoResult::AuthorizeFailed(e.to_string())), + }; + + if ctx.is_proc_on_remote() { + let vault = try_get_vault(&ctx)?; + + // Build latest info + let mut latest_info = LatestInfo::default(); + + // Sheet + let mut member_owned = Vec::new(); + let mut member_visible = Vec::new(); + + for sheet in vault.sheets().await? { + if sheet.holder() == &member_id { + member_owned.push(sheet.name().clone()); + } else { + member_visible.push(sheet.name().clone()); + } + } + + latest_info.my_sheets = member_owned; + latest_info.other_sheets = member_visible; + + // RefSheet + let ref_sheet_data = vault.sheet(&"ref".to_string()).await?.to_data(); + latest_info.ref_sheet_content = ref_sheet_data; + + // Members + let members = vault.members().await?; + latest_info.vault_members = members; + + // Send + instance + .lock() + .await + .write_large_msgpack(latest_info, 512 as u16) + .await?; + + return Ok(UpdateToLatestInfoResult::Success); + } + + if ctx.is_proc_on_local() { + let latest_info = instance + .lock() + .await + .read_large_msgpack::(512 as u16) + .await?; + LatestInfo::write(&latest_info).await?; + + return Ok(UpdateToLatestInfoResult::Success); + } + + Err(TcpTargetError::NoResult("No result.".to_string())) +} diff --git a/crates/vcs_actions/src/registry/client_registry.rs b/crates/vcs_actions/src/registry/client_registry.rs index dcad657..c7d6eb9 100644 --- a/crates/vcs_actions/src/registry/client_registry.rs +++ b/crates/vcs_actions/src/registry/client_registry.rs @@ -9,13 +9,16 @@ use vcs_data::data::{ }; use crate::{ - actions::local_actions::register_set_upstream_vault_action, + actions::local_actions::{ + register_set_upstream_vault_action, register_update_to_latest_info_action, + }, connection::protocol::RemoteActionInvoke, }; fn register_actions(pool: &mut ActionPool) { // Pool register here register_set_upstream_vault_action(pool); + register_update_to_latest_info_action(pool); } pub fn client_action_pool() -> ActionPool { diff --git a/crates/vcs_actions/src/registry/server_registry.rs b/crates/vcs_actions/src/registry/server_registry.rs index 3ecc103..3b6ab17 100644 --- a/crates/vcs_actions/src/registry/server_registry.rs +++ b/crates/vcs_actions/src/registry/server_registry.rs @@ -1,9 +1,12 @@ use action_system::action_pool::ActionPool; -use crate::actions::local_actions::register_set_upstream_vault_action; +use crate::actions::local_actions::{ + register_set_upstream_vault_action, register_update_to_latest_info_action, +}; pub fn server_action_pool() -> ActionPool { let mut pool = ActionPool::new(); register_set_upstream_vault_action(&mut pool); + register_update_to_latest_info_action(&mut pool); pool } -- cgit From d879d8864864d51f48201ea3fcf43baad2f969f6 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Thu, 30 Oct 2025 09:23:59 +0800 Subject: Add LatestInfo struct for client data persistence --- crates/vcs_data/src/data/local/latest_info.rs | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 crates/vcs_data/src/data/local/latest_info.rs (limited to 'crates') diff --git a/crates/vcs_data/src/data/local/latest_info.rs b/crates/vcs_data/src/data/local/latest_info.rs new file mode 100644 index 0000000..5a76277 --- /dev/null +++ b/crates/vcs_data/src/data/local/latest_info.rs @@ -0,0 +1,28 @@ +use cfg_file::ConfigFile; +use serde::{Deserialize, Serialize}; + +use crate::{ + constants::CLIENT_FILE_LATEST_INFO, + data::{ + member::Member, + sheet::{SheetData, SheetName}, + }, +}; + +#[derive(Default, Serialize, Deserialize, ConfigFile)] +#[cfg_file(path = CLIENT_FILE_LATEST_INFO)] +pub struct LatestInfo { + // Sheets + /// My sheets, indicating which sheets I can edit + pub my_sheets: Vec, + /// Other sheets, indicating which sheets I can export files to (these sheets are not readable to me) + pub other_sheets: Vec, + /// Reference sheet data, indicating what files I can get from the reference sheet + pub ref_sheet_content: SheetData, + + // Members + /// All member information of the vault, allowing me to contact them more conveniently + pub vault_members: Vec, +} + +impl LatestInfo {} -- cgit