| 1 | use std::time::SystemTime; |
| 2 | |
| 3 | use super::RepositoryResult; |
| 4 | use crate::domain::{PasswordReset, ResetHash, ResetId, UserId}; |
| 5 | |
| 6 | /// Persistence for [`PasswordReset`]. |
| 7 | pub trait PasswordResetRepository: Send + Sync { |
| 8 | /// Looks a reset up by the hash of the token in the link. |
| 9 | /// |
| 10 | /// By hash rather than by id, for the reason |
| 11 | /// [`TokenRepository::find_by_hash`](super::TokenRepository::find_by_hash) has: what |
| 12 | /// arrives is the secret, never its id. |
| 13 | /// |
| 14 | /// Returns whatever is stored, live or not — whether a token is spent or expired is |
| 15 | /// the caller's question, and collapsing "used" into "absent" here would make the |
| 16 | /// two indistinguishable to the one place that has to tell them apart. |
| 17 | fn find_by_hash( |
| 18 | &self, |
| 19 | hash: &ResetHash, |
| 20 | ) -> impl Future<Output = RepositoryResult<Option<PasswordReset>>> + Send; |
| 21 | |
| 22 | /// Inserts a reset. |
| 23 | fn save(&self, reset: &PasswordReset) -> impl Future<Output = RepositoryResult<()>> + Send; |
| 24 | |
| 25 | /// Marks a reset spent, succeeding if there was nothing to mark. |
| 26 | /// |
| 27 | /// Separate from [`save`](Self::save) because the caller holds a record it read a |
| 28 | /// moment ago and must move exactly this one field — the same reasoning as |
| 29 | /// [`touch_last_used`](super::TokenRepository::touch_last_used). |
| 30 | fn mark_used( |
| 31 | &self, |
| 32 | id: &ResetId, |
| 33 | at: SystemTime, |
| 34 | ) -> impl Future<Output = RepositoryResult<()>> + Send; |
| 35 | |
| 36 | /// Spends every still-live reset a user holds, returning how many went. |
| 37 | /// |
| 38 | /// Called when a new link is issued and again when a password actually changes, so |
| 39 | /// that at most one link is ever live: two outstanding links means a stale mail in |
| 40 | /// an inbox is still a way in. |
| 41 | /// |
| 42 | /// Marked used rather than deleted, so a replayed old link says "already used" |
| 43 | /// rather than "never existed". |
| 44 | fn invalidate_for_user( |
| 45 | &self, |
| 46 | user_id: &UserId, |
| 47 | at: SystemTime, |
| 48 | ) -> impl Future<Output = RepositoryResult<u64>> + Send; |
| 49 | } |