bitwarden_json/
client.rs

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
#[cfg(feature = "internal")]
use bitwarden::vault::ClientVaultExt;
use bitwarden::ClientSettings;
#[cfg(feature = "secrets")]
use bitwarden::{
    generators::ClientGeneratorExt,
    secrets_manager::{ClientProjectsExt, ClientSecretsExt},
};

#[cfg(feature = "secrets")]
use crate::command::{GeneratorsCommand, ProjectsCommand, SecretsCommand};
use crate::{
    command::Command,
    response::{Response, ResponseIntoString},
};

pub struct Client(bitwarden::Client);

impl Client {
    pub fn new(settings_input: Option<String>) -> Self {
        let settings = Self::parse_settings(settings_input);
        Self(bitwarden::Client::new(settings))
    }

    pub async fn run_command(&self, input_str: &str) -> String {
        const SUBCOMMANDS_TO_CLEAN: &[&str] = &["Secrets"];
        let mut cmd_value: serde_json::Value = match serde_json::from_str(input_str) {
            Ok(cmd) => cmd,
            Err(e) => {
                return Response::error(format!("Invalid command string: {}", e)).into_string()
            }
        };

        if let Some(cmd_value_map) = cmd_value.as_object_mut() {
            cmd_value_map.retain(|_, v| !v.is_null());

            for &subcommand in SUBCOMMANDS_TO_CLEAN {
                if let Some(cmd_value_secrets) = cmd_value_map
                    .get_mut(subcommand)
                    .and_then(|v| v.as_object_mut())
                {
                    cmd_value_secrets.retain(|_, v| !v.is_null());
                }
            }
        }

        let cmd: Command = match serde_json::from_value(cmd_value) {
            Ok(cmd) => cmd,
            Err(e) => {
                return Response::error(format!("Invalid command value: {}", e)).into_string()
            }
        };

        let client = &self.0;

        match cmd {
            #[cfg(feature = "internal")]
            Command::PasswordLogin(req) => client.auth().login_password(&req).await.into_string(),
            #[cfg(feature = "secrets")]
            Command::LoginAccessToken(req) => {
                client.auth().login_access_token(&req).await.into_string()
            }
            #[cfg(feature = "internal")]
            Command::GetUserApiKey(req) => {
                client.platform().get_user_api_key(req).await.into_string()
            }
            #[cfg(feature = "internal")]
            Command::ApiKeyLogin(req) => client.auth().login_api_key(&req).await.into_string(),
            #[cfg(feature = "internal")]
            Command::Sync(req) => client.vault().sync(&req).await.into_string(),
            #[cfg(feature = "internal")]
            Command::Fingerprint(req) => client.platform().fingerprint(&req).into_string(),

            #[cfg(feature = "secrets")]
            Command::Secrets(cmd) => match cmd {
                SecretsCommand::Get(req) => client.secrets().get(&req).await.into_string(),
                SecretsCommand::GetByIds(req) => {
                    client.secrets().get_by_ids(req).await.into_string()
                }
                SecretsCommand::Create(req) => client.secrets().create(&req).await.into_string(),
                SecretsCommand::List(req) => client.secrets().list(&req).await.into_string(),
                SecretsCommand::Update(req) => client.secrets().update(&req).await.into_string(),
                SecretsCommand::Delete(req) => client.secrets().delete(req).await.into_string(),
                SecretsCommand::Sync(req) => client.secrets().sync(&req).await.into_string(),
            },

            #[cfg(feature = "secrets")]
            Command::Projects(cmd) => match cmd {
                ProjectsCommand::Get(req) => client.projects().get(&req).await.into_string(),
                ProjectsCommand::Create(req) => client.projects().create(&req).await.into_string(),
                ProjectsCommand::List(req) => client.projects().list(&req).await.into_string(),
                ProjectsCommand::Update(req) => client.projects().update(&req).await.into_string(),
                ProjectsCommand::Delete(req) => client.projects().delete(req).await.into_string(),
            },

            #[cfg(feature = "secrets")]
            Command::Generators(cmd) => match cmd {
                GeneratorsCommand::GeneratePassword(req) => {
                    client.generator().password(req).into_string()
                }
            },
        }
    }

    fn parse_settings(settings_input: Option<String>) -> Option<ClientSettings> {
        if let Some(input) = settings_input.as_ref() {
            match serde_json::from_str(input) {
                Ok(settings) => return Some(settings),
                Err(e) => {
                    log::error!("Failed to parse settings: {}", e);
                }
            }
        }
        None
    }
}