mas_storage/user/
recovery.rs

1// Copyright 2024 New Vector Ltd.
2// Copyright 2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only
5// Please see LICENSE in the repository root for full details.
6
7use std::net::IpAddr;
8
9use async_trait::async_trait;
10use mas_data_model::{UserAgent, UserEmail, UserRecoverySession, UserRecoveryTicket};
11use rand_core::RngCore;
12use ulid::Ulid;
13
14use crate::{Clock, repository_impl};
15
16/// A [`UserRecoveryRepository`] helps interacting with [`UserRecoverySession`]
17/// and [`UserRecoveryTicket`] saved in the storage backend
18#[async_trait]
19pub trait UserRecoveryRepository: Send + Sync {
20    /// The error type returned by the repository
21    type Error;
22
23    /// Lookup an [`UserRecoverySession`] by its ID
24    ///
25    /// Returns `None` if no [`UserRecoverySession`] was found
26    ///
27    /// # Parameters
28    ///
29    /// * `id`: The ID of the [`UserRecoverySession`] to lookup
30    ///
31    /// # Errors
32    ///
33    /// Returns [`Self::Error`] if the underlying repository fails
34    async fn lookup_session(
35        &mut self,
36        id: Ulid,
37    ) -> Result<Option<UserRecoverySession>, Self::Error>;
38
39    /// Create a new [`UserRecoverySession`] for the given email
40    ///
41    /// Returns the newly created [`UserRecoverySession`]
42    ///
43    /// # Parameters
44    ///
45    /// * `rng`: The random number generator to use
46    /// * `clock`: The clock to use
47    /// * `email`: The email to create the session for
48    /// * `user_agent`: The user agent of the browser which initiated the
49    ///   session
50    /// * `ip_address`: The IP address of the browser which initiated the
51    ///   session, if known
52    /// * `locale`: The locale of the browser which initiated the session
53    ///
54    /// # Errors
55    ///
56    /// Returns [`Self::Error`] if the underlying repository fails
57    async fn add_session(
58        &mut self,
59        rng: &mut (dyn RngCore + Send),
60        clock: &dyn Clock,
61        email: String,
62        user_agent: UserAgent,
63        ip_address: Option<IpAddr>,
64        locale: String,
65    ) -> Result<UserRecoverySession, Self::Error>;
66
67    /// Find a [`UserRecoveryTicket`] by its ticket
68    ///
69    /// Returns `None` if no [`UserRecoveryTicket`] was found
70    ///
71    /// # Parameters
72    ///
73    /// * `ticket`: The ticket of the [`UserRecoveryTicket`] to lookup
74    ///
75    /// # Errors
76    ///
77    /// Returns [`Self::Error`] if the underlying repository fails
78    async fn find_ticket(
79        &mut self,
80        ticket: &str,
81    ) -> Result<Option<UserRecoveryTicket>, Self::Error>;
82
83    /// Add a [`UserRecoveryTicket`] to the given [`UserRecoverySession`] for
84    /// the given [`UserEmail`]
85    ///
86    /// # Parameters
87    ///
88    /// * `rng`: The random number generator to use
89    /// * `clock`: The clock to use
90    /// * `session`: The [`UserRecoverySession`] to add the ticket to
91    /// * `user_email`: The [`UserEmail`] to add the ticket for
92    /// * `ticket`: The ticket to add
93    ///
94    /// # Errors
95    ///
96    /// Returns [`Self::Error`] if the underlying repository fails
97    async fn add_ticket(
98        &mut self,
99        rng: &mut (dyn RngCore + Send),
100        clock: &dyn Clock,
101        user_recovery_session: &UserRecoverySession,
102        user_email: &UserEmail,
103        ticket: String,
104    ) -> Result<UserRecoveryTicket, Self::Error>;
105
106    /// Consume a [`UserRecoveryTicket`] and mark the session as used
107    ///
108    /// # Parameters
109    ///
110    /// * `clock`: The clock to use to record the time of consumption
111    /// * `ticket`: The [`UserRecoveryTicket`] to consume
112    /// * `session`: The [`UserRecoverySession`] to mark as used
113    ///
114    /// # Errors
115    ///
116    /// Returns [`Self::Error`] if the underlying repository fails or if the
117    /// recovery session was already used
118    async fn consume_ticket(
119        &mut self,
120        clock: &dyn Clock,
121        user_recovery_ticket: UserRecoveryTicket,
122        user_recovery_session: UserRecoverySession,
123    ) -> Result<UserRecoverySession, Self::Error>;
124}
125
126repository_impl!(UserRecoveryRepository:
127    async fn lookup_session(&mut self, id: Ulid) -> Result<Option<UserRecoverySession>, Self::Error>;
128
129    async fn add_session(
130        &mut self,
131        rng: &mut (dyn RngCore + Send),
132        clock: &dyn Clock,
133        email: String,
134        user_agent: UserAgent,
135        ip_address: Option<IpAddr>,
136        locale: String,
137    ) -> Result<UserRecoverySession, Self::Error>;
138
139    async fn find_ticket(
140        &mut self,
141        ticket: &str,
142    ) -> Result<Option<UserRecoveryTicket>, Self::Error>;
143
144    async fn add_ticket(
145        &mut self,
146        rng: &mut (dyn RngCore + Send),
147        clock: &dyn Clock,
148        user_recovery_session: &UserRecoverySession,
149        user_email: &UserEmail,
150        ticket: String,
151    ) -> Result<UserRecoveryTicket, Self::Error>;
152
153    async fn consume_ticket(
154        &mut self,
155        clock: &dyn Clock,
156        user_recovery_ticket: UserRecoveryTicket,
157        user_recovery_session: UserRecoverySession,
158    ) -> Result<UserRecoverySession, Self::Error>;
159);