Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't read secret with key other than 'value' #42

Closed
elsesiy opened this issue Feb 8, 2021 · 2 comments · Fixed by #44
Closed

Can't read secret with key other than 'value' #42

elsesiy opened this issue Feb 8, 2021 · 2 comments · Fixed by #44

Comments

@elsesiy
Copy link

elsesiy commented Feb 8, 2021

Hi @ChrisMacNaughton,

thanks for creating this library. This is my first time playing around with rust so excuse if this is a user error.

I'm trying to read an existing secret from vault, e.g

❯ vault kv get secret/hello/test
====== Metadata ======
Key              Value
---              -----
created_time     2021-02-08T04:13:52.094224Z
deletion_time    n/a
destroyed        false
version          1

== Data ==
Key    Value
---    -----
a      b

Creating a secret using your crate always writes them as:

❯ vault kv get secret/hello/bob
====== Metadata ======
Key              Value
---              -----
created_time     2021-02-08T04:14:23.762412Z
deletion_time    n/a
destroyed        false
version          1

==== Data ====
Key      Value
---      -----
value    world

As it stands I'm not able to retrieve a secret with a custom key as serde fails to deserialize:
Err(SerdeJson(Error("missing field value", line: 1, column: 128)))

I believe this is caused by the SecretData declaration https://github.com/ChrisMacNaughton/vault-rs/blob/master/src/client/mod.rs#L292 called here.

I couldn't find an API that allows me to set the key explicitly when calling get_secret.
Am I missing something or is this indeed not supported right now?

Thanks!

@ChrisMacNaughton
Copy link
Owner

I think that you're right that this crate doesn't support writing to more complex keys that {"key": "value"}, but that could be worth working on.

In terms of some early thinking about what that API may look like would be something generic over secret data, along these lines:

/// Saves a secret
///
/// ```
/// # extern crate hashicorp_vault as vault;
/// # use vault::Client;
///
/// #[derive(Deserialize, Serialize)]
/// struct MyThing {
///   awesome: String,
///   thing: String,
/// }
/// let host = "http://127.0.0.1:8200";
/// let token = "test12345";
/// let client = Client::new(host, token).unwrap();
/// let secret = MyThing {
///   awesome: "I really am cool".into(),
///   thing: "this is also in the secret".into(),
/// };
/// let res = client.set_custom_secret("hello_set", &secret);
/// assert!(res.is_ok());
/// ```
pub fn set_custom_secret<S1, T>(&self, secret_name: S1, secret: &T) -> Result<()>
where S1: Into<String>,
      T: Serialize {
    let _ = self.post::<_, String>(
        &format!("/v1/secret/data/{}", secret_name.into())[..],
        Some(
            &secret.to_string()?,
        ),
        None,
    )?;
    Ok(())
}

///
/// Fetches a saved secret
///
/// ```
/// # extern crate hashicorp_vault as vault;
/// # use vault::Client;
///
/// #[derive(Deserialize, Serialize)]
/// struct MyThing {
///   awesome: String,
///   thing: String,
/// }
/// let host = "http://127.0.0.1:8200";
/// let token = "test12345";
/// let client = Client::new(host, token).unwrap();
/// let secret = MyThing {
///   awesome: "I really am cool".into(),
///   thing: "this is also in the secret".into(),
/// };
/// let res = client.set_custom_secret("hello_set", &secret);
/// assert!(res.is_ok());
/// let res: MyThing = client.get_custom_secret("hello_get");
/// assert!(res.is_ok());
/// assert_eq!(res.unwrap().awesome, "I really am cool");
/// assert_eq!(res.unwrap().thing, "this is also in the secret");
/// ```
pub fn get_custom_secret<S: AsRef<str>, T: Deserialize>(&self, secret_name: S) -> Result<T> {
    let res = self.get::<_, String>(&format!("/v1/secret/data/{}", key.as_ref())[..], None)?;
    let decoded: VaultResponse<SecretDataWrapper<T>> = parse_vault_response(res)?;
    match decoded.data {
        Some(data) => Ok(data.data),
        _ => Err(Error::Vault(format!(
            "No secret found in response: `{:#?}`",
            decoded
        ))),
    }
}

As noted in the doc strings above, this could allow passing in a custom struct that allows serializing and deserializing a custom type, rather than requiring a strict {"value": "$value"} syntax.

@elsesiy
Copy link
Author

elsesiy commented Feb 10, 2021

That looks like a good addition. Do you have an ETA for when it will be ready? Thank you!

ChrisMacNaughton added a commit that referenced this issue Feb 10, 2021
This change additionally refactors the default secret methods,
`get_secret` and `set_secret` to use the new custom get and set
methods with a new internal type.

Closes #42
ChrisMacNaughton added a commit that referenced this issue Feb 10, 2021
This change additionally refactors the default secret methods,
`get_secret` and `set_secret` to use the new custom get and set
methods with a new internal type.

Closes #42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants