1use std::net::IpAddr;
8
9use async_trait::async_trait;
10use chrono::{DateTime, Utc};
11use mas_data_model::{BrowserSession, CompatSession, CompatSsoLogin, Device, User, UserAgent};
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 async fn add(
223 &mut self,
224 rng: &mut (dyn RngCore + Send),
225 clock: &dyn Clock,
226 user: &User,
227 device: Device,
228 browser_session: Option<&BrowserSession>,
229 is_synapse_admin: bool,
230 ) -> Result<CompatSession, Self::Error>;
231
232 async fn finish(
245 &mut self,
246 clock: &dyn Clock,
247 compat_session: CompatSession,
248 ) -> Result<CompatSession, Self::Error>;
249
250 async fn finish_bulk(
263 &mut self,
264 clock: &dyn Clock,
265 filter: CompatSessionFilter<'_>,
266 ) -> Result<usize, Self::Error>;
267
268 async fn list(
281 &mut self,
282 filter: CompatSessionFilter<'_>,
283 pagination: Pagination,
284 ) -> Result<Page<(CompatSession, Option<CompatSsoLogin>)>, Self::Error>;
285
286 async fn count(&mut self, filter: CompatSessionFilter<'_>) -> Result<usize, Self::Error>;
296
297 async fn record_batch_activity(
308 &mut self,
309 activity: Vec<(Ulid, DateTime<Utc>, Option<IpAddr>)>,
310 ) -> Result<(), Self::Error>;
311
312 async fn record_user_agent(
323 &mut self,
324 compat_session: CompatSession,
325 user_agent: UserAgent,
326 ) -> Result<CompatSession, Self::Error>;
327}
328
329repository_impl!(CompatSessionRepository:
330 async fn lookup(&mut self, id: Ulid) -> Result<Option<CompatSession>, Self::Error>;
331
332 async fn add(
333 &mut self,
334 rng: &mut (dyn RngCore + Send),
335 clock: &dyn Clock,
336 user: &User,
337 device: Device,
338 browser_session: Option<&BrowserSession>,
339 is_synapse_admin: bool,
340 ) -> Result<CompatSession, Self::Error>;
341
342 async fn finish(
343 &mut self,
344 clock: &dyn Clock,
345 compat_session: CompatSession,
346 ) -> Result<CompatSession, Self::Error>;
347
348 async fn finish_bulk(
349 &mut self,
350 clock: &dyn Clock,
351 filter: CompatSessionFilter<'_>,
352 ) -> Result<usize, Self::Error>;
353
354 async fn list(
355 &mut self,
356 filter: CompatSessionFilter<'_>,
357 pagination: Pagination,
358 ) -> Result<Page<(CompatSession, Option<CompatSsoLogin>)>, Self::Error>;
359
360 async fn count(&mut self, filter: CompatSessionFilter<'_>) -> Result<usize, Self::Error>;
361
362 async fn record_batch_activity(
363 &mut self,
364 activity: Vec<(Ulid, DateTime<Utc>, Option<IpAddr>)>,
365 ) -> Result<(), Self::Error>;
366
367 async fn record_user_agent(
368 &mut self,
369 compat_session: CompatSession,
370 user_agent: UserAgent,
371 ) -> Result<CompatSession, Self::Error>;
372);