diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2f82ada41..55da1a0b5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -215,7 +215,7 @@ jobs: uses: actions/checkout@v5 - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@1.87.0 + uses: dtolnay/rust-toolchain@1.89.0 with: components: clippy diff --git a/Dockerfile b/Dockerfile index 1c1c66b56..14eda1924 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ # The Debian version and version name must be in sync ARG DEBIAN_VERSION=12 ARG DEBIAN_VERSION_NAME=bookworm -ARG RUSTC_VERSION=1.87.0 +ARG RUSTC_VERSION=1.89.0 ARG NODEJS_VERSION=20.15.0 ARG OPA_VERSION=1.1.0 ARG CARGO_AUDITABLE_VERSION=0.6.6 diff --git a/clippy.toml b/clippy.toml index 41d584369..218811441 100644 --- a/clippy.toml +++ b/clippy.toml @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial # Please see LICENSE files in the repository root for full details. -doc-valid-idents = ["OpenID", "OAuth", "..", "PostgreSQL", "SQLite"] +doc-valid-idents = ["OpenID", "OAuth", "UserInfo", "..", "PostgreSQL", "SQLite"] disallowed-methods = [ { path = "rand::thread_rng", reason = "do not create rngs on the fly, pass them as parameters" }, diff --git a/crates/cli/build.rs b/crates/cli/build.rs index 7bad5d337..fd111273b 100644 --- a/crates/cli/build.rs +++ b/crates/cli/build.rs @@ -12,13 +12,13 @@ fn main() -> anyhow::Result<()> { // At build time, we override the version through the environment variable // VERGEN_GIT_DESCRIBE. In some contexts, it means this variable is set but // empty, so we unset it here. - if let Ok(ver) = std::env::var("VERGEN_GIT_DESCRIBE") { - if ver.is_empty() { - #[allow(unsafe_code)] - // SAFETY: This is safe because the build script is running a single thread - unsafe { - std::env::remove_var("VERGEN_GIT_DESCRIBE"); - } + if let Ok(ver) = std::env::var("VERGEN_GIT_DESCRIBE") + && ver.is_empty() + { + #[allow(unsafe_code)] + // SAFETY: This is safe because the build script is running a single thread + unsafe { + std::env::remove_var("VERGEN_GIT_DESCRIBE"); } } diff --git a/crates/cli/src/app_state.rs b/crates/cli/src/app_state.rs index dd7dd6c99..40ae94806 100644 --- a/crates/cli/src/app_state.rs +++ b/crates/cli/src/app_state.rs @@ -275,10 +275,10 @@ fn infer_client_ip( let peer = if let Some(info) = connection_info { // We can always trust the proxy protocol to give us the correct IP address - if let Some(proxy) = info.get_proxy_ref() { - if let Some(source) = proxy.source() { - return Some(source.ip()); - } + if let Some(proxy) = info.get_proxy_ref() + && let Some(source) = proxy.source() + { + return Some(source.ip()); } info.get_peer_addr().map(|addr| addr.ip()) diff --git a/crates/cli/src/commands/manage.rs b/crates/cli/src/commands/manage.rs index e991c1716..cedfcee2b 100644 --- a/crates/cli/src/commands/manage.rs +++ b/crates/cli/src/commands/manage.rs @@ -619,13 +619,12 @@ impl Options { let txn = conn.begin().await?; let mut repo = PgRepository::from_conn(txn); - if let Some(password) = &password { - if !ignore_password_complexity - && !password_manager.is_password_complex_enough(password)? - { - error!("That password is too weak."); - return Ok(ExitCode::from(1)); - } + if let Some(password) = &password + && !ignore_password_complexity + && !password_manager.is_password_complex_enough(password)? + { + error!("That password is too weak."); + return Ok(ExitCode::from(1)); } // If the username is provided, check if it's available and normalize it. diff --git a/crates/cli/src/sync.rs b/crates/cli/src/sync.rs index 700b8eea8..965a4fc95 100644 --- a/crates/cli/src/sync.rs +++ b/crates/cli/src/sync.rs @@ -208,11 +208,11 @@ pub async fn config_sync( // private key to hold the content of the private key file. // private key (raw) takes precedence so both can be defined // without issues - if siwa.private_key.is_none() { - if let Some(private_key_file) = siwa.private_key_file.take() { - let key = tokio::fs::read_to_string(private_key_file).await?; - siwa.private_key = Some(key); - } + if siwa.private_key.is_none() + && let Some(private_key_file) = siwa.private_key_file.take() + { + let key = tokio::fs::read_to_string(private_key_file).await?; + siwa.private_key = Some(key); } let encoded = serde_json::to_vec(&siwa)?; Some(encrypter.encrypt_to_string(&encoded)?) diff --git a/crates/config/src/sections/secrets.rs b/crates/config/src/sections/secrets.rs index 7886e9a57..4bfd30ce3 100644 --- a/crates/config/src/sections/secrets.rs +++ b/crates/config/src/sections/secrets.rs @@ -149,7 +149,7 @@ impl KeyConfig { /// Returns the password in case any is provided. /// /// If `password_file` was given, the password is read from that file. - async fn password(&self) -> anyhow::Result>> { + async fn password(&self) -> anyhow::Result>> { Ok(match &self.password { Some(Password::File(path)) => Some(Cow::Owned(tokio::fs::read(path).await?)), Some(Password::Value(password)) => Some(Cow::Borrowed(password.as_bytes())), @@ -160,7 +160,7 @@ impl KeyConfig { /// Returns the key. /// /// If `key_file` was given, the key is read from that file. - async fn key(&self) -> anyhow::Result> { + async fn key(&self) -> anyhow::Result> { Ok(match &self.key { Key::File(path) => Cow::Owned(tokio::fs::read(path).await?), Key::Value(key) => Cow::Borrowed(key.as_bytes()), diff --git a/crates/config/src/sections/telemetry.rs b/crates/config/src/sections/telemetry.rs index 8e2d995e9..44e75a354 100644 --- a/crates/config/src/sections/telemetry.rs +++ b/crates/config/src/sections/telemetry.rs @@ -198,34 +198,34 @@ impl ConfigurationSection for TelemetryConfig { &self, _figment: &figment::Figment, ) -> Result<(), Box> { - if let Some(sample_rate) = self.sentry.sample_rate { - if !(0.0..=1.0).contains(&sample_rate) { - return Err(figment::error::Error::custom( - "Sentry sample rate must be between 0.0 and 1.0", - ) - .with_path("sentry.sample_rate") - .into()); - } + if let Some(sample_rate) = self.sentry.sample_rate + && !(0.0..=1.0).contains(&sample_rate) + { + return Err(figment::error::Error::custom( + "Sentry sample rate must be between 0.0 and 1.0", + ) + .with_path("sentry.sample_rate") + .into()); } - if let Some(sample_rate) = self.sentry.traces_sample_rate { - if !(0.0..=1.0).contains(&sample_rate) { - return Err(figment::error::Error::custom( - "Sentry sample rate must be between 0.0 and 1.0", - ) - .with_path("sentry.traces_sample_rate") - .into()); - } + if let Some(sample_rate) = self.sentry.traces_sample_rate + && !(0.0..=1.0).contains(&sample_rate) + { + return Err(figment::error::Error::custom( + "Sentry sample rate must be between 0.0 and 1.0", + ) + .with_path("sentry.traces_sample_rate") + .into()); } - if let Some(sample_rate) = self.tracing.sample_rate { - if !(0.0..=1.0).contains(&sample_rate) { - return Err(figment::error::Error::custom( - "Tracing sample rate must be between 0.0 and 1.0", - ) - .with_path("tracing.sample_rate") - .into()); - } + if let Some(sample_rate) = self.tracing.sample_rate + && !(0.0..=1.0).contains(&sample_rate) + { + return Err(figment::error::Error::custom( + "Tracing sample rate must be between 0.0 and 1.0", + ) + .with_path("tracing.sample_rate") + .into()); } Ok(()) diff --git a/crates/config/src/sections/upstream_oauth2.rs b/crates/config/src/sections/upstream_oauth2.rs index 9b2768423..05f70cc67 100644 --- a/crates/config/src/sections/upstream_oauth2.rs +++ b/crates/config/src/sections/upstream_oauth2.rs @@ -652,7 +652,7 @@ pub struct Provider { /// What to do when receiving an OIDC Backchannel logout request. /// - /// Defaults to "do_nothing". + /// Defaults to `do_nothing`. #[serde(default, skip_serializing_if = "OnBackchannelLogout::is_default")] pub on_backchannel_logout: OnBackchannelLogout, } diff --git a/crates/context/src/fmt.rs b/crates/context/src/fmt.rs index 579ea63a9..25908a2ca 100644 --- a/crates/context/src/fmt.rs +++ b/crates/context/src/fmt.rs @@ -129,31 +129,31 @@ where field_fromatter.format_fields(writer.by_ref(), event)?; // If we have a OTEL span, we can add the trace ID to the end of the log line - if let Some(span) = ctx.lookup_current() { - if let Some(otel) = span.extensions().get::() { - let parent_cx_span = otel.parent_cx.span(); - let sc = parent_cx_span.span_context(); - - // Check if the span is sampled, first from the span builder, - // then from the parent context if nothing is set there - if otel - .builder - .sampling_result - .as_ref() - .map_or(sc.is_sampled(), |r| { - r.decision == SamplingDecision::RecordAndSample - }) - { - // If it is the root span, the trace ID will be in the span builder. Else, it - // will be in the parent OTEL context - let trace_id = otel.builder.trace_id.unwrap_or(sc.trace_id()); - if trace_id != TraceId::INVALID { - let label = Style::new() - .italic() - .force_styling(ansi) - .apply_to("trace.id"); - write!(&mut writer, " {label}={trace_id}")?; - } + if let Some(span) = ctx.lookup_current() + && let Some(otel) = span.extensions().get::() + { + let parent_cx_span = otel.parent_cx.span(); + let sc = parent_cx_span.span_context(); + + // Check if the span is sampled, first from the span builder, + // then from the parent context if nothing is set there + if otel + .builder + .sampling_result + .as_ref() + .map_or(sc.is_sampled(), |r| { + r.decision == SamplingDecision::RecordAndSample + }) + { + // If it is the root span, the trace ID will be in the span builder. Else, it + // will be in the parent OTEL context + let trace_id = otel.builder.trace_id.unwrap_or(sc.trace_id()); + if trace_id != TraceId::INVALID { + let label = Style::new() + .italic() + .force_styling(ansi) + .apply_to("trace.id"); + write!(&mut writer, " {label}={trace_id}")?; } } } diff --git a/crates/data-model/src/oauth2/authorization_grant.rs b/crates/data-model/src/oauth2/authorization_grant.rs index 383701bcb..d6699f575 100644 --- a/crates/data-model/src/oauth2/authorization_grant.rs +++ b/crates/data-model/src/oauth2/authorization_grant.rs @@ -173,7 +173,7 @@ impl std::ops::Deref for AuthorizationGrant { impl AuthorizationGrant { #[must_use] - pub fn parse_login_hint(&self, homeserver: &str) -> LoginHint { + pub fn parse_login_hint(&self, homeserver: &str) -> LoginHint<'_> { let Some(login_hint) = &self.login_hint else { return LoginHint::None; }; diff --git a/crates/data-model/src/upstream_oauth2/provider.rs b/crates/data-model/src/upstream_oauth2/provider.rs index c54e40d15..be42cb5a5 100644 --- a/crates/data-model/src/upstream_oauth2/provider.rs +++ b/crates/data-model/src/upstream_oauth2/provider.rs @@ -289,7 +289,7 @@ pub struct UpstreamOAuthProvider { impl PartialOrd for UpstreamOAuthProvider { fn partial_cmp(&self, other: &Self) -> Option { - Some(self.id.cmp(&other.id)) + Some(self.cmp(other)) } } diff --git a/crates/data-model/src/user_agent.rs b/crates/data-model/src/user_agent.rs index 4efb4ff9f..d0e930586 100644 --- a/crates/data-model/src/user_agent.rs +++ b/crates/data-model/src/user_agent.rs @@ -88,32 +88,31 @@ impl UserAgent { #[must_use] pub fn parse(user_agent: String) -> Self { - if !user_agent.contains("Mozilla/") { - if let Some((name, version, model, os, os_version)) = + if !user_agent.contains("Mozilla/") + && let Some((name, version, model, os, os_version)) = UserAgent::parse_custom(&user_agent) - { - let mut device_type = DeviceType::Unknown; - - // Handle mobile simple mobile devices - if os == "Android" || os == "iOS" { - device_type = DeviceType::Mobile; - } - - // Handle iPads - if model.contains("iPad") { - device_type = DeviceType::Tablet; - } - - return Self { - name: Some(name.to_owned()), - version: Some(version.to_owned()), - os: Some(os.to_owned()), - os_version: os_version.map(std::borrow::ToOwned::to_owned), - model: Some(model.to_owned()), - device_type, - raw: user_agent, - }; + { + let mut device_type = DeviceType::Unknown; + + // Handle mobile simple mobile devices + if os == "Android" || os == "iOS" { + device_type = DeviceType::Mobile; + } + + // Handle iPads + if model.contains("iPad") { + device_type = DeviceType::Tablet; } + + return Self { + name: Some(name.to_owned()), + version: Some(version.to_owned()), + os: Some(os.to_owned()), + os_version: os_version.map(std::borrow::ToOwned::to_owned), + model: Some(model.to_owned()), + device_type, + raw: user_agent, + }; } let mut model = None; @@ -205,11 +204,11 @@ impl UserAgent { } // Special handling for Electron applications e.g. Element Desktop - if user_agent.contains("Electron/") { - if let Some(app) = UserAgent::parse_electron(&user_agent) { - result.name = app.0; - result.version = app.1; - } + if user_agent.contains("Electron/") + && let Some(app) = UserAgent::parse_electron(&user_agent) + { + result.name = app.0; + result.version = app.1; } Self { diff --git a/crates/data-model/src/users.rs b/crates/data-model/src/users.rs index c2addf8a3..920726ef8 100644 --- a/crates/data-model/src/users.rs +++ b/crates/data-model/src/users.rs @@ -223,17 +223,17 @@ impl UserRegistrationToken { } // Check if expired - if let Some(expires_at) = self.expires_at { - if now >= expires_at { - return false; - } + if let Some(expires_at) = self.expires_at + && now >= expires_at + { + return false; } // Check if usage limit exceeded - if let Some(usage_limit) = self.usage_limit { - if self.times_used >= usage_limit { - return false; - } + if let Some(usage_limit) = self.usage_limit + && self.times_used >= usage_limit + { + return false; } true diff --git a/crates/handlers/src/admin/call_context.rs b/crates/handlers/src/admin/call_context.rs index d5a5f4b94..a0470f851 100644 --- a/crates/handlers/src/admin/call_context.rs +++ b/crates/handlers/src/admin/call_context.rs @@ -187,10 +187,10 @@ where }; // If there is a user for this session, check that it is not locked - if let Some(user) = &user { - if !user.is_valid() { - return Err(Rejection::UserLocked); - } + if let Some(user) = &user + && !user.is_valid() + { + return Err(Rejection::UserLocked); } if !session.is_valid() { diff --git a/crates/handlers/src/graphql/model/oauth.rs b/crates/handlers/src/graphql/model/oauth.rs index 4f628e8bd..20a4d527f 100644 --- a/crates/handlers/src/graphql/model/oauth.rs +++ b/crates/handlers/src/graphql/model/oauth.rs @@ -8,8 +8,7 @@ use anyhow::Context as _; use async_graphql::{Context, Description, Enum, ID, Object}; use chrono::{DateTime, Utc}; use mas_storage::{oauth2::OAuth2ClientRepository, user::BrowserSessionRepository}; -use oauth2_types::{oidc::ApplicationType, scope::Scope}; -use ulid::Ulid; +use oauth2_types::oidc::ApplicationType; use url::Url; use super::{BrowserSession, NodeType, SessionState, User, UserAgent}; @@ -200,33 +199,3 @@ impl OAuth2Client { } } } - -/// An OAuth 2.0 consent represents the scope a user consented to grant to a -/// client. -#[derive(Description)] -pub struct OAuth2Consent { - scope: Scope, - client_id: Ulid, -} - -#[Object(use_type_description)] -impl OAuth2Consent { - /// Scope consented by the user for this client. - pub async fn scope(&self) -> String { - self.scope.to_string() - } - - /// OAuth 2.0 client for which the user granted access. - pub async fn client(&self, ctx: &Context<'_>) -> Result { - let state = ctx.state(); - let mut repo = state.repository().await?; - let client = repo - .oauth2_client() - .lookup(self.client_id) - .await? - .context("Could not load client")?; - repo.cancel().await?; - - Ok(OAuth2Client(client)) - } -} diff --git a/crates/handlers/src/graphql/tests.rs b/crates/handlers/src/graphql/tests.rs index bc5079924..328b6f152 100644 --- a/crates/handlers/src/graphql/tests.rs +++ b/crates/handlers/src/graphql/tests.rs @@ -348,7 +348,7 @@ async fn test_oauth2_admin(pool: PgPool) { } /// Test that we can query the GraphQL endpoint with a token from a -/// client_credentials grant. +/// `client_credentials` grant. #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")] async fn test_oauth2_client_credentials(pool: PgPool) { setup(); diff --git a/crates/handlers/src/lib.rs b/crates/handlers/src/lib.rs index 08367bbf5..8de6707be 100644 --- a/crates/handlers/src/lib.rs +++ b/crates/handlers/src/lib.rs @@ -475,13 +475,13 @@ fn recover_error( ) -> axum::response::Response { // Error responses should have an ErrorContext attached to them let ext = response.extensions().get::(); - if let Some(ctx) = ext { - if let Ok(res) = templates.render_error(ctx) { - let (mut parts, _original_body) = response.into_parts(); - parts.headers.remove(CONTENT_TYPE); - parts.headers.remove(CONTENT_LENGTH); - return (parts, Html(res)).into_response(); - } + if let Some(ctx) = ext + && let Ok(res) = templates.render_error(ctx) + { + let (mut parts, _original_body) = response.into_parts(); + parts.headers.remove(CONTENT_TYPE); + parts.headers.remove(CONTENT_LENGTH); + return (parts, Html(res)).into_response(); } response diff --git a/crates/handlers/src/oauth2/introspection.rs b/crates/handlers/src/oauth2/introspection.rs index 678c7fa1c..e52e3c303 100644 --- a/crates/handlers/src/oauth2/introspection.rs +++ b/crates/handlers/src/oauth2/introspection.rs @@ -288,10 +288,10 @@ pub(crate) async fn post( let token = &form.token; let token_type = TokenType::check(token)?; - if let Some(hint) = form.token_type_hint { - if token_type != hint { - return Err(RouteError::UnexpectedTokenType); - } + if let Some(hint) = form.token_type_hint + && token_type != hint + { + return Err(RouteError::UnexpectedTokenType); } // Not all device IDs can be encoded as scope. On OAuth 2.0 sessions, we diff --git a/crates/handlers/src/oauth2/registration.rs b/crates/handlers/src/oauth2/registration.rs index 09ace7351..0cdfb32ea 100644 --- a/crates/handlers/src/oauth2/registration.rs +++ b/crates/handlers/src/oauth2/registration.rs @@ -241,34 +241,34 @@ pub(crate) async fn post( // Some extra validation that is hard to do in OPA and not done by the // `validate` method either - if let Some(client_uri) = &metadata.client_uri { - if localised_url_has_public_suffix(client_uri) { - return Err(RouteError::UrlIsPublicSuffix("client_uri")); - } + if let Some(client_uri) = &metadata.client_uri + && localised_url_has_public_suffix(client_uri) + { + return Err(RouteError::UrlIsPublicSuffix("client_uri")); } - if let Some(logo_uri) = &metadata.logo_uri { - if localised_url_has_public_suffix(logo_uri) { - return Err(RouteError::UrlIsPublicSuffix("logo_uri")); - } + if let Some(logo_uri) = &metadata.logo_uri + && localised_url_has_public_suffix(logo_uri) + { + return Err(RouteError::UrlIsPublicSuffix("logo_uri")); } - if let Some(policy_uri) = &metadata.policy_uri { - if localised_url_has_public_suffix(policy_uri) { - return Err(RouteError::UrlIsPublicSuffix("policy_uri")); - } + if let Some(policy_uri) = &metadata.policy_uri + && localised_url_has_public_suffix(policy_uri) + { + return Err(RouteError::UrlIsPublicSuffix("policy_uri")); } - if let Some(tos_uri) = &metadata.tos_uri { - if localised_url_has_public_suffix(tos_uri) { - return Err(RouteError::UrlIsPublicSuffix("tos_uri")); - } + if let Some(tos_uri) = &metadata.tos_uri + && localised_url_has_public_suffix(tos_uri) + { + return Err(RouteError::UrlIsPublicSuffix("tos_uri")); } - if let Some(initiate_login_uri) = &metadata.initiate_login_uri { - if host_is_public_suffix(initiate_login_uri) { - return Err(RouteError::UrlIsPublicSuffix("initiate_login_uri")); - } + if let Some(initiate_login_uri) = &metadata.initiate_login_uri + && host_is_public_suffix(initiate_login_uri) + { + return Err(RouteError::UrlIsPublicSuffix("initiate_login_uri")); } for redirect_uri in metadata.redirect_uris() { diff --git a/crates/handlers/src/upstream_oauth2/authorize.rs b/crates/handlers/src/upstream_oauth2/authorize.rs index 016dd36d3..c9e96f936 100644 --- a/crates/handlers/src/upstream_oauth2/authorize.rs +++ b/crates/handlers/src/upstream_oauth2/authorize.rs @@ -93,17 +93,15 @@ pub(crate) async fn get( // Forward the raw login hint upstream for the provider to handle however it // sees fit - if provider.forward_login_hint { - if let Some(PostAuthAction::ContinueAuthorizationGrant { id }) = &query.post_auth_action { - if let Some(login_hint) = repo - .oauth2_authorization_grant() - .lookup(*id) - .await? - .and_then(|grant| grant.login_hint) - { - data = data.with_login_hint(login_hint); - } - } + if provider.forward_login_hint + && let Some(PostAuthAction::ContinueAuthorizationGrant { id }) = &query.post_auth_action + && let Some(login_hint) = repo + .oauth2_authorization_grant() + .lookup(*id) + .await? + .and_then(|grant| grant.login_hint) + { + data = data.with_login_hint(login_hint); } let data = if let Some(methods) = lazy_metadata.pkce_methods().await? { diff --git a/crates/handlers/src/views/logout.rs b/crates/handlers/src/views/logout.rs index 731071c8b..9d0c3ec5a 100644 --- a/crates/handlers/src/views/logout.rs +++ b/crates/handlers/src/views/logout.rs @@ -33,14 +33,14 @@ pub(crate) async fn post( if let Some(session_id) = session_info.current_session_id() { let maybe_session = repo.browser_session().lookup(session_id).await?; - if let Some(session) = maybe_session { - if session.finished_at.is_none() { - activity_tracker - .record_browser_session(&clock, &session) - .await; - - repo.browser_session().finish(&clock, session).await?; - } + if let Some(session) = maybe_session + && session.finished_at.is_none() + { + activity_tracker + .record_browser_session(&clock, &session) + .await; + + repo.browser_session().finish(&clock, session).await?; } } diff --git a/crates/i18n-scan/src/minijinja.rs b/crates/i18n-scan/src/minijinja.rs index b6e9dda51..63aa63aea 100644 --- a/crates/i18n-scan/src/minijinja.rs +++ b/crates/i18n-scan/src/minijinja.rs @@ -110,36 +110,36 @@ fn find_in_call<'a>( call: &'a Spanned>, ) -> Result<(), minijinja::Error> { let span = call.span(); - if let Expr::Var(var_) = &call.expr { - if var_.id == context.func() { - let key = call - .args - .first() - .and_then(as_const) - .and_then(|const_| const_.value.as_str()) - .ok_or(minijinja::Error::new( - ErrorKind::UndefinedError, - "t() first argument must be a string literal", - ))?; - - let has_count = call - .args - .iter() - .any(|arg| matches!(arg, CallArg::Kwarg("count", _))); - - let key = Key::new( - if has_count { - crate::key::Kind::Plural - } else { - crate::key::Kind::Message - }, - key.to_owned(), - ); + if let Expr::Var(var_) = &call.expr + && var_.id == context.func() + { + let key = call + .args + .first() + .and_then(as_const) + .and_then(|const_| const_.value.as_str()) + .ok_or(minijinja::Error::new( + ErrorKind::UndefinedError, + "t() first argument must be a string literal", + ))?; + + let has_count = call + .args + .iter() + .any(|arg| matches!(arg, CallArg::Kwarg("count", _))); + + let key = Key::new( + if has_count { + crate::key::Kind::Plural + } else { + crate::key::Kind::Message + }, + key.to_owned(), + ); - let key = context.set_key_location(key, span); + let key = context.set_key_location(key, span); - context.record(key); - } + context.record(key); } find_in_expr(context, &call.expr)?; diff --git a/crates/i18n/src/sprintf/mod.rs b/crates/i18n/src/sprintf/mod.rs index 72ca75373..d58514f97 100644 --- a/crates/i18n/src/sprintf/mod.rs +++ b/crates/i18n/src/sprintf/mod.rs @@ -72,6 +72,7 @@ pub(crate) use sprintf; #[derive(Debug, thiserror::Error)] #[error(transparent)] +#[allow(dead_code)] enum Error { Format(#[from] self::formatter::FormatError), Parse(Box), diff --git a/crates/listener/src/server.rs b/crates/listener/src/server.rs index 026706a45..2a0b6ccda 100644 --- a/crates/listener/src/server.rs +++ b/crates/listener/src/server.rs @@ -279,11 +279,11 @@ where fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.project(); - if let Poll::Ready(()) = this.cancellation_future.poll(cx) { - if !*this.did_start_shutdown { - *this.did_start_shutdown = true; - this.connection.as_mut().graceful_shutdown(); - } + if let Poll::Ready(()) = this.cancellation_future.poll(cx) + && !*this.did_start_shutdown + { + *this.did_start_shutdown = true; + this.connection.as_mut().graceful_shutdown(); } this.connection.poll(cx) diff --git a/crates/oauth2-types/src/oidc.rs b/crates/oauth2-types/src/oidc.rs index a9befbea1..39e1074b1 100644 --- a/crates/oauth2-types/src/oidc.rs +++ b/crates/oauth2-types/src/oidc.rs @@ -577,7 +577,7 @@ pub struct ProviderMetadata { pub require_request_uri_registration: Option, /// Indicates where authorization request needs to be protected as [Request - /// Object] and provided through either request or request_uri parameter. + /// Object] and provided through either request or `request_uri` parameter. /// /// Defaults to `false`. /// @@ -680,10 +680,10 @@ impl ProviderMetadata { validate_url("registration_endpoint", url, ExtraUrlRestrictions::None)?; } - if let Some(scopes) = &metadata.scopes_supported { - if !scopes.iter().any(|s| s == "openid") { - return Err(ProviderMetadataVerificationError::ScopesMissingOpenid); - } + if let Some(scopes) = &metadata.scopes_supported + && !scopes.iter().any(|s| s == "openid") + { + return Err(ProviderMetadataVerificationError::ScopesMissingOpenid); } validate_signing_alg_values_supported( diff --git a/crates/oauth2-types/src/registration/mod.rs b/crates/oauth2-types/src/registration/mod.rs index fd1ab2a64..e6b6aa862 100644 --- a/crates/oauth2-types/src/registration/mod.rs +++ b/crates/oauth2-types/src/registration/mod.rs @@ -911,7 +911,8 @@ pub struct ClientRegistrationResponse { #[serde_as(as = "Option>")] pub client_id_issued_at: Option>, - /// Time at which the client_secret will expire or 0 if it will not expire. + /// Time at which the `client_secret` will expire or 0 if it will not + /// expire. /// /// Required if `client_secret` is issued. #[serde(default)] diff --git a/crates/oidc-client/tests/it/main.rs b/crates/oidc-client/tests/it/main.rs index ce595661d..cc8641085 100644 --- a/crates/oidc-client/tests/it/main.rs +++ b/crates/oidc-client/tests/it/main.rs @@ -74,7 +74,7 @@ fn keystore(alg: &JsonWebSignatureAlg) -> Keystore { } /// Generate an ID token. -fn id_token(issuer: &str) -> (IdToken, PublicJsonWebKeySet) { +fn id_token(issuer: &str) -> (IdToken<'_>, PublicJsonWebKeySet) { let signing_alg = ID_TOKEN_SIGNING_ALG; let keystore = keystore(&signing_alg); diff --git a/crates/oidc-client/tests/it/requests/jose.rs b/crates/oidc-client/tests/it/requests/jose.rs index dcca40252..6adedc570 100644 --- a/crates/oidc-client/tests/it/requests/jose.rs +++ b/crates/oidc-client/tests/it/requests/jose.rs @@ -34,7 +34,7 @@ fn id_token( issuer: &str, flag: Option, auth_time: Option>, -) -> (IdToken, PublicJsonWebKeySet) { +) -> (IdToken<'_>, PublicJsonWebKeySet) { let signing_alg = ID_TOKEN_SIGNING_ALG; let keystore = keystore(&signing_alg); diff --git a/crates/policy/src/lib.rs b/crates/policy/src/lib.rs index b45da09ac..3a3a23c3f 100644 --- a/crates/policy/src/lib.rs +++ b/crates/policy/src/lib.rs @@ -397,7 +397,7 @@ impl Policy { Ok(res) } - /// Evaluate the 'client_registration' entrypoint. + /// Evaluate the `client_registration` entrypoint. /// /// # Errors /// @@ -419,7 +419,7 @@ impl Policy { Ok(res) } - /// Evaluate the 'authorization_grant' entrypoint. + /// Evaluate the `authorization_grant` entrypoint. /// /// # Errors /// diff --git a/crates/storage-pg/src/compat/mod.rs b/crates/storage-pg/src/compat/mod.rs index 32f26303f..2e903af77 100644 --- a/crates/storage-pg/src/compat/mod.rs +++ b/crates/storage-pg/src/compat/mod.rs @@ -697,7 +697,7 @@ mod tests { // List all logins let logins = repo.compat_sso_login().list(all, pagination).await.unwrap(); assert!(!logins.has_next_page); - assert_eq!(logins.edges, &[login.clone()]); + assert_eq!(logins.edges, vec![login.clone()]); // List the logins for the user let logins = repo @@ -706,7 +706,7 @@ mod tests { .await .unwrap(); assert!(!logins.has_next_page); - assert_eq!(logins.edges, &[login.clone()]); + assert_eq!(logins.edges, vec![login.clone()]); // List only the pending logins for the user let logins = repo diff --git a/crates/storage-pg/src/user/registration.rs b/crates/storage-pg/src/user/registration.rs index e8c228771..fdbdb7139 100644 --- a/crates/storage-pg/src/user/registration.rs +++ b/crates/storage-pg/src/user/registration.rs @@ -524,7 +524,7 @@ mod tests { &mut rng, &clock, "alice".to_owned(), - Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), + Some(IpAddr::V4(Ipv4Addr::LOCALHOST)), Some("Mozilla/5.0".to_owned()), Some(serde_json::json!({"action": "continue_compat_sso_login", "id": "01FSHN9AG0MKGTBNZ16RDR3PVY"})), ) @@ -534,7 +534,7 @@ mod tests { assert_eq!(registration.user_agent, Some("Mozilla/5.0".to_owned())); assert_eq!( registration.ip_address, - Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))) + Some(IpAddr::V4(Ipv4Addr::LOCALHOST)) ); assert_eq!( registration.post_auth_action, diff --git a/crates/storage/src/oauth2/authorization_grant.rs b/crates/storage/src/oauth2/authorization_grant.rs index f61f6b8c0..f87c07df0 100644 --- a/crates/storage/src/oauth2/authorization_grant.rs +++ b/crates/storage/src/oauth2/authorization_grant.rs @@ -38,7 +38,7 @@ pub trait OAuth2AuthorizationGrantRepository: Send + Sync { /// * `response_mode`: The response mode the client requested /// * `response_type_id_token`: Whether the `id_token` `response_type` was /// requested - /// * `login_hint`: The login_hint the client sent, if set + /// * `login_hint`: The `login_hint` the client sent, if set /// * `locale`: The locale the detected when the user asked for the /// authorization grant /// diff --git a/crates/storage/src/oauth2/client.rs b/crates/storage/src/oauth2/client.rs index bf2d10a29..779b754ca 100644 --- a/crates/storage/src/oauth2/client.rs +++ b/crates/storage/src/oauth2/client.rs @@ -24,7 +24,7 @@ pub trait OAuth2ClientRepository: Send + Sync { /// The error type returned by the repository type Error; - /// Lookup an OAuth2 client by its ID + /// Lookup an OAuth client by its ID /// /// Returns `None` if the client does not exist /// @@ -37,7 +37,7 @@ pub trait OAuth2ClientRepository: Send + Sync { /// Returns [`Self::Error`] if the underlying repository fails async fn lookup(&mut self, id: Ulid) -> Result, Self::Error>; - /// Find an OAuth2 client by its client ID + /// Find an OAuth client by its client ID async fn find_by_client_id(&mut self, client_id: &str) -> Result, Self::Error> { let Ok(id) = client_id.parse() else { return Ok(None); @@ -45,7 +45,7 @@ pub trait OAuth2ClientRepository: Send + Sync { self.lookup(id).await } - /// Find an OAuth2 client by its metadata digest + /// Find an OAuth client by its metadata digest /// /// Returns `None` if the client does not exist /// @@ -62,7 +62,7 @@ pub trait OAuth2ClientRepository: Send + Sync { digest: &str, ) -> Result, Self::Error>; - /// Load a batch of OAuth2 clients by their IDs + /// Load a batch of OAuth clients by their IDs /// /// Returns a map of client IDs to clients. If a client does not exist, it /// is not present in the map. @@ -79,7 +79,7 @@ pub trait OAuth2ClientRepository: Send + Sync { ids: BTreeSet, ) -> Result, Self::Error>; - /// Add a new OAuth2 client + /// Add a new OAuth client /// /// Returns the client that was added /// diff --git a/crates/syn2mas/src/synapse_reader/checks.rs b/crates/syn2mas/src/synapse_reader/checks.rs index 655642770..aee91b62a 100644 --- a/crates/syn2mas/src/synapse_reader/checks.rs +++ b/crates/syn2mas/src/synapse_reader/checks.rs @@ -250,7 +250,8 @@ pub async fn synapse_config_check_against_mas_config( /// /// - If there is some database connection error, or the given database is not a /// Synapse database. -/// - If the OAuth2 section of the MAS configuration could not be parsed. +/// - If the Upstream OAuth section of the MAS configuration could not be +/// parsed. #[tracing::instrument(skip_all)] pub async fn synapse_database_check( synapse_connection: &mut PgConnection, diff --git a/crates/syn2mas/src/synapse_reader/config/mod.rs b/crates/syn2mas/src/synapse_reader/config/mod.rs index 4bd89921b..3c9454ba6 100644 --- a/crates/syn2mas/src/synapse_reader/config/mod.rs +++ b/crates/syn2mas/src/synapse_reader/config/mod.rs @@ -120,14 +120,14 @@ impl Config { pub fn all_oidc_providers(&self) -> BTreeMap { let mut out = BTreeMap::new(); - if let Some(provider) = &self.oidc_config { - if provider.has_required_fields() { - let mut provider = provider.clone(); - // The legacy configuration has an implied IdP ID of `oidc`. - let idp_id = provider.idp_id.take().unwrap_or("oidc".to_owned()); - provider.idp_id = Some(idp_id.clone()); - out.insert(idp_id, provider); - } + if let Some(provider) = &self.oidc_config + && provider.has_required_fields() + { + let mut provider = provider.clone(); + // The legacy configuration has an implied IdP ID of `oidc`. + let idp_id = provider.idp_id.take().unwrap_or("oidc".to_owned()); + provider.idp_id = Some(idp_id.clone()); + out.insert(idp_id, provider); } for provider in &self.oidc_providers { diff --git a/crates/tasks/src/matrix.rs b/crates/tasks/src/matrix.rs index d5d6e6e65..87e052d20 100644 --- a/crates/tasks/src/matrix.rs +++ b/crates/tasks/src/matrix.rs @@ -29,7 +29,7 @@ use crate::{ /// Job to provision a user on the Matrix homeserver. /// This works by doing a PUT request to the -/// /_synapse/admin/v2/users/{user_id} endpoint. +/// `/_synapse/admin/v2/users/{user_id}` endpoint. #[async_trait] impl RunnableJob for ProvisionUserJob { #[tracing::instrument( diff --git a/crates/templates/src/context.rs b/crates/templates/src/context.rs index 230dee67f..0a04677eb 100644 --- a/crates/templates/src/context.rs +++ b/crates/templates/src/context.rs @@ -1637,7 +1637,7 @@ impl TemplateContext for DeviceConsentContext { device_code: Alphanumeric.sample_string(rng, 32), created_at: now - Duration::try_minutes(5).unwrap(), expires_at: now + Duration::try_minutes(25).unwrap(), - ip_address: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), + ip_address: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)), user_agent: Some("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36".to_owned()), }; Self { grant, client } diff --git a/crates/templates/src/lib.rs b/crates/templates/src/lib.rs index d588e12ea..1c0bef423 100644 --- a/crates/templates/src/lib.rs +++ b/crates/templates/src/lib.rs @@ -328,7 +328,7 @@ register_templates! { /// Render the Swagger API reference pub fn render_swagger(ApiDocContext) { "swagger/doc.html" } - /// Render the Swagger OAuth2 callback page + /// Render the Swagger OAuth callback page pub fn render_swagger_callback(ApiDocContext) { "swagger/oauth2-redirect.html" } /// Render the login page @@ -382,7 +382,7 @@ register_templates! { /// Render the account recovery disabled page pub fn render_recovery_disabled(WithLanguage) { "pages/recovery/disabled.html" } - /// Render the form used by the form_post response mode + /// Render the form used by the `form_post` response mode pub fn render_form_post(WithLanguage>) { "form_post.html" } /// Render the HTML error page diff --git a/docs/config.schema.json b/docs/config.schema.json index 6c4fadfcd..761f1dd62 100644 --- a/docs/config.schema.json +++ b/docs/config.schema.json @@ -2155,7 +2155,7 @@ "type": "boolean" }, "on_backchannel_logout": { - "description": "What to do when receiving an OIDC Backchannel logout request.\n\nDefaults to \"do_nothing\".", + "description": "What to do when receiving an OIDC Backchannel logout request.\n\nDefaults to `do_nothing`.", "allOf": [ { "$ref": "#/definitions/OnBackchannelLogout"