cuenv_core/secrets/
mod.rs

1//! Secret and resolver types
2//!
3//! Based on schema/secrets.cue
4
5use crate::{Error, Result};
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::collections::HashMap;
9use tokio::process::Command;
10
11/// Resolver for executing commands to retrieve secret values
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
13pub struct ExecResolver {
14    /// Command to execute
15    pub command: String,
16
17    /// Arguments to pass to the command
18    pub args: Vec<String>,
19}
20
21/// Secret definition with resolver
22/// This is the base type that can be extended in CUE
23#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
24pub struct Secret {
25    /// Resolver type (currently only "exec" is supported)
26    pub resolver: String,
27
28    /// Command to execute
29    pub command: String,
30
31    /// Arguments to pass to the command
32    #[serde(default)]
33    pub args: Vec<String>,
34
35    /// Additional fields for extensibility
36    #[serde(flatten)]
37    pub extra: HashMap<String, Value>,
38}
39
40impl Secret {
41    /// Create a new secret with a resolver
42    pub fn new(command: String, args: Vec<String>) -> Self {
43        Secret {
44            resolver: "exec".to_string(),
45            command,
46            args,
47            extra: HashMap::new(),
48        }
49    }
50
51    /// Create a secret with additional fields
52    pub fn with_extra(command: String, args: Vec<String>, extra: HashMap<String, Value>) -> Self {
53        Secret {
54            resolver: "exec".to_string(),
55            command,
56            args,
57            extra,
58        }
59    }
60
61    /// Resolve the secret value
62    pub async fn resolve(&self) -> Result<String> {
63        match self.resolver.as_str() {
64            "exec" => {
65                let output = Command::new(&self.command)
66                    .args(&self.args)
67                    .output()
68                    .await
69                    .map_err(|e| {
70                        Error::configuration(format!(
71                            "Failed to execute secret resolver command '{}': {}",
72                            self.command, e
73                        ))
74                    })?;
75
76                if !output.status.success() {
77                    let stderr = String::from_utf8_lossy(&output.stderr);
78                    return Err(Error::configuration(format!(
79                        "Secret resolver command '{}' failed: {}",
80                        self.command, stderr
81                    )));
82                }
83
84                let stdout = String::from_utf8_lossy(&output.stdout);
85                Ok(stdout.trim().to_string())
86            }
87            other => Err(Error::configuration(format!(
88                "Unsupported secret resolver: {}",
89                other
90            ))),
91        }
92    }
93}