diff options
| -rw-r--r-- | crates/utils/tcp_connection/src/instance_challenge.rs | 10 | ||||
| -rw-r--r-- | crates/utils/tcp_connection/tcp_connection_test/src/test_challenge.rs | 9 | ||||
| -rw-r--r-- | crates/vcs_actions/src/actions.rs | 12 | ||||
| -rw-r--r-- | crates/vcs_actions/src/actions/local_actions.rs | 83 | ||||
| -rw-r--r-- | crates/vcs_actions/src/registry/client_registry.rs | 9 | ||||
| -rw-r--r-- | crates/vcs_actions/src/registry/server_registry.rs | 5 | ||||
| -rw-r--r-- | crates/vcs_data/src/constants.rs | 9 | ||||
| -rw-r--r-- | crates/vcs_data/src/data/local.rs | 1 | ||||
| -rw-r--r-- | crates/vcs_data/src/data/local/latest_info.rs | 28 | ||||
| -rw-r--r-- | crates/vcs_data/src/data/sheet.rs | 18 | ||||
| -rw-r--r-- | crates/vcs_data/vcs_data_test/src/test_sheet_creation_management_and_persistence.rs | 6 |
11 files changed, 162 insertions, 28 deletions
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<Path>, - ) -> Result<bool, TcpTargetError> { + ) -> 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<ExampleChallengeClientHandle> 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<ExampleChallengeClientHandle> 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<ExampleChallengeClientHandle> 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 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<Arc<UserDirectory>, 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<Mutex<ConnectionInstance>>, -) -> Result<(), TcpTargetError> { +) -> Result<MemberId, TcpTargetError> { // 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::<bool>().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/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<SetUpstreamVaultActionResult, TcpTargetError> { - // 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<UpdateToLatestInfoResult, TcpTargetError> { + 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::<LatestInfo>(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 6f820e6..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 { @@ -52,7 +55,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_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 } 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<Mutex<LocalConfig>>, 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<SheetName>, + /// Other sheets, indicating which sheets I can export files to (these sheets are not readable to me) + pub other_sheets: Vec<SheetName>, + /// 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<Member>, +} + +impl LatestInfo {} 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 |
