secret_vault/
multiple_sources.rs

1use crate::*;
2use async_trait::*;
3use rvstruct::ValueStruct;
4use std::collections::HashMap;
5
6pub struct MultipleSecretsSources {
7    sources: HashMap<SecretNamespace, Box<dyn SecretsSource + Send + Sync>>,
8}
9
10impl MultipleSecretsSources {
11    pub fn new() -> Self {
12        Self {
13            sources: HashMap::new(),
14        }
15    }
16
17    pub fn add_source<S>(mut self, namespace: &SecretNamespace, source: S) -> Self
18    where
19        S: SecretsSource + Send + Sync + 'static,
20    {
21        self.sources.insert(namespace.clone(), Box::new(source));
22        self
23    }
24
25    #[deprecated(
26        since = "1.1.7",
27        note = "Use add_source to make it obvious it adds a new source, not replace it as other with_ functions do. This will be removed in next releases"
28    )]
29    pub fn with_source<S>(mut self, namespace: &SecretNamespace, source: S) -> Self
30    where
31        S: SecretsSource + Send + Sync + 'static,
32    {
33        self.sources.insert(namespace.clone(), Box::new(source));
34        self
35    }
36}
37
38#[async_trait]
39impl SecretsSource for MultipleSecretsSources {
40    fn name(&self) -> String {
41        self.sources
42            .iter()
43            .map(|(namespace, source)| format!("{}:{}", namespace.value(), source.name()))
44            .collect::<Vec<String>>()
45            .join(", ")
46    }
47
48    async fn get_secrets(
49        &self,
50        references: &[SecretVaultRef],
51    ) -> SecretVaultResult<HashMap<SecretVaultRef, Secret>> {
52        let mut result_map: HashMap<SecretVaultRef, Secret> = HashMap::new();
53        for (namespace, source) in self.sources.iter() {
54            let source_references: Vec<SecretVaultRef> = references
55                .iter()
56                .filter(|reference| {
57                    reference
58                        .key
59                        .namespace
60                        .iter()
61                        .any(|ref_namespace| *ref_namespace == *namespace)
62                })
63                .cloned()
64                .collect();
65
66            let mut source_secrets = source.get_secrets(&source_references).await?;
67            for (secret_ref, secret) in source_secrets.drain() {
68                result_map.insert(secret_ref, secret);
69            }
70        }
71
72        Ok(result_map)
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use crate::source_tests::*;
79    use crate::*;
80    use proptest::prelude::*;
81    use proptest::strategy::ValueTree;
82    use proptest::test_runner::TestRunner;
83    use rvstruct::ValueStruct;
84
85    #[tokio::test]
86    async fn multiple_sources_test() {
87        let mut runner = TestRunner::default();
88
89        let mock_secrets_store1 = generate_mock_secrets_source("mock1".into())
90            .new_tree(&mut runner)
91            .unwrap()
92            .current();
93
94        let mock_secrets_store2 = generate_mock_secrets_source("mock2".into())
95            .new_tree(&mut runner)
96            .unwrap()
97            .current();
98
99        let mut vault = SecretVaultBuilder::with_source(
100            MultipleSecretsSources::new()
101                .add_source(&"mock1".into(), mock_secrets_store1.clone())
102                .add_source(&"mock2".into(), mock_secrets_store2.clone()),
103        )
104        .build()
105        .unwrap();
106
107        fn value_refs_with_namespaces(
108            namespace: &str,
109            source: &MockSecretsSource,
110        ) -> Vec<SecretVaultRef> {
111            source
112                .keys()
113                .into_iter()
114                .map(|reference| reference.clone().with_namespace(namespace.into()))
115                .collect()
116        }
117
118        let mock_secrets1: Vec<SecretVaultRef> =
119            value_refs_with_namespaces("mock1", &mock_secrets_store1);
120        let mock_secrets2: Vec<SecretVaultRef> =
121            value_refs_with_namespaces("mock2", &mock_secrets_store2);
122        let all_mock_secrets = [mock_secrets1, mock_secrets2].concat();
123
124        vault
125            .register_secret_refs(all_mock_secrets.iter().collect())
126            .refresh()
127            .await
128            .unwrap();
129
130        for secret_ref in all_mock_secrets {
131            let test_value =
132                if secret_ref.key.namespace.as_ref().unwrap().value().as_str() == "mock1" {
133                    mock_secrets_store1.get(&secret_ref)
134                } else {
135                    mock_secrets_store2.get(&secret_ref)
136                };
137
138            assert_eq!(
139                vault
140                    .get_secret_by_ref(&secret_ref)
141                    .await
142                    .unwrap()
143                    .map(|secret| secret.value)
144                    .as_ref(),
145                test_value.as_ref()
146            )
147        }
148    }
149}