cuenv_core/secrets/
mod.rs

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