Skip to main content

alibi_core/store/stateless/
verifications.rs

1use super::*;
2#[async_trait]
3impl VerificationStore<StatelessSchema> for StatelessStore {
4    async fn create_verification_record(
5        &self,
6        data: crate::verification::VerificationCreation,
7        publication: crate::verification::VerificationPublication,
8    ) -> AuthResult<Option<crate::verification::VerificationSnapshot>> {
9        let snapshot = if publication.store_in_database {
10            let model = VerificationView {
11                id: data.id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
12                identifier: data.identifier,
13                value: data.value,
14                expires_at: data.expires_at,
15                created_at: data.created_at,
16                updated_at: data.updated_at,
17            };
18            let mut state = self.lock()?;
19            if state.verifications.contains_key(&model.id) {
20                return Err(AuthError::internal("duplicate verification primary ID"));
21            }
22            drop(state.verifications.insert(model.id.clone(), model.clone()));
23            drop(state);
24            crate::verification::VerificationSnapshot::from_model(&model)
25        } else {
26            data.snapshot()
27        };
28        publication.publish(&snapshot).await?;
29        Ok(Some(snapshot))
30    }
31
32    async fn consume_verification_snapshot(
33        &self,
34        identifier: &str,
35    ) -> AuthResult<Option<VerificationView>> {
36        let mut state = self.lock()?;
37        let found = state
38            .verifications
39            .values()
40            .filter(|verification| verification.identifier == identifier)
41            .max_by_key(|verification| verification.created_at)
42            .cloned();
43        state
44            .verifications
45            .retain(|_, sibling| sibling.identifier != identifier);
46        drop(state);
47        Ok(found)
48    }
49
50    async fn update_verification_by_identifier(
51        &self,
52        identifier: &str,
53        data: crate::UpdateVerification,
54    ) -> AuthResult<Option<crate::verification::VerificationSnapshot>> {
55        let mut state = self.lock()?;
56        let mut found = None;
57        for model in state.verifications.values_mut() {
58            if model.identifier == identifier {
59                model.updated_at = Utc::now();
60                if let Some(value) = &data.value {
61                    model.value.clone_from(value);
62                }
63                if let Some(expiry) = data.expires_at {
64                    model.expires_at = expiry;
65                }
66                if found.is_none() {
67                    found = Some(crate::verification::VerificationSnapshot::from_model(model));
68                }
69            }
70        }
71        drop(state);
72        Ok(found)
73    }
74
75    async fn reserve_verification_record(
76        &self,
77        logical_identifier: &str,
78        data: CreateVerification,
79    ) -> AuthResult<Option<VerificationView>> {
80        let (id, _) = crate::store::verification_reservation_key(logical_identifier);
81        let mut state = self.lock()?;
82        let indexmap::map::Entry::Vacant(entry) = state.verifications.entry(id.clone()) else {
83            return Ok(None);
84        };
85        let now = Utc::now();
86        let model = VerificationView {
87            id,
88            identifier: data.identifier,
89            value: data.value,
90            expires_at: data.expires_at,
91            created_at: now,
92            updated_at: now,
93        };
94        _ = entry.insert(model.clone());
95        drop(state);
96        Ok(Some(model))
97    }
98
99    async fn create_verification(
100        &self,
101        verification: CreateVerification,
102    ) -> AuthResult<VerificationView> {
103        let now = Utc::now();
104        let verification = VerificationView {
105            id: uuid::Uuid::new_v4().to_string(),
106            identifier: verification.identifier,
107            value: verification.value,
108            expires_at: verification.expires_at,
109            created_at: now,
110            updated_at: now,
111        };
112        drop(
113            self.lock()?
114                .verifications
115                .insert(verification.id.clone(), verification.clone()),
116        );
117        Ok(verification)
118    }
119
120    async fn get_verification(
121        &self,
122        identifier: &str,
123        value: &str,
124    ) -> AuthResult<Option<VerificationView>> {
125        Ok(self
126            .lock()?
127            .verifications
128            .values()
129            .filter(|verification| {
130                verification.identifier == identifier
131                    && verification.value == value
132                    && verification.expires_at >= Utc::now()
133            })
134            .max_by_key(|verification| verification.created_at)
135            .cloned())
136    }
137
138    async fn get_verification_by_value(&self, value: &str) -> AuthResult<Option<VerificationView>> {
139        Ok(self
140            .lock()?
141            .verifications
142            .values()
143            .filter(|verification| {
144                verification.value == value && verification.expires_at >= Utc::now()
145            })
146            .max_by_key(|verification| verification.created_at)
147            .cloned())
148    }
149
150    async fn get_verification_by_identifier(
151        &self,
152        identifier: &str,
153    ) -> AuthResult<Option<VerificationView>> {
154        Ok(self
155            .lock()?
156            .verifications
157            .values()
158            .filter(|verification| {
159                verification.identifier == identifier && verification.expires_at >= Utc::now()
160            })
161            .max_by_key(|verification| verification.created_at)
162            .cloned())
163    }
164
165    async fn consume_verification(
166        &self,
167        identifier: &str,
168        value: &str,
169    ) -> AuthResult<Option<VerificationView>> {
170        let mut state = self.lock()?;
171        let found = state
172            .verifications
173            .values()
174            .filter(|verification| verification.identifier == identifier)
175            .max_by_key(|verification| verification.created_at)
176            .cloned();
177        if let Some(verification) = &found {
178            if verification.value != value {
179                return Ok(None);
180            }
181            state
182                .verifications
183                .retain(|_, sibling| sibling.identifier != identifier);
184        }
185        drop(state);
186
187        Ok(found.filter(|verification| verification.expires_at >= Utc::now()))
188    }
189
190    async fn get_latest_verification_by_identifier(
191        &self,
192        identifier: &str,
193    ) -> AuthResult<Option<VerificationView>> {
194        Ok(self
195            .lock()?
196            .verifications
197            .values()
198            .filter(|verification| verification.identifier == identifier)
199            .max_by_key(|verification| verification.created_at)
200            .cloned())
201    }
202
203    async fn consume_verification_by_identifier(
204        &self,
205        identifier: &str,
206    ) -> AuthResult<Option<VerificationView>> {
207        let mut state = self.lock()?;
208        let found = state
209            .verifications
210            .values()
211            .filter(|verification| verification.identifier == identifier)
212            .max_by_key(|verification| verification.created_at)
213            .cloned();
214        state
215            .verifications
216            .retain(|_, sibling| sibling.identifier != identifier);
217        drop(state);
218
219        Ok(found.filter(|verification| verification.expires_at >= Utc::now()))
220    }
221
222    async fn delete_verifications_by_identifier(&self, identifier: &str) -> AuthResult<()> {
223        self.lock()?
224            .verifications
225            .retain(|_, verification| verification.identifier != identifier);
226        Ok(())
227    }
228
229    async fn compare_and_swap_verification(
230        &self,
231        id: &str,
232        expected_value: &str,
233        value: &str,
234        expires_at: DateTime<Utc>,
235    ) -> AuthResult<bool> {
236        let mut state = self.lock()?;
237        let Some(verification) = state.verifications.get_mut(id) else {
238            return Ok(false);
239        };
240        if verification.value != expected_value {
241            return Ok(false);
242        }
243        verification.value = value.to_owned();
244        verification.expires_at = expires_at;
245        verification.updated_at = Utc::now();
246        drop(state);
247
248        Ok(true)
249    }
250
251    async fn reserve_verification(&self, verification: CreateVerification) -> AuthResult<bool> {
252        let (id, _) = crate::store::verification_reservation_key(&verification.identifier);
253        let mut state = self.lock()?;
254        let indexmap::map::Entry::Vacant(entry) = state.verifications.entry(id.clone()) else {
255            return Ok(false);
256        };
257
258        let now = Utc::now();
259        let _ignored_insert = entry.insert(VerificationView {
260            id,
261            identifier: verification.identifier,
262            value: verification.value,
263            expires_at: verification.expires_at,
264            created_at: now,
265            updated_at: now,
266        });
267        drop(state);
268        Ok(true)
269    }
270
271    async fn delete_verification(&self, id: &str) -> AuthResult<()> {
272        drop(self.lock()?.verifications.shift_remove(id));
273        Ok(())
274    }
275
276    async fn delete_expired_verifications(&self) -> AuthResult<usize> {
277        let now = Utc::now();
278        let mut state = self.lock()?;
279        let before = state.verifications.len();
280        state.verifications.retain(|_, verification| {
281            verification.expires_at.timestamp_millis() >= now.timestamp_millis()
282        });
283        Ok(before - state.verifications.len())
284    }
285}