diff --git a/crates/storage-pg/src/compat/session.rs b/crates/storage-pg/src/compat/session.rs index e372585c..1cdcd137 100644 --- a/crates/storage-pg/src/compat/session.rs +++ b/crates/storage-pg/src/compat/session.rs @@ -210,6 +210,10 @@ impl Filter for CompatSessionFilter<'_> { .add_option(self.user().map(|user| { Expr::col((CompatSessions::Table, CompatSessions::UserId)).eq(Uuid::from(user.id)) })) + .add_option(self.browser_session().map(|browser_session| { + Expr::col((CompatSessions::Table, CompatSessions::UserSessionId)) + .eq(Uuid::from(browser_session.id)) + })) .add_option(self.state().map(|state| { if state.is_active() { Expr::col((CompatSessions::Table, CompatSessions::FinishedAt)).is_null() diff --git a/crates/storage-pg/src/oauth2/session.rs b/crates/storage-pg/src/oauth2/session.rs index 42ae269e..3992615a 100644 --- a/crates/storage-pg/src/oauth2/session.rs +++ b/crates/storage-pg/src/oauth2/session.rs @@ -23,7 +23,7 @@ use mas_storage::{ }; use oauth2_types::scope::{Scope, ScopeToken}; use rand::RngCore; -use sea_query::{enum_def, extension::postgres::PgExpr, Expr, PostgresQueryBuilder, Query}; +use sea_query::{enum_def, extension::postgres::PgExpr, Expr, PgFunc, PostgresQueryBuilder, Query}; use sea_query_binder::SqlxBinder; use sqlx::PgConnection; use ulid::Ulid; @@ -112,6 +112,16 @@ impl Filter for OAuth2SessionFilter<'_> { Expr::col((OAuth2Sessions::Table, OAuth2Sessions::OAuth2ClientId)) .eq(Uuid::from(client.id)) })) + .add_option(self.device().map(|device| { + Expr::val(device.to_scope_token().to_string()).eq(PgFunc::any(Expr::col(( + OAuth2Sessions::Table, + OAuth2Sessions::ScopeList, + )))) + })) + .add_option(self.browser_session().map(|browser_session| { + Expr::col((OAuth2Sessions::Table, OAuth2Sessions::UserSessionId)) + .eq(Uuid::from(browser_session.id)) + })) .add_option(self.state().map(|state| { if state.is_active() { Expr::col((OAuth2Sessions::Table, OAuth2Sessions::FinishedAt)).is_null() diff --git a/crates/storage/src/app_session.rs b/crates/storage/src/app_session.rs index e9f4d04a..06caffbf 100644 --- a/crates/storage/src/app_session.rs +++ b/crates/storage/src/app_session.rs @@ -77,7 +77,7 @@ impl<'a> AppSessionFilter<'a> { /// Get the user filter #[must_use] - pub fn user(&self) -> Option<&User> { + pub fn user(&self) -> Option<&'a User> { self.user } @@ -90,7 +90,7 @@ impl<'a> AppSessionFilter<'a> { /// Get the browser session filter #[must_use] - pub fn browser_session(&self) -> Option<&BrowserSession> { + pub fn browser_session(&self) -> Option<&'a BrowserSession> { self.browser_session } diff --git a/crates/storage/src/compat/session.rs b/crates/storage/src/compat/session.rs index 227399ac..8ee076f4 100644 --- a/crates/storage/src/compat/session.rs +++ b/crates/storage/src/compat/session.rs @@ -66,6 +66,7 @@ impl CompatSessionType { #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub struct CompatSessionFilter<'a> { user: Option<&'a User>, + browser_session: Option<&'a BrowserSession>, state: Option, auth_type: Option, device: Option<&'a Device>, @@ -87,7 +88,7 @@ impl<'a> CompatSessionFilter<'a> { /// Get the user filter #[must_use] - pub fn user(&self) -> Option<&User> { + pub fn user(&self) -> Option<&'a User> { self.user } @@ -100,10 +101,23 @@ impl<'a> CompatSessionFilter<'a> { /// Get the device filter #[must_use] - pub fn device(&self) -> Option<&Device> { + pub fn device(&self) -> Option<&'a Device> { self.device } + /// 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 + } + /// Only return active compatibility sessions #[must_use] pub fn active_only(mut self) -> Self { diff --git a/crates/storage/src/oauth2/session.rs b/crates/storage/src/oauth2/session.rs index 46e96938..1ecf5eb1 100644 --- a/crates/storage/src/oauth2/session.rs +++ b/crates/storage/src/oauth2/session.rs @@ -16,7 +16,7 @@ use std::net::IpAddr; use async_trait::async_trait; use chrono::{DateTime, Utc}; -use mas_data_model::{BrowserSession, Client, Session, User, UserAgent}; +use mas_data_model::{BrowserSession, Client, Device, Session, User, UserAgent}; use oauth2_types::scope::Scope; use rand_core::RngCore; use ulid::Ulid; @@ -43,6 +43,8 @@ impl OAuth2SessionState { #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub struct OAuth2SessionFilter<'a> { user: Option<&'a User>, + browser_session: Option<&'a BrowserSession>, + device: Option<&'a Device>, client: Option<&'a Client>, state: Option, scope: Option<&'a Scope>, @@ -66,10 +68,25 @@ impl<'a> OAuth2SessionFilter<'a> { /// /// Returns [`None`] if no user filter was set #[must_use] - pub fn user(&self) -> Option<&User> { + pub fn user(&self) -> Option<&'a User> { self.user } + /// List sessions started by a specific browser session + #[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 + /// + /// Returns [`None`] if no browser session filter was set + #[must_use] + pub fn browser_session(&self) -> Option<&'a BrowserSession> { + self.browser_session + } + /// List sessions for a specific client #[must_use] pub fn for_client(mut self, client: &'a Client) -> Self { @@ -81,7 +98,7 @@ impl<'a> OAuth2SessionFilter<'a> { /// /// Returns [`None`] if no client filter was set #[must_use] - pub fn client(&self) -> Option<&Client> { + pub fn client(&self) -> Option<&'a Client> { self.client } @@ -118,9 +135,24 @@ impl<'a> OAuth2SessionFilter<'a> { /// /// Returns [`None`] if no scope filter was set #[must_use] - pub fn scope(&self) -> Option<&Scope> { + pub fn scope(&self) -> Option<&'a Scope> { self.scope } + + /// Only return sessions that have the given device in their scope + #[must_use] + pub fn for_device(mut self, device: &'a Device) -> Self { + self.device = Some(device); + self + } + + /// Get the device filter + /// + /// Returns [`None`] if no device filter was set + #[must_use] + pub fn device(&self) -> Option<&'a Device> { + self.device + } } /// An [`OAuth2SessionRepository`] helps interacting with [`Session`]