From f1c351829b1fd9738b4e8c31e914215cfa8efe21 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Tue, 1 Apr 2025 16:11:54 +0200 Subject: [PATCH 01/23] Clear the session cookie on logout from the GraphQL API --- crates/handlers/src/graphql/mod.rs | 29 ++++++++++++------ .../src/graphql/mutations/browser_session.rs | 9 ++++++ crates/handlers/src/graphql/state.rs | 30 +++++++++++++++++++ 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/crates/handlers/src/graphql/mod.rs b/crates/handlers/src/graphql/mod.rs index abf8d7c4b..4c854ed21 100644 --- a/crates/handlers/src/graphql/mod.rs +++ b/crates/handlers/src/graphql/mod.rs @@ -38,6 +38,7 @@ use opentelemetry_semantic_conventions::trace::{GRAPHQL_DOCUMENT, GRAPHQL_OPERAT use rand::{SeedableRng, thread_rng}; use rand_chacha::ChaChaRng; use sqlx::PgPool; +use state::has_session_ended; use tracing::{Instrument, info_span}; use ulid::Ulid; @@ -237,7 +238,7 @@ async fn get_requester( clock: &impl Clock, activity_tracker: &BoundActivityTracker, mut repo: BoxRepository, - session_info: SessionInfo, + session_info: &SessionInfo, user_agent: Option, token: Option<&str>, ) -> Result { @@ -328,13 +329,13 @@ pub async fn post( .as_ref() .map(|TypedHeader(Authorization(bearer))| bearer.token()); let user_agent = user_agent.map(|TypedHeader(h)| h.to_string()); - let (session_info, _cookie_jar) = cookie_jar.session_info(); + let (session_info, mut cookie_jar) = cookie_jar.session_info(); let requester = get_requester( undocumented_oauth2_access, &clock, &activity_tracker, repo, - session_info, + &session_info, user_agent, token, ) @@ -352,7 +353,12 @@ pub async fn post( .data(requester); // XXX: this should probably return another error response? let span = span_for_graphql_request(&request); - let response = schema.execute(request).instrument(span).await; + let mut response = schema.execute(request).instrument(span).await; + + if has_session_ended(&mut response) { + let session_info = session_info.mark_session_ended(); + cookie_jar = cookie_jar.update_session_info(&session_info); + } let cache_control = response .cache_control @@ -362,7 +368,7 @@ pub async fn post( let headers = response.http_headers.clone(); - Ok((headers, cache_control, Json(response))) + Ok((headers, cache_control, cookie_jar, Json(response))) } pub async fn get( @@ -382,13 +388,13 @@ pub async fn get( .as_ref() .map(|TypedHeader(Authorization(bearer))| bearer.token()); let user_agent = user_agent.map(|TypedHeader(h)| h.to_string()); - let (session_info, _cookie_jar) = cookie_jar.session_info(); + let (session_info, mut cookie_jar) = cookie_jar.session_info(); let requester = get_requester( undocumented_oauth2_access, &clock, &activity_tracker, repo, - session_info, + &session_info, user_agent, token, ) @@ -398,7 +404,12 @@ pub async fn get( async_graphql::http::parse_query_string(&query.unwrap_or_default())?.data(requester); let span = span_for_graphql_request(&request); - let response = schema.execute(request).instrument(span).await; + let mut response = schema.execute(request).instrument(span).await; + + if has_session_ended(&mut response) { + let session_info = session_info.mark_session_ended(); + cookie_jar = cookie_jar.update_session_info(&session_info); + } let cache_control = response .cache_control @@ -408,7 +419,7 @@ pub async fn get( let headers = response.http_headers.clone(); - Ok((headers, cache_control, Json(response))) + Ok((headers, cache_control, cookie_jar, Json(response))) } pub async fn playground() -> impl IntoResponse { diff --git a/crates/handlers/src/graphql/mutations/browser_session.rs b/crates/handlers/src/graphql/mutations/browser_session.rs index 8e43e5699..688e17dcb 100644 --- a/crates/handlers/src/graphql/mutations/browser_session.rs +++ b/crates/handlers/src/graphql/mutations/browser_session.rs @@ -88,6 +88,15 @@ impl BrowserSessionMutations { repo.save().await?; + // If we are ending the *current* session, we need to clear the session cookie + // as well + if requester + .browser_session() + .is_some_and(|s| s.id == session.id) + { + ctx.mark_session_ended(); + } + Ok(EndBrowserSessionPayload::Ended(Box::new(session))) } } diff --git a/crates/handlers/src/graphql/state.rs b/crates/handlers/src/graphql/state.rs index 83d50b86a..737f43340 100644 --- a/crates/handlers/src/graphql/state.rs +++ b/crates/handlers/src/graphql/state.rs @@ -4,6 +4,7 @@ // SPDX-License-Identifier: AGPL-3.0-only // Please see LICENSE in the repository root for full details. +use async_graphql::{Response, ServerError}; use mas_data_model::SiteConfig; use mas_matrix::HomeserverConnection; use mas_policy::Policy; @@ -12,6 +13,8 @@ use mas_storage::{BoxClock, BoxRepository, BoxRng, RepositoryError}; use crate::{Limiter, graphql::Requester, passwords::PasswordManager}; +const CLEAR_SESSION_SENTINEL: &str = "__CLEAR_SESSION__"; + #[async_trait::async_trait] pub trait State { async fn repository(&self) -> Result; @@ -30,6 +33,8 @@ pub type BoxState = Box; pub trait ContextExt { fn state(&self) -> &BoxState; + fn mark_session_ended(&self); + fn requester(&self) -> &Requester; } @@ -38,7 +43,32 @@ impl ContextExt for async_graphql::Context<'_> { self.data_unchecked() } + fn mark_session_ended(&self) { + // Add a sentinel to the error context, so that we can know that we need to + // clear the session + // XXX: this is a bit of a hack, but the only sane way to get infos from within + // a mutation up to the HTTP handler + self.add_error(ServerError::new(CLEAR_SESSION_SENTINEL, None)); + } + fn requester(&self) -> &Requester { self.data_unchecked() } } + +/// Returns true if the response contains a sentinel error indicating that the +/// current cookie session has ended, and the session cookie should be cleared. +/// +/// Also removes the sentinel error from the response. +pub fn has_session_ended(response: &mut Response) -> bool { + let errors = std::mem::take(&mut response.errors); + let mut must_clear_session = false; + for error in errors { + if error.message == CLEAR_SESSION_SENTINEL { + must_clear_session = true; + } else { + response.errors.push(error); + } + } + must_clear_session +} From 237a7a3ee7765d922acd33e8a09966ec3b0d3b2e Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Thu, 10 Apr 2025 18:27:22 +0200 Subject: [PATCH 02/23] Create missing indexes for all the foreign keys in the database. --- ...10000000_idx_compat_access_tokens_session_fk.sql | 9 +++++++++ ...0000001_idx_compat_refresh_tokens_session_fk.sql | 9 +++++++++ ...02_idx_compat_refresh_tokens_access_token_fk.sql | 9 +++++++++ .../20250410000003_idx_compat_sessions_user_fk.sql | 13 +++++++++++++ ...10000004_idx_compat_sessions_user_session_fk.sql | 9 +++++++++ ..._drop_compat_sessions_user_id_last_active_at.sql | 8 ++++++++ ...50410000006_idx_compat_sso_logins_session_fk.sql | 9 +++++++++ ...10000007_idx_oauth2_access_tokens_session_fk.sql | 9 +++++++++ ...8_idx_oauth2_authorization_grants_session_fk.sql | 9 +++++++++ ...09_idx_oauth2_authorization_grants_client_fk.sql | 9 +++++++++ ...20250410000010_idx_oauth2_consents_client_fk.sql | 9 +++++++++ .../20250410000011_idx_oauth2_consents_user_fk.sql | 9 +++++++++ ...0012_idx_oauth2_device_code_grants_client_fk.sql | 9 +++++++++ ...013_idx_oauth2_device_code_grants_session_fk.sql | 9 +++++++++ ...dx_oauth2_device_code_grants_user_session_fk.sql | 9 +++++++++ ...0000015_idx_oauth2_refresh_tokens_session_fk.sql | 9 +++++++++ ...16_idx_oauth2_refresh_tokens_access_token_fk.sql | 9 +++++++++ ..._oauth2_refresh_tokens_next_refresh_token_fk.sql | 9 +++++++++ ...10000018_idx_oauth2_sessions_user_session_fk.sql | 9 +++++++++ ...20250410000019_idx_oauth2_sessions_client_fk.sql | 9 +++++++++ .../20250410000020_idx_oauth2_sessions_user_fk.sql | 13 +++++++++++++ ..._drop_oauth2_sessions_user_id_last_active_at.sql | 8 ++++++++ .../20250410000022_idx_queue_jobs_started_by_fk.sql | 9 +++++++++ ...0250410000023_idx_queue_jobs_next_attempt_fk.sql | 9 +++++++++ ...250410000024_idx_queue_jobs_schedule_name_fk.sql | 9 +++++++++ ...eam_oauth_authorization_sessions_provider_fk.sql | 9 +++++++++ ...pstream_oauth_authorization_sessions_link_fk.sql | 9 +++++++++ ...0000027_idx_upstream_oauth_links_provider_fk.sql | 9 +++++++++ ...50410000028_idx_upstream_oauth_links_user_fk.sql | 9 +++++++++ ...email_authentication_codes_authentication_fk.sql | 9 +++++++++ ...x_user_email_authentications_user_session_fk.sql | 9 +++++++++ ...r_email_authentications_user_registration_fk.sql | 9 +++++++++ .../20250410000032_idx_user_emails_user_fk.sql | 9 +++++++++ .../20250410000033_idx_user_emails_email_idx.sql | 10 ++++++++++ .../20250410000034_idx_user_passwords_user_fk.sql | 9 +++++++++ ...0000035_idx_user_recovery_tickets_session_fk.sql | 9 +++++++++ ...0036_idx_user_recovery_tickets_user_email_fk.sql | 9 +++++++++ ...x_user_registrations_email_authentication_fk.sql | 9 +++++++++ ...user_session_authentications_user_session_fk.sql | 9 +++++++++ ...ser_session_authentications_user_password_fk.sql | 9 +++++++++ ...on_authentications_upstream_oauth_session_fk.sql | 9 +++++++++ .../20250410000041_idx_user_sessions_user_fk.sql | 13 +++++++++++++ ...42_drop_user_sessions_user_id_last_active_at.sql | 8 ++++++++ .../20250410000043_idx_user_terms_user_fk.sql | 9 +++++++++ .../20250410000044_idx_users_primary_email_fk.sql | 11 +++++++++++ ...0000045_idx_user_recovery_tickets_ticket_idx.sql | 10 ++++++++++ 46 files changed, 427 insertions(+) create mode 100644 crates/storage-pg/migrations/20250410000000_idx_compat_access_tokens_session_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000001_idx_compat_refresh_tokens_session_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000002_idx_compat_refresh_tokens_access_token_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000003_idx_compat_sessions_user_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000004_idx_compat_sessions_user_session_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000005_drop_compat_sessions_user_id_last_active_at.sql create mode 100644 crates/storage-pg/migrations/20250410000006_idx_compat_sso_logins_session_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000007_idx_oauth2_access_tokens_session_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000008_idx_oauth2_authorization_grants_session_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000009_idx_oauth2_authorization_grants_client_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000010_idx_oauth2_consents_client_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000011_idx_oauth2_consents_user_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000012_idx_oauth2_device_code_grants_client_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000013_idx_oauth2_device_code_grants_session_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000014_idx_oauth2_device_code_grants_user_session_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000015_idx_oauth2_refresh_tokens_session_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000016_idx_oauth2_refresh_tokens_access_token_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000017_idx_oauth2_refresh_tokens_next_refresh_token_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000018_idx_oauth2_sessions_user_session_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000019_idx_oauth2_sessions_client_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000020_idx_oauth2_sessions_user_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000021_drop_oauth2_sessions_user_id_last_active_at.sql create mode 100644 crates/storage-pg/migrations/20250410000022_idx_queue_jobs_started_by_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000023_idx_queue_jobs_next_attempt_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000024_idx_queue_jobs_schedule_name_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000025_idx_upstream_oauth_authorization_sessions_provider_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000026_idx_upstream_oauth_authorization_sessions_link_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000027_idx_upstream_oauth_links_provider_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000028_idx_upstream_oauth_links_user_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000029_idx_user_email_authentication_codes_authentication_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000030_idx_user_email_authentications_user_session_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000031_idx_user_email_authentications_user_registration_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000032_idx_user_emails_user_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000033_idx_user_emails_email_idx.sql create mode 100644 crates/storage-pg/migrations/20250410000034_idx_user_passwords_user_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000035_idx_user_recovery_tickets_session_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000036_idx_user_recovery_tickets_user_email_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000037_idx_user_registrations_email_authentication_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000038_idx_user_session_authentications_user_session_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000039_idx_user_session_authentications_user_password_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000040_idx_user_session_authentications_upstream_oauth_session_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000041_idx_user_sessions_user_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000042_drop_user_sessions_user_id_last_active_at.sql create mode 100644 crates/storage-pg/migrations/20250410000043_idx_user_terms_user_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000044_idx_users_primary_email_fk.sql create mode 100644 crates/storage-pg/migrations/20250410000045_idx_user_recovery_tickets_ticket_idx.sql diff --git a/crates/storage-pg/migrations/20250410000000_idx_compat_access_tokens_session_fk.sql b/crates/storage-pg/migrations/20250410000000_idx_compat_access_tokens_session_fk.sql new file mode 100644 index 000000000..777118233 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000000_idx_compat_access_tokens_session_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + compat_access_tokens_session_fk + ON compat_access_tokens (compat_session_id); diff --git a/crates/storage-pg/migrations/20250410000001_idx_compat_refresh_tokens_session_fk.sql b/crates/storage-pg/migrations/20250410000001_idx_compat_refresh_tokens_session_fk.sql new file mode 100644 index 000000000..d0e99ad9b --- /dev/null +++ b/crates/storage-pg/migrations/20250410000001_idx_compat_refresh_tokens_session_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + compat_refresh_tokens_session_fk + ON compat_refresh_tokens (compat_session_id); diff --git a/crates/storage-pg/migrations/20250410000002_idx_compat_refresh_tokens_access_token_fk.sql b/crates/storage-pg/migrations/20250410000002_idx_compat_refresh_tokens_access_token_fk.sql new file mode 100644 index 000000000..133b00072 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000002_idx_compat_refresh_tokens_access_token_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + compat_refresh_tokens_access_token_fk + ON compat_refresh_tokens (compat_access_token_id); diff --git a/crates/storage-pg/migrations/20250410000003_idx_compat_sessions_user_fk.sql b/crates/storage-pg/migrations/20250410000003_idx_compat_sessions_user_fk.sql new file mode 100644 index 000000000..3234fc8ca --- /dev/null +++ b/crates/storage-pg/migrations/20250410000003_idx_compat_sessions_user_fk.sql @@ -0,0 +1,13 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +-- Including the `last_active_at` column lets us effeciently filter in-memory +-- for those sessions without fetching the rows, and without including it in the +-- index btree +CREATE INDEX CONCURRENTLY + compat_sessions_user_fk + ON compat_sessions (user_id) + INCLUDE (last_active_at); diff --git a/crates/storage-pg/migrations/20250410000004_idx_compat_sessions_user_session_fk.sql b/crates/storage-pg/migrations/20250410000004_idx_compat_sessions_user_session_fk.sql new file mode 100644 index 000000000..4b35f9960 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000004_idx_compat_sessions_user_session_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + compat_sessions_user_session_fk + ON compat_sessions (user_session_id); diff --git a/crates/storage-pg/migrations/20250410000005_drop_compat_sessions_user_id_last_active_at.sql b/crates/storage-pg/migrations/20250410000005_drop_compat_sessions_user_id_last_active_at.sql new file mode 100644 index 000000000..4c37289ff --- /dev/null +++ b/crates/storage-pg/migrations/20250410000005_drop_compat_sessions_user_id_last_active_at.sql @@ -0,0 +1,8 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +-- Redundant with the `compat_sessions_user_fk` +DROP INDEX IF EXISTS compat_sessions_user_id_last_active_at; diff --git a/crates/storage-pg/migrations/20250410000006_idx_compat_sso_logins_session_fk.sql b/crates/storage-pg/migrations/20250410000006_idx_compat_sso_logins_session_fk.sql new file mode 100644 index 000000000..e88224e66 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000006_idx_compat_sso_logins_session_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + compat_sso_logins_session_fk + ON compat_sso_logins (compat_session_id); diff --git a/crates/storage-pg/migrations/20250410000007_idx_oauth2_access_tokens_session_fk.sql b/crates/storage-pg/migrations/20250410000007_idx_oauth2_access_tokens_session_fk.sql new file mode 100644 index 000000000..7495af2a3 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000007_idx_oauth2_access_tokens_session_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + oauth2_access_tokens_session_fk + ON oauth2_access_tokens (oauth2_session_id); diff --git a/crates/storage-pg/migrations/20250410000008_idx_oauth2_authorization_grants_session_fk.sql b/crates/storage-pg/migrations/20250410000008_idx_oauth2_authorization_grants_session_fk.sql new file mode 100644 index 000000000..dac57bf91 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000008_idx_oauth2_authorization_grants_session_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + oauth2_authorization_grants_session_fk + ON oauth2_authorization_grants (oauth2_session_id); diff --git a/crates/storage-pg/migrations/20250410000009_idx_oauth2_authorization_grants_client_fk.sql b/crates/storage-pg/migrations/20250410000009_idx_oauth2_authorization_grants_client_fk.sql new file mode 100644 index 000000000..fd5d30344 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000009_idx_oauth2_authorization_grants_client_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + oauth2_authorization_grants_client_fk + ON oauth2_authorization_grants (oauth2_client_id); diff --git a/crates/storage-pg/migrations/20250410000010_idx_oauth2_consents_client_fk.sql b/crates/storage-pg/migrations/20250410000010_idx_oauth2_consents_client_fk.sql new file mode 100644 index 000000000..ae8e0b5dd --- /dev/null +++ b/crates/storage-pg/migrations/20250410000010_idx_oauth2_consents_client_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + oauth2_consents_client_fk + ON oauth2_consents (oauth2_client_id); diff --git a/crates/storage-pg/migrations/20250410000011_idx_oauth2_consents_user_fk.sql b/crates/storage-pg/migrations/20250410000011_idx_oauth2_consents_user_fk.sql new file mode 100644 index 000000000..c9226b7d6 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000011_idx_oauth2_consents_user_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + oauth2_consents_user_fk + ON oauth2_consents (user_id); diff --git a/crates/storage-pg/migrations/20250410000012_idx_oauth2_device_code_grants_client_fk.sql b/crates/storage-pg/migrations/20250410000012_idx_oauth2_device_code_grants_client_fk.sql new file mode 100644 index 000000000..a9981fe4b --- /dev/null +++ b/crates/storage-pg/migrations/20250410000012_idx_oauth2_device_code_grants_client_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + oauth2_device_code_grants_client_fk + ON oauth2_device_code_grant (oauth2_client_id); diff --git a/crates/storage-pg/migrations/20250410000013_idx_oauth2_device_code_grants_session_fk.sql b/crates/storage-pg/migrations/20250410000013_idx_oauth2_device_code_grants_session_fk.sql new file mode 100644 index 000000000..be8f685d1 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000013_idx_oauth2_device_code_grants_session_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + oauth2_device_code_grants_session_fk + ON oauth2_device_code_grant (oauth2_session_id); diff --git a/crates/storage-pg/migrations/20250410000014_idx_oauth2_device_code_grants_user_session_fk.sql b/crates/storage-pg/migrations/20250410000014_idx_oauth2_device_code_grants_user_session_fk.sql new file mode 100644 index 000000000..f3f6613d7 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000014_idx_oauth2_device_code_grants_user_session_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + oauth2_device_code_grants_user_session_fk + ON oauth2_device_code_grant (user_session_id); diff --git a/crates/storage-pg/migrations/20250410000015_idx_oauth2_refresh_tokens_session_fk.sql b/crates/storage-pg/migrations/20250410000015_idx_oauth2_refresh_tokens_session_fk.sql new file mode 100644 index 000000000..897d247c8 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000015_idx_oauth2_refresh_tokens_session_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + oauth2_refresh_tokens_session_fk + ON oauth2_refresh_tokens (oauth2_session_id); diff --git a/crates/storage-pg/migrations/20250410000016_idx_oauth2_refresh_tokens_access_token_fk.sql b/crates/storage-pg/migrations/20250410000016_idx_oauth2_refresh_tokens_access_token_fk.sql new file mode 100644 index 000000000..5d391e6a5 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000016_idx_oauth2_refresh_tokens_access_token_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + oauth2_refresh_tokens_access_token_fk + ON oauth2_refresh_tokens (oauth2_access_token_id); diff --git a/crates/storage-pg/migrations/20250410000017_idx_oauth2_refresh_tokens_next_refresh_token_fk.sql b/crates/storage-pg/migrations/20250410000017_idx_oauth2_refresh_tokens_next_refresh_token_fk.sql new file mode 100644 index 000000000..f593f1d28 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000017_idx_oauth2_refresh_tokens_next_refresh_token_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + oauth2_refresh_tokens_next_refresh_token_fk + ON oauth2_refresh_tokens (next_oauth2_refresh_token_id); diff --git a/crates/storage-pg/migrations/20250410000018_idx_oauth2_sessions_user_session_fk.sql b/crates/storage-pg/migrations/20250410000018_idx_oauth2_sessions_user_session_fk.sql new file mode 100644 index 000000000..44d6d0e3c --- /dev/null +++ b/crates/storage-pg/migrations/20250410000018_idx_oauth2_sessions_user_session_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + oauth2_sessions_user_session_fk + ON oauth2_sessions (user_session_id); diff --git a/crates/storage-pg/migrations/20250410000019_idx_oauth2_sessions_client_fk.sql b/crates/storage-pg/migrations/20250410000019_idx_oauth2_sessions_client_fk.sql new file mode 100644 index 000000000..ad868a310 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000019_idx_oauth2_sessions_client_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + oauth2_sessions_client_fk + ON oauth2_sessions (oauth2_client_id); diff --git a/crates/storage-pg/migrations/20250410000020_idx_oauth2_sessions_user_fk.sql b/crates/storage-pg/migrations/20250410000020_idx_oauth2_sessions_user_fk.sql new file mode 100644 index 000000000..b47bb00a8 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000020_idx_oauth2_sessions_user_fk.sql @@ -0,0 +1,13 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +-- Including the `last_active_at` column lets us effeciently filter in-memory +-- for those sessions without fetching the rows, and without including it in the +-- index btree +CREATE INDEX CONCURRENTLY + oauth2_sessions_user_fk + ON oauth2_sessions (user_id) + INCLUDE (last_active_at); diff --git a/crates/storage-pg/migrations/20250410000021_drop_oauth2_sessions_user_id_last_active_at.sql b/crates/storage-pg/migrations/20250410000021_drop_oauth2_sessions_user_id_last_active_at.sql new file mode 100644 index 000000000..108dc290b --- /dev/null +++ b/crates/storage-pg/migrations/20250410000021_drop_oauth2_sessions_user_id_last_active_at.sql @@ -0,0 +1,8 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +-- Redundant with the `oauth2_sessions_user_fk` +DROP INDEX IF EXISTS oauth2_sessions_user_id_last_active_at; diff --git a/crates/storage-pg/migrations/20250410000022_idx_queue_jobs_started_by_fk.sql b/crates/storage-pg/migrations/20250410000022_idx_queue_jobs_started_by_fk.sql new file mode 100644 index 000000000..38bb79ede --- /dev/null +++ b/crates/storage-pg/migrations/20250410000022_idx_queue_jobs_started_by_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + queue_jobs_started_by_fk + ON queue_jobs (started_by); diff --git a/crates/storage-pg/migrations/20250410000023_idx_queue_jobs_next_attempt_fk.sql b/crates/storage-pg/migrations/20250410000023_idx_queue_jobs_next_attempt_fk.sql new file mode 100644 index 000000000..ea611f76e --- /dev/null +++ b/crates/storage-pg/migrations/20250410000023_idx_queue_jobs_next_attempt_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + queue_jobs_next_attempt_fk + ON queue_jobs (next_attempt_id); diff --git a/crates/storage-pg/migrations/20250410000024_idx_queue_jobs_schedule_name_fk.sql b/crates/storage-pg/migrations/20250410000024_idx_queue_jobs_schedule_name_fk.sql new file mode 100644 index 000000000..02b2bfaea --- /dev/null +++ b/crates/storage-pg/migrations/20250410000024_idx_queue_jobs_schedule_name_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + queue_jobs_schedule_name_fk + ON queue_jobs (schedule_name); diff --git a/crates/storage-pg/migrations/20250410000025_idx_upstream_oauth_authorization_sessions_provider_fk.sql b/crates/storage-pg/migrations/20250410000025_idx_upstream_oauth_authorization_sessions_provider_fk.sql new file mode 100644 index 000000000..f5e388613 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000025_idx_upstream_oauth_authorization_sessions_provider_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + upstream_oauth_authorization_sessions_provider_fk + ON upstream_oauth_authorization_sessions (upstream_oauth_provider_id); diff --git a/crates/storage-pg/migrations/20250410000026_idx_upstream_oauth_authorization_sessions_link_fk.sql b/crates/storage-pg/migrations/20250410000026_idx_upstream_oauth_authorization_sessions_link_fk.sql new file mode 100644 index 000000000..aa08e7fa1 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000026_idx_upstream_oauth_authorization_sessions_link_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + upstream_oauth_authorization_sessions_link_fk + ON upstream_oauth_authorization_sessions (upstream_oauth_link_id); diff --git a/crates/storage-pg/migrations/20250410000027_idx_upstream_oauth_links_provider_fk.sql b/crates/storage-pg/migrations/20250410000027_idx_upstream_oauth_links_provider_fk.sql new file mode 100644 index 000000000..9f6a9301f --- /dev/null +++ b/crates/storage-pg/migrations/20250410000027_idx_upstream_oauth_links_provider_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + upstream_oauth_links_provider_fk + ON upstream_oauth_links (upstream_oauth_provider_id); diff --git a/crates/storage-pg/migrations/20250410000028_idx_upstream_oauth_links_user_fk.sql b/crates/storage-pg/migrations/20250410000028_idx_upstream_oauth_links_user_fk.sql new file mode 100644 index 000000000..af7791f18 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000028_idx_upstream_oauth_links_user_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + upstream_oauth_links_user_fk + ON upstream_oauth_links (user_id); diff --git a/crates/storage-pg/migrations/20250410000029_idx_user_email_authentication_codes_authentication_fk.sql b/crates/storage-pg/migrations/20250410000029_idx_user_email_authentication_codes_authentication_fk.sql new file mode 100644 index 000000000..889fac2ab --- /dev/null +++ b/crates/storage-pg/migrations/20250410000029_idx_user_email_authentication_codes_authentication_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + user_email_authentication_codes_authentication_fk + ON user_email_authentication_codes (user_email_authentication_id); diff --git a/crates/storage-pg/migrations/20250410000030_idx_user_email_authentications_user_session_fk.sql b/crates/storage-pg/migrations/20250410000030_idx_user_email_authentications_user_session_fk.sql new file mode 100644 index 000000000..3a6284f67 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000030_idx_user_email_authentications_user_session_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + user_email_authentications_user_session_fk + ON user_email_authentications (user_session_id); diff --git a/crates/storage-pg/migrations/20250410000031_idx_user_email_authentications_user_registration_fk.sql b/crates/storage-pg/migrations/20250410000031_idx_user_email_authentications_user_registration_fk.sql new file mode 100644 index 000000000..9ad343ac9 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000031_idx_user_email_authentications_user_registration_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + user_email_authentications_user_registration_fk + ON user_email_authentications (user_registration_id); diff --git a/crates/storage-pg/migrations/20250410000032_idx_user_emails_user_fk.sql b/crates/storage-pg/migrations/20250410000032_idx_user_emails_user_fk.sql new file mode 100644 index 000000000..f66117d43 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000032_idx_user_emails_user_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + user_emails_user_fk + ON user_emails (user_id); diff --git a/crates/storage-pg/migrations/20250410000033_idx_user_emails_email_idx.sql b/crates/storage-pg/migrations/20250410000033_idx_user_emails_email_idx.sql new file mode 100644 index 000000000..e75e1e028 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000033_idx_user_emails_email_idx.sql @@ -0,0 +1,10 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +-- This isn't a foreign key, but we really need that to be indexed +CREATE INDEX CONCURRENTLY + user_emails_email_idx + ON user_emails (email); diff --git a/crates/storage-pg/migrations/20250410000034_idx_user_passwords_user_fk.sql b/crates/storage-pg/migrations/20250410000034_idx_user_passwords_user_fk.sql new file mode 100644 index 000000000..cfc1a9df7 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000034_idx_user_passwords_user_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + user_passwords_user_fk + ON user_passwords (user_id); diff --git a/crates/storage-pg/migrations/20250410000035_idx_user_recovery_tickets_session_fk.sql b/crates/storage-pg/migrations/20250410000035_idx_user_recovery_tickets_session_fk.sql new file mode 100644 index 000000000..22537f6a1 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000035_idx_user_recovery_tickets_session_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + user_recovery_tickets_session_fk + ON user_recovery_tickets (user_recovery_session_id); diff --git a/crates/storage-pg/migrations/20250410000036_idx_user_recovery_tickets_user_email_fk.sql b/crates/storage-pg/migrations/20250410000036_idx_user_recovery_tickets_user_email_fk.sql new file mode 100644 index 000000000..81bee0605 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000036_idx_user_recovery_tickets_user_email_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + user_recovery_tickets_user_email_fk + ON user_recovery_tickets (user_email_id); diff --git a/crates/storage-pg/migrations/20250410000037_idx_user_registrations_email_authentication_fk.sql b/crates/storage-pg/migrations/20250410000037_idx_user_registrations_email_authentication_fk.sql new file mode 100644 index 000000000..3385114d1 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000037_idx_user_registrations_email_authentication_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + user_registrations_email_authentication_fk + ON user_registrations (email_authentication_id); diff --git a/crates/storage-pg/migrations/20250410000038_idx_user_session_authentications_user_session_fk.sql b/crates/storage-pg/migrations/20250410000038_idx_user_session_authentications_user_session_fk.sql new file mode 100644 index 000000000..e71abc0a6 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000038_idx_user_session_authentications_user_session_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + user_session_authentications_user_session_fk + ON user_session_authentications (user_session_id); diff --git a/crates/storage-pg/migrations/20250410000039_idx_user_session_authentications_user_password_fk.sql b/crates/storage-pg/migrations/20250410000039_idx_user_session_authentications_user_password_fk.sql new file mode 100644 index 000000000..c6e262d0e --- /dev/null +++ b/crates/storage-pg/migrations/20250410000039_idx_user_session_authentications_user_password_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + user_session_authentications_user_password_fk + ON user_session_authentications (user_password_id); diff --git a/crates/storage-pg/migrations/20250410000040_idx_user_session_authentications_upstream_oauth_session_fk.sql b/crates/storage-pg/migrations/20250410000040_idx_user_session_authentications_upstream_oauth_session_fk.sql new file mode 100644 index 000000000..98f5728ad --- /dev/null +++ b/crates/storage-pg/migrations/20250410000040_idx_user_session_authentications_upstream_oauth_session_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + user_session_authentications_upstream_oauth_session_fk + ON user_session_authentications (upstream_oauth_authorization_session_id); diff --git a/crates/storage-pg/migrations/20250410000041_idx_user_sessions_user_fk.sql b/crates/storage-pg/migrations/20250410000041_idx_user_sessions_user_fk.sql new file mode 100644 index 000000000..e2b83345c --- /dev/null +++ b/crates/storage-pg/migrations/20250410000041_idx_user_sessions_user_fk.sql @@ -0,0 +1,13 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +-- Including the `last_active_at` column lets us effeciently filter in-memory +-- for those sessions without fetching the rows, and without including it in the +-- index btree +CREATE INDEX CONCURRENTLY + user_sessions_user_fk + ON user_sessions (user_id) + INCLUDE (last_active_at); diff --git a/crates/storage-pg/migrations/20250410000042_drop_user_sessions_user_id_last_active_at.sql b/crates/storage-pg/migrations/20250410000042_drop_user_sessions_user_id_last_active_at.sql new file mode 100644 index 000000000..1c95573c1 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000042_drop_user_sessions_user_id_last_active_at.sql @@ -0,0 +1,8 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +-- Redundant with the `user_sessions_user_fk` +DROP INDEX IF EXISTS user_sessions_user_id_last_active_at; diff --git a/crates/storage-pg/migrations/20250410000043_idx_user_terms_user_fk.sql b/crates/storage-pg/migrations/20250410000043_idx_user_terms_user_fk.sql new file mode 100644 index 000000000..f95b63a72 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000043_idx_user_terms_user_fk.sql @@ -0,0 +1,9 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +CREATE INDEX CONCURRENTLY + user_terms_user_fk + ON user_terms (user_id); diff --git a/crates/storage-pg/migrations/20250410000044_idx_users_primary_email_fk.sql b/crates/storage-pg/migrations/20250410000044_idx_users_primary_email_fk.sql new file mode 100644 index 000000000..57ae2d0d0 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000044_idx_users_primary_email_fk.sql @@ -0,0 +1,11 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +-- We don't use this column anymore, but… it will still tank the performance on +-- deletions of user_emails if we don't have it +CREATE INDEX CONCURRENTLY + users_primary_email_fk + ON users (primary_user_email_id); diff --git a/crates/storage-pg/migrations/20250410000045_idx_user_recovery_tickets_ticket_idx.sql b/crates/storage-pg/migrations/20250410000045_idx_user_recovery_tickets_ticket_idx.sql new file mode 100644 index 000000000..3030078f9 --- /dev/null +++ b/crates/storage-pg/migrations/20250410000045_idx_user_recovery_tickets_ticket_idx.sql @@ -0,0 +1,10 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +-- This isn't a foreign key, but we really need that to be indexed +CREATE INDEX CONCURRENTLY + user_recovery_tickets_ticket_idx + ON user_recovery_tickets (ticket); From 7f0dcaa73fd1246efffd7845519ceef797d0662c Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Thu, 10 Apr 2025 14:23:41 +0200 Subject: [PATCH 03/23] Lookup usernames case insensitively --- ...98529d295f7f121769ea5730d01c20c0ddbcdc79a5716.json} | 4 ++-- .../20250410121612_users_lower_username_idx.sql | 10 ++++++++++ crates/storage-pg/src/user/mod.rs | 2 +- crates/storage/src/user/mod.rs | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) rename crates/storage-pg/.sqlx/{query-48213d718a256a12540c0aec595ca3e436be423f2d0c868700c6397745ed0455.json => query-d2a4f5c01603463b78198529d295f7f121769ea5730d01c20c0ddbcdc79a5716.json} (88%) create mode 100644 crates/storage-pg/migrations/20250410121612_users_lower_username_idx.sql diff --git a/crates/storage-pg/.sqlx/query-48213d718a256a12540c0aec595ca3e436be423f2d0c868700c6397745ed0455.json b/crates/storage-pg/.sqlx/query-d2a4f5c01603463b78198529d295f7f121769ea5730d01c20c0ddbcdc79a5716.json similarity index 88% rename from crates/storage-pg/.sqlx/query-48213d718a256a12540c0aec595ca3e436be423f2d0c868700c6397745ed0455.json rename to crates/storage-pg/.sqlx/query-d2a4f5c01603463b78198529d295f7f121769ea5730d01c20c0ddbcdc79a5716.json index 52c7ab0bc..171a83623 100644 --- a/crates/storage-pg/.sqlx/query-48213d718a256a12540c0aec595ca3e436be423f2d0c868700c6397745ed0455.json +++ b/crates/storage-pg/.sqlx/query-d2a4f5c01603463b78198529d295f7f121769ea5730d01c20c0ddbcdc79a5716.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT user_id\n , username\n , created_at\n , locked_at\n , deactivated_at\n , can_request_admin\n FROM users\n WHERE username = $1\n ", + "query": "\n SELECT user_id\n , username\n , created_at\n , locked_at\n , deactivated_at\n , can_request_admin\n FROM users\n WHERE LOWER(username) = LOWER($1)\n ", "describe": { "columns": [ { @@ -48,5 +48,5 @@ false ] }, - "hash": "48213d718a256a12540c0aec595ca3e436be423f2d0c868700c6397745ed0455" + "hash": "d2a4f5c01603463b78198529d295f7f121769ea5730d01c20c0ddbcdc79a5716" } diff --git a/crates/storage-pg/migrations/20250410121612_users_lower_username_idx.sql b/crates/storage-pg/migrations/20250410121612_users_lower_username_idx.sql new file mode 100644 index 000000000..625e07e1b --- /dev/null +++ b/crates/storage-pg/migrations/20250410121612_users_lower_username_idx.sql @@ -0,0 +1,10 @@ +-- no-transaction +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +-- Create an index on the username column, lower-cased, so that we can lookup +-- usernames in a case-insensitive manner. +CREATE INDEX CONCURRENTLY users_lower_username_idx + ON users (LOWER(username)); diff --git a/crates/storage-pg/src/user/mod.rs b/crates/storage-pg/src/user/mod.rs index 2f5036134..abcd21ef0 100644 --- a/crates/storage-pg/src/user/mod.rs +++ b/crates/storage-pg/src/user/mod.rs @@ -175,7 +175,7 @@ impl UserRepository for PgUserRepository<'_> { , deactivated_at , can_request_admin FROM users - WHERE username = $1 + WHERE LOWER(username) = LOWER($1) "#, username, ) diff --git a/crates/storage/src/user/mod.rs b/crates/storage/src/user/mod.rs index 01feebbc4..395c6e615 100644 --- a/crates/storage/src/user/mod.rs +++ b/crates/storage/src/user/mod.rs @@ -155,7 +155,7 @@ pub trait UserRepository: Send + Sync { /// Returns [`Self::Error`] if the underlying repository fails async fn lookup(&mut self, id: Ulid) -> Result, Self::Error>; - /// Find a [`User`] by its username + /// Find a [`User`] by its username, in a case-insensitive manner /// /// Returns `None` if no [`User`] was found /// From b80a52e390f67d0a36bd2bf55046ed2f73e0d012 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Thu, 10 Apr 2025 18:45:13 +0200 Subject: [PATCH 04/23] Also lowercase the username when checking if it exists. --- ...7bc3a15afe7051658659347a1bf71dd62335df046708f19c967e.json} | 4 ++-- crates/storage-pg/src/user/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename crates/storage-pg/.sqlx/{query-94fd96446b237c87bd6bf741f3c42b37ee751b87b7fcc459602bdf8c46962443.json => query-7f8335cc94347bc3a15afe7051658659347a1bf71dd62335df046708f19c967e.json} (64%) diff --git a/crates/storage-pg/.sqlx/query-94fd96446b237c87bd6bf741f3c42b37ee751b87b7fcc459602bdf8c46962443.json b/crates/storage-pg/.sqlx/query-7f8335cc94347bc3a15afe7051658659347a1bf71dd62335df046708f19c967e.json similarity index 64% rename from crates/storage-pg/.sqlx/query-94fd96446b237c87bd6bf741f3c42b37ee751b87b7fcc459602bdf8c46962443.json rename to crates/storage-pg/.sqlx/query-7f8335cc94347bc3a15afe7051658659347a1bf71dd62335df046708f19c967e.json index f415823cc..9567c1555 100644 --- a/crates/storage-pg/.sqlx/query-94fd96446b237c87bd6bf741f3c42b37ee751b87b7fcc459602bdf8c46962443.json +++ b/crates/storage-pg/.sqlx/query-7f8335cc94347bc3a15afe7051658659347a1bf71dd62335df046708f19c967e.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT EXISTS(\n SELECT 1 FROM users WHERE username = $1\n ) AS \"exists!\"\n ", + "query": "\n SELECT EXISTS(\n SELECT 1 FROM users WHERE LOWER(username) = LOWER($1)\n ) AS \"exists!\"\n ", "describe": { "columns": [ { @@ -18,5 +18,5 @@ null ] }, - "hash": "94fd96446b237c87bd6bf741f3c42b37ee751b87b7fcc459602bdf8c46962443" + "hash": "7f8335cc94347bc3a15afe7051658659347a1bf71dd62335df046708f19c967e" } diff --git a/crates/storage-pg/src/user/mod.rs b/crates/storage-pg/src/user/mod.rs index abcd21ef0..9568416e3 100644 --- a/crates/storage-pg/src/user/mod.rs +++ b/crates/storage-pg/src/user/mod.rs @@ -250,7 +250,7 @@ impl UserRepository for PgUserRepository<'_> { let exists = sqlx::query_scalar!( r#" SELECT EXISTS( - SELECT 1 FROM users WHERE username = $1 + SELECT 1 FROM users WHERE LOWER(username) = LOWER($1) ) AS "exists!" "#, username From 73a4007c18325f3840e51fe2f2bde9369c6472a9 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Thu, 10 Apr 2025 19:57:45 +0200 Subject: [PATCH 05/23] Always ask for consent, never for reauth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we have deduplicated clients, we're in this weird situation where authorization grants just… go through. This is because 4 years ago, I designed it to support prompt=consent and prompt=none, but that never ended up being used/mentioned in the MSCs. We also had support for max_age, but that required reauthing, which doesn't work well with upstream providers. So this removes support for prompt=consent|none and max_age, and makes sure we always go through the consent page. Lots of code deleted, yay! --- .../src/oauth2/authorization_grant.rs | 19 +-- .../src/oauth2/authorization/complete.rs | 54 +----- .../handlers/src/oauth2/authorization/mod.rs | 156 ++---------------- crates/handlers/src/oauth2/consent.rs | 24 +-- crates/handlers/src/oauth2/discovery.rs | 2 +- crates/handlers/src/oauth2/token.rs | 4 - ...1aafadb0f4e98caeaba01597c6f62875ae691.json | 29 ---- ...18b8817d1bb511e08ee761a031fa12d27251.json} | 30 +--- ...6f7ae39bc5cb1572e3a2dcfcd67f196a1fa39.json | 23 --- ...c1eb529d321d0aa543be70f86ef96d0d8ff28.json | 27 +++ ...2ce7e16bc5df0677c7cd4ecb4fdbc5ee86395.json | 18 -- ...f0168a63e278d8f8c947cb3ab65173f92ae4.json} | 30 +--- ...56d3688851c36a48a50aa6104e8291e73630d.json | 14 -- ...authorization_default_requires_consent.sql | 9 + .../src/oauth2/authorization_grant.rs | 74 +-------- crates/storage-pg/src/oauth2/client.rs | 100 +---------- crates/storage-pg/src/oauth2/mod.rs | 25 --- .../storage/src/oauth2/authorization_grant.rs | 30 ---- crates/storage/src/oauth2/client.rs | 58 +------ 19 files changed, 88 insertions(+), 638 deletions(-) delete mode 100644 crates/storage-pg/.sqlx/query-854cc8cd3c1fc3dbbdf4ce81b561aafadb0f4e98caeaba01597c6f62875ae691.json rename crates/storage-pg/.sqlx/{query-1d9c478c7a5e3a672610376a290b9a1afaaa6fa2fb137f7307002f058b206dbd.json => query-890516adeeaf2d6a4fe83a69e42e18b8817d1bb511e08ee761a031fa12d27251.json} (75%) delete mode 100644 crates/storage-pg/.sqlx/query-8b7297c263336d70c2b647212b16f7ae39bc5cb1572e3a2dcfcd67f196a1fa39.json create mode 100644 crates/storage-pg/.sqlx/query-96208afd8b81e00f2700b0ded40c1eb529d321d0aa543be70f86ef96d0d8ff28.json delete mode 100644 crates/storage-pg/.sqlx/query-9a6c197ff4ad80217262d48f8792ce7e16bc5df0677c7cd4ecb4fdbc5ee86395.json rename crates/storage-pg/.sqlx/{query-e0d3be7e741581430e3e4719c7e19596837234c94a398570bdac42652c2c4652.json => query-bf6d1e3e3145438c988b1a47fc13f0168a63e278d8f8c947cb3ab65173f92ae4.json} (75%) delete mode 100644 crates/storage-pg/.sqlx/query-d83421d4a16f4ad084dd0db5abb56d3688851c36a48a50aa6104e8291e73630d.json create mode 100644 crates/storage-pg/migrations/20250410174306_oauth2_authorization_default_requires_consent.sql diff --git a/crates/data-model/src/oauth2/authorization_grant.rs b/crates/data-model/src/oauth2/authorization_grant.rs index a1b321e4c..170e476d0 100644 --- a/crates/data-model/src/oauth2/authorization_grant.rs +++ b/crates/data-model/src/oauth2/authorization_grant.rs @@ -4,9 +4,7 @@ // SPDX-License-Identifier: AGPL-3.0-only // Please see LICENSE in the repository root for full details. -use std::num::NonZeroU32; - -use chrono::{DateTime, Duration, Utc}; +use chrono::{DateTime, Utc}; use mas_iana::oauth::PkceCodeChallengeMethod; use oauth2_types::{ pkce::{CodeChallengeError, CodeChallengeMethodExt}, @@ -158,11 +156,9 @@ pub struct AuthorizationGrant { pub scope: Scope, pub state: Option, pub nonce: Option, - pub max_age: Option, pub response_mode: ResponseMode, pub response_type_id_token: bool, pub created_at: DateTime, - pub requires_consent: bool, pub login_hint: Option, } @@ -174,18 +170,7 @@ impl std::ops::Deref for AuthorizationGrant { } } -const DEFAULT_MAX_AGE: Duration = Duration::microseconds(3600 * 24 * 365 * 1000 * 1000); - impl AuthorizationGrant { - #[must_use] - pub fn max_auth_time(&self) -> DateTime { - let max_age = self - .max_age - .and_then(|x| Duration::try_seconds(x.get().into())) - .unwrap_or(DEFAULT_MAX_AGE); - self.created_at - max_age - } - #[must_use] pub fn parse_login_hint(&self, homeserver: &str) -> LoginHint { let Some(login_hint) = &self.login_hint else { @@ -274,11 +259,9 @@ impl AuthorizationGrant { scope: Scope::from_iter([OPENID, PROFILE]), state: Some(Alphanumeric.sample_string(rng, 10)), nonce: Some(Alphanumeric.sample_string(rng, 10)), - max_age: None, response_mode: ResponseMode::Query, response_type_id_token: false, created_at: now, - requires_consent: false, login_hint: Some(String::from("mxid:@example-user:example.com")), } } diff --git a/crates/handlers/src/oauth2/authorization/complete.rs b/crates/handlers/src/oauth2/authorization/complete.rs index bfd07531b..ea534df2c 100644 --- a/crates/handlers/src/oauth2/authorization/complete.rs +++ b/crates/handlers/src/oauth2/authorization/complete.rs @@ -11,7 +11,7 @@ use axum::{ use axum_extra::TypedHeader; use hyper::StatusCode; use mas_axum_utils::{SessionInfoExt, cookies::CookieJar, csrf::CsrfExt, sentry::SentryEventID}; -use mas_data_model::{AuthorizationGrant, BrowserSession, Client, Device}; +use mas_data_model::{AuthorizationGrant, BrowserSession, Client}; use mas_keystore::Keystore; use mas_policy::{EvaluationResult, Policy}; use mas_router::{PostAuthAction, UrlBuilder}; @@ -149,15 +149,6 @@ pub(crate) async fn get( let res = callback_destination.go(&templates, &locale, params).await?; Ok((cookie_jar, res).into_response()) } - Err(GrantCompletionError::RequiresReauth) => Ok(( - cookie_jar, - url_builder.redirect(&mas_router::Reauth::and_then(continue_grant)), - ) - .into_response()), - Err(GrantCompletionError::RequiresConsent) => { - let next = mas_router::Consent(grant_id); - Ok((cookie_jar, url_builder.redirect(&next)).into_response()) - } Err(GrantCompletionError::PolicyViolation(grant, res)) => { warn!(violation = ?res, "Authorization grant for client {} denied by policy", client.id); @@ -184,12 +175,6 @@ pub enum GrantCompletionError { #[error("authorization grant is not in a pending state")] NotPending, - #[error("user needs to reauthenticate")] - RequiresReauth, - - #[error("client lacks consent")] - RequiresConsent, - #[error("denied by the policy")] PolicyViolation(AuthorizationGrant, EvaluationResult), } @@ -218,18 +203,6 @@ pub(crate) async fn complete( return Err(GrantCompletionError::NotPending); } - // Check if the authentication is fresh enough - let authentication = repo - .browser_session() - .get_last_authentication(browser_session) - .await?; - let authentication = authentication.filter(|auth| auth.created_at > grant.max_auth_time()); - - let Some(valid_authentication) = authentication else { - repo.save().await?; - return Err(GrantCompletionError::RequiresReauth); - }; - // Run through the policy let res = policy .evaluate_authorization_grant(mas_policy::AuthorizationGrantInput { @@ -248,23 +221,6 @@ pub(crate) async fn complete( return Err(GrantCompletionError::PolicyViolation(grant, res)); } - let current_consent = repo - .oauth2_client() - .get_consent_for_user(client, &browser_session.user) - .await?; - - let lacks_consent = grant - .scope - .difference(¤t_consent) - .filter(|scope| Device::from_scope_token(scope).is_none()) - .any(|_| true); - - // Check if the client lacks consent *or* if consent was explicitly asked - if lacks_consent || grant.requires_consent { - repo.save().await?; - return Err(GrantCompletionError::RequiresConsent); - } - // All good, let's start the session let session = repo .oauth2_session() @@ -281,6 +237,12 @@ pub(crate) async fn complete( // Did they request an ID token? if grant.response_type_id_token { + // Fetch the last authentication + let last_authentication = repo + .browser_session() + .get_last_authentication(browser_session) + .await?; + params.id_token = Some(generate_id_token( rng, clock, @@ -290,7 +252,7 @@ pub(crate) async fn complete( Some(&grant), browser_session, None, - Some(&valid_authentication), + last_authentication.as_ref(), )?); } diff --git a/crates/handlers/src/oauth2/authorization/mod.rs b/crates/handlers/src/oauth2/authorization/mod.rs index 54d0641e3..c953f965b 100644 --- a/crates/handlers/src/oauth2/authorization/mod.rs +++ b/crates/handlers/src/oauth2/authorization/mod.rs @@ -6,20 +6,17 @@ use axum::{ extract::{Form, State}, - response::{Html, IntoResponse, Response}, + response::{IntoResponse, Response}, }; -use axum_extra::TypedHeader; use hyper::StatusCode; -use mas_axum_utils::{SessionInfoExt, cookies::CookieJar, csrf::CsrfExt, sentry::SentryEventID}; +use mas_axum_utils::{SessionInfoExt, cookies::CookieJar, sentry::SentryEventID}; use mas_data_model::{AuthorizationCode, Pkce}; -use mas_keystore::Keystore; -use mas_policy::Policy; use mas_router::{PostAuthAction, UrlBuilder}; use mas_storage::{ BoxClock, BoxRepository, BoxRng, oauth2::{OAuth2AuthorizationGrantRepository, OAuth2ClientRepository}, }; -use mas_templates::{PolicyViolationContext, TemplateContext, Templates}; +use mas_templates::Templates; use oauth2_types::{ errors::{ClientError, ClientErrorCode}, pkce, @@ -29,9 +26,8 @@ use oauth2_types::{ use rand::{Rng, distributions::Alphanumeric}; use serde::Deserialize; use thiserror::Error; -use tracing::warn; -use self::{callback::CallbackDestination, complete::GrantCompletionError}; +use self::callback::CallbackDestination; use crate::{BoundActivityTracker, PreferredLanguage, impl_from_error_for_route}; mod callback; @@ -134,10 +130,7 @@ pub(crate) async fn get( clock: BoxClock, PreferredLanguage(locale): PreferredLanguage, State(templates): State, - State(key_store): State, State(url_builder): State, - policy: Policy, - user_agent: Option>, activity_tracker: BoundActivityTracker, mut repo: BoxRepository, cookie_jar: CookieJar, @@ -166,9 +159,6 @@ pub(crate) async fn get( // Get the session info from the cookie let (session_info, cookie_jar) = cookie_jar.session_info(); - let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng); - - let user_agent = user_agent.map(|TypedHeader(ua)| ua.to_string()); // One day, we will have try blocks let res: Result = ({ @@ -235,8 +225,8 @@ pub(crate) async fn get( .await?); } - // Fail early if prompt=none and there is no active session - if prompt.contains(&Prompt::None) && maybe_session.is_none() { + // Fail early if prompt=none; we never let it go through + if prompt.contains(&Prompt::None) { return Ok(callback_destination .go( &templates, @@ -287,8 +277,6 @@ pub(crate) async fn get( None }; - let requires_consent = prompt.contains(&Prompt::Consent); - let grant = repo .oauth2_authorization_grant() .add( @@ -300,151 +288,43 @@ pub(crate) async fn get( code, params.auth.state.clone(), params.auth.nonce, - params.auth.max_age, response_mode, response_type.has_id_token(), - requires_consent, params.auth.login_hint, ) .await?; let continue_grant = PostAuthAction::continue_grant(grant.id); let res = match maybe_session { - // Cases where there is no active session, redirect to the relevant page - None if prompt.contains(&Prompt::None) => { - // This case should already be handled earlier - unreachable!(); - } None if prompt.contains(&Prompt::Create) => { // Client asked for a registration, show the registration prompt repo.save().await?; - url_builder.redirect(&mas_router::Register::and_then(continue_grant)) + url_builder + .redirect(&mas_router::Register::and_then(continue_grant)) .into_response() } + None => { // Other cases where we don't have a session, ask for a login repo.save().await?; - url_builder.redirect(&mas_router::Login::and_then(continue_grant)) + url_builder + .redirect(&mas_router::Login::and_then(continue_grant)) .into_response() } - // Special case when we already have a session but prompt=login|select_account - Some(session) - if prompt.contains(&Prompt::Login) - || prompt.contains(&Prompt::SelectAccount) => - { - // TODO: better pages here + Some(user_session) => { + // TODO: better support for prompt=create when we have a session repo.save().await?; - activity_tracker.record_browser_session(&clock, &session).await; - - url_builder.redirect(&mas_router::Reauth::and_then(continue_grant)) + activity_tracker + .record_browser_session(&clock, &user_session) + .await; + url_builder + .redirect(&mas_router::Consent(grant.id)) .into_response() } - - // Else, we immediately try to complete the authorization grant - Some(user_session) if prompt.contains(&Prompt::None) => { - activity_tracker.record_browser_session(&clock, &user_session).await; - - // With prompt=none, we should get back to the client immediately - match self::complete::complete( - &mut rng, - &clock, - &activity_tracker, - user_agent, - repo, - key_store, - policy, - &url_builder, - grant, - &client, - &user_session, - ) - .await - { - Ok(params) => callback_destination.go(&templates, &locale, params).await?, - Err(GrantCompletionError::RequiresConsent) => { - callback_destination - .go( - &templates, - &locale, - ClientError::from(ClientErrorCode::ConsentRequired), - ) - .await? - } - Err(GrantCompletionError::RequiresReauth) => { - callback_destination - .go( - &templates, - &locale, - ClientError::from(ClientErrorCode::InteractionRequired), - ) - .await? - } - Err(GrantCompletionError::PolicyViolation(_grant, _res)) => { - callback_destination - .go(&templates, &locale, ClientError::from(ClientErrorCode::AccessDenied)) - .await? - } - Err(GrantCompletionError::Internal(e)) => { - return Err(RouteError::Internal(e)) - } - Err(e @ GrantCompletionError::NotPending) => { - // This should never happen - return Err(RouteError::Internal(Box::new(e))); - } - } - } - Some(user_session) => { - activity_tracker.record_browser_session(&clock, &user_session).await; - - let grant_id = grant.id; - // Else, we show the relevant reauth/consent page if necessary - match self::complete::complete( - &mut rng, - &clock, - &activity_tracker, - user_agent, - repo, - key_store, - policy, - &url_builder, - grant, - &client, - &user_session, - ) - .await - { - Ok(params) => callback_destination.go(&templates, &locale, params).await?, - Err(GrantCompletionError::RequiresConsent) => { - url_builder.redirect(&mas_router::Consent(grant_id)).into_response() - } - Err(GrantCompletionError::PolicyViolation(grant, res)) => { - warn!(violation = ?res, "Authorization grant for client {} denied by policy", client.id); - - let ctx = PolicyViolationContext::for_authorization_grant(grant, client) - .with_session(user_session) - .with_csrf(csrf_token.form_value()) - .with_language(locale); - - let content = templates.render_policy_violation(&ctx)?; - Html(content).into_response() - } - Err(GrantCompletionError::RequiresReauth) => { - url_builder.redirect(&mas_router::Reauth::and_then(continue_grant)) - .into_response() - } - Err(GrantCompletionError::Internal(e)) => { - return Err(RouteError::Internal(e)) - } - Err(e @ GrantCompletionError::NotPending) => { - // This should never happen - return Err(RouteError::Internal(Box::new(e))); - } - } - } }; Ok(res) diff --git a/crates/handlers/src/oauth2/consent.rs b/crates/handlers/src/oauth2/consent.rs index 599ba080d..6912fd213 100644 --- a/crates/handlers/src/oauth2/consent.rs +++ b/crates/handlers/src/oauth2/consent.rs @@ -15,7 +15,7 @@ use mas_axum_utils::{ csrf::{CsrfExt, ProtectedForm}, sentry::SentryEventID, }; -use mas_data_model::{AuthorizationGrantStage, Device}; +use mas_data_model::AuthorizationGrantStage; use mas_policy::Policy; use mas_router::{PostAuthAction, UrlBuilder}; use mas_storage::{ @@ -240,28 +240,6 @@ pub(crate) async fn post( return Err(RouteError::PolicyViolation); } - // Do not consent for the "urn:matrix:org.matrix.msc2967.client:device:*" scope - let scope_without_device = grant - .scope - .iter() - .filter(|s| Device::from_scope_token(s).is_none()) - .cloned() - .collect(); - - repo.oauth2_client() - .give_consent_for_user( - &mut rng, - &clock, - &client, - &session.user, - &scope_without_device, - ) - .await?; - - repo.oauth2_authorization_grant() - .give_consent(grant) - .await?; - repo.save().await?; Ok((cookie_jar, next.go_next(&url_builder)).into_response()) diff --git a/crates/handlers/src/oauth2/discovery.rs b/crates/handlers/src/oauth2/discovery.rs index e72ebb358..bdefa3e62 100644 --- a/crates/handlers/src/oauth2/discovery.rs +++ b/crates/handlers/src/oauth2/discovery.rs @@ -132,7 +132,7 @@ pub(crate) async fn get( let request_uri_parameter_supported = Some(false); let prompt_values_supported = Some({ - let mut v = vec![Prompt::None, Prompt::Login]; + let mut v = vec![Prompt::Login]; // Advertise for prompt=create if password registration is enabled // TODO: we may want to be able to forward that to upstream providers if they // support it diff --git a/crates/handlers/src/oauth2/token.rs b/crates/handlers/src/oauth2/token.rs index c1a842be7..d361bcc99 100644 --- a/crates/handlers/src/oauth2/token.rs +++ b/crates/handlers/src/oauth2/token.rs @@ -978,10 +978,8 @@ mod tests { }), Some("state".to_owned()), Some("nonce".to_owned()), - None, ResponseMode::Query, false, - false, None, ) .await @@ -1079,10 +1077,8 @@ mod tests { }), Some("state".to_owned()), Some("nonce".to_owned()), - None, ResponseMode::Query, false, - false, None, ) .await diff --git a/crates/storage-pg/.sqlx/query-854cc8cd3c1fc3dbbdf4ce81b561aafadb0f4e98caeaba01597c6f62875ae691.json b/crates/storage-pg/.sqlx/query-854cc8cd3c1fc3dbbdf4ce81b561aafadb0f4e98caeaba01597c6f62875ae691.json deleted file mode 100644 index 0e114d418..000000000 --- a/crates/storage-pg/.sqlx/query-854cc8cd3c1fc3dbbdf4ce81b561aafadb0f4e98caeaba01597c6f62875ae691.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO oauth2_authorization_grants (\n oauth2_authorization_grant_id,\n oauth2_client_id,\n redirect_uri,\n scope,\n state,\n nonce,\n max_age,\n response_mode,\n code_challenge,\n code_challenge_method,\n response_type_code,\n response_type_id_token,\n authorization_code,\n requires_consent,\n login_hint,\n created_at\n )\n VALUES\n ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Text", - "Text", - "Text", - "Text", - "Int4", - "Text", - "Text", - "Text", - "Bool", - "Bool", - "Text", - "Bool", - "Text", - "Timestamptz" - ] - }, - "nullable": [] - }, - "hash": "854cc8cd3c1fc3dbbdf4ce81b561aafadb0f4e98caeaba01597c6f62875ae691" -} diff --git a/crates/storage-pg/.sqlx/query-1d9c478c7a5e3a672610376a290b9a1afaaa6fa2fb137f7307002f058b206dbd.json b/crates/storage-pg/.sqlx/query-890516adeeaf2d6a4fe83a69e42e18b8817d1bb511e08ee761a031fa12d27251.json similarity index 75% rename from crates/storage-pg/.sqlx/query-1d9c478c7a5e3a672610376a290b9a1afaaa6fa2fb137f7307002f058b206dbd.json rename to crates/storage-pg/.sqlx/query-890516adeeaf2d6a4fe83a69e42e18b8817d1bb511e08ee761a031fa12d27251.json index 3f0d2177d..d8fd25487 100644 --- a/crates/storage-pg/.sqlx/query-1d9c478c7a5e3a672610376a290b9a1afaaa6fa2fb137f7307002f058b206dbd.json +++ b/crates/storage-pg/.sqlx/query-890516adeeaf2d6a4fe83a69e42e18b8817d1bb511e08ee761a031fa12d27251.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT oauth2_authorization_grant_id\n , created_at\n , cancelled_at\n , fulfilled_at\n , exchanged_at\n , scope\n , state\n , redirect_uri\n , response_mode\n , nonce\n , max_age\n , oauth2_client_id\n , authorization_code\n , response_type_code\n , response_type_id_token\n , code_challenge\n , code_challenge_method\n , requires_consent\n , login_hint\n , oauth2_session_id\n FROM\n oauth2_authorization_grants\n\n WHERE authorization_code = $1\n ", + "query": "\n SELECT oauth2_authorization_grant_id\n , created_at\n , cancelled_at\n , fulfilled_at\n , exchanged_at\n , scope\n , state\n , redirect_uri\n , response_mode\n , nonce\n , oauth2_client_id\n , authorization_code\n , response_type_code\n , response_type_id_token\n , code_challenge\n , code_challenge_method\n , login_hint\n , oauth2_session_id\n FROM\n oauth2_authorization_grants\n\n WHERE authorization_code = $1\n ", "describe": { "columns": [ { @@ -55,51 +55,41 @@ }, { "ordinal": 10, - "name": "max_age", - "type_info": "Int4" - }, - { - "ordinal": 11, "name": "oauth2_client_id", "type_info": "Uuid" }, { - "ordinal": 12, + "ordinal": 11, "name": "authorization_code", "type_info": "Text" }, { - "ordinal": 13, + "ordinal": 12, "name": "response_type_code", "type_info": "Bool" }, { - "ordinal": 14, + "ordinal": 13, "name": "response_type_id_token", "type_info": "Bool" }, { - "ordinal": 15, + "ordinal": 14, "name": "code_challenge", "type_info": "Text" }, { - "ordinal": 16, + "ordinal": 15, "name": "code_challenge_method", "type_info": "Text" }, { - "ordinal": 17, - "name": "requires_consent", - "type_info": "Bool" - }, - { - "ordinal": 18, + "ordinal": 16, "name": "login_hint", "type_info": "Text" }, { - "ordinal": 19, + "ordinal": 17, "name": "oauth2_session_id", "type_info": "Uuid" } @@ -120,17 +110,15 @@ false, false, true, - true, false, true, false, false, true, true, - false, true, true ] }, - "hash": "1d9c478c7a5e3a672610376a290b9a1afaaa6fa2fb137f7307002f058b206dbd" + "hash": "890516adeeaf2d6a4fe83a69e42e18b8817d1bb511e08ee761a031fa12d27251" } diff --git a/crates/storage-pg/.sqlx/query-8b7297c263336d70c2b647212b16f7ae39bc5cb1572e3a2dcfcd67f196a1fa39.json b/crates/storage-pg/.sqlx/query-8b7297c263336d70c2b647212b16f7ae39bc5cb1572e3a2dcfcd67f196a1fa39.json deleted file mode 100644 index 0389ab030..000000000 --- a/crates/storage-pg/.sqlx/query-8b7297c263336d70c2b647212b16f7ae39bc5cb1572e3a2dcfcd67f196a1fa39.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT scope_token\n FROM oauth2_consents\n WHERE user_id = $1 AND oauth2_client_id = $2\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "scope_token", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Uuid" - ] - }, - "nullable": [ - false - ] - }, - "hash": "8b7297c263336d70c2b647212b16f7ae39bc5cb1572e3a2dcfcd67f196a1fa39" -} diff --git a/crates/storage-pg/.sqlx/query-96208afd8b81e00f2700b0ded40c1eb529d321d0aa543be70f86ef96d0d8ff28.json b/crates/storage-pg/.sqlx/query-96208afd8b81e00f2700b0ded40c1eb529d321d0aa543be70f86ef96d0d8ff28.json new file mode 100644 index 000000000..2f372898b --- /dev/null +++ b/crates/storage-pg/.sqlx/query-96208afd8b81e00f2700b0ded40c1eb529d321d0aa543be70f86ef96d0d8ff28.json @@ -0,0 +1,27 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO oauth2_authorization_grants (\n oauth2_authorization_grant_id,\n oauth2_client_id,\n redirect_uri,\n scope,\n state,\n nonce,\n response_mode,\n code_challenge,\n code_challenge_method,\n response_type_code,\n response_type_id_token,\n authorization_code,\n login_hint,\n created_at\n )\n VALUES\n ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Uuid", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Bool", + "Bool", + "Text", + "Text", + "Timestamptz" + ] + }, + "nullable": [] + }, + "hash": "96208afd8b81e00f2700b0ded40c1eb529d321d0aa543be70f86ef96d0d8ff28" +} diff --git a/crates/storage-pg/.sqlx/query-9a6c197ff4ad80217262d48f8792ce7e16bc5df0677c7cd4ecb4fdbc5ee86395.json b/crates/storage-pg/.sqlx/query-9a6c197ff4ad80217262d48f8792ce7e16bc5df0677c7cd4ecb4fdbc5ee86395.json deleted file mode 100644 index 07d5aa55f..000000000 --- a/crates/storage-pg/.sqlx/query-9a6c197ff4ad80217262d48f8792ce7e16bc5df0677c7cd4ecb4fdbc5ee86395.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO oauth2_consents\n (oauth2_consent_id, user_id, oauth2_client_id, scope_token, created_at)\n SELECT id, $2, $3, scope_token, $5 FROM UNNEST($1::uuid[], $4::text[]) u(id, scope_token)\n ON CONFLICT (user_id, oauth2_client_id, scope_token) DO UPDATE SET refreshed_at = $5\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "UuidArray", - "Uuid", - "Uuid", - "TextArray", - "Timestamptz" - ] - }, - "nullable": [] - }, - "hash": "9a6c197ff4ad80217262d48f8792ce7e16bc5df0677c7cd4ecb4fdbc5ee86395" -} diff --git a/crates/storage-pg/.sqlx/query-e0d3be7e741581430e3e4719c7e19596837234c94a398570bdac42652c2c4652.json b/crates/storage-pg/.sqlx/query-bf6d1e3e3145438c988b1a47fc13f0168a63e278d8f8c947cb3ab65173f92ae4.json similarity index 75% rename from crates/storage-pg/.sqlx/query-e0d3be7e741581430e3e4719c7e19596837234c94a398570bdac42652c2c4652.json rename to crates/storage-pg/.sqlx/query-bf6d1e3e3145438c988b1a47fc13f0168a63e278d8f8c947cb3ab65173f92ae4.json index 485b7fd9d..7a52e4781 100644 --- a/crates/storage-pg/.sqlx/query-e0d3be7e741581430e3e4719c7e19596837234c94a398570bdac42652c2c4652.json +++ b/crates/storage-pg/.sqlx/query-bf6d1e3e3145438c988b1a47fc13f0168a63e278d8f8c947cb3ab65173f92ae4.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT oauth2_authorization_grant_id\n , created_at\n , cancelled_at\n , fulfilled_at\n , exchanged_at\n , scope\n , state\n , redirect_uri\n , response_mode\n , nonce\n , max_age\n , oauth2_client_id\n , authorization_code\n , response_type_code\n , response_type_id_token\n , code_challenge\n , code_challenge_method\n , requires_consent\n , login_hint\n , oauth2_session_id\n FROM\n oauth2_authorization_grants\n\n WHERE oauth2_authorization_grant_id = $1\n ", + "query": "\n SELECT oauth2_authorization_grant_id\n , created_at\n , cancelled_at\n , fulfilled_at\n , exchanged_at\n , scope\n , state\n , redirect_uri\n , response_mode\n , nonce\n , oauth2_client_id\n , authorization_code\n , response_type_code\n , response_type_id_token\n , code_challenge\n , code_challenge_method\n , login_hint\n , oauth2_session_id\n FROM\n oauth2_authorization_grants\n\n WHERE oauth2_authorization_grant_id = $1\n ", "describe": { "columns": [ { @@ -55,51 +55,41 @@ }, { "ordinal": 10, - "name": "max_age", - "type_info": "Int4" - }, - { - "ordinal": 11, "name": "oauth2_client_id", "type_info": "Uuid" }, { - "ordinal": 12, + "ordinal": 11, "name": "authorization_code", "type_info": "Text" }, { - "ordinal": 13, + "ordinal": 12, "name": "response_type_code", "type_info": "Bool" }, { - "ordinal": 14, + "ordinal": 13, "name": "response_type_id_token", "type_info": "Bool" }, { - "ordinal": 15, + "ordinal": 14, "name": "code_challenge", "type_info": "Text" }, { - "ordinal": 16, + "ordinal": 15, "name": "code_challenge_method", "type_info": "Text" }, { - "ordinal": 17, - "name": "requires_consent", - "type_info": "Bool" - }, - { - "ordinal": 18, + "ordinal": 16, "name": "login_hint", "type_info": "Text" }, { - "ordinal": 19, + "ordinal": 17, "name": "oauth2_session_id", "type_info": "Uuid" } @@ -120,17 +110,15 @@ false, false, true, - true, false, true, false, false, true, true, - false, true, true ] }, - "hash": "e0d3be7e741581430e3e4719c7e19596837234c94a398570bdac42652c2c4652" + "hash": "bf6d1e3e3145438c988b1a47fc13f0168a63e278d8f8c947cb3ab65173f92ae4" } diff --git a/crates/storage-pg/.sqlx/query-d83421d4a16f4ad084dd0db5abb56d3688851c36a48a50aa6104e8291e73630d.json b/crates/storage-pg/.sqlx/query-d83421d4a16f4ad084dd0db5abb56d3688851c36a48a50aa6104e8291e73630d.json deleted file mode 100644 index 2d671aa52..000000000 --- a/crates/storage-pg/.sqlx/query-d83421d4a16f4ad084dd0db5abb56d3688851c36a48a50aa6104e8291e73630d.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE oauth2_authorization_grants AS og\n SET\n requires_consent = 'f'\n WHERE\n og.oauth2_authorization_grant_id = $1\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "d83421d4a16f4ad084dd0db5abb56d3688851c36a48a50aa6104e8291e73630d" -} diff --git a/crates/storage-pg/migrations/20250410174306_oauth2_authorization_default_requires_consent.sql b/crates/storage-pg/migrations/20250410174306_oauth2_authorization_default_requires_consent.sql new file mode 100644 index 000000000..05960c32e --- /dev/null +++ b/crates/storage-pg/migrations/20250410174306_oauth2_authorization_default_requires_consent.sql @@ -0,0 +1,9 @@ +-- Copyright 2025 New Vector Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only +-- Please see LICENSE in the repository root for full details. + +-- We stopped reading/writing to this column, but it's not nullable. +-- So we need to add a default value, and drop it in the next release +ALTER TABLE oauth2_authorization_grants + ALTER COLUMN requires_consent SET DEFAULT false; diff --git a/crates/storage-pg/src/oauth2/authorization_grant.rs b/crates/storage-pg/src/oauth2/authorization_grant.rs index 7a6162171..d619573e7 100644 --- a/crates/storage-pg/src/oauth2/authorization_grant.rs +++ b/crates/storage-pg/src/oauth2/authorization_grant.rs @@ -4,8 +4,6 @@ // SPDX-License-Identifier: AGPL-3.0-only // Please see LICENSE in the repository root for full details. -use std::num::NonZeroU32; - use async_trait::async_trait; use chrono::{DateTime, Utc}; use mas_data_model::{ @@ -48,13 +46,11 @@ struct GrantLookup { nonce: Option, redirect_uri: String, response_mode: String, - max_age: Option, response_type_code: bool, response_type_id_token: bool, authorization_code: Option, code_challenge: Option, code_challenge_method: Option, - requires_consent: bool, login_hint: Option, oauth2_client_id: Uuid, oauth2_session_id: Option, @@ -153,25 +149,6 @@ impl TryFrom for AuthorizationGrant { .source(e) })?; - let max_age = value - .max_age - .map(u32::try_from) - .transpose() - .map_err(|e| { - DatabaseInconsistencyError::on("oauth2_authorization_grants") - .column("max_age") - .row(id) - .source(e) - })? - .map(NonZeroU32::try_from) - .transpose() - .map_err(|e| { - DatabaseInconsistencyError::on("oauth2_authorization_grants") - .column("max_age") - .row(id) - .source(e) - })?; - Ok(AuthorizationGrant { id, stage, @@ -180,12 +157,10 @@ impl TryFrom for AuthorizationGrant { scope, state: value.state, nonce: value.nonce, - max_age, response_mode, redirect_uri, created_at: value.created_at, response_type_id_token: value.response_type_id_token, - requires_consent: value.requires_consent, login_hint: value.login_hint, }) } @@ -216,10 +191,8 @@ impl OAuth2AuthorizationGrantRepository for PgOAuth2AuthorizationGrantRepository code: Option, state: Option, nonce: Option, - max_age: Option, response_mode: ResponseMode, response_type_id_token: bool, - requires_consent: bool, login_hint: Option, ) -> Result { let code_challenge = code @@ -230,8 +203,6 @@ impl OAuth2AuthorizationGrantRepository for PgOAuth2AuthorizationGrantRepository .as_ref() .and_then(|c| c.pkce.as_ref()) .map(|p| p.challenge_method.to_string()); - // TODO: this conversion is a bit ugly - let max_age_i32 = max_age.map(|x| i32::try_from(u32::from(x)).unwrap_or(i32::MAX)); let code_str = code.as_ref().map(|c| &c.code); let created_at = clock.now(); @@ -247,19 +218,17 @@ impl OAuth2AuthorizationGrantRepository for PgOAuth2AuthorizationGrantRepository scope, state, nonce, - max_age, response_mode, code_challenge, code_challenge_method, response_type_code, response_type_id_token, authorization_code, - requires_consent, login_hint, created_at ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) "#, Uuid::from(id), Uuid::from(client.id), @@ -267,14 +236,12 @@ impl OAuth2AuthorizationGrantRepository for PgOAuth2AuthorizationGrantRepository scope.to_string(), state, nonce, - max_age_i32, response_mode.to_string(), code_challenge, code_challenge_method, code.is_some(), response_type_id_token, code_str, - requires_consent, login_hint, created_at, ) @@ -291,11 +258,9 @@ impl OAuth2AuthorizationGrantRepository for PgOAuth2AuthorizationGrantRepository scope, state, nonce, - max_age, response_mode, created_at, response_type_id_token, - requires_consent, login_hint, }) } @@ -323,14 +288,12 @@ impl OAuth2AuthorizationGrantRepository for PgOAuth2AuthorizationGrantRepository , redirect_uri , response_mode , nonce - , max_age , oauth2_client_id , authorization_code , response_type_code , response_type_id_token , code_challenge , code_challenge_method - , requires_consent , login_hint , oauth2_session_id FROM @@ -374,14 +337,12 @@ impl OAuth2AuthorizationGrantRepository for PgOAuth2AuthorizationGrantRepository , redirect_uri , response_mode , nonce - , max_age , oauth2_client_id , authorization_code , response_type_code , response_type_id_token , code_challenge , code_challenge_method - , requires_consent , login_hint , oauth2_session_id FROM @@ -480,37 +441,4 @@ impl OAuth2AuthorizationGrantRepository for PgOAuth2AuthorizationGrantRepository Ok(grant) } - - #[tracing::instrument( - name = "db.oauth2_authorization_grant.give_consent", - skip_all, - fields( - db.query.text, - %grant.id, - client.id = %grant.client_id, - ), - err, - )] - async fn give_consent( - &mut self, - mut grant: AuthorizationGrant, - ) -> Result { - sqlx::query!( - r#" - UPDATE oauth2_authorization_grants AS og - SET - requires_consent = 'f' - WHERE - og.oauth2_authorization_grant_id = $1 - "#, - Uuid::from(grant.id), - ) - .traced() - .execute(&mut *self.conn) - .await?; - - grant.requires_consent = false; - - Ok(grant) - } } diff --git a/crates/storage-pg/src/oauth2/client.rs b/crates/storage-pg/src/oauth2/client.rs index b846e80d4..02e57a01a 100644 --- a/crates/storage-pg/src/oauth2/client.rs +++ b/crates/storage-pg/src/oauth2/client.rs @@ -6,20 +6,15 @@ use std::{ collections::{BTreeMap, BTreeSet}, - str::FromStr, string::ToString, }; use async_trait::async_trait; -use mas_data_model::{Client, JwksOrJwksUri, User}; +use mas_data_model::{Client, JwksOrJwksUri}; use mas_iana::{jose::JsonWebSignatureAlg, oauth::OAuthClientAuthenticationMethod}; use mas_jose::jwk::PublicJsonWebKeySet; use mas_storage::{Clock, oauth2::OAuth2ClientRepository}; -use oauth2_types::{ - oidc::ApplicationType, - requests::GrantType, - scope::{Scope, ScopeToken}, -}; +use oauth2_types::{oidc::ApplicationType, requests::GrantType}; use opentelemetry_semantic_conventions::attribute::DB_QUERY_TEXT; use rand::RngCore; use sqlx::PgConnection; @@ -698,97 +693,6 @@ impl OAuth2ClientRepository for PgOAuth2ClientRepository<'_> { .collect() } - #[tracing::instrument( - name = "db.oauth2_client.get_consent_for_user", - skip_all, - fields( - db.query.text, - %user.id, - %client.id, - ), - err, - )] - async fn get_consent_for_user( - &mut self, - client: &Client, - user: &User, - ) -> Result { - let scope_tokens: Vec = sqlx::query_scalar!( - r#" - SELECT scope_token - FROM oauth2_consents - WHERE user_id = $1 AND oauth2_client_id = $2 - "#, - Uuid::from(user.id), - Uuid::from(client.id), - ) - .fetch_all(&mut *self.conn) - .await?; - - let scope: Result = scope_tokens - .into_iter() - .map(|s| ScopeToken::from_str(&s)) - .collect(); - - let scope = scope.map_err(|e| { - DatabaseInconsistencyError::on("oauth2_consents") - .column("scope_token") - .source(e) - })?; - - Ok(scope) - } - - #[tracing::instrument( - name = "db.oauth2_client.give_consent_for_user", - skip_all, - fields( - db.query.text, - %user.id, - %client.id, - %scope, - ), - err, - )] - async fn give_consent_for_user( - &mut self, - rng: &mut (dyn RngCore + Send), - clock: &dyn Clock, - client: &Client, - user: &User, - scope: &Scope, - ) -> Result<(), Self::Error> { - let now = clock.now(); - let (tokens, ids): (Vec, Vec) = scope - .iter() - .map(|token| { - ( - token.to_string(), - Uuid::from(Ulid::from_datetime_with_source(now.into(), rng)), - ) - }) - .unzip(); - - sqlx::query!( - r#" - INSERT INTO oauth2_consents - (oauth2_consent_id, user_id, oauth2_client_id, scope_token, created_at) - SELECT id, $2, $3, scope_token, $5 FROM UNNEST($1::uuid[], $4::text[]) u(id, scope_token) - ON CONFLICT (user_id, oauth2_client_id, scope_token) DO UPDATE SET refreshed_at = $5 - "#, - &ids, - Uuid::from(user.id), - Uuid::from(client.id), - &tokens, - now, - ) - .traced() - .execute(&mut *self.conn) - .await?; - - Ok(()) - } - #[tracing::instrument( name = "db.oauth2_client.delete_by_id", skip_all, diff --git a/crates/storage-pg/src/oauth2/mod.rs b/crates/storage-pg/src/oauth2/mod.rs index a3aadc2ad..5968e625d 100644 --- a/crates/storage-pg/src/oauth2/mod.rs +++ b/crates/storage-pg/src/oauth2/mod.rs @@ -135,10 +135,8 @@ mod tests { }), Some("state".to_owned()), Some("nonce".to_owned()), - None, ResponseMode::Query, true, - false, None, ) .await @@ -175,29 +173,6 @@ mod tests { .await .unwrap(); - // Lookup the consent the user gave to the client - let consent = repo - .oauth2_client() - .get_consent_for_user(&client, &user) - .await - .unwrap(); - assert!(consent.is_empty()); - - // Give consent to the client - let scope = Scope::from_iter([OPENID]); - repo.oauth2_client() - .give_consent_for_user(&mut rng, &clock, &client, &user, &scope) - .await - .unwrap(); - - // Lookup the consent the user gave to the client - let consent = repo - .oauth2_client() - .get_consent_for_user(&client, &user) - .await - .unwrap(); - assert_eq!(scope, consent); - // Lookup a non-existing session let session = repo.oauth2_session().lookup(Ulid::nil()).await.unwrap(); assert_eq!(session, None); diff --git a/crates/storage/src/oauth2/authorization_grant.rs b/crates/storage/src/oauth2/authorization_grant.rs index cff0ba418..7724ace87 100644 --- a/crates/storage/src/oauth2/authorization_grant.rs +++ b/crates/storage/src/oauth2/authorization_grant.rs @@ -4,8 +4,6 @@ // SPDX-License-Identifier: AGPL-3.0-only // Please see LICENSE in the repository root for full details. -use std::num::NonZeroU32; - use async_trait::async_trait; use mas_data_model::{AuthorizationCode, AuthorizationGrant, Client, Session}; use oauth2_types::{requests::ResponseMode, scope::Scope}; @@ -37,12 +35,9 @@ pub trait OAuth2AuthorizationGrantRepository: Send + Sync { /// `response_type` was requested /// * `state`: The state the client sent, if set /// * `nonce`: The nonce the client sent, if set - /// * `max_age`: The maximum age since the user last authenticated, if asked - /// by the client /// * `response_mode`: The response mode the client requested /// * `response_type_id_token`: Whether the `id_token` `response_type` was /// requested - /// * `requires_consent`: Whether the client explicitly requested consent /// * `login_hint`: The login_hint the client sent, if set /// /// # Errors @@ -59,10 +54,8 @@ pub trait OAuth2AuthorizationGrantRepository: Send + Sync { code: Option, state: Option, nonce: Option, - max_age: Option, response_mode: ResponseMode, response_type_id_token: bool, - requires_consent: bool, login_hint: Option, ) -> Result; @@ -131,22 +124,6 @@ pub trait OAuth2AuthorizationGrantRepository: Send + Sync { clock: &dyn Clock, authorization_grant: AuthorizationGrant, ) -> Result; - - /// Unset the `requires_consent` flag on an authorization grant - /// - /// Returns the updated authorization grant - /// - /// # Parameters - /// - /// * `authorization_grant`: The authorization grant to update - /// - /// # Errors - /// - /// Returns [`Self::Error`] if the underlying repository fails - async fn give_consent( - &mut self, - authorization_grant: AuthorizationGrant, - ) -> Result; } repository_impl!(OAuth2AuthorizationGrantRepository: @@ -160,10 +137,8 @@ repository_impl!(OAuth2AuthorizationGrantRepository: code: Option, state: Option, nonce: Option, - max_age: Option, response_mode: ResponseMode, response_type_id_token: bool, - requires_consent: bool, login_hint: Option, ) -> Result; @@ -184,9 +159,4 @@ repository_impl!(OAuth2AuthorizationGrantRepository: clock: &dyn Clock, authorization_grant: AuthorizationGrant, ) -> Result; - - async fn give_consent( - &mut self, - authorization_grant: AuthorizationGrant, - ) -> Result; ); diff --git a/crates/storage/src/oauth2/client.rs b/crates/storage/src/oauth2/client.rs index 113fe3e37..aa5a82a2a 100644 --- a/crates/storage/src/oauth2/client.rs +++ b/crates/storage/src/oauth2/client.rs @@ -7,10 +7,10 @@ use std::collections::{BTreeMap, BTreeSet}; use async_trait::async_trait; -use mas_data_model::{Client, User}; +use mas_data_model::Client; use mas_iana::{jose::JsonWebSignatureAlg, oauth::OAuthClientAuthenticationMethod}; use mas_jose::jwk::PublicJsonWebKeySet; -use oauth2_types::{oidc::ApplicationType, requests::GrantType, scope::Scope}; +use oauth2_types::{oidc::ApplicationType, requests::GrantType}; use rand_core::RngCore; use ulid::Ulid; use url::Url; @@ -171,45 +171,6 @@ pub trait OAuth2ClientRepository: Send + Sync { /// Returns [`Self::Error`] if the underlying repository fails async fn all_static(&mut self) -> Result, Self::Error>; - /// Get the list of scopes that the user has given consent for the given - /// client - /// - /// # Parameters - /// - /// * `client`: The client to get the consent for - /// * `user`: The user to get the consent for - /// - /// # Errors - /// - /// Returns [`Self::Error`] if the underlying repository fails - async fn get_consent_for_user( - &mut self, - client: &Client, - user: &User, - ) -> Result; - - /// Give consent for a set of scopes for the given client and user - /// - /// # Parameters - /// - /// * `rng`: The random number generator to use - /// * `clock`: The clock used to generate timestamps - /// * `client`: The client to give the consent for - /// * `user`: The user to give the consent for - /// * `scope`: The scope to give consent for - /// - /// # Errors - /// - /// Returns [`Self::Error`] if the underlying repository fails - async fn give_consent_for_user( - &mut self, - rng: &mut (dyn RngCore + Send), - clock: &dyn Clock, - client: &Client, - user: &User, - scope: &Scope, - ) -> Result<(), Self::Error>; - /// Delete a client /// /// # Parameters @@ -288,19 +249,4 @@ repository_impl!(OAuth2ClientRepository: async fn delete(&mut self, client: Client) -> Result<(), Self::Error>; async fn delete_by_id(&mut self, id: Ulid) -> Result<(), Self::Error>; - - async fn get_consent_for_user( - &mut self, - client: &Client, - user: &User, - ) -> Result; - - async fn give_consent_for_user( - &mut self, - rng: &mut (dyn RngCore + Send), - clock: &dyn Clock, - client: &Client, - user: &User, - scope: &Scope, - ) -> Result<(), Self::Error>; ); From 59e5068855def3117b4921e1513f0bf769f9e872 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Fri, 11 Apr 2025 13:35:59 +0200 Subject: [PATCH 06/23] Remove the reauth view --- crates/handlers/src/lib.rs | 4 - crates/handlers/src/views/mod.rs | 1 - crates/handlers/src/views/reauth.rs | 189 ---------------------------- crates/router/src/endpoints.rs | 60 --------- crates/templates/src/context.rs | 57 +-------- crates/templates/src/lib.rs | 12 +- 6 files changed, 6 insertions(+), 317 deletions(-) delete mode 100644 crates/handlers/src/views/reauth.rs diff --git a/crates/handlers/src/lib.rs b/crates/handlers/src/lib.rs index 3b7f15c02..cbf12ad50 100644 --- a/crates/handlers/src/lib.rs +++ b/crates/handlers/src/lib.rs @@ -371,10 +371,6 @@ where get(self::views::login::get).post(self::views::login::post), ) .route(mas_router::Logout::route(), post(self::views::logout::post)) - .route( - mas_router::Reauth::route(), - get(self::views::reauth::get).post(self::views::reauth::post), - ) .route( mas_router::Register::route(), get(self::views::register::get), diff --git a/crates/handlers/src/views/mod.rs b/crates/handlers/src/views/mod.rs index 336ec9f2f..5d5c615e8 100644 --- a/crates/handlers/src/views/mod.rs +++ b/crates/handlers/src/views/mod.rs @@ -8,7 +8,6 @@ pub mod app; pub mod index; pub mod login; pub mod logout; -pub mod reauth; pub mod recovery; pub mod register; pub mod shared; diff --git a/crates/handlers/src/views/reauth.rs b/crates/handlers/src/views/reauth.rs deleted file mode 100644 index d7f238c71..000000000 --- a/crates/handlers/src/views/reauth.rs +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2024 New Vector Ltd. -// Copyright 2021-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. - -use anyhow::Context; -use axum::{ - extract::{Form, Query, State}, - response::{Html, IntoResponse, Response}, -}; -use hyper::StatusCode; -use mas_axum_utils::{ - FancyError, SessionInfoExt, - cookies::CookieJar, - csrf::{CsrfExt, ProtectedForm}, -}; -use mas_router::UrlBuilder; -use mas_storage::{ - BoxClock, BoxRepository, BoxRng, - user::{BrowserSessionRepository, UserPasswordRepository}, -}; -use mas_templates::{ReauthContext, TemplateContext, Templates}; -use serde::Deserialize; -use zeroize::Zeroizing; - -use super::shared::OptionalPostAuthAction; -use crate::{ - BoundActivityTracker, PreferredLanguage, SiteConfig, - passwords::PasswordManager, - session::{SessionOrFallback, load_session_or_fallback}, -}; - -#[derive(Deserialize, Debug)] -pub(crate) struct ReauthForm { - password: String, -} - -#[tracing::instrument(name = "handlers.views.reauth.get", skip_all, err)] -pub(crate) async fn get( - mut rng: BoxRng, - clock: BoxClock, - PreferredLanguage(locale): PreferredLanguage, - State(templates): State, - State(url_builder): State, - State(site_config): State, - activity_tracker: BoundActivityTracker, - mut repo: BoxRepository, - Query(query): Query, - cookie_jar: CookieJar, -) -> Result { - if !site_config.password_login_enabled { - // XXX: do something better here - return Ok(url_builder - .redirect(&mas_router::Account::default()) - .into_response()); - } - - let (cookie_jar, maybe_session) = match load_session_or_fallback( - cookie_jar, &clock, &mut rng, &templates, &locale, &mut repo, - ) - .await? - { - SessionOrFallback::MaybeSession { - cookie_jar, - maybe_session, - .. - } => (cookie_jar, maybe_session), - SessionOrFallback::Fallback { response } => return Ok(response), - }; - - let Some(session) = maybe_session else { - // If there is no session, redirect to the login screen, keeping the - // PostAuthAction - let login = mas_router::Login::from(query.post_auth_action); - return Ok((cookie_jar, url_builder.redirect(&login)).into_response()); - }; - - let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng); - - activity_tracker - .record_browser_session(&clock, &session) - .await; - - let ctx = ReauthContext::default(); - let next = query.load_context(&mut repo).await?; - let ctx = if let Some(next) = next { - ctx.with_post_action(next) - } else { - ctx - }; - let ctx = ctx - .with_session(session) - .with_csrf(csrf_token.form_value()) - .with_language(locale); - - let content = templates.render_reauth(&ctx)?; - - Ok((cookie_jar, Html(content)).into_response()) -} - -#[tracing::instrument(name = "handlers.views.reauth.post", skip_all, err)] -pub(crate) async fn post( - mut rng: BoxRng, - clock: BoxClock, - PreferredLanguage(locale): PreferredLanguage, - State(templates): State, - State(password_manager): State, - State(url_builder): State, - State(site_config): State, - mut repo: BoxRepository, - Query(query): Query, - cookie_jar: CookieJar, - Form(form): Form>, -) -> Result { - if !site_config.password_login_enabled { - // XXX: do something better here - return Ok(StatusCode::METHOD_NOT_ALLOWED.into_response()); - } - - let form = cookie_jar.verify_form(&clock, form)?; - - let (cookie_jar, maybe_session) = match load_session_or_fallback( - cookie_jar, &clock, &mut rng, &templates, &locale, &mut repo, - ) - .await? - { - SessionOrFallback::MaybeSession { - cookie_jar, - maybe_session, - .. - } => (cookie_jar, maybe_session), - SessionOrFallback::Fallback { response } => return Ok(response), - }; - - let Some(session) = maybe_session else { - // If there is no session, redirect to the login screen, keeping the - // PostAuthAction - let login = mas_router::Login::from(query.post_auth_action); - return Ok((cookie_jar, url_builder.redirect(&login)).into_response()); - }; - - // Load the user password - let user_password = repo - .user_password() - .active(&session.user) - .await? - .context("User has no password")?; - - let password = Zeroizing::new(form.password.as_bytes().to_vec()); - - // TODO: recover from errors - // Verify the password, and upgrade it on-the-fly if needed - let new_password_hash = password_manager - .verify_and_upgrade( - &mut rng, - user_password.version, - password, - user_password.hashed_password.clone(), - ) - .await?; - - let user_password = if let Some((version, new_password_hash)) = new_password_hash { - // Save the upgraded password - repo.user_password() - .add( - &mut rng, - &clock, - &session.user, - version, - new_password_hash, - Some(&user_password), - ) - .await? - } else { - user_password - }; - - // Mark the session as authenticated by the password - repo.browser_session() - .authenticate_with_password(&mut rng, &clock, &session, &user_password) - .await?; - - let cookie_jar = cookie_jar.set_session(&session); - repo.save().await?; - - let reply = query.go_next(&url_builder); - Ok((cookie_jar, reply).into_response()) -} diff --git a/crates/router/src/endpoints.rs b/crates/router/src/endpoints.rs index 059dda31e..ceead6d12 100644 --- a/crates/router/src/endpoints.rs +++ b/crates/router/src/endpoints.rs @@ -255,66 +255,6 @@ impl SimpleRoute for Logout { const PATH: &'static str = "/logout"; } -/// `GET|POST /reauth` -#[derive(Default, Debug, Clone)] -pub struct Reauth { - post_auth_action: Option, -} - -impl Reauth { - #[must_use] - pub fn and_then(action: PostAuthAction) -> Self { - Self { - post_auth_action: Some(action), - } - } - - #[must_use] - pub fn and_continue_grant(data: Ulid) -> Self { - Self { - post_auth_action: Some(PostAuthAction::continue_grant(data)), - } - } - - #[must_use] - pub fn and_continue_device_code_grant(data: Ulid) -> Self { - Self { - post_auth_action: Some(PostAuthAction::continue_device_code_grant(data)), - } - } - - /// Get a reference to the reauth's post auth action. - #[must_use] - pub fn post_auth_action(&self) -> Option<&PostAuthAction> { - self.post_auth_action.as_ref() - } - - pub fn go_next(&self, url_builder: &UrlBuilder) -> axum::response::Redirect { - match &self.post_auth_action { - Some(action) => action.go_next(url_builder), - None => url_builder.redirect(&Index), - } - } -} - -impl Route for Reauth { - type Query = PostAuthAction; - - fn route() -> &'static str { - "/reauth" - } - - fn query(&self) -> Option<&Self::Query> { - self.post_auth_action.as_ref() - } -} - -impl From> for Reauth { - fn from(post_auth_action: Option) -> Self { - Self { post_auth_action } - } -} - /// `POST /register` #[derive(Default, Debug, Clone)] pub struct Register { diff --git a/crates/templates/src/context.rs b/crates/templates/src/context.rs index 26ed200e1..b6661d540 100644 --- a/crates/templates/src/context.rs +++ b/crates/templates/src/context.rs @@ -381,7 +381,7 @@ impl FormField for LoginFormField { } } -/// Inner context used in login and reauth screens. See [`PostAuthContext`]. +/// Inner context used in login screen. See [`PostAuthContext`]. #[derive(Serialize)] #[serde(tag = "kind", rename_all = "snake_case")] pub enum PostAuthContextInner { @@ -420,7 +420,7 @@ pub enum PostAuthContextInner { ManageAccount, } -/// Context used in login and reauth screens, for the post-auth action to do +/// Context used in login screen, for the post-auth action to do #[derive(Serialize)] pub struct PostAuthContext { /// The post auth action params from the URL @@ -734,59 +734,6 @@ impl PolicyViolationContext { } } -/// Fields of the reauthentication form -#[derive(Serialize, Deserialize, Debug, Clone, Copy, Hash, PartialEq, Eq)] -#[serde(rename_all = "kebab-case")] -pub enum ReauthFormField { - /// The password field - Password, -} - -impl FormField for ReauthFormField { - fn keep(&self) -> bool { - match self { - Self::Password => false, - } - } -} - -/// Context used by the `reauth.html` template -#[derive(Serialize, Default)] -pub struct ReauthContext { - form: FormState, - next: Option, -} - -impl TemplateContext for ReauthContext { - fn sample(_now: chrono::DateTime, _rng: &mut impl Rng) -> Vec - where - Self: Sized, - { - // TODO: samples with errors - vec![ReauthContext { - form: FormState::default(), - next: None, - }] - } -} - -impl ReauthContext { - /// Add an error on the reauthentication form - #[must_use] - pub fn with_form_state(self, form: FormState) -> Self { - Self { form, ..self } - } - - /// Add a post authentication action to the context - #[must_use] - pub fn with_post_action(self, next: PostAuthContext) -> Self { - Self { - next: Some(next), - ..self - } - } -} - /// Context used by the `sso.html` template #[derive(Serialize)] pub struct CompatSsoContext { diff --git a/crates/templates/src/lib.rs b/crates/templates/src/lib.rs index 982b3fc02..05422359d 100644 --- a/crates/templates/src/lib.rs +++ b/crates/templates/src/lib.rs @@ -38,10 +38,10 @@ pub use self::{ DeviceConsentContext, DeviceLinkContext, DeviceLinkFormField, EmailRecoveryContext, EmailVerificationContext, EmptyContext, ErrorContext, FormPostContext, IndexContext, LoginContext, LoginFormField, NotFoundContext, PasswordRegisterContext, - PolicyViolationContext, PostAuthContext, PostAuthContextInner, ReauthContext, - ReauthFormField, RecoveryExpiredContext, RecoveryFinishContext, RecoveryFinishFormField, - RecoveryProgressContext, RecoveryStartContext, RecoveryStartFormField, RegisterContext, - RegisterFormField, RegisterStepsDisplayNameContext, RegisterStepsDisplayNameFormField, + PolicyViolationContext, PostAuthContext, PostAuthContextInner, RecoveryExpiredContext, + RecoveryFinishContext, RecoveryFinishFormField, RecoveryProgressContext, + RecoveryStartContext, RecoveryStartFormField, RegisterContext, RegisterFormField, + RegisterStepsDisplayNameContext, RegisterStepsDisplayNameFormField, RegisterStepsEmailInUseContext, RegisterStepsVerifyEmailContext, RegisterStepsVerifyEmailFormField, SiteBranding, SiteConfigExt, SiteFeatures, TemplateContext, UpstreamExistingLinkContext, UpstreamRegister, UpstreamRegisterFormField, @@ -372,9 +372,6 @@ register_templates! { /// Render the account recovery disabled page pub fn render_recovery_disabled(WithLanguage) { "pages/recovery/disabled.html" } - /// Render the re-authentication form - pub fn render_reauth(WithLanguage>>) { "pages/reauth.html" } - /// Render the form used by the form_post response mode pub fn render_form_post(WithLanguage>) { "form_post.html" } @@ -456,7 +453,6 @@ impl Templates { check::render_recovery_expired(self, now, rng)?; check::render_recovery_consumed(self, now, rng)?; check::render_recovery_disabled(self, now, rng)?; - check::render_reauth(self, now, rng)?; check::render_form_post::(self, now, rng)?; check::render_error(self, now, rng)?; check::render_email_verification_txt(self, now, rng)?; From e0dacf0761669fc2e22da2315829f70cfed34dab Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Fri, 11 Apr 2025 14:08:28 +0200 Subject: [PATCH 07/23] Remove the `complete` handler, make it go through the consent page --- crates/handlers/src/lib.rs | 7 +- .../src/oauth2/authorization/callback.rs | 2 +- .../src/oauth2/authorization/complete.rs | 271 ------------------ .../src/oauth2/{ => authorization}/consent.rs | 116 ++++++-- .../handlers/src/oauth2/authorization/mod.rs | 110 +++---- crates/handlers/src/oauth2/mod.rs | 1 - crates/router/src/endpoints.rs | 19 +- 7 files changed, 142 insertions(+), 384 deletions(-) delete mode 100644 crates/handlers/src/oauth2/authorization/complete.rs rename crates/handlers/src/oauth2/{ => authorization}/consent.rs (69%) diff --git a/crates/handlers/src/lib.rs b/crates/handlers/src/lib.rs index cbf12ad50..4d610482f 100644 --- a/crates/handlers/src/lib.rs +++ b/crates/handlers/src/lib.rs @@ -405,13 +405,10 @@ where mas_router::OAuth2AuthorizationEndpoint::route(), get(self::oauth2::authorization::get), ) - .route( - mas_router::ContinueAuthorizationGrant::route(), - get(self::oauth2::authorization::complete::get), - ) .route( mas_router::Consent::route(), - get(self::oauth2::consent::get).post(self::oauth2::consent::post), + get(self::oauth2::authorization::consent::get) + .post(self::oauth2::authorization::consent::post), ) .route( mas_router::CompatLoginSsoComplete::route(), diff --git a/crates/handlers/src/oauth2/authorization/callback.rs b/crates/handlers/src/oauth2/authorization/callback.rs index b76722d5a..beb0868d7 100644 --- a/crates/handlers/src/oauth2/authorization/callback.rs +++ b/crates/handlers/src/oauth2/authorization/callback.rs @@ -101,7 +101,7 @@ impl CallbackDestination { }) } - pub async fn go( + pub fn go( self, templates: &Templates, locale: &DataLocale, diff --git a/crates/handlers/src/oauth2/authorization/complete.rs b/crates/handlers/src/oauth2/authorization/complete.rs deleted file mode 100644 index ea534df2c..000000000 --- a/crates/handlers/src/oauth2/authorization/complete.rs +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright 2024 New Vector Ltd. -// Copyright 2022-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. - -use axum::{ - extract::{Path, State}, - response::{Html, IntoResponse, Response}, -}; -use axum_extra::TypedHeader; -use hyper::StatusCode; -use mas_axum_utils::{SessionInfoExt, cookies::CookieJar, csrf::CsrfExt, sentry::SentryEventID}; -use mas_data_model::{AuthorizationGrant, BrowserSession, Client}; -use mas_keystore::Keystore; -use mas_policy::{EvaluationResult, Policy}; -use mas_router::{PostAuthAction, UrlBuilder}; -use mas_storage::{ - BoxClock, BoxRepository, BoxRng, Clock, RepositoryAccess, - oauth2::{OAuth2AuthorizationGrantRepository, OAuth2ClientRepository, OAuth2SessionRepository}, - user::BrowserSessionRepository, -}; -use mas_templates::{PolicyViolationContext, TemplateContext, Templates}; -use oauth2_types::requests::AuthorizationResponse; -use thiserror::Error; -use tracing::warn; -use ulid::Ulid; - -use super::callback::CallbackDestination; -use crate::{ - BoundActivityTracker, PreferredLanguage, impl_from_error_for_route, oauth2::generate_id_token, -}; - -#[derive(Debug, Error)] -pub enum RouteError { - #[error(transparent)] - Internal(Box), - - #[error("authorization grant was not found")] - NotFound, - - #[error("authorization grant is not in a pending state")] - NotPending, - - #[error("failed to load client")] - NoSuchClient, -} - -impl IntoResponse for RouteError { - fn into_response(self) -> axum::response::Response { - let event = sentry::capture_error(&self); - // TODO: better error pages - let response = match self { - RouteError::NotFound => { - (StatusCode::NOT_FOUND, "authorization grant was not found").into_response() - } - RouteError::NotPending => ( - StatusCode::BAD_REQUEST, - "authorization grant not in a pending state", - ) - .into_response(), - RouteError::Internal(_) | Self::NoSuchClient => { - (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response() - } - }; - - (SentryEventID::from(event), response).into_response() - } -} - -impl_from_error_for_route!(mas_storage::RepositoryError); -impl_from_error_for_route!(mas_templates::TemplateError); -impl_from_error_for_route!(mas_policy::LoadError); -impl_from_error_for_route!(mas_policy::EvaluationError); -impl_from_error_for_route!(super::callback::IntoCallbackDestinationError); -impl_from_error_for_route!(super::callback::CallbackDestinationError); - -#[tracing::instrument( - name = "handlers.oauth2.authorization_complete.get", - fields(grant.id = %grant_id), - skip_all, - err, -)] -pub(crate) async fn get( - mut rng: BoxRng, - clock: BoxClock, - PreferredLanguage(locale): PreferredLanguage, - State(templates): State, - State(url_builder): State, - State(key_store): State, - policy: Policy, - activity_tracker: BoundActivityTracker, - user_agent: Option>, - mut repo: BoxRepository, - cookie_jar: CookieJar, - Path(grant_id): Path, -) -> Result { - let (session_info, cookie_jar) = cookie_jar.session_info(); - - let maybe_session = session_info.load_active_session(&mut repo).await?; - - let user_agent = user_agent.map(|TypedHeader(ua)| ua.to_string()); - - let grant = repo - .oauth2_authorization_grant() - .lookup(grant_id) - .await? - .ok_or(RouteError::NotFound)?; - - let callback_destination = CallbackDestination::try_from(&grant)?; - let continue_grant = PostAuthAction::continue_grant(grant.id); - - let Some(session) = maybe_session else { - // If there is no session, redirect to the login screen, redirecting here after - // logout - return Ok(( - cookie_jar, - url_builder.redirect(&mas_router::Login::and_then(continue_grant)), - ) - .into_response()); - }; - - activity_tracker - .record_browser_session(&clock, &session) - .await; - - let client = repo - .oauth2_client() - .lookup(grant.client_id) - .await? - .ok_or(RouteError::NoSuchClient)?; - - match complete( - &mut rng, - &clock, - &activity_tracker, - user_agent, - repo, - key_store, - policy, - &url_builder, - grant, - &client, - &session, - ) - .await - { - Ok(params) => { - let res = callback_destination.go(&templates, &locale, params).await?; - Ok((cookie_jar, res).into_response()) - } - Err(GrantCompletionError::PolicyViolation(grant, res)) => { - warn!(violation = ?res, "Authorization grant for client {} denied by policy", client.id); - - let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng); - let ctx = PolicyViolationContext::for_authorization_grant(grant, client) - .with_session(session) - .with_csrf(csrf_token.form_value()) - .with_language(locale); - - let content = templates.render_policy_violation(&ctx)?; - - Ok((cookie_jar, Html(content)).into_response()) - } - Err(GrantCompletionError::NotPending) => Err(RouteError::NotPending), - Err(GrantCompletionError::Internal(e)) => Err(RouteError::Internal(e)), - } -} - -#[derive(Debug, Error)] -pub enum GrantCompletionError { - #[error(transparent)] - Internal(Box), - - #[error("authorization grant is not in a pending state")] - NotPending, - - #[error("denied by the policy")] - PolicyViolation(AuthorizationGrant, EvaluationResult), -} - -impl_from_error_for_route!(GrantCompletionError: mas_storage::RepositoryError); -impl_from_error_for_route!(GrantCompletionError: super::callback::IntoCallbackDestinationError); -impl_from_error_for_route!(GrantCompletionError: mas_policy::LoadError); -impl_from_error_for_route!(GrantCompletionError: mas_policy::EvaluationError); -impl_from_error_for_route!(GrantCompletionError: super::super::IdTokenSignatureError); - -pub(crate) async fn complete( - rng: &mut (impl rand::RngCore + rand::CryptoRng + Send), - clock: &impl Clock, - activity_tracker: &BoundActivityTracker, - user_agent: Option, - mut repo: BoxRepository, - key_store: Keystore, - mut policy: Policy, - url_builder: &UrlBuilder, - grant: AuthorizationGrant, - client: &Client, - browser_session: &BrowserSession, -) -> Result { - // Verify that the grant is in a pending stage - if !grant.stage.is_pending() { - return Err(GrantCompletionError::NotPending); - } - - // Run through the policy - let res = policy - .evaluate_authorization_grant(mas_policy::AuthorizationGrantInput { - user: Some(&browser_session.user), - client, - scope: &grant.scope, - grant_type: mas_policy::GrantType::AuthorizationCode, - requester: mas_policy::Requester { - ip_address: activity_tracker.ip(), - user_agent, - }, - }) - .await?; - - if !res.valid() { - return Err(GrantCompletionError::PolicyViolation(grant, res)); - } - - // All good, let's start the session - let session = repo - .oauth2_session() - .add_from_browser_session(rng, clock, client, browser_session, grant.scope.clone()) - .await?; - - let grant = repo - .oauth2_authorization_grant() - .fulfill(clock, &session, grant) - .await?; - - // Yep! Let's complete the auth now - let mut params = AuthorizationResponse::default(); - - // Did they request an ID token? - if grant.response_type_id_token { - // Fetch the last authentication - let last_authentication = repo - .browser_session() - .get_last_authentication(browser_session) - .await?; - - params.id_token = Some(generate_id_token( - rng, - clock, - url_builder, - &key_store, - client, - Some(&grant), - browser_session, - None, - last_authentication.as_ref(), - )?); - } - - // Did they request an auth code? - if let Some(code) = grant.code { - params.code = Some(code.code); - } - - repo.save().await?; - - activity_tracker - .record_oauth2_session(clock, &session) - .await; - - Ok(params) -} diff --git a/crates/handlers/src/oauth2/consent.rs b/crates/handlers/src/oauth2/authorization/consent.rs similarity index 69% rename from crates/handlers/src/oauth2/consent.rs rename to crates/handlers/src/oauth2/authorization/consent.rs index 6912fd213..273542383 100644 --- a/crates/handlers/src/oauth2/consent.rs +++ b/crates/handlers/src/oauth2/authorization/consent.rs @@ -16,6 +16,7 @@ use mas_axum_utils::{ sentry::SentryEventID, }; use mas_data_model::AuthorizationGrantStage; +use mas_keystore::Keystore; use mas_policy::Policy; use mas_router::{PostAuthAction, UrlBuilder}; use mas_storage::{ @@ -23,11 +24,14 @@ use mas_storage::{ oauth2::{OAuth2AuthorizationGrantRepository, OAuth2ClientRepository}, }; use mas_templates::{ConsentContext, PolicyViolationContext, TemplateContext, Templates}; +use oauth2_types::requests::AuthorizationResponse; use thiserror::Error; use ulid::Ulid; +use super::callback::CallbackDestination; use crate::{ BoundActivityTracker, PreferredLanguage, impl_from_error_for_route, + oauth2::generate_id_token, session::{SessionOrFallback, load_session_or_fallback}, }; @@ -45,9 +49,6 @@ pub enum RouteError { #[error("Authorization grant already used")] GrantNotPending, - #[error("Policy violation")] - PolicyViolation, - #[error("Failed to load client")] NoSuchClient, } @@ -57,20 +58,24 @@ impl_from_error_for_route!(mas_storage::RepositoryError); impl_from_error_for_route!(mas_policy::LoadError); impl_from_error_for_route!(mas_policy::EvaluationError); impl_from_error_for_route!(crate::session::SessionLoadError); +impl_from_error_for_route!(crate::oauth2::IdTokenSignatureError); +impl_from_error_for_route!(super::callback::IntoCallbackDestinationError); +impl_from_error_for_route!(super::callback::CallbackDestinationError); impl IntoResponse for RouteError { fn into_response(self) -> axum::response::Response { let event_id = sentry::capture_error(&self); ( - SentryEventID::from(event_id), StatusCode::INTERNAL_SERVER_ERROR, + SentryEventID::from(event_id), + self.to_string(), ) .into_response() } } #[tracing::instrument( - name = "handlers.oauth2.consent.get", + name = "handlers.oauth2.authorization.consent.get", fields(grant.id = %grant_id), skip_all, err, @@ -142,17 +147,7 @@ pub(crate) async fn get( }, }) .await?; - - if res.valid() { - let ctx = ConsentContext::new(grant, client) - .with_session(session) - .with_csrf(csrf_token.form_value()) - .with_language(locale); - - let content = templates.render_consent(&ctx)?; - - Ok((cookie_jar, Html(content)).into_response()) - } else { + if !res.valid() { let ctx = PolicyViolationContext::for_authorization_grant(grant, client) .with_session(session) .with_csrf(csrf_token.form_value()) @@ -160,12 +155,21 @@ pub(crate) async fn get( let content = templates.render_policy_violation(&ctx)?; - Ok((cookie_jar, Html(content)).into_response()) + return Ok((cookie_jar, Html(content)).into_response()); } + + let ctx = ConsentContext::new(grant, client) + .with_session(session) + .with_csrf(csrf_token.form_value()) + .with_language(locale); + + let content = templates.render_consent(&ctx)?; + + Ok((cookie_jar, Html(content)).into_response()) } #[tracing::instrument( - name = "handlers.oauth2.consent.post", + name = "handlers.oauth2.authorization.consent.post", fields(grant.id = %grant_id), skip_all, err, @@ -175,6 +179,7 @@ pub(crate) async fn post( clock: BoxClock, PreferredLanguage(locale): PreferredLanguage, State(templates): State, + State(key_store): State, mut policy: Policy, mut repo: BoxRepository, activity_tracker: BoundActivityTracker, @@ -199,6 +204,8 @@ pub(crate) async fn post( SessionOrFallback::Fallback { response } => return Ok(response), }; + let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng); + let user_agent = user_agent.map(|ua| ua.to_string()); let grant = repo @@ -206,15 +213,16 @@ pub(crate) async fn post( .lookup(grant_id) .await? .ok_or(RouteError::GrantNotFound)?; - let next = PostAuthAction::continue_grant(grant_id); + let callback_destination = CallbackDestination::try_from(&grant)?; - let Some(session) = maybe_session else { + let Some(browser_session) = maybe_session else { + let next = PostAuthAction::continue_grant(grant_id); let login = mas_router::Login::and_then(next); return Ok((cookie_jar, url_builder.redirect(&login)).into_response()); }; activity_tracker - .record_browser_session(&clock, &session) + .record_browser_session(&clock, &browser_session) .await; let client = repo @@ -225,7 +233,7 @@ pub(crate) async fn post( let res = policy .evaluate_authorization_grant(mas_policy::AuthorizationGrantInput { - user: Some(&session.user), + user: Some(&browser_session.user), client: &client, scope: &grant.scope, grant_type: mas_policy::GrantType::AuthorizationCode, @@ -237,10 +245,70 @@ pub(crate) async fn post( .await?; if !res.valid() { - return Err(RouteError::PolicyViolation); + let ctx = PolicyViolationContext::for_authorization_grant(grant, client) + .with_session(browser_session) + .with_csrf(csrf_token.form_value()) + .with_language(locale); + + let content = templates.render_policy_violation(&ctx)?; + + return Ok((cookie_jar, Html(content)).into_response()); + } + + // All good, let's start the session + let session = repo + .oauth2_session() + .add_from_browser_session( + &mut rng, + &clock, + &client, + &browser_session, + grant.scope.clone(), + ) + .await?; + + let grant = repo + .oauth2_authorization_grant() + .fulfill(&clock, &session, grant) + .await?; + + let mut params = AuthorizationResponse::default(); + + // Did they request an ID token? + if grant.response_type_id_token { + // Fetch the last authentication + let last_authentication = repo + .browser_session() + .get_last_authentication(&browser_session) + .await?; + + params.id_token = Some(generate_id_token( + &mut rng, + &clock, + &url_builder, + &key_store, + &client, + Some(&grant), + &browser_session, + None, + last_authentication.as_ref(), + )?); + } + + // Did they request an auth code? + if let Some(code) = grant.code { + params.code = Some(code.code); } repo.save().await?; - Ok((cookie_jar, next.go_next(&url_builder)).into_response()) + activity_tracker + .record_oauth2_session(&clock, &session) + .await; + + Ok(( + cookie_jar, + callback_destination.go(&templates, &locale, params)?, + ) + .into_response()) } diff --git a/crates/handlers/src/oauth2/authorization/mod.rs b/crates/handlers/src/oauth2/authorization/mod.rs index c953f965b..7e131f812 100644 --- a/crates/handlers/src/oauth2/authorization/mod.rs +++ b/crates/handlers/src/oauth2/authorization/mod.rs @@ -31,7 +31,7 @@ use self::callback::CallbackDestination; use crate::{BoundActivityTracker, PreferredLanguage, impl_from_error_for_route}; mod callback; -pub mod complete; +pub(crate) mod consent; #[derive(Debug, Error)] pub enum RouteError { @@ -172,80 +172,66 @@ pub(crate) async fn get( // Check if the request/request_uri/registration params are used. If so, reply // with the right error since we don't support them. if params.auth.request.is_some() { - return Ok(callback_destination - .go( - &templates, - &locale, - ClientError::from(ClientErrorCode::RequestNotSupported), - ) - .await?); + return Ok(callback_destination.go( + &templates, + &locale, + ClientError::from(ClientErrorCode::RequestNotSupported), + )?); } if params.auth.request_uri.is_some() { - return Ok(callback_destination - .go( - &templates, - &locale, - ClientError::from(ClientErrorCode::RequestUriNotSupported), - ) - .await?); + return Ok(callback_destination.go( + &templates, + &locale, + ClientError::from(ClientErrorCode::RequestUriNotSupported), + )?); } // Check if the client asked for a `token` response type, and bail out if it's // the case, since we don't support them if response_type.has_token() { - return Ok(callback_destination - .go( - &templates, - &locale, - ClientError::from(ClientErrorCode::UnsupportedResponseType), - ) - .await?); + return Ok(callback_destination.go( + &templates, + &locale, + ClientError::from(ClientErrorCode::UnsupportedResponseType), + )?); } // If the client asked for a `id_token` response type, we must check if it can // use the `implicit` grant type if response_type.has_id_token() && !client.grant_types.contains(&GrantType::Implicit) { - return Ok(callback_destination - .go( - &templates, - &locale, - ClientError::from(ClientErrorCode::UnauthorizedClient), - ) - .await?); + return Ok(callback_destination.go( + &templates, + &locale, + ClientError::from(ClientErrorCode::UnauthorizedClient), + )?); } if params.auth.registration.is_some() { - return Ok(callback_destination - .go( - &templates, - &locale, - ClientError::from(ClientErrorCode::RegistrationNotSupported), - ) - .await?); + return Ok(callback_destination.go( + &templates, + &locale, + ClientError::from(ClientErrorCode::RegistrationNotSupported), + )?); } // Fail early if prompt=none; we never let it go through if prompt.contains(&Prompt::None) { - return Ok(callback_destination - .go( - &templates, - &locale, - ClientError::from(ClientErrorCode::LoginRequired), - ) - .await?); + return Ok(callback_destination.go( + &templates, + &locale, + ClientError::from(ClientErrorCode::LoginRequired), + )?); } let code: Option = if response_type.has_code() { // Check if it is allowed to use this grant type if !client.grant_types.contains(&GrantType::AuthorizationCode) { - return Ok(callback_destination - .go( - &templates, - &locale, - ClientError::from(ClientErrorCode::UnauthorizedClient), - ) - .await?); + return Ok(callback_destination.go( + &templates, + &locale, + ClientError::from(ClientErrorCode::UnauthorizedClient), + )?); } // 32 random alphanumeric characters, about 190bit of entropy @@ -265,13 +251,11 @@ pub(crate) async fn get( // If the request had PKCE params but no code asked, it should get back with an // error if params.pkce.is_some() { - return Ok(callback_destination - .go( - &templates, - &locale, - ClientError::from(ClientErrorCode::InvalidRequest), - ) - .await?); + return Ok(callback_destination.go( + &templates, + &locale, + ClientError::from(ClientErrorCode::InvalidRequest), + )?); } None @@ -336,13 +320,11 @@ pub(crate) async fn get( Ok(r) => r, Err(err) => { tracing::error!(%err); - callback_destination - .go( - &templates, - &locale, - ClientError::from(ClientErrorCode::ServerError), - ) - .await? + callback_destination.go( + &templates, + &locale, + ClientError::from(ClientErrorCode::ServerError), + )? } }; diff --git a/crates/handlers/src/oauth2/mod.rs b/crates/handlers/src/oauth2/mod.rs index c80d7212b..f15a1ae9d 100644 --- a/crates/handlers/src/oauth2/mod.rs +++ b/crates/handlers/src/oauth2/mod.rs @@ -23,7 +23,6 @@ use mas_storage::{Clock, RepositoryAccess}; use thiserror::Error; pub mod authorization; -pub mod consent; pub mod device; pub mod discovery; pub mod introspection; diff --git a/crates/router/src/endpoints.rs b/crates/router/src/endpoints.rs index ceead6d12..89a9d726e 100644 --- a/crates/router/src/endpoints.rs +++ b/crates/router/src/endpoints.rs @@ -60,9 +60,7 @@ impl PostAuthAction { pub fn go_next(&self, url_builder: &UrlBuilder) -> axum::response::Redirect { match self { - Self::ContinueAuthorizationGrant { id } => { - url_builder.redirect(&ContinueAuthorizationGrant(*id)) - } + Self::ContinueAuthorizationGrant { id } => url_builder.redirect(&Consent(*id)), Self::ContinueDeviceCodeGrant { id } => { url_builder.redirect(&DeviceCodeConsent::new(*id)) } @@ -521,21 +519,6 @@ impl SimpleRoute for AccountPasswordChange { const PATH: &'static str = "/account/password/change"; } -/// `GET /authorize/{grant_id}` -#[derive(Debug, Clone)] -pub struct ContinueAuthorizationGrant(pub Ulid); - -impl Route for ContinueAuthorizationGrant { - type Query = (); - fn route() -> &'static str { - "/authorize/{grant_id}" - } - - fn path(&self) -> std::borrow::Cow<'static, str> { - format!("/authorize/{}", self.0).into() - } -} - /// `GET /consent/{grant_id}` #[derive(Debug, Clone)] pub struct Consent(pub Ulid); From 3eb9822791fb3ed5ab99a98fe7cd55c0c2f49a26 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Fri, 11 Apr 2025 15:33:11 +0200 Subject: [PATCH 08/23] Handle the case where there are multiple users with the same username, but with a different casing. --- crates/storage-pg/src/user/mod.rs | 24 +++++++++++++--- crates/storage-pg/src/user/tests.rs | 44 +++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/crates/storage-pg/src/user/mod.rs b/crates/storage-pg/src/user/mod.rs index 9568416e3..659a2172a 100644 --- a/crates/storage-pg/src/user/mod.rs +++ b/crates/storage-pg/src/user/mod.rs @@ -165,6 +165,9 @@ impl UserRepository for PgUserRepository<'_> { err, )] async fn find_by_username(&mut self, username: &str) -> Result, Self::Error> { + // We may have multiple users with the same username, but with a different + // casing. In this case, we want to return the one which matches the exact + // casing let res = sqlx::query_as!( UserLookup, r#" @@ -180,12 +183,25 @@ impl UserRepository for PgUserRepository<'_> { username, ) .traced() - .fetch_optional(&mut *self.conn) + .fetch_all(&mut *self.conn) .await?; - let Some(res) = res else { return Ok(None) }; - - Ok(Some(res.into())) + match &res[..] { + // Happy path: there is only one user matching the username… + [user] => Ok(Some(user.clone().into())), + // …or none. + [] => Ok(None), + list => { + // If there are multiple users with the same username, we want to + // return the one which matches the exact casing + if let Some(user) = list.iter().find(|user| user.username == username) { + Ok(Some(user.clone().into())) + } else { + // If none match exactly, we prefer to return nothing + Ok(None) + } + } + } } #[tracing::instrument( diff --git a/crates/storage-pg/src/user/tests.rs b/crates/storage-pg/src/user/tests.rs index d6f26c885..c37c7ff8e 100644 --- a/crates/storage-pg/src/user/tests.rs +++ b/crates/storage-pg/src/user/tests.rs @@ -216,6 +216,50 @@ async fn test_user_repo(pool: PgPool) { repo.save().await.unwrap(); } +/// Test [`UserRepository::find_by_username`] with different casings. +#[sqlx::test(migrator = "crate::MIGRATOR")] +async fn test_user_repo_find_by_username(pool: PgPool) { + let mut repo = PgRepository::from_pool(&pool).await.unwrap().boxed(); + let mut rng = ChaChaRng::seed_from_u64(42); + let clock = MockClock::default(); + + let alice = repo + .user() + .add(&mut rng, &clock, "Alice".to_owned()) + .await + .unwrap(); + let bob1 = repo + .user() + .add(&mut rng, &clock, "Bob".to_owned()) + .await + .unwrap(); + let bob2 = repo + .user() + .add(&mut rng, &clock, "BOB".to_owned()) + .await + .unwrap(); + + // This is fine, we can do a case-insensitive search + assert_eq!( + repo.user().find_by_username("alice").await.unwrap(), + Some(alice) + ); + + // In case there are multiple users with the same username, we should return the + // one that matches the exact casing + assert_eq!( + repo.user().find_by_username("Bob").await.unwrap(), + Some(bob1) + ); + assert_eq!( + repo.user().find_by_username("BOB").await.unwrap(), + Some(bob2) + ); + + // If none match, we should return None + assert!(repo.user().find_by_username("bob").await.unwrap().is_none()); +} + /// Test the user email repository, by trying out most of its methods #[sqlx::test(migrator = "crate::MIGRATOR")] async fn test_user_email_repo(pool: PgPool) { From 9cce8389b06d444a2b29b9c490e66b29fa004081 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 13:13:27 +0000 Subject: [PATCH 09/23] build(deps-dev): bump @types/node Bumps the development group in /tools/syn2mas with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node). Updates `@types/node` from 22.14.0 to 22.14.1 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 22.14.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development ... Signed-off-by: dependabot[bot] --- tools/syn2mas/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/syn2mas/package-lock.json b/tools/syn2mas/package-lock.json index 35c8fb2d5..00e2abd36 100644 --- a/tools/syn2mas/package-lock.json +++ b/tools/syn2mas/package-lock.json @@ -687,9 +687,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.14.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", - "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==", + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", "dev": true, "license": "MIT", "dependencies": { From 2e3eba18b653d193d0495b5bc7f0e4bd3c9221d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 13:17:05 +0000 Subject: [PATCH 10/23] build(deps): bump the tanstack-query group in /frontend with 2 updates Bumps the tanstack-query group in /frontend with 2 updates: [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) and [@tanstack/react-query-devtools](https://github.com/TanStack/query/tree/HEAD/packages/react-query-devtools). Updates `@tanstack/react-query` from 5.72.2 to 5.74.3 - [Release notes](https://github.com/TanStack/query/releases) - [Commits](https://github.com/TanStack/query/commits/v5.74.3/packages/react-query) Updates `@tanstack/react-query-devtools` from 5.72.2 to 5.74.3 - [Release notes](https://github.com/TanStack/query/releases) - [Commits](https://github.com/TanStack/query/commits/v5.74.3/packages/react-query-devtools) --- updated-dependencies: - dependency-name: "@tanstack/react-query" dependency-version: 5.74.3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: tanstack-query - dependency-name: "@tanstack/react-query-devtools" dependency-version: 5.74.3 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: tanstack-query ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 34 +++++++++++++++++----------------- frontend/package.json | 4 ++-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index bfc8ab24b..836ffa90c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -12,7 +12,7 @@ "@fontsource/inter": "^5.2.5", "@radix-ui/react-collapsible": "^1.1.3", "@radix-ui/react-dialog": "^1.1.6", - "@tanstack/react-query": "^5.72.2", + "@tanstack/react-query": "^5.74.3", "@tanstack/react-router": "^1.115.2", "@vector-im/compound-design-tokens": "4.0.1", "@vector-im/compound-web": "^7.10.1", @@ -42,7 +42,7 @@ "@storybook/react": "^8.6.12", "@storybook/react-vite": "^8.6.12", "@storybook/test": "^8.5.5", - "@tanstack/react-query-devtools": "^5.72.2", + "@tanstack/react-query-devtools": "^5.74.3", "@tanstack/react-router-devtools": "^1.115.2", "@tanstack/router-plugin": "^1.115.2", "@testing-library/jest-dom": "^6.6.3", @@ -5303,9 +5303,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.72.2", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.72.2.tgz", - "integrity": "sha512-fxl9/0yk3mD/FwTmVEf1/H6N5B975H0luT+icKyX566w6uJG0x6o+Yl+I38wJRCaogiMkstByt+seXfDbWDAcA==", + "version": "5.74.3", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.74.3.tgz", + "integrity": "sha512-Mqk+5o3qTuAiZML248XpNH8r2cOzl15+LTbUsZQEwvSvn1GU4VQhvqzAbil36p+MBxpr/58oBSnRzhrBevDhfg==", "license": "MIT", "funding": { "type": "github", @@ -5313,9 +5313,9 @@ } }, "node_modules/@tanstack/query-devtools": { - "version": "5.72.2", - "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.72.2.tgz", - "integrity": "sha512-mMKnGb+iOhVBcj6jaerCFRpg8pACStdG8hmUBHPtToeZzs4ctjBUL1FajqpVn2WaMxnq8Wya+P3Q5tPFNM9jQw==", + "version": "5.73.3", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.73.3.tgz", + "integrity": "sha512-hBQyYwsOuO7QOprK75NzfrWs/EQYjgFA0yykmcvsV62q0t6Ua97CU3sYgjHx0ZvxkXSOMkY24VRJ5uv9f5Ik4w==", "dev": true, "license": "MIT", "funding": { @@ -5324,12 +5324,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.72.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.72.2.tgz", - "integrity": "sha512-SVNHzyBUYiis+XiCl+8yiPZmMYei2AKYY94wM/zpvB5l1jxqOo82FQTziSJ4pBi96jtYqvYrTMxWynmbQh3XKw==", + "version": "5.74.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.74.3.tgz", + "integrity": "sha512-QrycUn0wxjVPzITvQvOxFRdhlAwIoOQSuav7qWD4SWCoKCdLbyRZ2vji2GuBq/glaxbF4wBx3fqcYRDOt8KDTA==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.72.2" + "@tanstack/query-core": "5.74.3" }, "funding": { "type": "github", @@ -5340,20 +5340,20 @@ } }, "node_modules/@tanstack/react-query-devtools": { - "version": "5.72.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.72.2.tgz", - "integrity": "sha512-n53qr9JdHCJTCUba6OvMhwiV2CcsckngOswKEE7nM5pQBa/fW9c43qw8omw1RPT2s+aC7MuwS8fHsWT8g+j6IQ==", + "version": "5.74.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.74.3.tgz", + "integrity": "sha512-H7TsOBB1fRCuuawrBzKMoIszqqILr2IN5oGLYMl7QG7ERJpMdc4hH8OwzBhVxJnmKeGwgtTQgcdKepfoJCWvFg==", "dev": true, "license": "MIT", "dependencies": { - "@tanstack/query-devtools": "5.72.2" + "@tanstack/query-devtools": "5.73.3" }, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/react-query": "^5.72.2", + "@tanstack/react-query": "^5.74.3", "react": "^18 || ^19" } }, diff --git a/frontend/package.json b/frontend/package.json index 9a583a7ec..e16b923e4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,7 +22,7 @@ "@fontsource/inter": "^5.2.5", "@radix-ui/react-collapsible": "^1.1.3", "@radix-ui/react-dialog": "^1.1.6", - "@tanstack/react-query": "^5.72.2", + "@tanstack/react-query": "^5.74.3", "@tanstack/react-router": "^1.115.2", "@vector-im/compound-design-tokens": "4.0.1", "@vector-im/compound-web": "^7.10.1", @@ -52,7 +52,7 @@ "@storybook/react": "^8.6.12", "@storybook/react-vite": "^8.6.12", "@storybook/test": "^8.5.5", - "@tanstack/react-query-devtools": "^5.72.2", + "@tanstack/react-query-devtools": "^5.74.3", "@tanstack/react-router-devtools": "^1.115.2", "@tanstack/router-plugin": "^1.115.2", "@testing-library/jest-dom": "^6.6.3", From 71440dd1c4eaf97d59585348d2f35be877ececfb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 13:17:25 +0000 Subject: [PATCH 11/23] build(deps-dev): bump the types group in /frontend with 2 updates Bumps the types group in /frontend with 2 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react). Updates `@types/node` from 22.14.0 to 22.14.1 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `@types/react` from 19.1.0 to 19.1.1 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 22.14.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: types - dependency-name: "@types/react" dependency-version: 19.1.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: types ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 16 ++++++++-------- frontend/package.json | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index bfc8ab24b..e2861cfbd 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -48,8 +48,8 @@ "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", - "@types/node": "^22.14.0", - "@types/react": "19.1.0", + "@types/node": "^22.14.1", + "@types/react": "19.1.1", "@types/react-dom": "19.1.2", "@types/swagger-ui-dist": "^3.30.5", "@vitejs/plugin-react": "^4.3.4", @@ -5819,9 +5819,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.14.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", - "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==", + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", "dev": true, "license": "MIT", "dependencies": { @@ -5829,9 +5829,9 @@ } }, "node_modules/@types/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz", - "integrity": "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.1.tgz", + "integrity": "sha512-ePapxDL7qrgqSF67s0h9m412d9DbXyC1n59O2st+9rjuuamWsZuD2w55rqY12CbzsZ7uVXb5Nw0gEp9Z8MMutQ==", "devOptional": true, "license": "MIT", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index 9a583a7ec..0fc74e45c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -58,8 +58,8 @@ "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", - "@types/node": "^22.14.0", - "@types/react": "19.1.0", + "@types/node": "^22.14.1", + "@types/react": "19.1.1", "@types/react-dom": "19.1.2", "@types/swagger-ui-dist": "^3.30.5", "@vitejs/plugin-react": "^4.3.4", From c68362cd8b353fa2e9026b8c19a3dc6b99cb1c94 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 13:17:40 +0000 Subject: [PATCH 12/23] build(deps-dev): bump msw from 2.7.3 to 2.7.4 in /frontend Bumps [msw](https://github.com/mswjs/msw) from 2.7.3 to 2.7.4. - [Release notes](https://github.com/mswjs/msw/releases) - [Changelog](https://github.com/mswjs/msw/blob/main/CHANGELOG.md) - [Commits](https://github.com/mswjs/msw/compare/v2.7.3...v2.7.4) --- updated-dependencies: - dependency-name: msw dependency-version: 2.7.4 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 8 ++++---- frontend/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index bfc8ab24b..957a8fa12 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -60,7 +60,7 @@ "happy-dom": "^17.4.4", "i18next-parser": "^9.3.0", "knip": "^5.50.2", - "msw": "^2.7.3", + "msw": "^2.7.4", "msw-storybook-addon": "^2.0.4", "postcss": "^8.5.3", "postcss-import": "^16.1.0", @@ -10528,9 +10528,9 @@ "license": "MIT" }, "node_modules/msw": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.7.3.tgz", - "integrity": "sha512-+mycXv8l2fEAjFZ5sjrtjJDmm2ceKGjrNbBr1durRg6VkU9fNUE/gsmQ51hWbHqs+l35W1iM+ZsmOD9Fd6lspw==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.7.4.tgz", + "integrity": "sha512-A2kuMopOjAjNEYkn0AnB1uj+x7oBjLIunFk7Ud4icEnVWFf6iBekn8oXW4zIwcpfEdWP9sLqyVaHVzneWoGEww==", "dev": true, "hasInstallScript": true, "license": "MIT", diff --git a/frontend/package.json b/frontend/package.json index 9a583a7ec..080463861 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -70,7 +70,7 @@ "happy-dom": "^17.4.4", "i18next-parser": "^9.3.0", "knip": "^5.50.2", - "msw": "^2.7.3", + "msw": "^2.7.4", "msw-storybook-addon": "^2.0.4", "postcss": "^8.5.3", "postcss-import": "^16.1.0", From b8e39a9b2f25268f487d9572f7fffbedce3d7273 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 13:25:29 +0000 Subject: [PATCH 13/23] build(deps): bump actions/setup-node from 4.3.0 to 4.4.0 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.3.0 to 4.4.0. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v4.3.0...v4.4.0) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: 4.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yaml | 2 +- .github/workflows/ci.yaml | 8 ++++---- .github/workflows/docs.yaml | 2 +- .github/workflows/release-branch.yaml | 2 +- .github/workflows/translations-download.yaml | 2 +- .github/workflows/translations-upload.yaml | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 89330dcff..17adc0012 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -340,7 +340,7 @@ jobs: uses: actions/checkout@v4.2.2 - name: Install Node - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version-file: ./tools/syn2mas/.nvmrc diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 038a589b8..397de666b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -58,7 +58,7 @@ jobs: uses: actions/checkout@v4.2.2 - name: Install Node - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: 22 @@ -82,7 +82,7 @@ jobs: uses: actions/checkout@v4.2.2 - name: Install Node - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: 22 @@ -106,7 +106,7 @@ jobs: uses: actions/checkout@v4.2.2 - name: Install Node - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: 20 @@ -323,7 +323,7 @@ jobs: uses: actions/checkout@v4.2.2 - name: Install Node - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version-file: ./tools/syn2mas/.nvmrc diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 12a76a3f6..811a35b9b 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -34,7 +34,7 @@ jobs: tool: mdbook - name: Install Node - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: 22 diff --git a/.github/workflows/release-branch.yaml b/.github/workflows/release-branch.yaml index 574d4ae3a..fac7624c8 100644 --- a/.github/workflows/release-branch.yaml +++ b/.github/workflows/release-branch.yaml @@ -48,7 +48,7 @@ jobs: uses: actions/checkout@v4.2.2 - name: Install Node - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: 22 diff --git a/.github/workflows/translations-download.yaml b/.github/workflows/translations-download.yaml index 626de5ee5..d2380c0df 100644 --- a/.github/workflows/translations-download.yaml +++ b/.github/workflows/translations-download.yaml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v4.2.2 - name: Install Node - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: 22 diff --git a/.github/workflows/translations-upload.yaml b/.github/workflows/translations-upload.yaml index d71ce4dde..2c5a27c3e 100644 --- a/.github/workflows/translations-upload.yaml +++ b/.github/workflows/translations-upload.yaml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@v4.2.2 - name: Install Node - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: 22 From 21f83e3e4930ecc1a0b41947d6fc6fc8dc2f79f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:07:17 +0000 Subject: [PATCH 14/23] build(deps): bump the tanstack-router group in /frontend with 3 updates Bumps the tanstack-router group in /frontend with 3 updates: [@tanstack/react-router](https://github.com/TanStack/router/tree/HEAD/packages/react-router), [@tanstack/react-router-devtools](https://github.com/TanStack/router/tree/HEAD/packages/react-router-devtools) and [@tanstack/router-plugin](https://github.com/TanStack/router/tree/HEAD/packages/router-plugin). Updates `@tanstack/react-router` from 1.115.2 to 1.116.0 - [Release notes](https://github.com/TanStack/router/releases) - [Commits](https://github.com/TanStack/router/commits/v1.116.0/packages/react-router) Updates `@tanstack/react-router-devtools` from 1.115.2 to 1.116.0 - [Release notes](https://github.com/TanStack/router/releases) - [Commits](https://github.com/TanStack/router/commits/v1.116.0/packages/react-router-devtools) Updates `@tanstack/router-plugin` from 1.115.2 to 1.116.1 - [Release notes](https://github.com/TanStack/router/releases) - [Commits](https://github.com/TanStack/router/commits/v1.116.1/packages/router-plugin) --- updated-dependencies: - dependency-name: "@tanstack/react-router" dependency-version: 1.116.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: tanstack-router - dependency-name: "@tanstack/react-router-devtools" dependency-version: 1.116.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: tanstack-router - dependency-name: "@tanstack/router-plugin" dependency-version: 1.116.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: tanstack-router ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 58 +++++++++++++++++++------------------- frontend/package.json | 6 ++-- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ae88d8838..5714ead4c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -13,7 +13,7 @@ "@radix-ui/react-collapsible": "^1.1.3", "@radix-ui/react-dialog": "^1.1.6", "@tanstack/react-query": "^5.74.3", - "@tanstack/react-router": "^1.115.2", + "@tanstack/react-router": "^1.116.0", "@vector-im/compound-design-tokens": "4.0.1", "@vector-im/compound-web": "^7.10.1", "@zxcvbn-ts/core": "^3.0.4", @@ -43,8 +43,8 @@ "@storybook/react-vite": "^8.6.12", "@storybook/test": "^8.5.5", "@tanstack/react-query-devtools": "^5.74.3", - "@tanstack/react-router-devtools": "^1.115.2", - "@tanstack/router-plugin": "^1.115.2", + "@tanstack/react-router-devtools": "^1.116.0", + "@tanstack/router-plugin": "^1.116.1", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", @@ -5358,14 +5358,14 @@ } }, "node_modules/@tanstack/react-router": { - "version": "1.115.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.115.2.tgz", - "integrity": "sha512-KWRtoDp1odMUUd0m7utTot3NsAxfb/W8UlPG5omtS0TCl8F+ErwurjS6Qn7rKg7q0CF8KcFDvhhBC1cXnOpoSQ==", + "version": "1.116.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.116.0.tgz", + "integrity": "sha512-ZBAg5Q6zJf0mnP9DYPiaaQ/wLDH2ujCMi/2RllpH86VUkdkyvQQzpAyKoiYJ891wh9OPgj6W6tPrzB4qy5FpRA==", "license": "MIT", "dependencies": { "@tanstack/history": "1.115.0", "@tanstack/react-store": "^0.7.0", - "@tanstack/router-core": "1.115.0", + "@tanstack/router-core": "1.115.3", "jsesc": "^3.1.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" @@ -5383,13 +5383,13 @@ } }, "node_modules/@tanstack/react-router-devtools": { - "version": "1.115.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-router-devtools/-/react-router-devtools-1.115.2.tgz", - "integrity": "sha512-g8lK8MXj9Mv0QKUNNC6QooUn9KJXcRZFQ0JiWUZNxeluTww43JFZ37zmD3fQugWRPOrcX9UaaJCjMaO/b+Sb6g==", + "version": "1.116.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-router-devtools/-/react-router-devtools-1.116.0.tgz", + "integrity": "sha512-PsJZWPjcmwZGe71kUvH4bI1ozkv1FgBuBEE0hTYlTCSJ3uG+qv3ndGEI+AiFyuF5OStrbfg0otW1OxeNq5vdGQ==", "dev": true, "license": "MIT", "dependencies": { - "@tanstack/router-devtools-core": "^1.115.0", + "@tanstack/router-devtools-core": "^1.115.3", "solid-js": "^1.9.5" }, "engines": { @@ -5400,7 +5400,7 @@ "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/react-router": "^1.115.2", + "@tanstack/react-router": "^1.116.0", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } @@ -5424,9 +5424,9 @@ } }, "node_modules/@tanstack/router-core": { - "version": "1.115.0", - "resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.115.0.tgz", - "integrity": "sha512-5XgesPkppANSnR3lrzakjx5+Vx1q4azI1t+kG2ZFvcLG8iRiJ564bDB1W3X2PZQgfKD78jDO/uWAcJTHH4sXuw==", + "version": "1.115.3", + "resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.115.3.tgz", + "integrity": "sha512-gynHs72LHVg05fuJTwZZYhDL4VNEAK0sXz7IqiBv7a3qsYeEmIZsGaFr9sVjTkuF1kbrFBdJd5JYutzBh9Uuhw==", "license": "MIT", "dependencies": { "@tanstack/history": "1.115.0", @@ -5442,9 +5442,9 @@ } }, "node_modules/@tanstack/router-devtools-core": { - "version": "1.115.0", - "resolved": "https://registry.npmjs.org/@tanstack/router-devtools-core/-/router-devtools-core-1.115.0.tgz", - "integrity": "sha512-s46V8bWxp4fWWjjDm7aGIYw8uPDXu8l1HkwGJwxkf1OQn1MdE7KRIVhGs/GM3Hp2KptQe4Gjomr7r1xrajuMhA==", + "version": "1.115.3", + "resolved": "https://registry.npmjs.org/@tanstack/router-devtools-core/-/router-devtools-core-1.115.3.tgz", + "integrity": "sha512-VBdgw1qxeOD/6FlZ9gitrWPUKGW83CuAW31gf32E0dxL7sIXP+yEFyPlNsVlENan1oSaEuV8tjKkuq5s4MfaPw==", "dev": true, "license": "MIT", "dependencies": { @@ -5459,7 +5459,7 @@ "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/router-core": "^1.115.0", + "@tanstack/router-core": "^1.115.3", "csstype": "^3.0.10", "solid-js": ">=1.9.5", "tiny-invariant": "^1.3.3" @@ -5471,9 +5471,9 @@ } }, "node_modules/@tanstack/router-generator": { - "version": "1.115.2", - "resolved": "https://registry.npmjs.org/@tanstack/router-generator/-/router-generator-1.115.2.tgz", - "integrity": "sha512-T77B6MnEdCPU9QFhjX/bhzaHKlKSo6n2MkIc78WrsnZ0Zx/zTtbzsGiLYyFZQ0tvB4/eazRrBh6YYY3qRwkGhg==", + "version": "1.116.0", + "resolved": "https://registry.npmjs.org/@tanstack/router-generator/-/router-generator-1.116.0.tgz", + "integrity": "sha512-XhCp85zP87G2bpSXnosiP3fiMo8HMQD2mvWqFFTFKz87WocabQYGlfhmNYWmBwI50EuS7Ph9lwXsSkV0oKh0xw==", "dev": true, "license": "MIT", "dependencies": { @@ -5490,7 +5490,7 @@ "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/react-router": "^1.115.2" + "@tanstack/react-router": "^1.116.0" }, "peerDependenciesMeta": { "@tanstack/react-router": { @@ -5499,9 +5499,9 @@ } }, "node_modules/@tanstack/router-plugin": { - "version": "1.115.2", - "resolved": "https://registry.npmjs.org/@tanstack/router-plugin/-/router-plugin-1.115.2.tgz", - "integrity": "sha512-81poBAU55nauRPddjbtRzGZwPy0/+SXIn6yRUXlMBQhnpMNlnsWbMyigV/iNm5F7SEUOI2u2Q79bt5Fvk2FNbA==", + "version": "1.116.1", + "resolved": "https://registry.npmjs.org/@tanstack/router-plugin/-/router-plugin-1.116.1.tgz", + "integrity": "sha512-9A8DAyRejTzvkVOzgVPUY6l2aH7xOMEXSJJtV9GNbi4NtE6AXUCoFe3mtvYnHSzRqAUMCO0wnfVENCjXQoQYZw==", "dev": true, "license": "MIT", "dependencies": { @@ -5511,8 +5511,8 @@ "@babel/template": "^7.26.8", "@babel/traverse": "^7.26.8", "@babel/types": "^7.26.8", - "@tanstack/router-core": "^1.115.0", - "@tanstack/router-generator": "^1.115.2", + "@tanstack/router-core": "^1.115.3", + "@tanstack/router-generator": "^1.116.0", "@tanstack/router-utils": "^1.115.0", "@tanstack/virtual-file-routes": "^1.115.0", "@types/babel__core": "^7.20.5", @@ -5532,7 +5532,7 @@ }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", - "@tanstack/react-router": "^1.115.2", + "@tanstack/react-router": "^1.116.0", "vite": ">=5.0.0 || >=6.0.0", "vite-plugin-solid": "^2.11.2", "webpack": ">=5.92.0" diff --git a/frontend/package.json b/frontend/package.json index 8bd33869f..cd1057027 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -23,7 +23,7 @@ "@radix-ui/react-collapsible": "^1.1.3", "@radix-ui/react-dialog": "^1.1.6", "@tanstack/react-query": "^5.74.3", - "@tanstack/react-router": "^1.115.2", + "@tanstack/react-router": "^1.116.0", "@vector-im/compound-design-tokens": "4.0.1", "@vector-im/compound-web": "^7.10.1", "@zxcvbn-ts/core": "^3.0.4", @@ -53,8 +53,8 @@ "@storybook/react-vite": "^8.6.12", "@storybook/test": "^8.5.5", "@tanstack/react-query-devtools": "^5.74.3", - "@tanstack/react-router-devtools": "^1.115.2", - "@tanstack/router-plugin": "^1.115.2", + "@tanstack/react-router-devtools": "^1.116.0", + "@tanstack/router-plugin": "^1.116.1", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", From 138822f8a90fcbefc47b640ecca34657e729c213 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:12:17 +0000 Subject: [PATCH 15/23] build(deps): bump anyhow from 1.0.97 to 1.0.98 Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.97 to 1.0.98. - [Release notes](https://github.com/dtolnay/anyhow/releases) - [Commits](https://github.com/dtolnay/anyhow/compare/1.0.97...1.0.98) --- updated-dependencies: - dependency-name: anyhow dependency-version: 1.0.98 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e32a197e..8c813dd21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -190,9 +190,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "arbitrary" diff --git a/Cargo.toml b/Cargo.toml index 325a5e7fe..3c2d07cb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,7 +80,7 @@ version = "0.1.88" # High-level error handling [workspace.dependencies.anyhow] -version = "1.0.97" +version = "1.0.98" # HTTP router [workspace.dependencies.axum] From 1479e5bd117d8e68bc06a724ab8a8f3464d47b32 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:12:42 +0000 Subject: [PATCH 16/23] build(deps): bump clap from 4.5.35 to 4.5.36 Bumps [clap](https://github.com/clap-rs/clap) from 4.5.35 to 4.5.36. - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.35...clap_complete-v4.5.36) --- updated-dependencies: - dependency-name: clap dependency-version: 4.5.36 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e32a197e..86daf9679 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1004,9 +1004,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.35" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" +checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" dependencies = [ "clap_builder", "clap_derive", @@ -1014,9 +1014,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.35" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" +checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" dependencies = [ "anstream", "anstyle", diff --git a/Cargo.toml b/Cargo.toml index 325a5e7fe..61b2940b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -119,7 +119,7 @@ features = ["serde", "clock"] # CLI argument parsing [workspace.dependencies.clap] -version = "4.5.35" +version = "4.5.36" features = ["derive"] # Cron expressions From b869e94cb2660196cdd37e55075838815ddb84da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:13:02 +0000 Subject: [PATCH 17/23] build(deps): bump sqlx from 0.8.3 to 0.8.4 Bumps [sqlx](https://github.com/launchbadge/sqlx) from 0.8.3 to 0.8.4. - [Changelog](https://github.com/launchbadge/sqlx/blob/main/CHANGELOG.md) - [Commits](https://github.com/launchbadge/sqlx/commits) --- updated-dependencies: - dependency-name: sqlx dependency-version: 0.8.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 31 ++++++++++++++++--------------- Cargo.toml | 2 +- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e32a197e..4ec9fa248 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5861,9 +5861,9 @@ checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" [[package]] name = "sqlx" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4410e73b3c0d8442c5f99b425d7a435b5ee0ae4167b3196771dd3f7a01be745f" +checksum = "14e22987355fbf8cfb813a0cf8cd97b1b4ec834b94dbd759a9e8679d41fabe83" dependencies = [ "sqlx-core", "sqlx-macros", @@ -5874,10 +5874,11 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a007b6936676aa9ab40207cde35daab0a04b823be8ae004368c0793b96a61e0" +checksum = "55c4720d7d4cd3d5b00f61d03751c685ad09c33ae8290c8a2c11335e0604300b" dependencies = [ + "base64 0.22.1", "bytes", "chrono", "crc", @@ -5897,7 +5898,6 @@ dependencies = [ "once_cell", "percent-encoding", "rustls", - "rustls-pemfile", "serde", "serde_json", "sha2", @@ -5913,9 +5913,9 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310" +checksum = "175147fcb75f353ac7675509bc58abb2cb291caf0fd24a3623b8f7e3eb0a754b" dependencies = [ "proc-macro2", "quote", @@ -5926,9 +5926,9 @@ dependencies = [ [[package]] name = "sqlx-macros-core" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad" +checksum = "1cde983058e53bfa75998e1982086c5efe3c370f3250bf0357e344fa3352e32b" dependencies = [ "dotenvy", "either", @@ -5952,9 +5952,9 @@ dependencies = [ [[package]] name = "sqlx-mysql" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4560278f0e00ce64938540546f59f590d60beee33fffbd3b9cd47851e5fff233" +checksum = "847d2e5393a4f39e47e4f36cab419709bc2b83cbe4223c60e86e1471655be333" dependencies = [ "atoi", "base64 0.22.1", @@ -5996,9 +5996,9 @@ dependencies = [ [[package]] name = "sqlx-postgres" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b98a57f363ed6764d5b3a12bfedf62f07aa16e1856a7ddc2a0bb190a959613" +checksum = "cc35947a541b9e0a2e3d85da444f1c4137c13040267141b208395a0d0ca4659f" dependencies = [ "atoi", "base64 0.22.1", @@ -6036,9 +6036,9 @@ dependencies = [ [[package]] name = "sqlx-sqlite" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f85ca71d3a5b24e64e1d08dd8fe36c6c95c339a896cc33068148906784620540" +checksum = "6c48291dac4e5ed32da0927a0b981788be65674aeb62666d19873ab4289febde" dependencies = [ "atoi", "chrono", @@ -6054,6 +6054,7 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core", + "thiserror 2.0.12", "tracing", "url", "uuid", diff --git a/Cargo.toml b/Cargo.toml index 325a5e7fe..c3c4c38d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -337,7 +337,7 @@ features = ["preserve_order"] # SQL database support [workspace.dependencies.sqlx] -version = "0.8.3" +version = "0.8.4" features = [ "runtime-tokio", "tls-rustls-aws-lc-rs", From ff89559a63fae9a03ea6c25df2be1f3564840116 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Mon, 14 Apr 2025 17:31:15 +0200 Subject: [PATCH 18/23] Allow database tests to run in parallel again --- .config/nextest.toml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.config/nextest.toml b/.config/nextest.toml index 428b597a6..351fb92d7 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -1,14 +1,2 @@ -[test-groups] -database = { max-threads = 1 } - [profile.default] retries = 1 - -# sqlx has a problem with nextest, as it uses a process-local semaphore to have -# tests use different databases. This doesn't work with nextest, as it has a -# process-per-test model, which is why we need to make sure only one test uses -# the database at a time. -# See https://github.com/launchbadge/sqlx/pull/3334 -[[profile.default.overrides]] -filter = 'package(mas-handlers) or package(mas-storage-pg)' -test-group = 'database' From 656ce8cf280bb1066ad03ca3f156b0583eeca215 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 15:32:48 +0000 Subject: [PATCH 19/23] Translations updates --- frontend/locales/et.json | 8 +++---- frontend/locales/uk.json | 46 +++++++++++++++++++------------------- translations/cs.json | 3 ++- translations/da.json | 5 +++-- translations/de.json | 3 ++- translations/et.json | 9 ++++---- translations/fi.json | 3 ++- translations/fr.json | 3 ++- translations/nl.json | 3 ++- translations/pt.json | 3 ++- translations/ru.json | 3 ++- translations/sv.json | 3 ++- translations/uk.json | 47 ++++++++++++++++++++------------------- translations/zh-Hans.json | 3 ++- 14 files changed, 77 insertions(+), 65 deletions(-) diff --git a/frontend/locales/et.json b/frontend/locales/et.json index 90228a53b..563268162 100644 --- a/frontend/locales/et.json +++ b/frontend/locales/et.json @@ -87,8 +87,8 @@ "title": "Vigane e-posti aadress" }, "email_invalid_error": "Lisatud e-posti aadress on vigane", - "incorrect_password_error": "Vale parool, proovige uuesti", - "password_confirmation": "Selle e-posti aadressi lisamiseks kinnitage oma konto salasõnaga" + "incorrect_password_error": "Vale salasõna, palun proovi uuesti", + "password_confirmation": "Selle e-posti aadressi lisamiseks kinnita tegevus oma kasutajakonto salasõnaga" }, "app_sessions_list": { "error": "Rakenduse sessioonide laadimine ei õnnestunud", @@ -327,8 +327,8 @@ "delete_button_confirmation_modal": { "action": "Kustuta e-posti aadress", "body": "Kas kustutame selle e-posti aadressi?", - "incorrect_password": "Vale parool, proovige uuesti", - "password_confirmation": "Selle e-posti aadressi kustutamiseks kinnitage parooliga" + "incorrect_password": "Vale salasõna, palun proovi uuesti", + "password_confirmation": "Selle e-posti aadressi kustutamiseks kinnita tegevus oma salasõnaga" }, "delete_button_title": "Eemalda e-posti aadress", "email": "E-posti aadress", diff --git a/frontend/locales/uk.json b/frontend/locales/uk.json index 55a849c22..26399a9ec 100644 --- a/frontend/locales/uk.json +++ b/frontend/locales/uk.json @@ -16,12 +16,12 @@ }, "branding": { "privacy_policy": { - "alt": "Посилання на політику конфіденційності сервісу", - "link": "Політика конфіденційності" + "alt": "Посилання на політику приватності служби", + "link": "Політика приватності" }, "terms_and_conditions": { "alt": "Посилання на умови надання послуг", - "link": "Умови використання" + "link": "Умови та положення" } }, "common": { @@ -43,7 +43,7 @@ "alert_description": "Цей обліковий запис буде стерто назавжди, і ви більше не матимете доступу до всіх своїх повідомлень.", "alert_title": "Ви можете втратити всі свої дані", "button": "Видалити обліковий запис", - "dialog_description": "Підтвердьте, що хочете видалити свій обліковий запис:\n\n\nВи не зможете повторно активувати свій обліковий запис\nВи більше не зможете ввійти \nНіхто не зможе повторно використовувати ваше ім'я користувача (MXID), включно з вами\nВи вийдете з усіх кімнат та особистих розмов, в яких ви перебуваєте\nВас буде вилучено з сервера ідентифікації, і ніхто не зможе знайти вас за вашою електронною поштою або номером телефону\n\nВаші старі повідомлення все одно будуть видимі людям, які їх отримали. Чи хотіли б ви сховати свої надіслані повідомлення від людей, які приєднуються до кімнат у майбутньому?", + "dialog_description": "Підтвердьте, що хочете видалити свій обліковий запис:\n\n\nВи не зможете повторно активувати свій обліковий запис\nВи більше не зможете ввійти \nНіхто не зможе повторно використовувати ваше ім'я користувача (MXID), включно з вами\nВи вийдете з усіх кімнат та особистих розмов, в яких ви перебуваєте\nВас буде вилучено з сервера ідентифікації, і ніхто не зможе знайти вас за вашою електронною поштою або номером телефону\n\nВаші старі повідомлення все одно будуть видимі людям, які їх отримали. Чи хотіли б ви сховати свої надіслані повідомлення від людей, які приєднаються до кімнат у майбутньому?", "dialog_title": "Видалити цей обліковий запис?", "erase_checkbox_label": "Так, сховати всі мої повідомлення від нових учасників", "incorrect_password": "Пароль неправильний, повторіть спробу", @@ -132,7 +132,7 @@ "error": { "hideDetails": "Сховати подробиці", "showDetails": "Показати подробиці", - "subtitle": "Сталася неочікувана помилка. Будь ласка спробуйте ще раз.", + "subtitle": "Сталася неочікувана помилка. Повторіть спробу.", "title": "Щось пішло не так" }, "error_boundary_title": "Щось пішло не так", @@ -155,7 +155,7 @@ "not_logged_in_alert": "Ви не ввійшли в систему.", "oauth2_client_detail": { "details_title": "Інформація про клієнт", - "id": "Ідентифікатор клієнта", + "id": "ID клієнта", "name": "Ім'я", "policy": "Політика", "terms": "Умови надання послуг" @@ -173,8 +173,8 @@ "failure": { "description": { "account_locked": "Ваш обліковий запис заблокований і не може бути відновлений на цей час. Якщо цього не очікується, зверніться до адміністратора сервера.", - "expired_recovery_ticket": "Термін дії посилання для відновлення закінчився. Будь ласка, почніть процес відновлення облікового запису спочатку.", - "invalid_new_password": "Вибраний вами новий пароль недійсний; він може не відповідати налаштованій політиці безпеки.", + "expired_recovery_ticket": "Посилання для відновлення застаріло. Розпочніть процес відновлення облікового запису спочатку.", + "invalid_new_password": "Обраний вами новий пароль неприпустимий; він може не відповідати налаштованій політиці безпеки.", "no_current_password": "У вас немає поточного пароля.", "no_such_recovery_ticket": "Посилання для відновлення недійсне. Якщо ви скопіювали посилання з електронної пошти для відновлення, перевірте, чи скопійовано повне посилання.", "password_changes_disabled": "Зміна пароля вимкнена.", @@ -203,7 +203,7 @@ "expired": { "resend_email": "Повторно надіслати електронний лист", "subtitle": "Запит на новий електронний лист, який буде надіслано на адресу: {{email}}", - "title": "Термін дії посилання для скидання пароля закінчився" + "title": "Посилання для скидання пароля застаріло" }, "subtitle": "Виберіть новий пароль для свого облікового запису.", "title": "Скидання пароля" @@ -218,15 +218,15 @@ "4": "Дуже надійний пароль" }, "suggestion": { - "all_uppercase": "Використайте великі літери, але не всі.", - "another_word": "Додайте більше слів, які є менш поширеними.", + "all_uppercase": "Використайте великі букви, але не всі.", + "another_word": "Додайте більше менш вживаних слів.", "associated_years": "Уникайте років, які пов'язані з вами.", - "capitalization": "Використайте більше великих літер, не тільки першу.", + "capitalization": "Використайте більше великих букв, не лише першу.", "dates": "Уникайте дат і років, які пов'язані з вами.", "l33t": "Уникайте передбачуваних замін букв, таких як «@» замість «a».", "longer_keyboard_pattern": "Використовуйте довші патерни клавіатури та змінюйте напрямок друку кілька разів.", - "no_need": "Ви можете створювати надійні паролі без використання символів, цифр або великих літер.", - "pwned": "Якщо ви використовуєте цей пароль деінде, вам слід змінити його.", + "no_need": "Ви можете створювати надійні паролі не вживаючи символів, цифр або великих букв.", + "pwned": "Якщо ви використовуєте цей пароль ще десь, вам слід змінити його.", "recent_years": "Уникайте останніх років.", "repeated": "Уникайте повторювання слів і символів.", "reverse_words": "Уникайте зворотного написання звичайних слів.", @@ -241,7 +241,7 @@ "extended_repeat": "Повторювані шаблони символів, такі як \"abcabcabc\", легко вгадати.", "key_pattern": "Короткі послідовності клавіш легко вгадати.", "names_by_themselves": "Поодинокі імена або прізвища легко вгадати.", - "pwned": "Ваш пароль було розкрито внаслідок витоку даних в Інтернеті.", + "pwned": "Ваш пароль розкрито внаслідок витоку даних в інтернеті.", "recent_years": "Пароль із нещодавніми роками легко вгадати.", "sequences": "Поширені послідовності символів, такі як «abc», легко вгадати.", "similar_to_common": "Це схоже на часто використовуваний пароль.", @@ -249,7 +249,7 @@ "straight_row": "Прямі послідовності клавіш на клавіатурі легко вгадати.", "top_hundred": "Це часто використовуваний пароль.", "top_ten": "Це широко використовуваний пароль.", - "user_inputs": "Не повинно бути ніяких особистих даних або даних, пов'язаних зі сторінкою.", + "user_inputs": "Не повинно бути жодних особистих даних або даних, пов'язаних зі сторінкою.", "word_by_itself": "Окремі слова легко вгадати." } }, @@ -263,9 +263,9 @@ "description": "Якщо ви не ввійшли в обліковий запис на інших пристроях і втратили ключ відновлення, вам потрібно буде скинути свою ідентичність, щоб продовжити користуватися застосунком.", "effect_list": { "negative_1": "Ви втратите наявну історію повідомлень", - "negative_2": "Вам потрібно буде знову підтвердити всі наявні пристрої та контакти", + "negative_2": "Вам потрібно буде знову верифікувати всі наявні пристрої та контакти", "neutral_1": "Ви втратите історію повідомлень, яка зберігається лише на сервері", - "neutral_2": "Вам потрібно буде знову підтвердити всі наявні пристрої та контакти", + "neutral_2": "Вам потрібно буде знову верифікувати всі наявні пристрої та контакти", "positive_1": "Ваші дані облікового запису, контакти, налаштування та список бесід будуть збережені" }, "failure": { @@ -277,7 +277,7 @@ "heading": "Скиньте свій обліковий запис, якщо не можете підтвердити його іншим способом", "start_reset": "Почати скидання", "success": { - "description": "Скидання профілю було схвалено на наступні {{minutes}} хвилин. Ви можете закрити це вікно та повернутися до застосунку, щоб продовжити.", + "description": "Скидання профілю схвалено на наступні {{minutes}} хвилин. Ви можете закрити це вікно та повернутися до застосунку, щоб продовжити.", "heading": "Облікові дані успішно скинуто. Поверніться до застосунку, щоб завершити процес.", "title": "Скидання криптоідентичності тимчасово дозволено" }, @@ -287,7 +287,7 @@ "label": "Вибрати сеанс" }, "session": { - "client_id_label": "Ідентифікатор клієнта", + "client_id_label": "ID клієнта", "current": "Поточний", "current_badge": "Поточний", "device_id_label": "ID пристрою", @@ -306,7 +306,7 @@ "unknown_browser": "Невідомий браузер", "unknown_device": "Невідомий пристрій", "uri_label": "Uri", - "user_id_label": "Ідентифікатор користувача", + "user_id_label": "ID користувача", "username_label": "Ім'я користувача" }, "session_detail": { @@ -318,7 +318,7 @@ }, "unknown_route": "Невідомий роут {{route}}", "unverified_email_alert": { - "button": "Перегляньте та перевірте", + "button": "Переглянути та підтвердити", "text:one": "У вас є {{count}} непідтверджена адреса електронної пошти.", "text:few": "У вас є {{count}} непідтверджені адреси електронної пошти.", "text:many": "У вас є {{count}} непідтверджених адрес електронної пошти.", @@ -363,7 +363,7 @@ "verify_email": { "code_expired_alert": { "description": "Термін дії коду закінчився. Будь ласка, надішліть запит на новий код.", - "title": "Термін дії коду закінчився" + "title": "Код застарів" }, "code_field_error": "Код не розпізнано", "code_field_label": "6-значний код", diff --git a/translations/cs.json b/translations/cs.json index 87251c309..710bb9f3a 100644 --- a/translations/cs.json +++ b/translations/cs.json @@ -151,7 +151,8 @@ "headline": "Přihlaste se k odkazu" }, "no_login_methods": "Nejsou k dispozici žádné metody přihlášení.", - "separator": "Nebo" + "separator": "Nebo", + "username_or_email": "Uživatelské jméno nebo e-mail" }, "navbar": { "my_account": "Můj účet", diff --git a/translations/da.json b/translations/da.json index ef20ccc82..b0da7552a 100644 --- a/translations/da.json +++ b/translations/da.json @@ -110,7 +110,7 @@ "click_button": "Klik på knappen nedenfor for at oprette en ny adgangskode:", "copy_link": "Kopier følgende link, og indsæt det i en browser for at oprette en ny adgangskode:", "create_new_password": "Opret ny adgangskode", - "fallback": "The button doesn't work for you?", + "fallback": "Virker knappen ikke for dig?", "headline": "Du har anmodet om en nulstilling af adgangskoden til din %(server_name)s konto.", "subject": "Nulstil adgangskoden til din konto (%(mxid)s)", "you_can_ignore": "Hvis du ikke har bedt om en ny adgangskode, kan du ignorere denne e-mail. Din nuværende adgangskode vil fortsætte med at fungere." @@ -151,7 +151,8 @@ "headline": "Log ind for at forbinde" }, "no_login_methods": "Ingen tilgængelige login metoder.", - "separator": "Eller" + "separator": "Eller", + "username_or_email": "Brugernavn eller e-mail" }, "navbar": { "my_account": "Min konto", diff --git a/translations/de.json b/translations/de.json index aab688c0b..d7f1f56fc 100644 --- a/translations/de.json +++ b/translations/de.json @@ -151,7 +151,8 @@ "headline": "Melden Sie sich zum Verbinden an" }, "no_login_methods": "Keine Anmeldemethoden verfügbar.", - "separator": "Oder" + "separator": "Oder", + "username_or_email": "Benutzername oder E-Mail-Adresse" }, "navbar": { "my_account": "Mein Konto", diff --git a/translations/et.json b/translations/et.json index a3e0d2a8f..2ca8dd065 100644 --- a/translations/et.json +++ b/translations/et.json @@ -110,7 +110,7 @@ "click_button": "Uue salasõna loomiseks klõpsa alloleval nupul:", "copy_link": "Uue salasõna loomiseks kopeeri see link ja aseta ta veebibrauseri aadressireale:", "create_new_password": "Loo uus salasõna", - "fallback": "The button doesn't work for you?", + "fallback": "Kas nupp ei toimi sinu jaoks?", "headline": "Sa soovisid lähtestada oma kasutajakontot serveris %(server_name)s", "subject": "Lähtesta oma kasutajakonto salasõna (%(mxid)s)", "you_can_ignore": "Kui sa ei soovinud oma salasõna muuta, siis palun eira seda kirja. Sinu senine salasõna jääb kehtima." @@ -124,10 +124,10 @@ "errors": { "captcha": "Robotilõksu tehtud kontroll ei luba jätkata. Palun proovi uuesti.", "denied_policy": "See on keelatud serveri kasutusjuhendi alusel: %(policy)s", - "email_banned": "E-post on serveri reeglite alusel keelatud", + "email_banned": "E-posti aadress on serveri reeglite alusel keelatud", "email_domain_banned": "E-posti aadressi domeen on serverireeglite alusel keelatud", "email_domain_not_allowed": "E-posti aadressi domeen pole serverireeglite alusel lubatud", - "email_not_allowed": "E-post pole serveri reeglite alusel lubatud", + "email_not_allowed": "E-posti aadress pole serveri reeglite alusel lubatud", "field_required": "Selle välja täitmine on kohustuslik", "invalid_credentials": "Vigased kasutajatunnused", "password_mismatch": "Salasõnad ei klapi omavahel", @@ -151,7 +151,8 @@ "headline": "Konto sidumiseks logi sisse" }, "no_login_methods": "Ühtegi sisselogimisvõimalust pole saadaval.", - "separator": "või" + "separator": "või", + "username_or_email": "Kasutajanimi või e-posti aadress" }, "navbar": { "my_account": "Minu kasutajakonto", diff --git a/translations/fi.json b/translations/fi.json index 89942236f..7780dfc11 100644 --- a/translations/fi.json +++ b/translations/fi.json @@ -151,7 +151,8 @@ "headline": "Kirjaudu sisään yhdistääksesi tilit" }, "no_login_methods": "Kirjautumistapoja ei ole käytettävissä.", - "separator": "Tai" + "separator": "Tai", + "username_or_email": "Username or Email" }, "navbar": { "my_account": "Oma tili", diff --git a/translations/fr.json b/translations/fr.json index ce303bcc7..2ec358849 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -151,7 +151,8 @@ "headline": "Se connecter pour associer" }, "no_login_methods": "Aucune méthode de connexion n'est disponible.", - "separator": "Ou" + "separator": "Ou", + "username_or_email": "Nom d'utilisateur ou adresse email" }, "navbar": { "my_account": "Mon compte", diff --git a/translations/nl.json b/translations/nl.json index 070eceadd..f96e851ba 100644 --- a/translations/nl.json +++ b/translations/nl.json @@ -151,7 +151,8 @@ "headline": "Sign in to link" }, "no_login_methods": "No login methods available.", - "separator": "Of" + "separator": "Of", + "username_or_email": "Username or Email" }, "navbar": { "my_account": "Mijn account", diff --git a/translations/pt.json b/translations/pt.json index 14f0cfd58..60e898cb8 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -151,7 +151,8 @@ "headline": "Faça login para vincular" }, "no_login_methods": "Não há métodos de login disponíveis.", - "separator": "Ou" + "separator": "Ou", + "username_or_email": "Username or Email" }, "navbar": { "my_account": "A minha conta", diff --git a/translations/ru.json b/translations/ru.json index 37e657ed9..9f69768e9 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -151,7 +151,8 @@ "headline": "Войдите, чтобы перейти по ссылке" }, "no_login_methods": "Нет доступных методов входа.", - "separator": "Или" + "separator": "Или", + "username_or_email": "Имя пользователя или электронная почта" }, "navbar": { "my_account": "Моя учётная запись", diff --git a/translations/sv.json b/translations/sv.json index 5b3c1037f..4290fa2e5 100644 --- a/translations/sv.json +++ b/translations/sv.json @@ -151,7 +151,8 @@ "headline": "Logga in för att länka" }, "no_login_methods": "Inga inloggningsmetoder tillgängliga.", - "separator": "eller" + "separator": "eller", + "username_or_email": "Username or Email" }, "navbar": { "my_account": "Mitt konto", diff --git a/translations/uk.json b/translations/uk.json index 28e3092dc..b9f4c52e7 100644 --- a/translations/uk.json +++ b/translations/uk.json @@ -17,12 +17,12 @@ }, "branding": { "privacy_policy": { - "alt": "Посилання на політику конфіденційності сервісу", - "link": "Політика конфіденційності" + "alt": "Посилання на політику приватності служби", + "link": "Політика приватності" }, "terms_and_conditions": { "alt": "Посилання на умови надання послуг", - "link": "Умови використання" + "link": "Умови та положення" } }, "common": { @@ -69,8 +69,8 @@ "new": "Новий пароль" }, "choose_display_name": { - "description": "Це ім'я, яке побачать інші люди. Ви можете змінити його в будь-який час.", - "headline": "Виберіть своє показуване ім'я" + "description": "Це ім'я, яке бачитимуть інші люди. Ви можете змінити його будь-коли.", + "headline": "Оберіть псевдонім" }, "consent": { "client_wants_access": "%(client_name)s за %(redirect_uri)s хоче отримати доступ до вашого облікового запису.", @@ -86,7 +86,7 @@ "ip_address": "IP-адреса" }, "device_code_link": { - "description": "Зв'язати пристрій", + "description": "Пов'язати пристрій", "headline": "Введіть код, що показаний на вашому пристрої" }, "device_consent": { @@ -108,21 +108,21 @@ "greeting": "Вітаємо %(username)s,", "recovery": { "click_button": "Натисніть кнопку нижче, щоб створити новий пароль:", - "copy_link": "Скопіюйте наступне посилання та вставте його в браузер, щоб створити новий пароль:", + "copy_link": "Скопіюйте це посилання та вставте його в браузер, щоб створити новий пароль:", "create_new_password": "Створити новий пароль", - "fallback": "The button doesn't work for you?", + "fallback": "Кнопка не працює?", "headline": "Ви надіслали запит на скидання пароля для свого %(server_name)s облікового запису.", "subject": "Скинути пароль облікового запису (%(mxid)s)", "you_can_ignore": "Якщо ви не запитували новий пароль, можете проігнорувати цей електронний лист. Ваш поточний пароль продовжить працювати." }, "verify": { - "body_html": "Ваш код підтвердження для підтвердження цієї електронної адреси:%(code)s", - "body_text": "Ваш код підтвердження для підтвердження цієї адреси електронної пошти: %(code)s", + "body_html": "Ваш код верифікації для підтвердження цієї електронної адреси:%(code)s", + "body_text": "Ваш код верифікації для підтвердження цієї адреси електронної пошти: %(code)s", "subject": "Ваш код підтвердження електронної пошти: %(code)s" } }, "errors": { - "captcha": "Помилка перевірки CAPTCHA, повторіть спробу", + "captcha": "Помилка верифікації CAPTCHA, повторіть спробу", "denied_policy": "Відхилено політикою: %(policy)s", "email_banned": "Електронна пошта заборонена політикою сервера", "email_domain_banned": "Домен електронної пошти заборонений політикою сервера", @@ -134,11 +134,11 @@ "rate_limit_exceeded": "Ви зробили забагато запитів за короткий проміжок часу. Зачекайте кілька хвилин і повторіть спробу.", "username_all_numeric": "Ім'я користувача не може складатися тільки з цифр", "username_banned": "Ім'я користувача заборонено політикою сервера", - "username_invalid_chars": "Ім'я користувача містить недопустимі символи. Використовуйте лише малі літери, цифри, тире та підкреслення.", + "username_invalid_chars": "Ім'я користувача містить неприпустимі символи. Використовуйте лише малі букви, цифри, тире та підкреслення.", "username_not_allowed": "Ім'я користувача не дозволено політикою сервера", "username_taken": "Це ім'я користувача вже зайнято", - "username_too_long": "Ім'я користувача занадто довге", - "username_too_short": "Ім'я користувача занадто коротке" + "username_too_long": "Ім'я користувача задовге", + "username_too_short": "Ім'я користувача закоротке" }, "login": { "call_to_register": "У вас ще немає облікового запису?", @@ -151,7 +151,8 @@ "headline": "Увійдіть, щоб повʼязати" }, "no_login_methods": "Немає доступних методів входу.", - "separator": "або" + "separator": "або", + "username_or_email": "Ім'я користувача або адреса е-пошти" }, "navbar": { "my_account": "Мій обліковий запис", @@ -166,7 +167,7 @@ "or_separator": "або", "policy_violation": { "description": "Це може бути пов'язано з клієнтом, який створив запит, користувачем, який зараз ввійшов у систему, або самим запитом.", - "heading": "Запит на авторизацію був відхилений політикою, застосованою цим сервісом", + "heading": "Запит на авторизацію відхилений політикою, застосованою цим сервісом", "logged_as": "Зареєстровано як %(username)s " }, "recovery": { @@ -180,7 +181,7 @@ }, "expired": { "description": "Запит на новий електронний лист, який буде надіслано на адресу: %(email)s.", - "heading": "Термін дії посилання для скидання пароля закінчився", + "heading": "Посилання для скидання пароля застаріло", "resend_email": "Повторно надіслати електронний лист" }, "finish": { @@ -209,7 +210,7 @@ "heading": "Створити обліковий запис" }, "sign_in_instead": "Натомість увійти", - "terms_of_service": "Я погоджуюся з Умовами використання" + "terms_of_service": "Я погоджуюся з Умовами та положеннями" }, "scope": { "edit_profile": "Редагування профілю та контактних даних", @@ -222,7 +223,7 @@ }, "upstream_oauth2": { "link_mismatch": { - "heading": "Цей upstream account вже пов'язаний з іншим обліковим записом." + "heading": "Такий обліковий запис уже пов'язаний з іншим обліковим записом." }, "register": { "choose_username": { @@ -231,11 +232,11 @@ }, "create_account": "Створити новий обліковий запис", "enforced_by_policy": "Застосовується політикою сервера", - "forced_display_name": "Використовуватиме таке показуване ім’я", - "forced_email": "Буде використано наступну електронну адресу", - "forced_localpart": "Буде використовувати наступне ім'я користувача", + "forced_display_name": "Використовуватиме такий псевдонім", + "forced_email": "Буде використано цю електронну адресу", + "forced_localpart": "Буде використовувати це ім'я користувача", "import_data": { - "description": "Підтвердіть інформацію, яка буде прив'язана до вашого нового %(server_name)s облікового запису.", + "description": "Підтвердьте інформацію, яка буде прив'язана до вашого нового %(server_name)s облікового запису.", "heading": "Імпортуйте свої дані" }, "imported_from_upstream": "Імпортовано з вашого upstream облікового запису", diff --git a/translations/zh-Hans.json b/translations/zh-Hans.json index 73b959d71..0deed26de 100644 --- a/translations/zh-Hans.json +++ b/translations/zh-Hans.json @@ -151,7 +151,8 @@ "headline": "登录以链接" }, "no_login_methods": "没有可用的登录途径。", - "separator": "或" + "separator": "或", + "username_or_email": "Username or Email" }, "navbar": { "my_account": "我的账户", From 0b43c935a32a4efca7d0a3fed388ce9777f58306 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 15:43:33 +0000 Subject: [PATCH 20/23] build(deps): bump psl from 2.1.99 to 2.1.100 Bumps [psl](https://github.com/addr-rs/psl) from 2.1.99 to 2.1.100. - [Release notes](https://github.com/addr-rs/psl/releases) - [Commits](https://github.com/addr-rs/psl/compare/v2.1.99...v2.1.100) --- updated-dependencies: - dependency-name: psl dependency-version: 2.1.100 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- crates/handlers/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9af582a2..2f1ff71eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4724,9 +4724,9 @@ dependencies = [ [[package]] name = "psl" -version = "2.1.99" +version = "2.1.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89186bbd6ebdabc007b20bda4807abe8f4ad0636d0fb86be20f8cfce25b8f4b3" +checksum = "70295efe3fd3db60e81f452e2eacc407b4e6c2e1ff7f763424ae6e16105cee26" dependencies = [ "psl-types", ] diff --git a/crates/handlers/Cargo.toml b/crates/handlers/Cargo.toml index b8678ce9f..65c7bbb6f 100644 --- a/crates/handlers/Cargo.toml +++ b/crates/handlers/Cargo.toml @@ -76,7 +76,7 @@ hex.workspace = true governor.workspace = true indexmap.workspace = true pkcs8.workspace = true -psl = "2.1.99" +psl = "2.1.100" sha2.workspace = true time = "0.3.41" url.workspace = true From a4188bb0e3e98faae7f4f23cfa169ceef2c5a49e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 15:44:34 +0000 Subject: [PATCH 21/23] Translations updates --- translations/fi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/fi.json b/translations/fi.json index 7780dfc11..22dbc0919 100644 --- a/translations/fi.json +++ b/translations/fi.json @@ -152,7 +152,7 @@ }, "no_login_methods": "Kirjautumistapoja ei ole käytettävissä.", "separator": "Tai", - "username_or_email": "Username or Email" + "username_or_email": "Käyttäjänimi tai Sähköpostiosoite" }, "navbar": { "my_account": "Oma tili", From c636125d405bf78f1f3ba435106b46c150b41890 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Mon, 14 Apr 2025 17:52:12 +0200 Subject: [PATCH 22/23] Update MSW --- frontend/.storybook/public/mockServiceWorker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/.storybook/public/mockServiceWorker.js b/frontend/.storybook/public/mockServiceWorker.js index 34057e898..a02290705 100644 --- a/frontend/.storybook/public/mockServiceWorker.js +++ b/frontend/.storybook/public/mockServiceWorker.js @@ -8,7 +8,7 @@ * - Please do NOT serve this file on production. */ -const PACKAGE_VERSION = '2.7.3' +const PACKAGE_VERSION = '2.7.4' const INTEGRITY_CHECKSUM = '00729d72e3b82faf54ca8b9621dbb96f' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() From 6232cb9dc023f6cfc577d60957a5809271ca0c0d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 15:57:46 +0000 Subject: [PATCH 23/23] 0.15.0-rc.0 --- Cargo.lock | 54 +++++++++++++++--------------- Cargo.toml | 58 ++++++++++++++++----------------- tools/syn2mas/package-lock.json | 4 +-- tools/syn2mas/package.json | 2 +- 4 files changed, 59 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f1ff71eb..d02df0709 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3114,7 +3114,7 @@ dependencies = [ [[package]] name = "mas-axum-utils" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "axum", "axum-extra", @@ -3147,7 +3147,7 @@ dependencies = [ [[package]] name = "mas-cli" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "anyhow", "axum", @@ -3218,7 +3218,7 @@ dependencies = [ [[package]] name = "mas-config" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "anyhow", "camino", @@ -3248,7 +3248,7 @@ dependencies = [ [[package]] name = "mas-data-model" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "base64ct", "chrono", @@ -3269,7 +3269,7 @@ dependencies = [ [[package]] name = "mas-email" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "async-trait", "lettre", @@ -3280,7 +3280,7 @@ dependencies = [ [[package]] name = "mas-handlers" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "aide", "anyhow", @@ -3357,7 +3357,7 @@ dependencies = [ [[package]] name = "mas-http" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "futures-util", "headers", @@ -3378,7 +3378,7 @@ dependencies = [ [[package]] name = "mas-i18n" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "camino", "icu_calendar", @@ -3400,7 +3400,7 @@ dependencies = [ [[package]] name = "mas-i18n-scan" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "camino", "clap", @@ -3414,7 +3414,7 @@ dependencies = [ [[package]] name = "mas-iana" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "schemars", "serde", @@ -3422,7 +3422,7 @@ dependencies = [ [[package]] name = "mas-iana-codegen" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "anyhow", "async-trait", @@ -3438,7 +3438,7 @@ dependencies = [ [[package]] name = "mas-jose" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "base64ct", "chrono", @@ -3468,7 +3468,7 @@ dependencies = [ [[package]] name = "mas-keystore" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "aead", "base64ct", @@ -3496,7 +3496,7 @@ dependencies = [ [[package]] name = "mas-listener" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "anyhow", "bytes", @@ -3520,7 +3520,7 @@ dependencies = [ [[package]] name = "mas-matrix" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "anyhow", "async-trait", @@ -3530,7 +3530,7 @@ dependencies = [ [[package]] name = "mas-matrix-synapse" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "anyhow", "async-trait", @@ -3547,7 +3547,7 @@ dependencies = [ [[package]] name = "mas-oidc-client" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "assert_matches", "async-trait", @@ -3583,7 +3583,7 @@ dependencies = [ [[package]] name = "mas-policy" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "anyhow", "arc-swap", @@ -3600,7 +3600,7 @@ dependencies = [ [[package]] name = "mas-router" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "axum", "serde", @@ -3611,7 +3611,7 @@ dependencies = [ [[package]] name = "mas-spa" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "camino", "serde", @@ -3620,7 +3620,7 @@ dependencies = [ [[package]] name = "mas-storage" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "async-trait", "chrono", @@ -3642,7 +3642,7 @@ dependencies = [ [[package]] name = "mas-storage-pg" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "async-trait", "chrono", @@ -3668,7 +3668,7 @@ dependencies = [ [[package]] name = "mas-tasks" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "anyhow", "async-trait", @@ -3699,7 +3699,7 @@ dependencies = [ [[package]] name = "mas-templates" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "anyhow", "arc-swap", @@ -3729,7 +3729,7 @@ dependencies = [ [[package]] name = "mas-tower" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "http", "opentelemetry", @@ -3999,7 +3999,7 @@ dependencies = [ [[package]] name = "oauth2-types" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "assert_matches", "base64ct", @@ -6149,7 +6149,7 @@ dependencies = [ [[package]] name = "syn2mas" -version = "0.14.1" +version = "0.15.0-rc.0" dependencies = [ "anyhow", "arc-swap", diff --git a/Cargo.toml b/Cargo.toml index 97ab4d497..a5f1ee216 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = ["crates/*"] resolver = "2" # Updated in the CI with a `sed` command -package.version = "0.14.1" +package.version = "0.15.0-rc.0" package.license = "AGPL-3.0-only" package.authors = ["Element Backend Team"] package.edition = "2024" @@ -27,34 +27,34 @@ broken_intra_doc_links = "deny" [workspace.dependencies] # Workspace crates -mas-axum-utils = { path = "./crates/axum-utils/", version = "=0.14.1" } -mas-cli = { path = "./crates/cli/", version = "=0.14.1" } -mas-config = { path = "./crates/config/", version = "=0.14.1" } -mas-data-model = { path = "./crates/data-model/", version = "=0.14.1" } -mas-email = { path = "./crates/email/", version = "=0.14.1" } -mas-graphql = { path = "./crates/graphql/", version = "=0.14.1" } -mas-handlers = { path = "./crates/handlers/", version = "=0.14.1" } -mas-http = { path = "./crates/http/", version = "=0.14.1" } -mas-i18n = { path = "./crates/i18n/", version = "=0.14.1" } -mas-i18n-scan = { path = "./crates/i18n-scan/", version = "=0.14.1" } -mas-iana = { path = "./crates/iana/", version = "=0.14.1" } -mas-iana-codegen = { path = "./crates/iana-codegen/", version = "=0.14.1" } -mas-jose = { path = "./crates/jose/", version = "=0.14.1" } -mas-keystore = { path = "./crates/keystore/", version = "=0.14.1" } -mas-listener = { path = "./crates/listener/", version = "=0.14.1" } -mas-matrix = { path = "./crates/matrix/", version = "=0.14.1" } -mas-matrix-synapse = { path = "./crates/matrix-synapse/", version = "=0.14.1" } -mas-oidc-client = { path = "./crates/oidc-client/", version = "=0.14.1" } -mas-policy = { path = "./crates/policy/", version = "=0.14.1" } -mas-router = { path = "./crates/router/", version = "=0.14.1" } -mas-spa = { path = "./crates/spa/", version = "=0.14.1" } -mas-storage = { path = "./crates/storage/", version = "=0.14.1" } -mas-storage-pg = { path = "./crates/storage-pg/", version = "=0.14.1" } -mas-tasks = { path = "./crates/tasks/", version = "=0.14.1" } -mas-templates = { path = "./crates/templates/", version = "=0.14.1" } -mas-tower = { path = "./crates/tower/", version = "=0.14.1" } -oauth2-types = { path = "./crates/oauth2-types/", version = "=0.14.1" } -syn2mas = { path = "./crates/syn2mas", version = "=0.14.1" } +mas-axum-utils = { path = "./crates/axum-utils/", version = "=0.15.0-rc.0" } +mas-cli = { path = "./crates/cli/", version = "=0.15.0-rc.0" } +mas-config = { path = "./crates/config/", version = "=0.15.0-rc.0" } +mas-data-model = { path = "./crates/data-model/", version = "=0.15.0-rc.0" } +mas-email = { path = "./crates/email/", version = "=0.15.0-rc.0" } +mas-graphql = { path = "./crates/graphql/", version = "=0.15.0-rc.0" } +mas-handlers = { path = "./crates/handlers/", version = "=0.15.0-rc.0" } +mas-http = { path = "./crates/http/", version = "=0.15.0-rc.0" } +mas-i18n = { path = "./crates/i18n/", version = "=0.15.0-rc.0" } +mas-i18n-scan = { path = "./crates/i18n-scan/", version = "=0.15.0-rc.0" } +mas-iana = { path = "./crates/iana/", version = "=0.15.0-rc.0" } +mas-iana-codegen = { path = "./crates/iana-codegen/", version = "=0.15.0-rc.0" } +mas-jose = { path = "./crates/jose/", version = "=0.15.0-rc.0" } +mas-keystore = { path = "./crates/keystore/", version = "=0.15.0-rc.0" } +mas-listener = { path = "./crates/listener/", version = "=0.15.0-rc.0" } +mas-matrix = { path = "./crates/matrix/", version = "=0.15.0-rc.0" } +mas-matrix-synapse = { path = "./crates/matrix-synapse/", version = "=0.15.0-rc.0" } +mas-oidc-client = { path = "./crates/oidc-client/", version = "=0.15.0-rc.0" } +mas-policy = { path = "./crates/policy/", version = "=0.15.0-rc.0" } +mas-router = { path = "./crates/router/", version = "=0.15.0-rc.0" } +mas-spa = { path = "./crates/spa/", version = "=0.15.0-rc.0" } +mas-storage = { path = "./crates/storage/", version = "=0.15.0-rc.0" } +mas-storage-pg = { path = "./crates/storage-pg/", version = "=0.15.0-rc.0" } +mas-tasks = { path = "./crates/tasks/", version = "=0.15.0-rc.0" } +mas-templates = { path = "./crates/templates/", version = "=0.15.0-rc.0" } +mas-tower = { path = "./crates/tower/", version = "=0.15.0-rc.0" } +oauth2-types = { path = "./crates/oauth2-types/", version = "=0.15.0-rc.0" } +syn2mas = { path = "./crates/syn2mas", version = "=0.15.0-rc.0" } # OpenAPI schema generation and validation [workspace.dependencies.aide] diff --git a/tools/syn2mas/package-lock.json b/tools/syn2mas/package-lock.json index 00e2abd36..1e9e16bf7 100644 --- a/tools/syn2mas/package-lock.json +++ b/tools/syn2mas/package-lock.json @@ -1,12 +1,12 @@ { "name": "@vector-im/syn2mas", - "version": "0.14.1", + "version": "0.15.0-rc.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@vector-im/syn2mas", - "version": "0.14.1", + "version": "0.15.0-rc.0", "license": "AGPL-3.0-only", "dependencies": { "command-line-args": "^6.0.0", diff --git a/tools/syn2mas/package.json b/tools/syn2mas/package.json index 807535860..ed777771c 100644 --- a/tools/syn2mas/package.json +++ b/tools/syn2mas/package.json @@ -1,6 +1,6 @@ { "name": "@vector-im/syn2mas", - "version": "0.14.1", + "version": "0.15.0-rc.0", "description": "A tool to migrate Synapse users and sessions to the Matrix Authentication Service", "license": "AGPL-3.0-only", "author": "Matrix.org",