diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 000000000..0e4510bbb --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,6 @@ +max_width = 100 +comment_width = 80 +wrap_comments = true +imports_granularity = "Crate" +use_small_heuristics = "Default" +group_imports = "StdExternalCrate" diff --git a/matrix-authentication-service/src/config/csrf.rs b/matrix-authentication-service/src/config/csrf.rs index c24c59f44..dd491655c 100644 --- a/matrix-authentication-service/src/config/csrf.rs +++ b/matrix-authentication-service/src/config/csrf.rs @@ -12,12 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use time::Duration; - use csrf::AesGcmCsrfProtection; use serde::Deserialize; use serde_with::serde_as; use tide::Middleware; +use time::Duration; use crate::middlewares::CsrfMiddleware; diff --git a/matrix-authentication-service/src/config/database.rs b/matrix-authentication-service/src/config/database.rs index 69073a200..493242a06 100644 --- a/matrix-authentication-service/src/config/database.rs +++ b/matrix-authentication-service/src/config/database.rs @@ -29,10 +29,12 @@ fn default_connect_timeout() -> Duration { Duration::from_secs(30) } +#[allow(clippy::unnecessary_wraps)] fn default_idle_timeout() -> Option { Some(Duration::from_secs(10 * 60)) } +#[allow(clippy::unnecessary_wraps)] fn default_max_lifetime() -> Option { Some(Duration::from_secs(30 * 60)) } diff --git a/matrix-authentication-service/src/config/mod.rs b/matrix-authentication-service/src/config/mod.rs index 355f20d3f..3d5164936 100644 --- a/matrix-authentication-service/src/config/mod.rs +++ b/matrix-authentication-service/src/config/mod.rs @@ -24,10 +24,12 @@ mod database; mod http; mod oauth2; -pub use self::csrf::Config as CsrfConfig; -pub use self::database::Config as DatabaseConfig; -pub use self::http::Config as HttpConfig; -pub use self::oauth2::{ClientConfig as OAuth2ClientConfig, Config as OAuth2Config}; +pub use self::{ + csrf::Config as CsrfConfig, + database::Config as DatabaseConfig, + http::Config as HttpConfig, + oauth2::{ClientConfig as OAuth2ClientConfig, Config as OAuth2Config}, +}; #[derive(Debug, Deserialize)] pub struct RootConfig { diff --git a/matrix-authentication-service/src/config/oauth2.rs b/matrix-authentication-service/src/config/oauth2.rs index cdef04599..2e78e756b 100644 --- a/matrix-authentication-service/src/config/oauth2.rs +++ b/matrix-authentication-service/src/config/oauth2.rs @@ -40,7 +40,7 @@ impl Default for Config { fn default() -> Self { Self { issuer: default_oauth2_issuer(), - clients: Default::default(), + clients: Vec::new(), } } } diff --git a/matrix-authentication-service/src/handlers/mod.rs b/matrix-authentication-service/src/handlers/mod.rs index ae3312d1b..31eb82245 100644 --- a/matrix-authentication-service/src/handlers/mod.rs +++ b/matrix-authentication-service/src/handlers/mod.rs @@ -58,7 +58,7 @@ async fn redirect_uri_from_params( params: QueryParams, storage: &Storage, ) -> Result { - use RedirectUriLookupError::*; + use RedirectUriLookupError::MissingClientId; let client_id = params.client_id.ok_or(MissingClientId)?; let client = storage.lookup_client(&client_id).await?; let redirect_uri: Option = if let Some(uri) = params.redirect_uri { diff --git a/matrix-authentication-service/src/handlers/oauth2/authorization.rs b/matrix-authentication-service/src/handlers/oauth2/authorization.rs index 18f10c553..65fecbd37 100644 --- a/matrix-authentication-service/src/handlers/oauth2/authorization.rs +++ b/matrix-authentication-service/src/handlers/oauth2/authorization.rs @@ -12,9 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use tide::{Body, Request, Response}; - use oauth2_types::requests::AuthorizationRequest; +use tide::{Body, Request, Response}; use crate::state::State; diff --git a/matrix-authentication-service/src/handlers/oauth2/discovery.rs b/matrix-authentication-service/src/handlers/oauth2/discovery.rs index b7f2e670e..c6e07e170 100644 --- a/matrix-authentication-service/src/handlers/oauth2/discovery.rs +++ b/matrix-authentication-service/src/handlers/oauth2/discovery.rs @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use tide::{Body, Request, Response}; +use std::collections::HashSet; use oauth2_types::oidc::Metadata; +use tide::{Body, Request, Response}; use crate::state::State; @@ -26,10 +27,10 @@ pub async fn get(req: Request) -> tide::Result { token_endpoint: state.token_endpoint(), jwks_uri: state.jwks_uri(), registration_endpoint: None, - scopes_supported: Default::default(), - response_types_supported: Default::default(), - response_modes_supported: Default::default(), - grant_types_supported: Default::default(), + scopes_supported: HashSet::default(), + response_types_supported: HashSet::default(), + response_modes_supported: HashSet::default(), + grant_types_supported: HashSet::default(), }; let body = Body::from_json(&m)?; diff --git a/matrix-authentication-service/src/handlers/views/index.rs b/matrix-authentication-service/src/handlers/views/index.rs index 9544e4f13..7a4146de6 100644 --- a/matrix-authentication-service/src/handlers/views/index.rs +++ b/matrix-authentication-service/src/handlers/views/index.rs @@ -14,8 +14,7 @@ use tide::{Request, Response}; -use crate::state::State; -use crate::templates::common_context; +use crate::{state::State, templates::common_context}; pub async fn get(req: Request) -> tide::Result { let state = req.state(); diff --git a/matrix-authentication-service/src/handlers/views/login.rs b/matrix-authentication-service/src/handlers/views/login.rs index 1e36ac556..f2cdc31cb 100644 --- a/matrix-authentication-service/src/handlers/views/login.rs +++ b/matrix-authentication-service/src/handlers/views/login.rs @@ -15,9 +15,7 @@ use serde::Deserialize; use tide::{Redirect, Request, Response}; -use crate::csrf::CsrfForm; -use crate::state::State; -use crate::templates::common_context; +use crate::{csrf::CsrfForm, state::State, templates::common_context}; #[derive(Deserialize)] struct LoginForm { diff --git a/matrix-authentication-service/src/handlers/views/logout.rs b/matrix-authentication-service/src/handlers/views/logout.rs index 0827bc5fd..ad1fbf518 100644 --- a/matrix-authentication-service/src/handlers/views/logout.rs +++ b/matrix-authentication-service/src/handlers/views/logout.rs @@ -18,7 +18,7 @@ use crate::{csrf::CsrfForm, state::State}; pub async fn post(mut req: Request) -> tide::Result { let form: CsrfForm<()> = req.body_form().await?; - let _ = form.verify_csrf(&req)?; + form.verify_csrf(&req)?; let session = req.session_mut(); session.remove("current_user"); diff --git a/matrix-authentication-service/src/main.rs b/matrix-authentication-service/src/main.rs index 839ec640c..45e2418da 100644 --- a/matrix-authentication-service/src/main.rs +++ b/matrix-authentication-service/src/main.rs @@ -13,6 +13,9 @@ // limitations under the License. #![forbid(unsafe_code)] +#![deny(clippy::all)] +#![warn(clippy::pedantic)] +#![allow(clippy::module_name_repetitions)] use anyhow::Context; use tracing::{info_span, Instrument}; @@ -26,9 +29,7 @@ mod state; mod storage; mod templates; -use self::config::RootConfig; -use self::state::State; -use self::storage::MIGRATOR; +use self::{config::RootConfig, state::State, storage::MIGRATOR}; #[async_std::main] async fn main() -> tide::Result<()> { diff --git a/matrix-authentication-service/src/middlewares/errors.rs b/matrix-authentication-service/src/middlewares/errors.rs index 4dc2aaee2..47666403a 100644 --- a/matrix-authentication-service/src/middlewares/errors.rs +++ b/matrix-authentication-service/src/middlewares/errors.rs @@ -12,9 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::cmp::Reverse; -use std::future::Future; -use std::pin::Pin; +use std::{cmp::Reverse, future::Future, pin::Pin}; use mime::{Mime, STAR}; use serde::Serialize; @@ -25,19 +23,20 @@ use tide::{ }; use tracing::debug; -use crate::state::State; -use crate::templates::common_context; +use crate::{state::State, templates::common_context}; /// Get the weight parameter for a mime type from 0 to 1000 +#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] fn get_weight(mime: &Mime) -> usize { let q = mime .get_param("q") - .map(|q| q.as_str().parse().unwrap_or(0.0)) - .unwrap_or(1.0_f64) + .map_or(1.0_f64, |q| q.as_str().parse().unwrap_or(0.0)) .min(1.0) .max(0.0); - // Weight have a 3 digit precision so we can multiply by 1000 and cast to int + // Weight have a 3 digit precision so we can multiply by 1000 and cast to + // int. Sign loss should not happen here because of the min/max up there and + // truncation does not matter here. (q * 1000.0) as _ } @@ -47,12 +46,12 @@ fn preferred_mime_type<'a>( supported_types: &'a [Mime], ) -> Option<&'a Mime> { let accept = request.header(ACCEPT)?; - // Parse the Accept header as a list of mime types with their associated weight + // Parse the Accept header as a list of mime types with their associated + // weight let accepted_types: Vec<(Mime, usize)> = { let v: Option> = accept .into_iter() - .map(|value| value.as_str().split(',')) - .flatten() + .flat_map(|value| value.as_str().split(',')) .map(|mime| { mime.trim().parse().ok().map(|mime| { let q = get_weight(&mime); @@ -65,7 +64,8 @@ fn preferred_mime_type<'a>( v }; - // For each supported content type, find out if it is accepted with what weight and specificity + // For each supported content type, find out if it is accepted with what + // weight and specificity let mut types: Vec<_> = supported_types .iter() .enumerate() @@ -133,7 +133,8 @@ pub fn middleware<'a>( let mut response = next.run(request).await; - // Find out what message should be displayed from the response status code + // Find out what message should be displayed from the response status + // code let (code, description) = match response.status() { StatusCode::NotFound => (Some("Not found".to_string()), None), StatusCode::MethodNotAllowed => (Some("Method not allowed".to_string()), None), @@ -148,8 +149,8 @@ pub fn middleware<'a>( _ => (None, None), }; - // If there is an error associated to the response, format it in a nice way with - // a backtrace if we have one + // If there is an error associated to the response, format it in a nice + // way with a backtrace if we have one let details = response.take_error().map(|err| { format!( "{}{}", @@ -166,7 +167,8 @@ pub fn middleware<'a>( details, }; - // This is the case if one of the code, description or details is not None + // This is the case if one of the code, description or details is not + // None if error_context.should_render() { match content_type { Some(c) if c == &mime::APPLICATION_JSON => { diff --git a/matrix-authentication-service/src/middlewares/mod.rs b/matrix-authentication-service/src/middlewares/mod.rs index 10f22181d..167ee0c1e 100644 --- a/matrix-authentication-service/src/middlewares/mod.rs +++ b/matrix-authentication-service/src/middlewares/mod.rs @@ -15,5 +15,4 @@ mod csrf; mod errors; -pub use self::csrf::Middleware as CsrfMiddleware; -pub use self::errors::middleware as errors; +pub use self::{csrf::Middleware as CsrfMiddleware, errors::middleware as errors}; diff --git a/matrix-authentication-service/src/storage/mod.rs b/matrix-authentication-service/src/storage/mod.rs index 2071f58ca..ddd97f239 100644 --- a/matrix-authentication-service/src/storage/mod.rs +++ b/matrix-authentication-service/src/storage/mod.rs @@ -20,8 +20,10 @@ use sqlx::migrate::Migrator; mod client; mod user; -pub use self::client::{Client, ClientLookupError, InvalidRedirectUriError}; -pub use self::user::User; +pub use self::{ + client::{Client, ClientLookupError, InvalidRedirectUriError}, + user::User, +}; pub static MIGRATOR: Migrator = sqlx::migrate!(); @@ -36,8 +38,8 @@ impl Storage { pub fn new(pool: Pool) -> Self { Self { pool, - clients: Default::default(), - users: Default::default(), + clients: RwLock::default(), + users: RwLock::default(), } } diff --git a/matrix-authentication-service/src/storage/user.rs b/matrix-authentication-service/src/storage/user.rs index 30f429e56..96407243f 100644 --- a/matrix-authentication-service/src/storage/user.rs +++ b/matrix-authentication-service/src/storage/user.rs @@ -29,17 +29,17 @@ impl User { #[derive(Debug, Error)] #[error("Invalid credentials")] -pub struct UserLoginError; +pub struct LoginError; #[derive(Debug, Error)] #[error("Could not find user")] -pub struct UserLookupError; +pub struct LookupError; impl super::Storage { - pub async fn login(&self, name: &str, password: &str) -> Result { + pub async fn login(&self, name: &str, password: &str) -> Result { // Hardcoded bad password to test login failures if password == "bad" { - Err(UserLoginError) + Err(LoginError) } else { // First lookup for an existing user let users = self.users.upgradable_read().await; @@ -57,8 +57,8 @@ impl super::Storage { } } - pub async fn lookup_user(&self, name: &str) -> Result { + pub async fn lookup_user(&self, name: &str) -> Result { let users = self.users.read().await; - users.get(name).cloned().ok_or(UserLookupError) + users.get(name).cloned().ok_or(LookupError) } } diff --git a/oauth2-types/src/errors.rs b/oauth2-types/src/errors.rs index d217720e8..92d588510 100644 --- a/oauth2-types/src/errors.rs +++ b/oauth2-types/src/errors.rs @@ -22,23 +22,25 @@ pub trait OAuth2Error: std::fmt::Debug { /// Maps to the required "error" field. fn error(&self) -> &'static str; - /// Human-readable ASCII text providing additional information, used to assist the client - /// developer in understanding the error that occurred. + /// Human-readable ASCII text providing additional information, used to + /// assist the client developer in understanding the error that + /// occurred. /// - /// Maps to the optional "error_description" field. + /// Maps to the optional `error_description` field. fn description(&self) -> Option { None } - /// A URI identifying a human-readable web page with information about the error, used to - /// provide the client developer with additional information about the error. + /// A URI identifying a human-readable web page with information about the + /// error, used to provide the client developer with additional + /// information about the error. /// - /// Maps to the optional "error_uri" field. + /// Maps to the optional `error_uri` field. fn uri(&self) -> Option { None } - /// Wraps the error with an ErrorResponse to help serializing. + /// Wraps the error with an `ErrorResponse` to help serializing. fn into_response(self) -> ErrorResponse where Self: Sized, diff --git a/oauth2-types/src/lib.rs b/oauth2-types/src/lib.rs index cafdc1754..b9df81007 100644 --- a/oauth2-types/src/lib.rs +++ b/oauth2-types/src/lib.rs @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![forbid(unsafe_code)] +#![deny(clippy::all)] +#![warn(clippy::pedantic)] + pub mod errors; pub mod oidc; pub mod requests; diff --git a/oauth2-types/src/oidc.rs b/oauth2-types/src/oidc.rs index 07fedec09..77449a86c 100644 --- a/oauth2-types/src/oidc.rs +++ b/oauth2-types/src/oidc.rs @@ -22,8 +22,8 @@ use crate::requests::{GrantType, ResponseMode, ResponseType}; // TODO: https://datatracker.ietf.org/doc/html/rfc8414#section-2 #[derive(Serialize)] pub struct Metadata { - /// The authorization server's issuer identifier, which is a URL that uses the "https" scheme - /// and has no query or fragment components. + /// The authorization server's issuer identifier, which is a URL that uses + /// the "https" scheme and has no query or fragment components. pub issuer: Url, /// URL of the authorization server's authorization endpoint. @@ -38,28 +38,29 @@ pub struct Metadata { #[serde(skip_serializing_if = "Option::is_none")] pub jwks_uri: Option, - /// URL of the authorization server's OAuth 2.0 Dynamic Client Registration endpoint. + /// URL of the authorization server's OAuth 2.0 Dynamic Client Registration + /// endpoint. #[serde(skip_serializing_if = "Option::is_none")] pub registration_endpoint: Option, - /// JSON array containing a list of the OAuth 2.0 "scope" values that this authorization server - /// supports. + /// JSON array containing a list of the OAuth 2.0 "scope" values that this + /// authorization server supports. #[serde(skip_serializing_if = "HashSet::is_empty")] pub scopes_supported: HashSet, - /// JSON array containing a list of the OAuth 2.0 "response_type" values that this - /// authorization server supports. + /// JSON array containing a list of the OAuth 2.0 "response_type" values + /// that this authorization server supports. #[serde(skip_serializing_if = "HashSet::is_empty")] pub response_types_supported: HashSet, - /// JSON array containing a list of the OAuth 2.0 "response_mode" values that this - /// authorization server supports, as specified in "OAuth 2.0 Multiple Response Type Encoding - /// Practices". + /// JSON array containing a list of the OAuth 2.0 "response_mode" values + /// that this authorization server supports, as specified in "OAuth 2.0 + /// Multiple Response Type Encoding Practices". #[serde(skip_serializing_if = "HashSet::is_empty")] pub response_modes_supported: HashSet, - /// JSON array containing a list of the OAuth 2.0 grant type values that this authorization - /// server supports. + /// JSON array containing a list of the OAuth 2.0 grant type values that + /// this authorization server supports. #[serde(skip_serializing_if = "HashSet::is_empty")] pub grant_types_supported: HashSet, } diff --git a/oauth2-types/src/types.rs b/oauth2-types/src/types.rs index dd3b19273..d741e367c 100644 --- a/oauth2-types/src/types.rs +++ b/oauth2-types/src/types.rs @@ -14,10 +14,16 @@ //! Utilitary types for serde -use serde::{Deserialize, Serialize}; -use std::{collections::HashSet, hash::Hash, time::Duration}; +use std::{ + collections::{hash_map::RandomState, HashSet}, + hash::Hash, + time::Duration, +}; -/// A HashSet that serializes to a space-separated string in alphanumerical order +use serde::{Deserialize, Serialize}; + +/// A `HashSet` that serializes to a space-separated string in alphanumerical +/// order #[derive(Debug, PartialEq)] pub struct StringHashSet(HashSet); @@ -27,7 +33,7 @@ impl From> for StringHashSet { } } -impl From> for HashSet { +impl From> for HashSet { fn from(set: StringHashSet) -> Self { set.0 }