summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/utils/tcp_connection/src/instance_challenge.rs10
-rw-r--r--crates/utils/tcp_connection/tcp_connection_test/src/test_challenge.rs9
-rw-r--r--crates/vcs_actions/src/actions.rs12
-rw-r--r--crates/vcs_actions/src/actions/local_actions.rs83
-rw-r--r--crates/vcs_actions/src/registry/client_registry.rs9
-rw-r--r--crates/vcs_actions/src/registry/server_registry.rs5
-rw-r--r--crates/vcs_data/src/constants.rs9
-rw-r--r--crates/vcs_data/src/data/local.rs1
-rw-r--r--crates/vcs_data/src/data/local/latest_info.rs28
-rw-r--r--crates/vcs_data/src/data/sheet.rs18
-rw-r--r--crates/vcs_data/vcs_data_test/src/test_sheet_creation_management_and_persistence.rs6
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