mas_storage/personal/access_token.rs
1// Copyright 2025 New Vector Ltd.
2//
3// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
4// Please see LICENSE files in the repository root for full details.
5
6use async_trait::async_trait;
7use chrono::Duration;
8use mas_data_model::{
9 Clock,
10 personal::{PersonalAccessToken, session::PersonalSession},
11};
12use rand_core::RngCore;
13use ulid::Ulid;
14
15use crate::repository_impl;
16
17/// An [`PersonalAccessTokenRepository`] helps interacting with
18/// [`PersonalAccessToken`] saved in the storage backend
19#[async_trait]
20pub trait PersonalAccessTokenRepository: Send + Sync {
21 /// The error type returned by the repository
22 type Error;
23
24 /// Lookup an access token by its ID
25 ///
26 /// Returns the access token if it exists, `None` otherwise
27 ///
28 /// # Parameters
29 ///
30 /// * `id`: The ID of the access token to lookup
31 ///
32 /// # Errors
33 ///
34 /// Returns [`Self::Error`] if the underlying repository fails
35 async fn lookup(&mut self, id: Ulid) -> Result<Option<PersonalAccessToken>, Self::Error>;
36
37 /// Find an access token by its token
38 ///
39 /// Returns the access token if it exists, `None` otherwise
40 ///
41 /// # Parameters
42 ///
43 /// * `access_token`: The token of the access token to lookup
44 ///
45 /// # Errors
46 ///
47 /// Returns [`Self::Error`] if the underlying repository fails
48 async fn find_by_token(
49 &mut self,
50 access_token: &str,
51 ) -> Result<Option<PersonalAccessToken>, Self::Error>;
52
53 /// Find the active access token belonging to a given session.
54 ///
55 /// Returns the active access token if it exists, `None` otherwise
56 ///
57 /// # Parameters
58 ///
59 /// * `session`: The session to lookup
60 ///
61 /// # Errors
62 ///
63 /// Returns [`Self::Error`] if the underlying repository fails
64 async fn find_active_for_session(
65 &mut self,
66 session: &PersonalSession,
67 ) -> Result<Option<PersonalAccessToken>, Self::Error>;
68
69 /// Add a new access token to the database
70 ///
71 /// Returns the newly created access token
72 ///
73 /// # Parameters
74 ///
75 /// * `rng`: A random number generator
76 /// * `clock`: The clock used to generate timestamps
77 /// * `session`: The session the access token is associated with
78 /// * `access_token`: The access token to add
79 /// * `expires_after`: The duration after which the access token expires. If
80 /// [`None`] the access token never expires
81 ///
82 /// # Errors
83 ///
84 /// Returns [`Self::Error`] if the underlying repository fails
85 async fn add(
86 &mut self,
87 rng: &mut (dyn RngCore + Send),
88 clock: &dyn Clock,
89 session: &PersonalSession,
90 access_token: &str,
91 expires_after: Option<Duration>,
92 ) -> Result<PersonalAccessToken, Self::Error>;
93
94 /// Revoke an access token
95 ///
96 /// Returns the revoked access token
97 ///
98 /// # Parameters
99 ///
100 /// * `clock`: The clock used to generate timestamps
101 /// * `access_token`: The access token to revoke
102 ///
103 /// # Errors
104 ///
105 /// Returns [`Self::Error`] if the underlying repository fails
106 async fn revoke(
107 &mut self,
108 clock: &dyn Clock,
109 access_token: PersonalAccessToken,
110 ) -> Result<PersonalAccessToken, Self::Error>;
111}
112
113repository_impl!(PersonalAccessTokenRepository:
114 async fn lookup(&mut self, id: Ulid) -> Result<Option<PersonalAccessToken>, Self::Error>;
115
116 async fn find_by_token(
117 &mut self,
118 access_token: &str,
119 ) -> Result<Option<PersonalAccessToken>, Self::Error>;
120
121 async fn find_active_for_session(
122 &mut self,
123 session: &PersonalSession,
124 ) -> Result<Option<PersonalAccessToken>, Self::Error>;
125
126 async fn add(
127 &mut self,
128 rng: &mut (dyn RngCore + Send),
129 clock: &dyn Clock,
130 session: &PersonalSession,
131 access_token: &str,
132 expires_after: Option<Duration>,
133 ) -> Result<PersonalAccessToken, Self::Error>;
134
135 async fn revoke(
136 &mut self,
137 clock: &dyn Clock,
138 access_token: PersonalAccessToken,
139 ) -> Result<PersonalAccessToken, Self::Error>;
140);