1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
// Copyright 2024 New Vector Ltd.
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//! Repositories to interact with all kinds of sessions
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use mas_data_model::{BrowserSession, CompatSession, Device, Session, User};
use crate::{repository_impl, Page, Pagination};
/// The state of a session
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AppSessionState {
/// The session is active
Active,
/// The session is finished
Finished,
}
impl AppSessionState {
/// Returns [`true`] if we're looking for active sessions
#[must_use]
pub fn is_active(self) -> bool {
matches!(self, Self::Active)
}
/// Returns [`true`] if we're looking for finished sessions
#[must_use]
pub fn is_finished(self) -> bool {
matches!(self, Self::Finished)
}
}
/// An [`AppSession`] is either a [`CompatSession`] or an OAuth 2.0 [`Session`]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AppSession {
/// A compatibility layer session
Compat(Box<CompatSession>),
/// An OAuth 2.0 session
OAuth2(Box<Session>),
}
/// Filtering parameters for application sessions
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub struct AppSessionFilter<'a> {
user: Option<&'a User>,
browser_session: Option<&'a BrowserSession>,
state: Option<AppSessionState>,
device_id: Option<&'a Device>,
last_active_before: Option<DateTime<Utc>>,
last_active_after: Option<DateTime<Utc>>,
}
impl<'a> AppSessionFilter<'a> {
/// Create a new [`AppSessionFilter`] with default values
#[must_use]
pub fn new() -> Self {
Self::default()
}
/// Set the user who owns the sessions
#[must_use]
pub fn for_user(mut self, user: &'a User) -> Self {
self.user = Some(user);
self
}
/// Get the user filter
#[must_use]
pub fn user(&self) -> Option<&'a User> {
self.user
}
/// Set the browser session filter
#[must_use]
pub fn for_browser_session(mut self, browser_session: &'a BrowserSession) -> Self {
self.browser_session = Some(browser_session);
self
}
/// Get the browser session filter
#[must_use]
pub fn browser_session(&self) -> Option<&'a BrowserSession> {
self.browser_session
}
/// Set the device ID filter
#[must_use]
pub fn for_device(mut self, device_id: &'a Device) -> Self {
self.device_id = Some(device_id);
self
}
/// Get the device ID filter
#[must_use]
pub fn device(&self) -> Option<&'a Device> {
self.device_id
}
/// Only return sessions with a last active time before the given time
#[must_use]
pub fn with_last_active_before(mut self, last_active_before: DateTime<Utc>) -> Self {
self.last_active_before = Some(last_active_before);
self
}
/// Only return sessions with a last active time after the given time
#[must_use]
pub fn with_last_active_after(mut self, last_active_after: DateTime<Utc>) -> Self {
self.last_active_after = Some(last_active_after);
self
}
/// Get the last active before filter
///
/// Returns [`None`] if no client filter was set
#[must_use]
pub fn last_active_before(&self) -> Option<DateTime<Utc>> {
self.last_active_before
}
/// Get the last active after filter
///
/// Returns [`None`] if no client filter was set
#[must_use]
pub fn last_active_after(&self) -> Option<DateTime<Utc>> {
self.last_active_after
}
/// Only return active compatibility sessions
#[must_use]
pub fn active_only(mut self) -> Self {
self.state = Some(AppSessionState::Active);
self
}
/// Only return finished compatibility sessions
#[must_use]
pub fn finished_only(mut self) -> Self {
self.state = Some(AppSessionState::Finished);
self
}
/// Get the state filter
#[must_use]
pub fn state(&self) -> Option<AppSessionState> {
self.state
}
}
/// A [`AppSessionRepository`] helps interacting with both [`CompatSession`] and
/// OAuth 2.0 [`Session`] at the same time saved in the storage backend
#[async_trait]
pub trait AppSessionRepository: Send + Sync {
/// The error type returned by the repository
type Error;
/// List [`AppSession`] with the given filter and pagination
///
/// Returns a page of [`AppSession`] matching the given filter
///
/// # Parameters
///
/// * `filter`: The filter to apply
/// * `pagination`: The pagination parameters
///
/// # Errors
///
/// Returns [`Self::Error`] if the underlying repository fails
async fn list(
&mut self,
filter: AppSessionFilter<'_>,
pagination: Pagination,
) -> Result<Page<AppSession>, Self::Error>;
/// Count the number of [`AppSession`] with the given filter
///
/// # Parameters
///
/// * `filter`: The filter to apply
///
/// # Errors
///
/// Returns [`Self::Error`] if the underlying repository fails
async fn count(&mut self, filter: AppSessionFilter<'_>) -> Result<usize, Self::Error>;
}
repository_impl!(AppSessionRepository:
async fn list(
&mut self,
filter: AppSessionFilter<'_>,
pagination: Pagination,
) -> Result<Page<AppSession>, Self::Error>;
async fn count(&mut self, filter: AppSessionFilter<'_>) -> Result<usize, Self::Error>;
);