1use std::net::IpAddr;
8
9use async_trait::async_trait;
10use chrono::{DateTime, Utc};
11use mas_data_model::{BrowserSession, Client, Clock, Device, Session, User};
12use oauth2_types::scope::Scope;
13use rand_core::RngCore;
14use ulid::Ulid;
15
16use crate::{Pagination, pagination::Page, repository_impl, user::BrowserSessionFilter};
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub enum OAuth2SessionState {
20    Active,
21    Finished,
22}
23
24impl OAuth2SessionState {
25    pub fn is_active(self) -> bool {
26        matches!(self, Self::Active)
27    }
28
29    pub fn is_finished(self) -> bool {
30        matches!(self, Self::Finished)
31    }
32}
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
35pub enum ClientKind {
36    Static,
37    Dynamic,
38}
39
40impl ClientKind {
41    pub fn is_static(self) -> bool {
42        matches!(self, Self::Static)
43    }
44}
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
48pub struct OAuth2SessionFilter<'a> {
49    user: Option<&'a User>,
50    any_user: Option<bool>,
51    browser_session: Option<&'a BrowserSession>,
52    browser_session_filter: Option<BrowserSessionFilter<'a>>,
53    device: Option<&'a Device>,
54    client: Option<&'a Client>,
55    client_kind: Option<ClientKind>,
56    state: Option<OAuth2SessionState>,
57    scope: Option<&'a Scope>,
58    last_active_before: Option<DateTime<Utc>>,
59    last_active_after: Option<DateTime<Utc>>,
60}
61
62impl<'a> OAuth2SessionFilter<'a> {
63    #[must_use]
65    pub fn new() -> Self {
66        Self::default()
67    }
68
69    #[must_use]
71    pub fn for_user(mut self, user: &'a User) -> Self {
72        self.user = Some(user);
73        self
74    }
75
76    #[must_use]
80    pub fn user(&self) -> Option<&'a User> {
81        self.user
82    }
83
84    #[must_use]
86    pub fn for_any_user(mut self) -> Self {
87        self.any_user = Some(true);
88        self
89    }
90
91    #[must_use]
93    pub fn for_no_user(mut self) -> Self {
94        self.any_user = Some(false);
95        self
96    }
97
98    #[must_use]
102    pub fn any_user(&self) -> Option<bool> {
103        self.any_user
104    }
105
106    #[must_use]
108    pub fn for_browser_session(mut self, browser_session: &'a BrowserSession) -> Self {
109        self.browser_session = Some(browser_session);
110        self
111    }
112
113    #[must_use]
115    pub fn for_browser_sessions(
116        mut self,
117        browser_session_filter: BrowserSessionFilter<'a>,
118    ) -> Self {
119        self.browser_session_filter = Some(browser_session_filter);
120        self
121    }
122
123    #[must_use]
127    pub fn browser_session(&self) -> Option<&'a BrowserSession> {
128        self.browser_session
129    }
130
131    #[must_use]
135    pub fn browser_session_filter(&self) -> Option<BrowserSessionFilter<'a>> {
136        self.browser_session_filter
137    }
138
139    #[must_use]
141    pub fn for_client(mut self, client: &'a Client) -> Self {
142        self.client = Some(client);
143        self
144    }
145
146    #[must_use]
150    pub fn client(&self) -> Option<&'a Client> {
151        self.client
152    }
153
154    #[must_use]
156    pub fn only_static_clients(mut self) -> Self {
157        self.client_kind = Some(ClientKind::Static);
158        self
159    }
160
161    #[must_use]
163    pub fn only_dynamic_clients(mut self) -> Self {
164        self.client_kind = Some(ClientKind::Dynamic);
165        self
166    }
167
168    #[must_use]
172    pub fn client_kind(&self) -> Option<ClientKind> {
173        self.client_kind
174    }
175
176    #[must_use]
178    pub fn with_last_active_before(mut self, last_active_before: DateTime<Utc>) -> Self {
179        self.last_active_before = Some(last_active_before);
180        self
181    }
182
183    #[must_use]
185    pub fn with_last_active_after(mut self, last_active_after: DateTime<Utc>) -> Self {
186        self.last_active_after = Some(last_active_after);
187        self
188    }
189
190    #[must_use]
194    pub fn last_active_before(&self) -> Option<DateTime<Utc>> {
195        self.last_active_before
196    }
197
198    #[must_use]
202    pub fn last_active_after(&self) -> Option<DateTime<Utc>> {
203        self.last_active_after
204    }
205
206    #[must_use]
208    pub fn active_only(mut self) -> Self {
209        self.state = Some(OAuth2SessionState::Active);
210        self
211    }
212
213    #[must_use]
215    pub fn finished_only(mut self) -> Self {
216        self.state = Some(OAuth2SessionState::Finished);
217        self
218    }
219
220    #[must_use]
224    pub fn state(&self) -> Option<OAuth2SessionState> {
225        self.state
226    }
227
228    #[must_use]
230    pub fn with_scope(mut self, scope: &'a Scope) -> Self {
231        self.scope = Some(scope);
232        self
233    }
234
235    #[must_use]
239    pub fn scope(&self) -> Option<&'a Scope> {
240        self.scope
241    }
242
243    #[must_use]
245    pub fn for_device(mut self, device: &'a Device) -> Self {
246        self.device = Some(device);
247        self
248    }
249
250    #[must_use]
254    pub fn device(&self) -> Option<&'a Device> {
255        self.device
256    }
257}
258
259#[async_trait]
262pub trait OAuth2SessionRepository: Send + Sync {
263    type Error;
265
266    async fn lookup(&mut self, id: Ulid) -> Result<Option<Session>, Self::Error>;
278
279    async fn add(
297        &mut self,
298        rng: &mut (dyn RngCore + Send),
299        clock: &dyn Clock,
300        client: &Client,
301        user: Option<&User>,
302        user_session: Option<&BrowserSession>,
303        scope: Scope,
304    ) -> Result<Session, Self::Error>;
305
306    async fn add_from_browser_session(
323        &mut self,
324        rng: &mut (dyn RngCore + Send),
325        clock: &dyn Clock,
326        client: &Client,
327        user_session: &BrowserSession,
328        scope: Scope,
329    ) -> Result<Session, Self::Error> {
330        self.add(
331            rng,
332            clock,
333            client,
334            Some(&user_session.user),
335            Some(user_session),
336            scope,
337        )
338        .await
339    }
340
341    async fn add_from_client_credentials(
357        &mut self,
358        rng: &mut (dyn RngCore + Send),
359        clock: &dyn Clock,
360        client: &Client,
361        scope: Scope,
362    ) -> Result<Session, Self::Error> {
363        self.add(rng, clock, client, None, None, scope).await
364    }
365
366    async fn finish(&mut self, clock: &dyn Clock, session: Session)
379    -> Result<Session, Self::Error>;
380
381    async fn finish_bulk(
394        &mut self,
395        clock: &dyn Clock,
396        filter: OAuth2SessionFilter<'_>,
397    ) -> Result<usize, Self::Error>;
398
399    async fn list(
410        &mut self,
411        filter: OAuth2SessionFilter<'_>,
412        pagination: Pagination,
413    ) -> Result<Page<Session>, Self::Error>;
414
415    async fn count(&mut self, filter: OAuth2SessionFilter<'_>) -> Result<usize, Self::Error>;
425
426    async fn record_batch_activity(
437        &mut self,
438        activity: Vec<(Ulid, DateTime<Utc>, Option<IpAddr>)>,
439    ) -> Result<(), Self::Error>;
440
441    async fn record_user_agent(
448        &mut self,
449        session: Session,
450        user_agent: String,
451    ) -> Result<Session, Self::Error>;
452
453    async fn set_human_name(
460        &mut self,
461        session: Session,
462        human_name: Option<String>,
463    ) -> Result<Session, Self::Error>;
464}
465
466repository_impl!(OAuth2SessionRepository:
467    async fn lookup(&mut self, id: Ulid) -> Result<Option<Session>, Self::Error>;
468
469    async fn add(
470        &mut self,
471        rng: &mut (dyn RngCore + Send),
472        clock: &dyn Clock,
473        client: &Client,
474        user: Option<&User>,
475        user_session: Option<&BrowserSession>,
476        scope: Scope,
477    ) -> Result<Session, Self::Error>;
478
479    async fn add_from_browser_session(
480        &mut self,
481        rng: &mut (dyn RngCore + Send),
482        clock: &dyn Clock,
483        client: &Client,
484        user_session: &BrowserSession,
485        scope: Scope,
486    ) -> Result<Session, Self::Error>;
487
488    async fn add_from_client_credentials(
489        &mut self,
490        rng: &mut (dyn RngCore + Send),
491        clock: &dyn Clock,
492        client: &Client,
493        scope: Scope,
494    ) -> Result<Session, Self::Error>;
495
496    async fn finish(&mut self, clock: &dyn Clock, session: Session)
497        -> Result<Session, Self::Error>;
498
499    async fn finish_bulk(
500        &mut self,
501        clock: &dyn Clock,
502        filter: OAuth2SessionFilter<'_>,
503    ) -> Result<usize, Self::Error>;
504
505    async fn list(
506        &mut self,
507        filter: OAuth2SessionFilter<'_>,
508        pagination: Pagination,
509    ) -> Result<Page<Session>, Self::Error>;
510
511    async fn count(&mut self, filter: OAuth2SessionFilter<'_>) -> Result<usize, Self::Error>;
512
513    async fn record_batch_activity(
514        &mut self,
515        activity: Vec<(Ulid, DateTime<Utc>, Option<IpAddr>)>,
516    ) -> Result<(), Self::Error>;
517
518    async fn record_user_agent(
519        &mut self,
520        session: Session,
521        user_agent: String,
522    ) -> Result<Session, Self::Error>;
523
524    async fn set_human_name(
525        &mut self,
526        session: Session,
527        human_name: Option<String>,
528    ) -> Result<Session, Self::Error>;
529);