1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
use secrecy::{ExposeSecret, SecretString};
use super::{DeserializeContext, DeserializeParam, WellKnown, WellKnownOption};
use crate::{
error::ErrorWithOrigin,
metadata::{BasicTypes, ParamMetadata, TypeDescription},
value::{StrValue, Value},
};
/// Deserializer for secret strings (any type convertible from [`SecretString`], including `SecretString` itself).
/// Will set the corresponding flag for [`ParamMetadata`], making raw param value hidden in the debug output etc.
///
/// # Examples
///
/// ```
/// use secrecy::ExposeSecret;
/// # use smart_config::{testing, DescribeConfig, DeserializeConfig};
///
/// #[derive(DescribeConfig, DeserializeConfig)]
/// struct TestConfig {
/// secret: secrecy::SecretString,
/// }
///
/// let input = smart_config::config!("secret": "correct horse battery staple");
/// let config: TestConfig = testing::test(input)?;
/// assert_eq!(config.secret.expose_secret(), "correct horse battery staple");
/// # anyhow::Ok(())
/// ```
#[derive(Debug)]
pub struct FromSecretString;
impl<T: From<SecretString> + ExposeSecret<str>> DeserializeParam<T> for FromSecretString {
const EXPECTING: BasicTypes = BasicTypes::STRING;
fn describe(&self, description: &mut TypeDescription) {
description.set_secret();
}
fn deserialize_param(
&self,
ctx: DeserializeContext<'_>,
param: &'static ParamMetadata,
) -> Result<T, ErrorWithOrigin> {
let de = ctx.current_value_deserializer(param.name)?;
let s: SecretString = match de.value() {
Value::String(StrValue::Secret(s)) => s.clone(),
Value::String(StrValue::Plain(s)) => s.clone().into(),
_ => return Err(de.invalid_type("secret string")),
};
Ok(s.into())
}
fn serialize_param(&self, param: &T) -> serde_json::Value {
param.expose_secret().into()
}
}
impl WellKnown for SecretString {
type Deserializer = FromSecretString;
const DE: Self::Deserializer = FromSecretString;
}
impl WellKnownOption for SecretString {}
/// Deserializer for arbitrary secret params. Will set the corresponding flag for [`ParamMetadata`],
/// making raw param value hidden in the debug output etc.
///
/// Can be used by placing `#[serde(secret)]` on the param.
///
/// **Important.** The deserializer does not hide the deserialized value of the param! You are responsible
/// for doing it by selecting an appropriate param type (e.g., one that zeroizes its contents on drop).
///
/// # Examples
///
/// ```
/// use secrecy::{ExposeSecret, ExposeSecretMut, SecretBox};
/// use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// use smart_config::{de::Serde, testing, DescribeConfig, DeserializeConfig};
///
/// // It is generally a good idea to wrap a secret into a `SecretBox`
/// // so that it is zeroized on drop and has an opaque `Debug` representation.
/// #[derive(Debug)]
/// struct NumSecret(SecretBox<u64>);
///
/// impl Serialize for NumSecret {
/// fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
/// // Serialize the underlying secret
/// self.0.expose_secret().serialize(serializer)
/// }
/// }
///
/// impl<'de> serde::Deserialize<'de> for NumSecret {
/// fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
/// // Deserialize a `u64` and wrap it into a secret.
/// let mut secret = SecretBox::default();
/// *secret.expose_secret_mut() = u64::deserialize(deserializer)?;
/// Ok(Self(secret))
/// }
/// }
///
/// #[derive(DescribeConfig, DeserializeConfig)]
/// struct TestConfig {
/// // Secret values must be deserializable from a string
/// // because all secrets are strings. Because of type coercion, a `u64` deserializer
/// // will work correctly if supplied with a string, which we express
/// // through `Serde![]` args.
/// #[config(secret, with = Serde![int, str])]
/// secret: NumSecret,
/// }
///
/// let input = smart_config::config!("secret": "123");
/// let config: TestConfig = testing::test(input)?;
/// assert_eq!(*config.secret.0.expose_secret(), 123);
/// # anyhow::Ok(())
/// ```
#[derive(Debug)]
pub struct Secret<De>(pub De);
impl<T, De> DeserializeParam<T> for Secret<De>
where
De: DeserializeParam<T>,
{
const EXPECTING: BasicTypes = {
assert!(
De::EXPECTING.contains(BasicTypes::STRING),
"must be able to deserialize from string"
);
BasicTypes::STRING
};
fn describe(&self, description: &mut TypeDescription) {
self.0.describe(description);
description.set_secret();
}
fn deserialize_param(
&self,
ctx: DeserializeContext<'_>,
param: &'static ParamMetadata,
) -> Result<T, ErrorWithOrigin> {
self.0.deserialize_param(ctx, param)
}
fn serialize_param(&self, param: &T) -> serde_json::Value {
self.0.serialize_param(param)
}
}