authlogic/
secret.rs

1use zeroize::{Zeroize, ZeroizeOnDrop};
2
3/// A secret string (a password, session token, challenge code, or hash). Use
4/// `Secret::from` to convert a `String` to a `Secret`, and `secret.expose()`
5/// to access the string value where necessary.
6/// 
7/// Secrets are redacted in `std::fmt::Debug` displays, and are automatically
8/// zeroed-out in memory when the value is dropped.
9#[cfg_attr(feature = "diesel", derive(diesel_derive_newtype::DieselNewType))]
10#[cfg_attr(feature = "sqlx", derive(sqlx::Type), sqlx(transparent))]
11pub struct Secret(pub(crate) String);
12
13/// Either a password hash, or nothing, if password authentication is not
14/// available for the user. Use `PasswordHash::from` to convert a `String` or
15/// `Option<String>` to a `PasswordHash`, and `hash.expose()` to access the
16/// string value where necessary.
17/// 
18/// Secrets are redacted in `std::fmt::Debug` displays, and are automatically
19/// zeroed-out in memory when the value is dropped.
20#[cfg_attr(feature = "diesel", derive(diesel_derive_newtype::DieselNewType))]
21#[cfg_attr(feature = "sqlx", derive(sqlx::Type), sqlx(transparent))]
22pub struct PasswordHash(pub(crate) Option<Secret>);
23
24impl Secret {
25    /// Make use of this secret as a `&str`. This may be needed when sending a
26    /// secret to the client, or storing a hashed secret in the database.
27    pub fn expose(&self) -> &str {
28        &self.0
29    }
30}
31
32impl Drop for Secret {
33    fn drop(&mut self) {
34        self.0.zeroize();
35    }
36}
37
38impl ZeroizeOnDrop for Secret {}
39
40impl PasswordHash {
41    pub const NONE: Self = Self(None);
42    
43    pub fn exists(&self) -> bool {
44        self.0.is_some()
45    }
46    
47    /// Make use of this password hash as a `&str`. This may be needed when
48    /// storing in the database.
49    pub fn expose(&self) -> Option<&str> {
50        self.0.as_ref()
51            .map(Secret::expose)
52    }
53}
54
55impl From<String> for Secret {
56    fn from(string: String) -> Self {
57        Self(string)
58    }
59}
60
61impl From<String> for PasswordHash {
62    fn from(string: String) -> Self {
63        Self(Some(Secret(string)))
64    }
65}
66
67impl From<Option<String>> for PasswordHash {
68    fn from(string: Option<String>) -> Self {
69        Self(string.map(Secret))
70    }
71}
72
73impl std::fmt::Debug for Secret {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        f.write_str("[SECRET]")
76    }
77}
78
79impl std::fmt::Debug for PasswordHash {
80    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81        f.write_str(if self.exists() { "[SECRET]" } else { "[BLANK]" })
82    }
83}
84
85impl<'de> serde::Deserialize<'de> for Secret {
86    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
87        String::deserialize(deserializer)
88            .map(Self::from)
89    }
90}
91
92impl<'de> serde::Deserialize<'de> for PasswordHash {
93    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
94        Option::<Secret>::deserialize(deserializer)
95            .map(Self)
96    }
97}
98
99// impl serde::Serialize for Secret {
100//     fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
101//         String::serialize(&self.0, serializer)
102//     }
103// }
104
105// impl serde::Serialize for PasswordHash {
106//     fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
107//         Option::<Secret>::serialize(&self.0, serializer)
108//     }
109// }