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