secret_vault/
multiple_sources.rs1use 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}