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, user::BrowserSessionFilter};
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 browser_session_filter: Option<BrowserSessionFilter<'a>>,
63 state: Option<CompatSessionState>,
64 auth_type: Option<CompatSessionType>,
65 device: Option<&'a Device>,
66 last_active_before: Option<DateTime<Utc>>,
67 last_active_after: Option<DateTime<Utc>>,
68}
69
70impl<'a> CompatSessionFilter<'a> {
71 #[must_use]
73 pub fn new() -> Self {
74 Self::default()
75 }
76
77 #[must_use]
79 pub fn for_user(mut self, user: &'a User) -> Self {
80 self.user = Some(user);
81 self
82 }
83
84 #[must_use]
86 pub fn user(&self) -> Option<&'a User> {
87 self.user
88 }
89
90 #[must_use]
92 pub fn for_device(mut self, device: &'a Device) -> Self {
93 self.device = Some(device);
94 self
95 }
96
97 #[must_use]
99 pub fn device(&self) -> Option<&'a Device> {
100 self.device
101 }
102
103 #[must_use]
105 pub fn for_browser_session(mut self, browser_session: &'a BrowserSession) -> Self {
106 self.browser_session = Some(browser_session);
107 self
108 }
109
110 #[must_use]
112 pub fn for_browser_sessions(
113 mut self,
114 browser_session_filter: BrowserSessionFilter<'a>,
115 ) -> Self {
116 self.browser_session_filter = Some(browser_session_filter);
117 self
118 }
119
120 #[must_use]
122 pub fn browser_session(&self) -> Option<&'a BrowserSession> {
123 self.browser_session
124 }
125
126 #[must_use]
128 pub fn browser_session_filter(&self) -> Option<BrowserSessionFilter<'a>> {
129 self.browser_session_filter
130 }
131
132 #[must_use]
134 pub fn with_last_active_before(mut self, last_active_before: DateTime<Utc>) -> Self {
135 self.last_active_before = Some(last_active_before);
136 self
137 }
138
139 #[must_use]
141 pub fn with_last_active_after(mut self, last_active_after: DateTime<Utc>) -> Self {
142 self.last_active_after = Some(last_active_after);
143 self
144 }
145
146 #[must_use]
150 pub fn last_active_before(&self) -> Option<DateTime<Utc>> {
151 self.last_active_before
152 }
153
154 #[must_use]
158 pub fn last_active_after(&self) -> Option<DateTime<Utc>> {
159 self.last_active_after
160 }
161
162 #[must_use]
164 pub fn active_only(mut self) -> Self {
165 self.state = Some(CompatSessionState::Active);
166 self
167 }
168
169 #[must_use]
171 pub fn finished_only(mut self) -> Self {
172 self.state = Some(CompatSessionState::Finished);
173 self
174 }
175
176 #[must_use]
178 pub fn state(&self) -> Option<CompatSessionState> {
179 self.state
180 }
181
182 #[must_use]
184 pub fn sso_login_only(mut self) -> Self {
185 self.auth_type = Some(CompatSessionType::SsoLogin);
186 self
187 }
188
189 #[must_use]
191 pub fn unknown_only(mut self) -> Self {
192 self.auth_type = Some(CompatSessionType::Unknown);
193 self
194 }
195
196 #[must_use]
198 pub fn auth_type(&self) -> Option<CompatSessionType> {
199 self.auth_type
200 }
201}
202
203#[async_trait]
206pub trait CompatSessionRepository: Send + Sync {
207 type Error;
209
210 async fn lookup(&mut self, id: Ulid) -> Result<Option<CompatSession>, Self::Error>;
222
223 #[expect(clippy::too_many_arguments)]
242 async fn add(
243 &mut self,
244 rng: &mut (dyn RngCore + Send),
245 clock: &dyn Clock,
246 user: &User,
247 device: Device,
248 browser_session: Option<&BrowserSession>,
249 is_synapse_admin: bool,
250 human_name: Option<String>,
251 ) -> Result<CompatSession, Self::Error>;
252
253 async fn finish(
266 &mut self,
267 clock: &dyn Clock,
268 compat_session: CompatSession,
269 ) -> Result<CompatSession, Self::Error>;
270
271 async fn finish_bulk(
284 &mut self,
285 clock: &dyn Clock,
286 filter: CompatSessionFilter<'_>,
287 ) -> Result<usize, Self::Error>;
288
289 async fn list(
302 &mut self,
303 filter: CompatSessionFilter<'_>,
304 pagination: Pagination,
305 ) -> Result<Page<(CompatSession, Option<CompatSsoLogin>)>, Self::Error>;
306
307 async fn count(&mut self, filter: CompatSessionFilter<'_>) -> Result<usize, Self::Error>;
317
318 async fn record_batch_activity(
329 &mut self,
330 activity: Vec<(Ulid, DateTime<Utc>, Option<IpAddr>)>,
331 ) -> Result<(), Self::Error>;
332
333 async fn record_user_agent(
344 &mut self,
345 compat_session: CompatSession,
346 user_agent: String,
347 ) -> Result<CompatSession, Self::Error>;
348
349 async fn set_human_name(
360 &mut self,
361 compat_session: CompatSession,
362 human_name: Option<String>,
363 ) -> Result<CompatSession, Self::Error>;
364}
365
366repository_impl!(CompatSessionRepository:
367 async fn lookup(&mut self, id: Ulid) -> Result<Option<CompatSession>, Self::Error>;
368
369 async fn add(
370 &mut self,
371 rng: &mut (dyn RngCore + Send),
372 clock: &dyn Clock,
373 user: &User,
374 device: Device,
375 browser_session: Option<&BrowserSession>,
376 is_synapse_admin: bool,
377 human_name: Option<String>,
378 ) -> Result<CompatSession, Self::Error>;
379
380 async fn finish(
381 &mut self,
382 clock: &dyn Clock,
383 compat_session: CompatSession,
384 ) -> Result<CompatSession, Self::Error>;
385
386 async fn finish_bulk(
387 &mut self,
388 clock: &dyn Clock,
389 filter: CompatSessionFilter<'_>,
390 ) -> Result<usize, Self::Error>;
391
392 async fn list(
393 &mut self,
394 filter: CompatSessionFilter<'_>,
395 pagination: Pagination,
396 ) -> Result<Page<(CompatSession, Option<CompatSsoLogin>)>, Self::Error>;
397
398 async fn count(&mut self, filter: CompatSessionFilter<'_>) -> Result<usize, Self::Error>;
399
400 async fn record_batch_activity(
401 &mut self,
402 activity: Vec<(Ulid, DateTime<Utc>, Option<IpAddr>)>,
403 ) -> Result<(), Self::Error>;
404
405 async fn record_user_agent(
406 &mut self,
407 compat_session: CompatSession,
408 user_agent: String,
409 ) -> Result<CompatSession, Self::Error>;
410
411 async fn set_human_name(
412 &mut self,
413 compat_session: CompatSession,
414 human_name: Option<String>,
415 ) -> Result<CompatSession, Self::Error>;
416);