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    /// Add a new access token to the database
54    ///
55    /// Returns the newly created access token
56    ///
57    /// # Parameters
58    ///
59    /// * `rng`: A random number generator
60    /// * `clock`: The clock used to generate timestamps
61    /// * `session`: The session the access token is associated with
62    /// * `access_token`: The access token to add
63    /// * `expires_after`: The duration after which the access token expires. If
64    ///   [`None`] the access token never expires
65    ///
66    /// # Errors
67    ///
68    /// Returns [`Self::Error`] if the underlying repository fails
69    async fn add(
70        &mut self,
71        rng: &mut (dyn RngCore + Send),
72        clock: &dyn Clock,
73        session: &PersonalSession,
74        access_token: &str,
75        expires_after: Option<Duration>,
76    ) -> Result<PersonalAccessToken, Self::Error>;
77
78    /// Revoke an access token
79    ///
80    /// Returns the revoked access token
81    ///
82    /// # Parameters
83    ///
84    /// * `clock`: The clock used to generate timestamps
85    /// * `access_token`: The access token to revoke
86    ///
87    /// # Errors
88    ///
89    /// Returns [`Self::Error`] if the underlying repository fails
90    async fn revoke(
91        &mut self,
92        clock: &dyn Clock,
93        access_token: PersonalAccessToken,
94    ) -> Result<PersonalAccessToken, Self::Error>;
95}
96
97repository_impl!(PersonalAccessTokenRepository:
98    async fn lookup(&mut self, id: Ulid) -> Result<Option<PersonalAccessToken>, Self::Error>;
99
100    async fn find_by_token(
101        &mut self,
102        access_token: &str,
103    ) -> Result<Option<PersonalAccessToken>, Self::Error>;
104
105    async fn add(
106        &mut self,
107        rng: &mut (dyn RngCore + Send),
108        clock: &dyn Clock,
109        session: &PersonalSession,
110        access_token: &str,
111        expires_after: Option<Duration>,
112    ) -> Result<PersonalAccessToken, Self::Error>;
113
114    async fn revoke(
115        &mut self,
116        clock: &dyn Clock,
117        access_token: PersonalAccessToken,
118    ) -> Result<PersonalAccessToken, Self::Error>;
119);