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);