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)
    }
}