1use std::net::IpAddr;
8
9use async_trait::async_trait;
10use chrono::{DateTime, Utc};
11use mas_data_model::{BrowserSession, CompatSession, CompatSsoLogin, Device, User};
12use rand_core::RngCore;
13use ulid::Ulid;
14
15use crate::{Clock, Page, Pagination, repository_impl};
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum CompatSessionState {
19 Active,
20 Finished,
21}
22
23impl CompatSessionState {
24 #[must_use]
26 pub fn is_active(self) -> bool {
27 matches!(self, Self::Active)
28 }
29
30 #[must_use]
32 pub fn is_finished(self) -> bool {
33 matches!(self, Self::Finished)
34 }
35}
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38pub enum CompatSessionType {
39 SsoLogin,
40 Unknown,
41}
42
43impl CompatSessionType {
44 #[must_use]
46 pub fn is_sso_login(self) -> bool {
47 matches!(self, Self::SsoLogin)
48 }
49
50 #[must_use]
52 pub fn is_unknown(self) -> bool {
53 matches!(self, Self::Unknown)
54 }
55}
56
57#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
59pub struct CompatSessionFilter<'a> {
60 user: Option<&'a User>,
61 browser_session: Option<&'a BrowserSession>,
62 state: Option<CompatSessionState>,
63 auth_type: Option<CompatSessionType>,
64 device: Option<&'a Device>,
65 last_active_before: Option<DateTime<Utc>>,
66 last_active_after: Option<DateTime<Utc>>,
67}
68
69impl<'a> CompatSessionFilter<'a> {
70 #[must_use]
72 pub fn new() -> Self {
73 Self::default()
74 }
75
76 #[must_use]
78 pub fn for_user(mut self, user: &'a User) -> Self {
79 self.user = Some(user);
80 self
81 }
82
83 #[must_use]
85 pub fn user(&self) -> Option<&'a User> {
86 self.user
87 }
88
89 #[must_use]
91 pub fn for_device(mut self, device: &'a Device) -> Self {
92 self.device = Some(device);
93 self
94 }
95
96 #[must_use]
98 pub fn device(&self) -> Option<&'a Device> {
99 self.device
100 }
101
102 #[must_use]
104 pub fn for_browser_session(mut self, browser_session: &'a BrowserSession) -> Self {
105 self.browser_session = Some(browser_session);
106 self
107 }
108
109 #[must_use]
111 pub fn browser_session(&self) -> Option<&'a BrowserSession> {
112 self.browser_session
113 }
114
115 #[must_use]
117 pub fn with_last_active_before(mut self, last_active_before: DateTime<Utc>) -> Self {
118 self.last_active_before = Some(last_active_before);
119 self
120 }
121
122 #[must_use]
124 pub fn with_last_active_after(mut self, last_active_after: DateTime<Utc>) -> Self {
125 self.last_active_after = Some(last_active_after);
126 self
127 }
128
129 #[must_use]
133 pub fn last_active_before(&self) -> Option<DateTime<Utc>> {
134 self.last_active_before
135 }
136
137 #[must_use]
141 pub fn last_active_after(&self) -> Option<DateTime<Utc>> {
142 self.last_active_after
143 }
144
145 #[must_use]
147 pub fn active_only(mut self) -> Self {
148 self.state = Some(CompatSessionState::Active);
149 self
150 }
151
152 #[must_use]
154 pub fn finished_only(mut self) -> Self {
155 self.state = Some(CompatSessionState::Finished);
156 self
157 }
158
159 #[must_use]
161 pub fn state(&self) -> Option<CompatSessionState> {
162 self.state
163 }
164
165 #[must_use]
167 pub fn sso_login_only(mut self) -> Self {
168 self.auth_type = Some(CompatSessionType::SsoLogin);
169 self
170 }
171
172 #[must_use]
174 pub fn unknown_only(mut self) -> Self {
175 self.auth_type = Some(CompatSessionType::Unknown);
176 self
177 }
178
179 #[must_use]
181 pub fn auth_type(&self) -> Option<CompatSessionType> {
182 self.auth_type
183 }
184}
185
186#[async_trait]
189pub trait CompatSessionRepository: Send + Sync {
190 type Error;
192
193 async fn lookup(&mut self, id: Ulid) -> Result<Option<CompatSession>, Self::Error>;
205
206 #[expect(clippy::too_many_arguments)]
225 async fn add(
226 &mut self,
227 rng: &mut (dyn RngCore + Send),
228 clock: &dyn Clock,
229 user: &User,
230 device: Device,
231 browser_session: Option<&BrowserSession>,
232 is_synapse_admin: bool,
233 human_name: Option<String>,
234 ) -> Result<CompatSession, Self::Error>;
235
236 async fn finish(
249 &mut self,
250 clock: &dyn Clock,
251 compat_session: CompatSession,
252 ) -> Result<CompatSession, Self::Error>;
253
254 async fn finish_bulk(
267 &mut self,
268 clock: &dyn Clock,
269 filter: CompatSessionFilter<'_>,
270 ) -> Result<usize, Self::Error>;
271
272 async fn list(
285 &mut self,
286 filter: CompatSessionFilter<'_>,
287 pagination: Pagination,
288 ) -> Result<Page<(CompatSession, Option<CompatSsoLogin>)>, Self::Error>;
289
290 async fn count(&mut self, filter: CompatSessionFilter<'_>) -> Result<usize, Self::Error>;
300
301 async fn record_batch_activity(
312 &mut self,
313 activity: Vec<(Ulid, DateTime<Utc>, Option<IpAddr>)>,
314 ) -> Result<(), Self::Error>;
315
316 async fn record_user_agent(
327 &mut self,
328 compat_session: CompatSession,
329 user_agent: String,
330 ) -> Result<CompatSession, Self::Error>;
331
332 async fn set_human_name(
343 &mut self,
344 compat_session: CompatSession,
345 human_name: Option<String>,
346 ) -> Result<CompatSession, Self::Error>;
347}
348
349repository_impl!(CompatSessionRepository:
350 async fn lookup(&mut self, id: Ulid) -> Result<Option<CompatSession>, Self::Error>;
351
352 async fn add(
353 &mut self,
354 rng: &mut (dyn RngCore + Send),
355 clock: &dyn Clock,
356 user: &User,
357 device: Device,
358 browser_session: Option<&BrowserSession>,
359 is_synapse_admin: bool,
360 human_name: Option<String>,
361 ) -> Result<CompatSession, Self::Error>;
362
363 async fn finish(
364 &mut self,
365 clock: &dyn Clock,
366 compat_session: CompatSession,
367 ) -> Result<CompatSession, Self::Error>;
368
369 async fn finish_bulk(
370 &mut self,
371 clock: &dyn Clock,
372 filter: CompatSessionFilter<'_>,
373 ) -> Result<usize, Self::Error>;
374
375 async fn list(
376 &mut self,
377 filter: CompatSessionFilter<'_>,
378 pagination: Pagination,
379 ) -> Result<Page<(CompatSession, Option<CompatSsoLogin>)>, Self::Error>;
380
381 async fn count(&mut self, filter: CompatSessionFilter<'_>) -> Result<usize, Self::Error>;
382
383 async fn record_batch_activity(
384 &mut self,
385 activity: Vec<(Ulid, DateTime<Utc>, Option<IpAddr>)>,
386 ) -> Result<(), Self::Error>;
387
388 async fn record_user_agent(
389 &mut self,
390 compat_session: CompatSession,
391 user_agent: String,
392 ) -> Result<CompatSession, Self::Error>;
393
394 async fn set_human_name(
395 &mut self,
396 compat_session: CompatSession,
397 human_name: Option<String>,
398 ) -> Result<CompatSession, Self::Error>;
399);