mas_storage/user/
registration.rs

1// Copyright 2025, 2026 Element Creations Ltd.
2// Copyright 2025 New Vector Ltd.
3//
4// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5// Please see LICENSE files in the repository root for full details.
6
7use std::net::IpAddr;
8
9use async_trait::async_trait;
10use mas_data_model::{
11    Clock, UpstreamOAuthAuthorizationSession, UserEmailAuthentication, UserRegistration,
12    UserRegistrationToken,
13};
14use rand_core::RngCore;
15use ulid::Ulid;
16use url::Url;
17
18use crate::repository_impl;
19
20/// A [`UserRegistrationRepository`] helps interacting with [`UserRegistration`]
21/// saved in the storage backend
22#[async_trait]
23pub trait UserRegistrationRepository: Send + Sync {
24    /// The error type returned by the repository
25    type Error;
26
27    /// Lookup a [`UserRegistration`] by its ID
28    ///
29    /// Returns `None` if no [`UserRegistration`] was found
30    ///
31    /// # Parameters
32    ///
33    /// * `id`: The ID of the [`UserRegistration`] to lookup
34    ///
35    /// # Errors
36    ///
37    /// Returns [`Self::Error`] if the underlying repository fails
38    async fn lookup(&mut self, id: Ulid) -> Result<Option<UserRegistration>, Self::Error>;
39
40    /// Create a new [`UserRegistration`] session
41    ///
42    /// Returns the newly created [`UserRegistration`]
43    ///
44    /// # Parameters
45    ///
46    /// * `rng`: The random number generator to use
47    /// * `clock`: The clock used to generate timestamps
48    /// * `username`: The username of the user
49    /// * `ip_address`: The IP address of the user agent, if any
50    /// * `user_agent`: The user agent of the user agent, if any
51    /// * `post_auth_action`: The post auth action to execute after the
52    ///   registration, if any
53    ///
54    /// # Errors
55    ///
56    /// Returns [`Self::Error`] if the underlying repository fails
57    async fn add(
58        &mut self,
59        rng: &mut (dyn RngCore + Send),
60        clock: &dyn Clock,
61        username: String,
62        ip_address: Option<IpAddr>,
63        user_agent: Option<String>,
64        post_auth_action: Option<serde_json::Value>,
65    ) -> Result<UserRegistration, Self::Error>;
66
67    /// Set the display name of a [`UserRegistration`]
68    ///
69    /// Returns the updated [`UserRegistration`]
70    ///
71    /// # Parameters
72    ///
73    /// * `user_registration`: The [`UserRegistration`] to update
74    /// * `display_name`: The display name to set
75    ///
76    /// # Errors
77    ///
78    /// Returns [`Self::Error`] if the underlying repository fails or if the
79    /// registration is already completed
80    async fn set_display_name(
81        &mut self,
82        user_registration: UserRegistration,
83        display_name: String,
84    ) -> Result<UserRegistration, Self::Error>;
85
86    /// Set the terms URL of a [`UserRegistration`]
87    ///
88    /// Returns the updated [`UserRegistration`]
89    ///
90    /// # Parameters
91    ///
92    /// * `user_registration`: The [`UserRegistration`] to update
93    /// * `terms_url`: The terms URL to set
94    ///
95    /// # Errors
96    ///
97    /// Returns [`Self::Error`] if the underlying repository fails or if the
98    /// registration is already completed
99    async fn set_terms_url(
100        &mut self,
101        user_registration: UserRegistration,
102        terms_url: Url,
103    ) -> Result<UserRegistration, Self::Error>;
104
105    /// Set the email authentication code of a [`UserRegistration`]
106    ///
107    /// Returns the updated [`UserRegistration`]
108    ///
109    /// # Parameters
110    ///
111    /// * `user_registration`: The [`UserRegistration`] to update
112    /// * `email_authentication`: The [`UserEmailAuthentication`] to set
113    ///
114    /// # Errors
115    ///
116    /// Returns [`Self::Error`] if the underlying repository fails or if the
117    /// registration is already completed
118    async fn set_email_authentication(
119        &mut self,
120        user_registration: UserRegistration,
121        email_authentication: &UserEmailAuthentication,
122    ) -> Result<UserRegistration, Self::Error>;
123
124    /// Set the password of a [`UserRegistration`]
125    ///
126    /// Returns the updated [`UserRegistration`]
127    ///
128    /// # Parameters
129    ///
130    /// * `user_registration`: The [`UserRegistration`] to update
131    /// * `hashed_password`: The hashed password to set
132    /// * `version`: The version of the hashing scheme
133    ///
134    /// # Errors
135    ///
136    /// Returns [`Self::Error`] if the underlying repository fails or if the
137    /// registration is already completed
138    async fn set_password(
139        &mut self,
140        user_registration: UserRegistration,
141        hashed_password: String,
142        version: u16,
143    ) -> Result<UserRegistration, Self::Error>;
144
145    /// Set the registration token of a [`UserRegistration`]
146    ///
147    /// Returns the updated [`UserRegistration`]
148    ///
149    /// # Parameters
150    ///
151    /// * `user_registration`: The [`UserRegistration`] to update
152    /// * `user_registration_token`: The [`UserRegistrationToken`] to set
153    ///
154    /// # Errors
155    ///
156    /// Returns [`Self::Error`] if the underlying repository fails or if the
157    /// registration is already completed
158    async fn set_registration_token(
159        &mut self,
160        user_registration: UserRegistration,
161        user_registration_token: &UserRegistrationToken,
162    ) -> Result<UserRegistration, Self::Error>;
163
164    /// Set an [`UpstreamOAuthAuthorizationSession`] to associate with a
165    /// [`UserRegistration`]
166    ///
167    /// Returns the updated [`UserRegistration`]
168    ///
169    /// # Parameters
170    ///
171    /// * `user_registration`: The [`UserRegistration`] to update
172    /// * `upstream_oauth_authorization_session`: The
173    ///   [`UpstreamOAuthAuthorizationSession`] to set
174    ///
175    /// # Errors
176    ///
177    /// Returns [`Self::Error`] if the underlying repository fails or if the
178    /// registration is already completed
179    async fn set_upstream_oauth_authorization_session(
180        &mut self,
181        user_registration: UserRegistration,
182        upstream_oauth_authorization_session: &UpstreamOAuthAuthorizationSession,
183    ) -> Result<UserRegistration, Self::Error>;
184
185    /// Complete a [`UserRegistration`]
186    ///
187    /// Returns the updated [`UserRegistration`]
188    ///
189    /// # Parameters
190    ///
191    /// * `clock`: The clock used to generate timestamps
192    /// * `user_registration`: The [`UserRegistration`] to complete
193    ///
194    /// # Errors
195    ///
196    /// Returns [`Self::Error`] if the underlying repository fails or if the
197    /// registration is already completed
198    async fn complete(
199        &mut self,
200        clock: &dyn Clock,
201        user_registration: UserRegistration,
202    ) -> Result<UserRegistration, Self::Error>;
203
204    /// Cleanup [`UserRegistration`]s between the given IDs.
205    ///
206    /// Returns the number of registrations deleted, as well as the ID of the
207    /// last registration deleted.
208    ///
209    /// # Parameters
210    ///
211    /// * `since`: An optional ID to start from
212    /// * `until`: The ID until which to clean up registrations
213    /// * `limit`: The maximum number of registrations to clean up
214    ///
215    /// # Errors
216    ///
217    /// Returns [`Self::Error`] if the underlying repository fails
218    async fn cleanup(
219        &mut self,
220        since: Option<Ulid>,
221        until: Ulid,
222        limit: usize,
223    ) -> Result<(usize, Option<Ulid>), Self::Error>;
224}
225
226repository_impl!(UserRegistrationRepository:
227    async fn lookup(&mut self, id: Ulid) -> Result<Option<UserRegistration>, Self::Error>;
228    async fn add(
229        &mut self,
230        rng: &mut (dyn RngCore + Send),
231        clock: &dyn Clock,
232        username: String,
233        ip_address: Option<IpAddr>,
234        user_agent: Option<String>,
235        post_auth_action: Option<serde_json::Value>,
236    ) -> Result<UserRegistration, Self::Error>;
237    async fn set_display_name(
238        &mut self,
239        user_registration: UserRegistration,
240        display_name: String,
241    ) -> Result<UserRegistration, Self::Error>;
242    async fn set_terms_url(
243        &mut self,
244        user_registration: UserRegistration,
245        terms_url: Url,
246    ) -> Result<UserRegistration, Self::Error>;
247    async fn set_email_authentication(
248        &mut self,
249        user_registration: UserRegistration,
250        email_authentication: &UserEmailAuthentication,
251    ) -> Result<UserRegistration, Self::Error>;
252    async fn set_password(
253        &mut self,
254        user_registration: UserRegistration,
255        hashed_password: String,
256        version: u16,
257    ) -> Result<UserRegistration, Self::Error>;
258    async fn set_registration_token(
259        &mut self,
260        user_registration: UserRegistration,
261        user_registration_token: &UserRegistrationToken,
262    ) -> Result<UserRegistration, Self::Error>;
263    async fn set_upstream_oauth_authorization_session(
264        &mut self,
265        user_registration: UserRegistration,
266        upstream_oauth_authorization_session: &UpstreamOAuthAuthorizationSession,
267    ) -> Result<UserRegistration, Self::Error>;
268    async fn complete(
269        &mut self,
270        clock: &dyn Clock,
271        user_registration: UserRegistration,
272    ) -> Result<UserRegistration, Self::Error>;
273    async fn cleanup(
274        &mut self,
275        since: Option<Ulid>,
276        until: Ulid,
277        limit: usize,
278    ) -> Result<(usize, Option<Ulid>), Self::Error>;
279);