From fdbb1c86b10086608d63c413ce9a4830ba5bd721 Mon Sep 17 00:00:00 2001 From: Ginger Date: Thu, 6 Nov 2025 10:30:41 -0500 Subject: [PATCH] chore: Clippy and formatting --- src/api/client/keys.rs | 3 +- src/api/client/oidc/authorize.rs | 66 +++++----- src/api/client/oidc/login.rs | 4 +- src/api/client/oidc/register.rs | 55 ++++---- src/database/maps.rs | 16 +-- src/service/oidc/mod.rs | 209 ++++++++++++++++--------------- src/service/users/mod.rs | 46 ++++--- src/web/oidc.rs | 2 +- src/web/oidc/consent.rs | 11 +- src/web/oidc/login.rs | 6 +- src/web/oidc/response.rs | 3 +- 11 files changed, 224 insertions(+), 197 deletions(-) diff --git a/src/api/client/keys.rs b/src/api/client/keys.rs index f532045a1..06ca0de49 100644 --- a/src/api/client/keys.rs +++ b/src/api/client/keys.rs @@ -76,7 +76,8 @@ pub(crate) async fn upload_keys_route( if deser_device_keys.device_id != sender_device { return Err!(Request(Unknown( "Device ID in keys uploaded ({}) does not match your own device ID ({})", - deser_device_keys.device_id, sender_device, + deser_device_keys.device_id, + sender_device, ))); } diff --git a/src/api/client/oidc/authorize.rs b/src/api/client/oidc/authorize.rs index 45f80ecce..a2fcbfd0b 100644 --- a/src/api/client/oidc/authorize.rs +++ b/src/api/client/oidc/authorize.rs @@ -1,7 +1,7 @@ use axum::extract::{Query, State}; use conduwuit::{Result, err, utils::ReadyExt}; use conduwuit_web::oidc::{ - oidc_consent_form, oidc_login_form, AuthorizationQuery, OidcRequest, OidcResponse, + AuthorizationQuery, OidcRequest, OidcResponse, oidc_consent_form, oidc_login_form, }; use oxide_auth::{ endpoint::{OwnerConsent, Solicitation}, @@ -41,15 +41,15 @@ pub(crate) async fn authorize( // Redirect to the login page if no token or token not known. let hostname = services.config.server_name.host(); - let Some(token) = oauth.authorization_header() else { - return Ok(oidc_login_form(hostname, &query)); - }; + let Some(token) = oauth.authorization_header() else { + return Ok(oidc_login_form(hostname, &query)); + }; tracing::debug!("submitting OIDC authorisation for token : {token:#?}"); // Get the user id from the token and add it to the query. let (owner_id, _) = services.oidc.user_and_device_from_token(token).await?; - let mut query_with_user_id = query.clone(); - query_with_user_id.username = Some(owner_id.localpart().to_string()); + let mut query_with_user_id = query.clone(); + query_with_user_id.username = Some(owner_id.localpart().to_owned()); services .oidc @@ -88,47 +88,49 @@ pub(crate) async fn authorize_consent( }; let server_name = services.globals.server_name(); let owner_id = UserId::parse_with_server_name(owner_id.clone(), server_name) - .map_err(|err| - err!(Request(InvalidUsername("invalid username {owner_id:?}: {err}"))) - )?; - let Some(matrix_client) = services.oidc + .map_err(|err| err!(Request(InvalidUsername("invalid username {owner_id:?}: {err}"))))?; + let Some(matrix_client) = services + .oidc .client_from_client_id(&query.client_id) - .await? else { - return Err(err!(Request(Unknown("no client has registered client_id {:?}", query.client_id)))); + .await? + else { + return Err(err!(Request(Unknown( + "no client has registered client_id {:?}", + query.client_id + )))); }; - let scope = query.scope - .parse() - .map_err(|err| - err!(Request(Unknown("could not parse scope {:?}: {}", query.scope, err))) - )?; - let device_id = services.oidc.device_id_from_scope(scope)?; + let scope = query.scope.parse().map_err(|err| { + err!(Request(Unknown("could not parse scope {:?}: {}", query.scope, err))) + })?; + let device_id = services.oidc.device_id_from_scope(&scope)?; // Check that the device is registered in the owner devices list. // Note that this is _not_ the OIDC client registration. let device_is_registered_with_owner = services .users .all_device_ids(&owner_id) .ready_any(|v| v == device_id) - .await; - if ! device_is_registered_with_owner { + .await; + if !device_is_registered_with_owner { // TODO get the client's IP from the request. let client_ip = None; - services.oidc.register_device( - &query.client_id, - (&owner_id, &device_id), - matrix_client.name.as_deref(), - client_ip, - ).await?; + services + .oidc + .register_device( + &query.client_id, + (&owner_id, &device_id), + matrix_client.name.as_deref(), + client_ip, + ) + .await?; } services .oidc .endpoint() - .with_solicitor(FnSolicitor( - move |_: &mut _, _: Solicitation<'_>| match allow.clone() { - | None => OwnerConsent::Denied, - | Some(user_id) => OwnerConsent::Authorized(user_id), - }, - )) + .with_solicitor(FnSolicitor(move |_: &mut _, _: Solicitation<'_>| match allow.clone() { + | None => OwnerConsent::Denied, + | Some(user_id) => OwnerConsent::Authorized(user_id), + })) .authorization_flow() .execute(oauth) .map_err(|err| err!(Request(Unknown("consent request failed: {err:?}")))) diff --git a/src/api/client/oidc/login.rs b/src/api/client/oidc/login.rs index c02d2cf80..4f4d63e8a 100644 --- a/src/api/client/oidc/login.rs +++ b/src/api/client/oidc/login.rs @@ -1,8 +1,6 @@ use axum::extract::State; use conduwuit::{Result, err, utils::hash::verify_password}; -use conduwuit_web::oidc::{ - LoginError, LoginQuery, OidcRequest, OidcResponse, oidc_consent_form, -}; +use conduwuit_web::oidc::{LoginError, LoginQuery, OidcRequest, OidcResponse, oidc_consent_form}; use ruma::user_id::UserId; //#[axum::debug_handler] diff --git a/src/api/client/oidc/register.rs b/src/api/client/oidc/register.rs index a82179c0f..260ffa048 100644 --- a/src/api/client/oidc/register.rs +++ b/src/api/client/oidc/register.rs @@ -1,9 +1,9 @@ use axum::{Json, extract::State}; use conduwuit::{Result, err}; +use conduwuit_service::oidc::normalize_redirect; use oxide_auth::primitives::prelude::Client; use reqwest::Url; -use ruma::{DeviceId, ClientSecret, identifiers_validation}; -use conduwuit_service::oidc::normalize_redirect; +use ruma::{ClientSecret, DeviceId, identifiers_validation}; /// The required parameters to register a new client for OAuth2 application. /// See the required metadata in OAuth2 authorization grant flow in [MSC2966]. @@ -22,8 +22,9 @@ pub(crate) struct ClientQuery { response_types: Vec, /// Must include the literals "authorization_code" and "refresh_token". grant_types: Vec, - /// How the client intends to authenticate its requests. Can be "none", meaning - /// that the client will negotiate its token with the "authorization code" flow. + /// How the client intends to authenticate its requests. Can be "none", + /// meaning that the client will negotiate its token with the + /// "authorization code" flow. token_endpoint_auth_method: String, /// Link to the logo. logo_uri: Option, @@ -42,8 +43,8 @@ pub(crate) struct ClientResponse { client_id: String, /// If the client is private, the secret it authenticates itself with. client_secret: Option, - /// If there's a `client_secret`, its expiration date in seconds since 1970-01-01T00:00. - /// Some(0) means no expiration date. + /// If there's a `client_secret`, its expiration date in seconds since + /// 1970-01-01T00:00. Some(0) means no expiration date. client_secret_expires_at: Option, client_name: String, /// Points to the "about" page of the client. @@ -51,10 +52,10 @@ pub(crate) struct ClientResponse { logo_uri: Option, tos_uri: Option, policy_uri: Option, - /// Registered redirect uris, which will be matched against when authenticating. - /// If a localhost address, must contain instances of oxide-auth's - /// `RegisteredUrl::IgnorePortOnLocalhost` to let authorization flow through any port over - /// localhost. + /// Registered redirect uris, which will be matched against when + /// authenticating. If a localhost address, must contain instances of + /// oxide-auth's `RegisteredUrl::IgnorePortOnLocalhost` to let + /// authorization flow through any port over localhost. redirect_uris: Vec, token_endpoint_auth_method: String, response_types: Vec, @@ -74,20 +75,18 @@ pub(crate) async fn register_client( Json(client): Json, ) -> Result> { tracing::trace!("processing OIDC device register request for client: {client:#?}"); - if client.redirect_uris.len() == 0 { - return Err(err!(Request(Unknown( - "the client's registration request should contain at least a redirect_uri" - )))); + if client.redirect_uris.is_empty() { + return Err(err!(Request(Unknown( + "the client's registration request should contain at least a redirect_uri" + )))); } let mut redirect_uris = client.redirect_uris.clone(); let redirect_uri = redirect_uris.pop().expect("at least one redirect_uri"); let redirect_uri = normalize_redirect(redirect_uri); - let remaining_uris = redirect_uris - .into_iter() - .map(|url| normalize_redirect(url)) - .collect(); + let remaining_uris = redirect_uris.into_iter().map(normalize_redirect).collect(); let device_id = DeviceId::new(); - // Only provide a default scope, we'll test the client's proposed scope for consent anyway. + // Only provide a default scope, we'll test the client's proposed scope for + // consent anyway. let scope = "default".parse().unwrap(); // TODO check if the users service needs an update. //services.users.update_device_metadata(); @@ -96,12 +95,12 @@ pub(crate) async fn register_client( // it's a public client. This is usually the case in Matrix. let is_private = client.token_endpoint_auth_method != "none"; let client_secret = match is_private { - true => { + | true => { let secret = ClientSecret::new(); identifiers_validation::client_secret::validate(secret.as_str())?; Some(secret.to_string()) }, - false => None + | false => None, }; let registration = match is_private { | true => &Client::confidential( @@ -109,15 +108,15 @@ pub(crate) async fn register_client( redirect_uri, scope, client_secret.as_ref().unwrap().as_bytes(), - ).with_additional_redirect_uris(remaining_uris), - | _ => &Client::public( - device_id.as_ref(), - redirect_uri, - scope, - ).with_additional_redirect_uris(remaining_uris) + ) + .with_additional_redirect_uris(remaining_uris), + | _ => &Client::public(device_id.as_ref(), redirect_uri, scope) + .with_additional_redirect_uris(remaining_uris), }; tracing::trace!("registering OIDC device : {registration:#?}"); - services.oidc.register_client(Some(client.client_name.clone()), ®istration); + services + .oidc + .register_client(Some(client.client_name.clone()), registration); let client_response = ClientResponse { client_id: device_id.to_string(), diff --git a/src/database/maps.rs b/src/database/maps.rs index 97a79c201..1d1b80d1d 100644 --- a/src/database/maps.rs +++ b/src/database/maps.rs @@ -434,14 +434,14 @@ pub(super) static MAPS: &[Descriptor] = &[ name: "userroomid_notificationcount", ..descriptor::RANDOM }, - Descriptor { - name: "client_registrar", - ..descriptor::RANDOM - }, - Descriptor { - name: "deviceid_clientidmap", - ..descriptor::RANDOM_SMALL - }, + Descriptor { + name: "client_registrar", + ..descriptor::RANDOM + }, + Descriptor { + name: "deviceid_clientidmap", + ..descriptor::RANDOM_SMALL + }, Descriptor { name: "userroomid_invitesender", ..descriptor::RANDOM_SMALL diff --git a/src/service/oidc/mod.rs b/src/service/oidc/mod.rs index 040ab9f49..cfa9a0b29 100644 --- a/src/service/oidc/mod.rs +++ b/src/service/oidc/mod.rs @@ -5,11 +5,15 @@ //! //! [oxide-auth]: https://docs.rs/oxide-auth -use std::{borrow::Cow, sync::{Arc, Mutex}}; +use std::{ + borrow::Cow, + sync::{Arc, Mutex}, +}; use async_trait::async_trait; -use conduwuit::{err, Result}; +use conduwuit::{Result, err}; use conduwuit_core::utils; +use database::{Deserialized, Json, Map}; use oxide_auth::{ endpoint::{PreGrant, Scope}, frontends::simple::endpoint::{Generic, Vacant}, @@ -24,25 +28,17 @@ use oxide_auth::{ }, }; use ruma::{ - api::client::device::Device, - MilliSecondsSinceUnixEpoch, - OwnedDeviceId, - OwnedUserId, - UserId, + MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedUserId, UserId, api::client::device::Device, }; -use database::{Deserialized, Json, Map}; use serde::{Deserialize, Serialize}; use url::Url; -use once_cell::sync::Lazy; - -use crate::{globals, Dep}; +use crate::{Dep, globals}; pub const SCOPE_PREFIX_DEVICE: &str = "urn:matrix:org.matrix.msc2967.client:device:"; -pub const SCOPE_PREFIX_API : &str = "urn:matrix:org.matrix.msc2967.client:api:"; - -static PASSWORD_POLICY: Lazy = Lazy::new(Argon2::default); +pub const SCOPE_PREFIX_API: &str = "urn:matrix:org.matrix.msc2967.client:api:"; +static PASSWORD_POLICY: std::sync::LazyLock = std::sync::LazyLock::new(Argon2::default); /// A client app that connects to continuwuity via OIDC, as recorded in the /// database. @@ -116,14 +112,12 @@ impl Service { let client = client.clone().encode(&*PASSWORD_POLICY); let client_id = client.client_id.clone(); let oidc_client = OidcClient { - name: display_name.clone(), + name: display_name, // Matrix clients have no device_id at registration time. device_id: None, client, }; - self.db - .client_registrar - .put(client_id, Json(oidc_client)); + self.db.client_registrar.put(client_id, Json(oidc_client)); } /// Register a device in the main continuwuity database. This should only @@ -139,17 +133,15 @@ impl Service { let device_key = (user_id, device_id); let device = Device { device_id: device_id.into(), - display_name: display_name.map(|n| n.to_string()), + display_name: display_name.map(ToOwned::to_owned), last_seen_ip: client_ip, last_seen_ts: Some(MilliSecondsSinceUnixEpoch::now()), }; - increment(&self.db.userid_devicelistversion, user_id.as_bytes()) - .await; - self.db - .userdeviceid_metadata - .put(device_key, Json(device)); + increment(&self.db.userid_devicelistversion, user_id.as_bytes()).await; + self.db.userdeviceid_metadata.put(device_key, Json(device)); - let mut client : OidcClient = self.db + let mut client: OidcClient = self + .db .client_registrar .get(client_id) .await? @@ -157,11 +149,11 @@ impl Service { client.device_id = Some(device_id.to_string()); self.db .client_registrar - .put(client_id.to_string(), Json(client)); + .put(client_id.to_owned(), Json(client)); self.db .deviceid_clientidmap - .put(device_id, client_id.to_string()); + .put(device_id, client_id.to_owned()); Ok(()) } @@ -169,14 +161,12 @@ impl Service { fn grant_from_token(&self, token: &str) -> Option { let issuer = self.issuer.lock().expect("lockable issuer"); - issuer.recover_token(token) + issuer + .recover_token(token) .expect("infallible recover_token implementation") } - pub async fn client_from_client_id( - &self, - client_id: &str, - ) -> Result> { + pub async fn client_from_client_id(&self, client_id: &str) -> Result> { self.db .client_registrar .get(client_id) @@ -188,7 +178,8 @@ impl Service { &self, device_id: OwnedDeviceId, ) -> Result> { - let client_id: String = self.db + let client_id: String = self + .db .deviceid_clientidmap .get(&device_id) .await? @@ -201,19 +192,14 @@ impl Service { .deserialized() } - pub fn device_id_from_scope(&self, scope: Scope) -> Result { - let Some(device_id) = scope - .iter() - .find(|s| s.starts_with(SCOPE_PREFIX_DEVICE)) else { - tracing::warn!("device_id not found in scope {scope:?}"); - return Err(err!(Request(InvalidParam("something went wrong with the scope")))); - }; + pub fn device_id_from_scope(&self, scope: &Scope) -> Result { + let Some(device_id) = scope.iter().find(|s| s.starts_with(SCOPE_PREFIX_DEVICE)) else { + tracing::warn!("device_id not found in scope {scope:?}"); + return Err(err!(Request(InvalidParam("something went wrong with the scope")))); + }; let device_id = device_id.replace(SCOPE_PREFIX_DEVICE, ""); - OwnedDeviceId::try_from(device_id.clone()) - .map_err(|err| - err!(Request(InvalidParam("invalid device_id {device_id:?}: {err}"))) - ) + Ok(device_id.into()) } pub async fn user_and_device_from_token( @@ -224,17 +210,18 @@ impl Service { return Err(err!(Request(MissingToken("unknown token: {token:?}")))); }; let server_name = self.services.globals.server_name(); - let owner_id = UserId::parse_with_server_name(owner_id.clone(), server_name) - .map_err(|err| + let owner_id = + UserId::parse_with_server_name(owner_id.clone(), server_name).map_err(|err| { err!(Request(InvalidUsername("invalid username {owner_id:?}: {err}"))) - )?; - let client = self.client_from_client_id(&client_id) + })?; + let client = self + .client_from_client_id(&client_id) .await? .expect("validated client_id"); let Some(device_id) = client.device_id else { return Err(err!(Request(Unknown("this client has no device_id yet")))); }; - let device_id = OwnedDeviceId::from(device_id.to_string()); + let device_id = OwnedDeviceId::from(device_id); Ok((owner_id, device_id)) } @@ -264,95 +251,111 @@ async fn increment(db: &Arc, key: &[u8]) { /// Substitute "127.0.0.1" and "[::1]" for "localhost" to let oxide-auth compare /// them ignoring their port. -pub fn normalize_redirect_hostname(url: Url) -> Url { - let mut new_url = url.clone(); - let new_host = url.host_str().map(|h| - h.replace("127.0.0.1", "localhost").replace("[::1]", "localhost") - ); - new_url.set_host(new_host.as_deref()).expect("replaceable redirect hostname"); +pub fn normalize_redirect_hostname(url: &mut Url) { + let new_host = url.host_str().map(|h| { + h.replace("127.0.0.1", "localhost") + .replace("[::1]", "localhost") + }); - new_url + url.set_host(new_host.as_deref()) + .expect("replaceable redirect hostname"); } /// If `url` is a localhost (either 'localhost', '127.0.0.1' or '[::1]'), wrap /// it in an `IgnorePortOnLocalhost`, so that oxide-auth ignores the port when /// comparing it with the registered ones. -pub fn normalize_redirect(url: Url) -> RegisteredUrl { - let new_url = normalize_redirect_hostname(url); +#[must_use] +pub fn normalize_redirect(mut url: Url) -> RegisteredUrl { + normalize_redirect_hostname(&mut url); - match new_url.host_str() { - Some("localhost") => RegisteredUrl::IgnorePortOnLocalhost(new_url.into()), - _ => RegisteredUrl::Semantic(new_url) + match url.host_str() { + | Some("localhost") => RegisteredUrl::IgnorePortOnLocalhost(url.into()), + | _ => RegisteredUrl::Semantic(url), } } /// Let this service act as an oxide-auth `Registrar`. impl Registrar for Service { - fn bound_redirect<'a>(&self, bound: ClientUrl<'a>) -> Result, RegistrarError> { - let client_handle = self.db + fn bound_redirect<'a>( + &self, + bound: ClientUrl<'a>, + ) -> Result, RegistrarError> { + let client_handle = self + .db .client_registrar .get_blocking(bound.client_id.as_ref()) .map_err(|_| RegistrarError::Unspecified)?; - let oidc_client: OidcClient = client_handle.deserialized() + let oidc_client: OidcClient = client_handle + .deserialized() .map_err(|_| RegistrarError::Unspecified)?; let client = oidc_client.client; - // Perform exact matching as motivated in the rfc, but substitute + // Perform exact matching as motivated in the rfc, but substitute // "127.0.0.1" and "[::1]" for "localhost" to let oxide-auth ignore // their port. let redirect_uri = bound.redirect_uri; - let normalized_uri = redirect_uri - .clone() - .map(|u| normalize_redirect(u.to_url())); - let redirect_uri = match normalized_uri { - None => client.redirect_uri.clone(), - Some(url) => { - let original = std::iter::once(&client.redirect_uri); - let alternatives = client.additional_redirect_uris.iter(); - if original - .chain(alternatives) - .any(|registered| *registered == url) - { + let normalized_uri = redirect_uri.clone().map(|u| normalize_redirect(u.to_url())); + let redirect_uri = match normalized_uri { + | None => client.redirect_uri, + | Some(url) => { + let original = std::iter::once(&client.redirect_uri); + let alternatives = client.additional_redirect_uris.iter(); + if original + .chain(alternatives) + .any(|registered| *registered == url) + { // If normalized_uri is Some(url), so is redirect_uri, so unwrap(). - redirect_uri.unwrap().into_owned().into() - } else { - tracing::debug!("the request's redirect url didn't match any registered. bound: {:?}, in client {:#?}", url, client); - return Err(RegistrarError::Unspecified); - } - } - }; + redirect_uri.unwrap().into_owned().into() + } else { + tracing::debug!( + "the request's redirect url didn't match any registered. bound: {:?}, \ + in client {:#?}", + url, + client + ); + return Err(RegistrarError::Unspecified); + } + }, + }; - Ok(BoundClient { - client_id: bound.client_id, - redirect_uri: Cow::Owned(redirect_uri), - }) + Ok(BoundClient { + client_id: bound.client_id, + redirect_uri: Cow::Owned(redirect_uri), + }) } - fn negotiate(&self, bound: BoundClient<'_>, _scope: Option) -> Result { - let client_handle = self.db + fn negotiate( + &self, + bound: BoundClient<'_>, + _scope: Option, + ) -> Result { + let client_handle = self + .db .client_registrar .get_blocking(bound.client_id.as_ref()) .map_err(|_| RegistrarError::Unspecified)?; - let oidc_client: OidcClient = client_handle.deserialized() + let oidc_client: OidcClient = client_handle + .deserialized() .map_err(|_| RegistrarError::Unspecified)?; - Ok(PreGrant { - client_id: bound.client_id.into_owned(), - redirect_uri: bound.redirect_uri.into_owned(), + Ok(PreGrant { + client_id: bound.client_id.into_owned(), + redirect_uri: bound.redirect_uri.into_owned(), // Always use the client's scope. - scope: oidc_client.client.default_scope.clone(), - }) - } + scope: oidc_client.client.default_scope, + }) + } - fn check(&self, client_id: &str, passphrase: Option<&[u8]>) -> Result<(), RegistrarError> { - let client_handle = self.db + fn check(&self, client_id: &str, passphrase: Option<&[u8]>) -> Result<(), RegistrarError> { + let client_handle = self + .db .client_registrar .get_blocking(client_id) .map_err(|_| RegistrarError::Unspecified)?; - let oidc_client: OidcClient = client_handle.deserialized() + let oidc_client: OidcClient = client_handle + .deserialized() .map_err(|_| RegistrarError::Unspecified)?; let client = oidc_client.client; - RegisteredClient::new(&client, &*PASSWORD_POLICY) - .check_authentication(passphrase) - } + RegisteredClient::new(&client, &*PASSWORD_POLICY).check_authentication(passphrase) + } } diff --git a/src/service/users/mod.rs b/src/service/users/mod.rs index 560134490..cb9706839 100644 --- a/src/service/users/mod.rs +++ b/src/service/users/mod.rs @@ -272,7 +272,14 @@ impl Service { /// Find out which user an access token belongs to. pub async fn find_from_token(&self, token: &str) -> Result<(OwnedUserId, OwnedDeviceId)> { - if self.services.server.config.auth.as_ref().is_some_and(|auth| auth.enable_oidc_login) { + if self + .services + .server + .config + .auth + .as_ref() + .is_some_and(|auth| auth.enable_oidc_login) + { self.services.oidc.user_and_device_from_token(token).await } else { self.db.token_userdeviceid.get(token).await.deserialized() @@ -542,22 +549,29 @@ impl Service { // Only existing devices should be able to call this, but we shouldn't assert // either... let key = (user_id, device_id); - if self.services.server.config.auth.as_ref().is_some_and(|auth| auth.enable_oidc_login) { - if self.services.oidc.client_from_device_id(device_id.into()).await?.is_none() { - return Err!(Database(error!( - ?user_id, - ?device_id, - "Device has no metadata." - ))); - } - } else { - if self.db.userdeviceid_metadata.qry(&key).await.is_err() { - return Err!(Database(error!( - ?user_id, - ?device_id, - "User does not exist or device has no metadata." - ))); + if self + .services + .server + .config + .auth + .as_ref() + .is_some_and(|auth| auth.enable_oidc_login) + { + if self + .services + .oidc + .client_from_device_id(device_id.into()) + .await? + .is_none() + { + return Err!(Database(error!(?user_id, ?device_id, "Device has no metadata."))); } + } else if self.db.userdeviceid_metadata.qry(&key).await.is_err() { + return Err!(Database(error!( + ?user_id, + ?device_id, + "User does not exist or device has no metadata." + ))); } let mut key = user_id.as_bytes().to_vec(); diff --git a/src/web/oidc.rs b/src/web/oidc.rs index 0605a2791..4a7e929c7 100644 --- a/src/web/oidc.rs +++ b/src/web/oidc.rs @@ -1,9 +1,9 @@ use askama::Template; +use conduwuit_build_metadata::version_tag; use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode}; // Imports needed by askama templates. use crate::{GIT_REMOTE_COMMIT_URL, GIT_REMOTE_WEB_URL}; -use conduwuit_build_metadata::version_tag; mod authorize; mod consent; diff --git a/src/web/oidc/consent.rs b/src/web/oidc/consent.rs index 4b76f7aa9..c125175dd 100644 --- a/src/web/oidc/consent.rs +++ b/src/web/oidc/consent.rs @@ -26,8 +26,15 @@ pub fn oidc_consent_form(hostname: &str, query: &AuthorizationQuery) -> OidcResp /// Render the html contents of the user consent page. fn consent_page(hostname: &str, query: &AuthorizationQuery, route: &str, nonce: &str) -> String { - let response_mode = &query.response_mode.clone().unwrap_or("fragment".to_string()); - let user_id = query.username.clone().expect("user_id in authorization query"); + let response_mode = query + .response_mode + .as_deref() + .unwrap_or("fragment") + .to_owned(); + let user_id = query + .username + .clone() + .expect("user_id in authorization query"); let template = ConsentPageTemplate { nonce, hostname, diff --git a/src/web/oidc/login.rs b/src/web/oidc/login.rs index c221a3101..4015ba766 100644 --- a/src/web/oidc/login.rs +++ b/src/web/oidc/login.rs @@ -64,7 +64,9 @@ impl TryFrom for LoginQuery { }; // response_mode is not strictly needed : it must be the literal "fragment" // when over https. It's required by the spec but Fractal doesn't provide it. - let response_mode = body.unique_value("response_mode").unwrap_or(Cow::Borrowed("fragment")); + let response_mode = body + .unique_value("response_mode") + .unwrap_or(Cow::Borrowed("fragment")); let client_secret = body.unique_value("client_secret").map(|s| s.to_string()); Ok(Self { @@ -104,7 +106,7 @@ pub fn oidc_login_form(hostname: &str, query: &AuthorizationQuery) -> OidcRespon /// Render the html contents of the login page. fn login_page(hostname: &str, query: &AuthorizationQuery, route: &str, nonce: &str) -> String { - let response_mode = &query.response_mode.clone().unwrap_or("fragment".to_string()); + let response_mode = query.response_mode.as_deref().unwrap_or("fragment"); let template = LoginPageTemplate { nonce, hostname, diff --git a/src/web/oidc/response.rs b/src/web/oidc/response.rs index 9d6eac1b8..34dbf0d68 100644 --- a/src/web/oidc/response.rs +++ b/src/web/oidc/response.rs @@ -28,7 +28,8 @@ impl IntoResponse for OidcResponse { | Some(nonce) => &format!("default-src 'nonce-{nonce}';"), | None => "default-src 'none';", }; - let csp_form_action = "form-action 'self' http://localhost http://127.0.0.1 http://[::1];"; + let csp_form_action = + "form-action 'self' http://localhost http://127.0.0.1 http://[::1];"; let content_csp = format!("{csp_src} {csp_form_action}"); let content_type = match self.body { | Some(OAuthRequestBody::Json(_)) => "application/json",