Use the user_session_id on upstream authorisations for filtering instead

of authentications

This makes it one less table to read
This commit is contained in:
Quentin Gliech
2026-01-21 14:41:29 +01:00
parent b912fbc0c9
commit f8e87ec2c4
5 changed files with 22 additions and 41 deletions

View File

@@ -1,3 +1,4 @@
// Copyright 2025, 2026 Element Creations Ltd.
// Copyright 2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
@@ -249,14 +250,14 @@ pub(crate) async fn post(
}
UpstreamOAuthProviderOnBackchannelLogout::LogoutBrowserOnly => {
let filter = BrowserSessionFilter::new()
.authenticated_by_upstream_sessions_only(auth_session_filter)
.linked_to_upstream_sessions_only(auth_session_filter)
.active_only();
let affected = repo.browser_session().finish_bulk(&clock, filter).await?;
tracing::info!("Finished {affected} browser sessions");
}
UpstreamOAuthProviderOnBackchannelLogout::LogoutAll => {
let browser_session_filter = BrowserSessionFilter::new()
.authenticated_by_upstream_sessions_only(auth_session_filter);
let browser_session_filter =
BrowserSessionFilter::new().linked_to_upstream_sessions_only(auth_session_filter);
// We need to loop through all the browser sessions to find all the
// users affected so that we can trigger a device sync job for them

View File

@@ -1,3 +1,4 @@
// Copyright 2025, 2026 Element Creations Ltd.
// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
//
@@ -18,18 +19,6 @@ pub enum UserSessions {
LastActiveIp,
}
#[derive(sea_query::Iden)]
#[expect(dead_code)]
pub enum UserSessionAuthentications {
Table,
UserSessionAuthenticationId,
UserSessionId,
CreatedAt,
UserPasswordId,
#[iden = "upstream_oauth_authorization_session_id"]
UpstreamOAuthAuthorizationSessionId,
}
#[derive(sea_query::Iden)]
pub enum Users {
Table,
@@ -204,6 +193,7 @@ pub enum UpstreamOAuthAuthorizationSessions {
CompletedAt,
ConsumedAt,
UnlinkedAt,
UserSessionId,
}
#[derive(sea_query::Iden)]

View File

@@ -1,3 +1,4 @@
// Copyright 2025, 2026 Element Creations Ltd.
// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
//
@@ -27,7 +28,7 @@ use uuid::Uuid;
use crate::{
DatabaseError, DatabaseInconsistencyError,
filter::StatementExt,
iden::{UpstreamOAuthAuthorizationSessions, UserSessionAuthentications, UserSessions, Users},
iden::{UpstreamOAuthAuthorizationSessions, UserSessions, Users},
pagination::QueryBuilderExt,
tracing::ExecuteExt,
};
@@ -154,26 +155,14 @@ impl crate::filter::Filter for BrowserSessionFilter<'_> {
.add_option(self.last_active_before().map(|last_active_before| {
Expr::col((UserSessions::Table, UserSessions::LastActiveAt)).lt(last_active_before)
}))
.add_option(self.authenticated_by_upstream_sessions().map(|filter| {
// For filtering by upstream sessions, we need to hop over the
// `user_session_authentications` table
let join_expr = Expr::col((
UserSessionAuthentications::Table,
UserSessionAuthentications::UpstreamOAuthAuthorizationSessionId,
))
.eq(Expr::col((
UpstreamOAuthAuthorizationSessions::Table,
UpstreamOAuthAuthorizationSessions::UpstreamOAuthAuthorizationSessionId,
)));
.add_option(self.linked_to_upstream_sessions().map(|filter| {
Expr::col((UserSessions::Table, UserSessions::UserSessionId)).in_subquery(
Query::select()
.expr(Expr::col((
UserSessionAuthentications::Table,
UserSessionAuthentications::UserSessionId,
UpstreamOAuthAuthorizationSessions::Table,
UpstreamOAuthAuthorizationSessions::UserSessionId,
)))
.from(UserSessionAuthentications::Table)
.inner_join(UpstreamOAuthAuthorizationSessions::Table, join_expr)
.from(UpstreamOAuthAuthorizationSessions::Table)
.apply_filter(filter)
.take(),
)

View File

@@ -1,3 +1,4 @@
// Copyright 2025, 2026 Element Creations Ltd.
// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
//
@@ -799,8 +800,8 @@ async fn test_user_session(pool: PgPool) {
// This will match all authorization sessions, which matches exactly that one
// authorization session
let upstream_oauth_session_filter = UpstreamOAuthSessionFilter::new();
let filter = BrowserSessionFilter::new()
.authenticated_by_upstream_sessions_only(upstream_oauth_session_filter);
let filter =
BrowserSessionFilter::new().linked_to_upstream_sessions_only(upstream_oauth_session_filter);
// Now try to look it up
let page = repo

View File

@@ -1,3 +1,4 @@
// Copyright 2025, 2026 Element Creations Ltd.
// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
//
@@ -41,7 +42,7 @@ pub struct BrowserSessionFilter<'a> {
state: Option<BrowserSessionState>,
last_active_before: Option<DateTime<Utc>>,
last_active_after: Option<DateTime<Utc>>,
authenticated_by_upstream_sessions: Option<UpstreamOAuthSessionFilter<'a>>,
linked_to_upstream_sessions: Option<UpstreamOAuthSessionFilter<'a>>,
}
impl<'a> BrowserSessionFilter<'a> {
@@ -114,21 +115,20 @@ impl<'a> BrowserSessionFilter<'a> {
self.state
}
/// Only return browser sessions authenticated by the given upstream OAuth
/// sessions
/// Only return browser sessions linked to the given upstream OAuth sessions
#[must_use]
pub fn authenticated_by_upstream_sessions_only(
pub fn linked_to_upstream_sessions_only(
mut self,
filter: UpstreamOAuthSessionFilter<'a>,
) -> Self {
self.authenticated_by_upstream_sessions = Some(filter);
self.linked_to_upstream_sessions = Some(filter);
self
}
/// Get the upstream OAuth session filter
#[must_use]
pub fn authenticated_by_upstream_sessions(&self) -> Option<UpstreamOAuthSessionFilter<'a>> {
self.authenticated_by_upstream_sessions
pub fn linked_to_upstream_sessions(&self) -> Option<UpstreamOAuthSessionFilter<'a>> {
self.linked_to_upstream_sessions
}
}