cuenv_secrets/resolvers/
env.rs

1//! Environment variable secret resolver
2
3use crate::{SecretError, SecretResolver, SecretSpec};
4use async_trait::async_trait;
5
6/// Resolves secrets from environment variables
7///
8/// The `source` field in [`SecretSpec`] is interpreted as the environment variable name.
9#[derive(Debug, Clone, Default)]
10pub struct EnvSecretResolver;
11
12impl EnvSecretResolver {
13    /// Create a new environment variable resolver
14    #[must_use]
15    pub fn new() -> Self {
16        Self
17    }
18}
19
20#[async_trait]
21impl SecretResolver for EnvSecretResolver {
22    fn provider_name(&self) -> &'static str {
23        "env"
24    }
25
26    async fn resolve(&self, name: &str, spec: &SecretSpec) -> Result<String, SecretError> {
27        std::env::var(&spec.source).map_err(|_| SecretError::NotFound {
28            name: name.to_string(),
29            secret_source: spec.source.clone(),
30        })
31    }
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37    use std::collections::HashMap;
38
39    #[tokio::test]
40    async fn test_resolve_from_env() {
41        temp_env::async_with_vars([("TEST_SECRET_ENV_1", Some("value1"))], async {
42            let resolver = EnvSecretResolver::new();
43            let spec = SecretSpec::new("TEST_SECRET_ENV_1");
44            let result = resolver.resolve("secret1", &spec).await;
45
46            assert_eq!(result.unwrap(), "value1");
47        })
48        .await;
49    }
50
51    #[tokio::test]
52    async fn test_missing_env_var() {
53        let resolver = EnvSecretResolver::new();
54        let spec = SecretSpec::new("NONEXISTENT_ENV_VAR_12345");
55        let result = resolver.resolve("missing", &spec).await;
56
57        assert!(matches!(result, Err(SecretError::NotFound { .. })));
58    }
59
60    #[tokio::test]
61    async fn test_resolve_all() {
62        temp_env::async_with_vars(
63            [
64                ("TEST_SECRET_ENV_2", Some("value2")),
65                ("TEST_SECRET_ENV_3", Some("value3")),
66            ],
67            async {
68                let resolver = EnvSecretResolver::new();
69                let secrets = HashMap::from([
70                    ("secret2".to_string(), SecretSpec::new("TEST_SECRET_ENV_2")),
71                    ("secret3".to_string(), SecretSpec::new("TEST_SECRET_ENV_3")),
72                ]);
73
74                let result = resolver.resolve_all(&secrets).await.unwrap();
75
76                assert_eq!(result.get("secret2"), Some(&"value2".to_string()));
77                assert_eq!(result.get("secret3"), Some(&"value3".to_string()));
78            },
79        )
80        .await;
81    }
82}