summaryrefslogtreecommitdiff
path: root/crates/vcs_actions/src/actions.rs
blob: 795d2b086e27df7d33d4952208ea1a80bbe4fb11 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use std::sync::Arc;

use action_system::action::ActionContext;
use tcp_connection::{error::TcpTargetError, instance::ConnectionInstance};
use tokio::sync::Mutex;
use vcs_data::{
    constants::SERVER_PATH_MEMBER_PUB,
    data::{local::LocalWorkspace, member::MemberId, user::UserDirectory, vault::Vault},
};

pub mod local_actions;
pub mod sheet_actions;
pub mod user_actions;
pub mod vault_actions;
pub mod virtual_file_actions;

/// Check if the connection instance is valid in the given context.
/// This function is used to verify the connection instance in actions that require remote calls.
pub fn check_connection_instance(
    ctx: &ActionContext,
) -> Result<&Arc<Mutex<ConnectionInstance>>, TcpTargetError> {
    let Some(instance) = ctx.instance() else {
        return Err(TcpTargetError::NotFound(
            "Connection instance lost.".to_string(),
        ));
    };
    Ok(instance)
}

/// Try to get the Vault instance from the context.
pub fn try_get_vault(ctx: &ActionContext) -> Result<Arc<Vault>, TcpTargetError> {
    let Some(vault) = ctx.get_arc::<Vault>() else {
        return Err(TcpTargetError::NotFound(
            "Vault instance not found".to_string(),
        ));
    };
    Ok(vault)
}

/// Try to get the LocalWorkspace instance from the context.
pub fn try_get_local_workspace(ctx: &ActionContext) -> Result<Arc<LocalWorkspace>, TcpTargetError> {
    let Some(local_workspace) = ctx.get_arc::<LocalWorkspace>() else {
        return Err(TcpTargetError::NotFound(
            "LocalWorkspace instance not found".to_string(),
        ));
    };
    Ok(local_workspace)
}

/// Try to get the UserDirectory instance from the context.
pub fn try_get_user_directory(ctx: &ActionContext) -> Result<Arc<UserDirectory>, TcpTargetError> {
    let Some(user_directory) = ctx.get_arc::<UserDirectory>() else {
        return Err(TcpTargetError::NotFound(
            "UserDirectory instance not found".to_string(),
        ));
    };
    Ok(user_directory)
}

/// Authenticate member based on context and return MemberId
pub async fn auth_member(
    ctx: &ActionContext,
    instance: &Arc<Mutex<ConnectionInstance>>,
) -> Result<MemberId, TcpTargetError> {
    // Start Challenge (Remote)
    if ctx.is_proc_on_remote() {
        let vault = try_get_vault(ctx)?;
        let result = instance
            .lock()
            .await
            .challenge(vault.vault_path().join(SERVER_PATH_MEMBER_PUB))
            .await;

        return match result {
            Ok((pass, member_id)) => {
                if !pass {
                    // Send false to inform the client that authentication failed
                    instance.lock().await.write(false).await?;
                    Err(TcpTargetError::Authentication(
                        "Authenticate failed.".to_string(),
                    ))
                } else {
                    // Send true to inform the client that authentication was successful
                    instance.lock().await.write(true).await?;
                    Ok(member_id)
                }
            }
            Err(e) => Err(e),
        };
    }

    // Accept Challenge (Local)
    if ctx.is_proc_on_local() {
        let local_workspace = try_get_local_workspace(ctx)?;
        let user_directory = try_get_user_directory(ctx)?;

        // Member name & Private key
        let member_name = local_workspace.config().lock().await.current_account();
        let private_key = user_directory.account_private_key_path(&member_name);
        let _ = instance
            .lock()
            .await
            .accept_challenge(private_key, &member_name)
            .await?;

        // Read result
        let challenge_result = instance.lock().await.read::<bool>().await?;
        if challenge_result {
            return Ok(member_name.clone());
        } else {
            return Err(TcpTargetError::Authentication(
                "Authenticate failed.".to_string(),
            ));
        }
    }

    Err(TcpTargetError::NoResult("Auth failed.".to_string()))
}