1use std::net::IpAddr;
8
9use async_trait::async_trait;
10use chrono::{DateTime, Utc};
11use mas_data_model::{BrowserSession, Client, Device, Session, User};
12use oauth2_types::scope::Scope;
13use rand_core::RngCore;
14use ulid::Ulid;
15
16use crate::{Clock, 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);