Skip to main content

secret_vault_value/
value_serde.rs

1use crate::*;
2use serde::de::{self, Visitor};
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use zeroize::Zeroize;
5
6impl SecretValue {
7    /// Parses the secret as JSON into `T`.
8    ///
9    /// The `Zeroize` bound lets the caller wipe the result, but the result is
10    /// not zeroized automatically: call `zeroize()` on it, or wrap it in
11    /// [`zeroize::Zeroizing`], once it is no longer needed.
12    ///
13    /// # Errors
14    ///
15    /// Returns [`serde_json::Error`] if the secret is not valid JSON for `T`.
16    ///
17    /// ```
18    /// use secret_vault_value::SecretValue;
19    /// use serde::Deserialize;
20    /// use zeroize::{Zeroize, Zeroizing};
21    ///
22    /// #[derive(Deserialize, Zeroize)]
23    /// struct Credentials {
24    ///     user: String,
25    ///     password: String,
26    /// }
27    ///
28    /// let secret_value = SecretValue::from(r#"{"user":"admin","password":"my-password"}"#);
29    /// let credentials = Zeroizing::new(secret_value.expose_json_value_as::<Credentials>()?);
30    /// assert_eq!(credentials.user, "admin");
31    /// # Ok::<(), serde_json::Error>(())
32    /// ```
33    pub fn expose_json_value_as<T>(&self) -> serde_json::Result<T>
34    where
35        for<'de> T: Deserialize<'de> + Zeroize,
36    {
37        serde_json::from_slice(self.ref_sensitive_value())
38    }
39}
40
41/// Serializes the secret as a string.
42///
43/// Fails if the bytes are not valid UTF-8.
44impl Serialize for SecretValue {
45    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
46    where
47        S: Serializer,
48    {
49        serializer.serialize_str(
50            String::from_utf8(self.ref_sensitive_value().clone())
51                .map_err(serde::ser::Error::custom)?
52                .as_str(),
53        )
54    }
55}
56
57struct SecretValueVisitor;
58
59#[allow(clippy::needless_lifetimes)]
60impl<'de> Visitor<'de> for SecretValueVisitor {
61    type Value = SecretValue;
62
63    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
64        formatter.write_str("a string expected as a secret value")
65    }
66
67    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
68    where
69        E: de::Error,
70    {
71        Ok(SecretValue::new(value.as_bytes().to_vec()))
72    }
73
74    fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
75    where
76        E: de::Error,
77    {
78        Ok(SecretValue::new(value.into_bytes()))
79    }
80
81    fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
82    where
83        E: de::Error,
84    {
85        Ok(SecretValue::new(value.to_vec()))
86    }
87
88    fn visit_byte_buf<E>(self, value: Vec<u8>) -> Result<Self::Value, E>
89    where
90        E: de::Error,
91    {
92        Ok(SecretValue::new(value))
93    }
94}
95
96/// Deserializes the secret from a string or from bytes.
97impl<'de> Deserialize<'de> for SecretValue {
98    fn deserialize<D>(deserializer: D) -> Result<SecretValue, D::Error>
99    where
100        D: Deserializer<'de>,
101    {
102        deserializer.deserialize_string(SecretValueVisitor)
103    }
104}
105
106#[cfg(test)]
107mod test {
108    use super::*;
109    use proptest::prelude::*;
110
111    proptest! {
112        #[test]
113        fn serialize_as_string(mock_secret in "[a-zA-Z0-9]*") {
114            let mock_secret_vec = mock_secret.as_bytes().to_vec();
115            let secret_value = SecretValue::new(mock_secret_vec);
116            let test_serialized_string = serde_json::to_string(&secret_value).unwrap();
117            assert_eq!(test_serialized_string, format!("\"{}\"",mock_secret));
118        }
119
120        #[test]
121        fn deserialize_from_string(mock_secret in "[a-zA-Z0-9]*") {
122            let mock_secret_quoted = format!("\"{}\"",mock_secret);
123            let secret_value: SecretValue = serde_json::from_str(&mock_secret_quoted).unwrap();
124            assert_eq!(String::from_utf8(secret_value.ref_sensitive_value().clone()).unwrap(), mock_secret);
125        }
126    }
127
128    #[test]
129    fn deserialize_embedded_json() {
130        #[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Zeroize)]
131        struct TestJson {
132            pub test_field: String,
133        }
134        let mock_json_struct = TestJson {
135            test_field: "TestValue".into(),
136        };
137
138        let secret_value: SecretValue = serde_json::to_string(&mock_json_struct).unwrap().into();
139
140        assert_eq!(
141            secret_value.expose_json_value_as::<TestJson>().unwrap(),
142            mock_json_struct
143        );
144    }
145}