Skip to main content

avassa_client/strongbox/
mod.rs

1//!
2//! Strongbox clients
3//!
4use crate::client::Client;
5use std::collections::HashMap;
6
7use crate::error::{Error, Result};
8
9/// Certificate API
10pub mod tls;
11
12const SBOX_VAULTS: &str = "v1/state/strongbox/vaults";
13
14/// A Strobox vault can contain one or more `Secret` key value stores.
15pub struct Vault {
16    client: Client,
17    vault_url: String,
18}
19
20impl Vault {
21    /// List all secret stores
22    pub async fn list(client: &Client) -> Result<Vec<String>> {
23        let resp: serde_json::Value = client.get_json(SBOX_VAULTS, Some(&[("keys", "")])).await?;
24
25        resp.as_array()
26            .ok_or_else(|| crate::Error::API("Expected an array".into()))?
27            .iter()
28            .inspect(|s| tracing::debug!("{:#?}", s))
29            .map(|s| {
30                s.as_str()
31                    .map(str::to_string)
32                    .ok_or_else(|| crate::Error::API("Expected a name".into()))
33            })
34            .collect()
35    }
36
37    pub(crate) async fn open(client: &Client, vault: &str) -> Result<Self> {
38        let vault_url = format!("{SBOX_VAULTS}/{vault}");
39        tracing::debug!("Opening vault at path: {vault_url}");
40        // Try to get the sbox vault
41        let _vault: serde_json::Value = client.get_json(&vault_url, None).await?;
42        Ok(Self {
43            client: client.clone(),
44            vault_url,
45        })
46    }
47
48    /// Open a secret
49    pub async fn open_secrets(&self, name: &str) -> Result<Secrets> {
50        let map_url = format!("{}/secrets/{name}", self.vault_url);
51
52        let json: serde_json::Value = self.client.get_json(&map_url, None).await?;
53
54        let kv = json
55            .as_object()
56            .ok_or_else(|| Error::general("expected a JSON object in secrets"))?;
57
58        let mut cache = HashMap::new();
59        if let Some(data) = kv.get("dict").and_then(serde_json::Value::as_object) {
60            for (k, v) in data {
61                cache.insert(
62                    k.clone(),
63                    v.as_str()
64                        .ok_or_else(|| Error::general("Expected secret value to be a string"))?
65                        .to_string(),
66                );
67            }
68        }
69
70        tracing::debug!("Successfully loaded {}", name);
71
72        Ok(Secrets { cache })
73    }
74}
75
76/// Strongbox key value map
77#[derive(Clone)]
78pub struct Secrets {
79    cache: HashMap<String, String>,
80}
81
82impl Secrets {
83    /// Try to get a value
84    #[must_use]
85    pub fn get(&self, key: &str) -> Option<&String> {
86        self.cache.get(key)
87    }
88}
89
90impl std::fmt::Debug for Secrets {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        f.debug_list().entries(self.cache.keys()).finish()
93    }
94}