geekorm_core/utils/
tfa.rs1use crate::Value;
20use std::fmt::Display;
21use totp_rs::{Algorithm, Secret, TOTP};
22
23#[derive(Debug, Clone, serde::Serialize)]
25pub struct TwoFactorAuth {
26 totp: TOTP,
27}
28
29impl TwoFactorAuth {
30 pub fn new() -> Self {
34 #[cfg(feature = "two-factor-auth-qr")]
35 let issuer = match std::env::var("GEEKORM_TFA_ISSUER") {
36 Ok(issuer) => Some(issuer),
37 Err(_) => Some(env!("CARGO_PKG_NAME").to_string()),
38 };
39 #[cfg(feature = "two-factor-auth-qr")]
40 let account_name = match std::env::var("GEEKORM_TFA_ACCOUNT_NAME") {
41 Ok(account_name) => account_name,
42 Err(_) => env!("CARGO_PKG_NAME").to_string(),
43 };
44
45 Self {
46 totp: totp_rs::TOTP {
47 secret: Secret::generate_secret().to_bytes().unwrap(),
48 algorithm: Algorithm::SHA256,
49 digits: 6,
50 skew: 1,
51 step: 30,
52 #[cfg(feature = "two-factor-auth-qr")]
53 issuer,
54 #[cfg(feature = "two-factor-auth-qr")]
55 account_name,
56 },
57 }
58 }
59
60 #[cfg(feature = "two-factor-auth-qr")]
62 pub fn new_with_issuer(issuer: impl Into<String>, account_name: impl Into<String>) -> Self {
63 Self {
64 totp: totp_rs::TOTP {
65 algorithm: Algorithm::SHA256,
66 digits: 6,
67 skew: 1,
68 step: 30,
69 secret: Secret::generate_secret().to_bytes().unwrap(),
70 issuer: Some(issuer.into()),
71 account_name: account_name.into(),
72 },
73 }
74 }
75
76 pub fn generate_current(&self) -> Result<String, crate::Error> {
78 self.totp
79 .generate_current()
80 .map_err(|e| crate::Error::TotpError(e.to_string()))
81 }
82
83 pub fn check<'a>(&self, code: impl Into<&'a str>) -> Result<bool, crate::Error> {
85 Ok(self.totp.check_current(code.into())?)
86 }
87}
88
89impl Display for TwoFactorAuth {
90 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
91 write!(
92 f,
93 "TwoFactorAuth({}, {}, {})",
94 self.totp.algorithm, self.totp.skew, self.totp.step
95 )
96 }
97}
98
99impl From<TwoFactorAuth> for Value {
100 fn from(value: TwoFactorAuth) -> Self {
101 serde_json::to_vec(&value.totp)
102 .map(Value::Json)
103 .unwrap_or(Value::Null)
104 }
105}
106
107impl From<&TwoFactorAuth> for Value {
108 fn from(value: &TwoFactorAuth) -> Self {
109 serde_json::to_vec(&value.totp)
110 .map(Value::Json)
111 .unwrap_or(Value::Null)
112 }
113}
114
115impl From<Value> for TwoFactorAuth {
116 fn from(value: Value) -> Self {
117 match value {
118 Value::Blob(s) | Value::Json(s) => serde_json::from_slice(&s).unwrap(),
119 Value::Text(t) => serde_json::from_str(&t).unwrap(),
120 _ => {
121 panic!("Error parsing unknown type")
122 }
123 }
124 }
125}
126
127impl<'de> serde::de::Deserialize<'de> for TwoFactorAuth {
128 fn deserialize<D>(deserializer: D) -> Result<TwoFactorAuth, D::Error>
129 where
130 D: serde::de::Deserializer<'de>,
131 {
132 pub struct TFAVisitor;
134
135 impl<'de> serde::de::Visitor<'de> for TFAVisitor {
136 type Value = TwoFactorAuth;
137
138 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
139 formatter.write_str("a TwoFactorAuth struct")
140 }
141
142 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
143 where
144 E: serde::de::Error,
145 {
146 serde_json::from_str(value).map_err(serde::de::Error::custom)
147 }
148
149 fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
150 where
151 A: serde::de::MapAccess<'de>,
152 {
153 let totp: totp_rs::TOTP = serde::de::Deserialize::deserialize(
154 serde::de::value::MapAccessDeserializer::new(map),
155 )?;
156
157 Ok(TwoFactorAuth { totp })
158 }
159
160 fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
161 where
162 A: serde::de::SeqAccess<'de>,
163 {
164 let v: Vec<u8> = serde::de::Deserialize::deserialize(
165 serde::de::value::SeqAccessDeserializer::new(seq),
166 )?;
167 self.visit_bytes(&v)
168 }
169
170 fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
171 where
172 E: serde::de::Error,
173 {
174 serde_json::from_slice(value).map_err(serde::de::Error::custom)
175 }
176 }
177
178 deserializer.deserialize_struct("TwoFactorAuth", &["totp"], TFAVisitor)
179 }
180}