wash_cli/
secrets.rs

1use std::collections::HashMap;
2
3use anyhow::bail;
4use clap::Subcommand;
5use tracing::trace;
6use wash_lib::cli::{input_vec_to_hashmap, CliConnectionOpts, CommandOutput, OutputKind};
7use wasmcloud_secrets_types::{SecretConfig, SECRET_PREFIX};
8
9use crate::cmd;
10
11#[derive(Debug, Clone, Subcommand)]
12pub enum SecretsCliCommand {
13    #[clap(name = "put", alias = "create", about = "Put secret reference")]
14    PutCommand {
15        #[clap(flatten)]
16        opts: CliConnectionOpts,
17        /// The name of the secret reference to create.
18        #[clap(name = "name")]
19        name: String,
20        /// The backend to fetch the secret from at runtime.
21        #[clap(name = "backend")]
22        backend: String,
23        /// The key to use for retrieving the secret from the backend.
24        #[clap(name = "key")]
25        key: String,
26        /// The field to use for retrieving the secret from the backend.
27        #[clap(long = "field")]
28        field: Option<String>,
29        /// The version of the secret to retrieve. If not supplied, the latest version will be used.
30        #[clap(short = 'v', long = "version")]
31        version: Option<String>,
32        /// Freeform policy properties to pass to the secrets backend, in the form of `key=value`. Can be specified multiple times.
33        #[clap(long = "property")]
34        policy_properties: Vec<String>,
35    },
36
37    /// Get a secret reference by name
38    #[clap(name = "get")]
39    GetCommand {
40        #[clap(flatten)]
41        opts: CliConnectionOpts,
42        #[clap(name = "name")]
43        name: String,
44    },
45
46    /// Delete a secret reference by name
47    #[clap(name = "del", alias = "delete")]
48    DelCommand {
49        #[clap(flatten)]
50        opts: CliConnectionOpts,
51        #[clap(name = "name")]
52        name: String,
53    },
54}
55
56pub async fn handle_command(
57    command: SecretsCliCommand,
58    output_kind: OutputKind,
59) -> anyhow::Result<CommandOutput> {
60    match command {
61        SecretsCliCommand::PutCommand {
62            opts,
63            name,
64            backend,
65            key,
66            field,
67            version,
68            policy_properties,
69        } => {
70            let policy_property_map = input_vec_to_hashmap(policy_properties)?;
71            let secret_name = name.clone();
72            let secret_config = SecretConfig::new(
73                secret_name,
74                backend,
75                key,
76                field,
77                version,
78                policy_property_map
79                    .into_iter()
80                    .map(|(k, v)| (k, v.into()))
81                    .collect(),
82            );
83            trace!(?secret_config, "Putting secret config");
84            let values: HashMap<String, String> = secret_config.try_into()?;
85
86            cmd::config::put::invoke(opts, &secret_configdata_key(&name), values, output_kind).await
87        }
88        SecretsCliCommand::GetCommand { opts, name } => {
89            cmd::config::get::invoke(opts, &secret_configdata_key(&name), output_kind).await
90        }
91        SecretsCliCommand::DelCommand { opts, name } => {
92            cmd::config::delete::invoke(opts, &secret_configdata_key(&name), output_kind).await
93        }
94    }
95}
96
97/// Ensure that a given config KV name is *not* a secret
98pub(crate) fn ensure_not_secret(name: &str) -> anyhow::Result<()> {
99    if name.starts_with(SECRET_PREFIX) {
100        bail!("Configuration names cannot start with '{SECRET_PREFIX}'. Did you mean to use the 'secrets' command?");
101    }
102    Ok(())
103}
104
105/// Check if a given configuration KV name name represents a secret
106pub(crate) fn is_secret(name: &str) -> bool {
107    name.starts_with(SECRET_PREFIX)
108}
109
110/// Ensures the secret name is prefixed by `SECRET_`
111pub(crate) fn secret_configdata_key(name: &str) -> String {
112    if is_secret(name) {
113        name.to_string()
114    } else {
115        format!("{SECRET_PREFIX}_{}", name)
116    }
117}