Skip to main content

bark_common/
secret.rs

1/// A utility to keep secrets safe and prevent us from
2/// accidentally writing them into the logs.
3///
4/// Note, that anyone who has access to the [Secret] can
5/// access the inner value. This only prevents a developer
6/// from accidentally writing the secret to logs when doing
7/// a [std::fmt::Debug]-print.
8///
9/// Wraps a [Secret] and ensures and hides it in [std::fmt::Debug]-logs.
10///
11/// # Usage
12/// The developer can access the secret using [leak_ref] or [leak_owned].
13/// You should only do this when passing the secret to an external library.
14///
15/// ```
16/// # fn connect(user: &str, pass: &str) {
17/// #    // This is a sturb
18/// # }
19/// use bark_common::secret::Secret;
20///
21/// let user = String::from("my-user");
22/// let pass = Secret::new(String::from("my-password"));
23///
24/// connect(&user, pass.leak_ref());
25/// ```
26///
27/// # Debug formatted strings are safe
28///
29/// ```
30/// use bark_common::secret::Secret;
31/// use tracing::debug;
32///
33/// #[derive(Debug)]
34/// struct Config {
35///     user: String,
36///     pass: Secret<String>
37/// }
38///
39/// let config = Config {
40///    user: String::from("user") ,
41///    pass: Secret::new(String::from("my-secret-password")),
42/// };
43///
44/// // The secret will be redacted when writing the debug log
45/// debug!("Initiating connection with config {:?}", &config)
46/// ````
47
48use serde::{Serialize, Deserialize};
49
50
51pub struct Secret<T> {
52	inner: T,
53}
54
55impl<T> Secret<T> {
56	pub fn new(inner: T) -> Self {
57		Self { inner }
58	}
59
60	pub fn leak_mut(&mut self) -> &mut T {
61		&mut self.inner
62	}
63
64	pub fn leak_ref(&self) -> &T {
65		&self.inner
66	}
67
68	pub fn leak_owned(self) -> T {
69		self.inner
70	}
71}
72
73impl<T> std::fmt::Debug for Secret<T> {
74	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75		write!(f, "[redacted]")
76	}
77}
78
79impl<T: Clone> Clone for Secret<T> {
80	fn clone(&self) -> Self {
81		Self {
82			inner: self.inner.clone()
83		}
84	}
85}
86
87impl <T: Copy> Copy for Secret<T> {}
88
89impl<T: PartialEq> PartialEq for Secret<T> {
90	fn eq(&self, other: &Self) -> bool {
91		T::eq(&self.inner, &other.inner)
92	}
93
94	fn ne(&self, other: &Self) -> bool {
95		T::ne(&self.inner, &other.inner)
96	}
97}
98
99impl<T: Eq> Eq for Secret<T> {}
100
101impl<T: Serialize> Serialize for Secret<T> {
102	fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
103		self.inner.serialize(serializer)
104	}
105}
106
107impl<'de, T: Deserialize<'de>> Deserialize<'de> for Secret<T> {
108	fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
109		T::deserialize(deserializer).map(|x| Secret::new(x))
110	}
111}
112
113impl<T> From<T> for Secret<T> {
114	fn from(value: T) -> Self {
115		Secret::new(value)
116	}
117}
118
119#[cfg(test)]
120mod test {
121
122	use super::Secret;
123	use std::io::Write;
124
125	#[test]
126	fn debug_format_is_redacted() {
127		let secret = Secret::new(String::from("my-secret"));
128
129		let mut vec = Vec::new();
130		write!(vec, "{:?}", &secret).expect("Can write to Vec");
131		assert_eq!(String::from_utf8(vec).unwrap(), "[redacted]");
132	}
133
134	#[test]
135	fn cannot_pretty_print_by_accident() {
136		let secret = Secret::new(String::from("my-secret"));
137
138		let mut vec = Vec::new();
139		write!(vec, "{:#?}", &secret).expect("Can write to vec");
140		assert_eq!(String::from_utf8(vec).unwrap(), "[redacted]");
141	}
142
143	// #[test]
144	// #[ignore = "Does not compile"]
145	// fn cannot_display_by_accident() {
146	// 	let secret = Secret::new(String::from("my-secret"));
147
148	// 		let mut vec = Vec::new();
149	// 		write!(vec, "{}", &secret).expect("Can write to vec");
150	// 		assert_eq!(String::from_utf8(vec).unwrap(), "[redacted]");
151	// }
152}