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