chore: Fix most clippy issue, format & typos

This commit is contained in:
Jade Ellis 2025-05-10 13:29:31 +01:00 committed by nexy7574
commit d7b48a0f7c
No known key found for this signature in database
GPG key ID: 0FA334385D0B689F
19 changed files with 369 additions and 409 deletions

View file

@ -1,20 +1,19 @@
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use askama::Template;
use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode};
// Imports needed by askama templates.
use crate::{
VERSION_EXTRA, GIT_REMOTE_WEB_URL, GIT_REMOTE_COMMIT_URL,
};
use crate::{GIT_REMOTE_COMMIT_URL, GIT_REMOTE_WEB_URL, VERSION_EXTRA};
mod authorize;
mod consent;
mod error;
mod login;
mod response;
mod request;
mod response;
pub use authorize::AuthorizationQuery;
pub use consent::oidc_consent_form;
pub use error::OidcError;
pub use login::{LoginQuery, LoginError, oidc_login_form};
pub use login::{LoginError, LoginQuery, oidc_login_form};
pub use request::OidcRequest;
pub use response::OidcResponse;
@ -35,7 +34,6 @@ pub(crate) struct LoginPageTemplate<'a> {
response_mode: &'a str,
}
/// The parameters for the OIDC consent page template.
#[derive(Template)]
#[template(path = "consent.html.j2")]

View file

@ -1,4 +1,5 @@
use url::Url;
use super::LoginQuery;
/// The set of parameters required for an OIDC authorization request.
@ -27,8 +28,8 @@ impl From<LoginQuery> for AuthorizationQuery {
response_mode,
..
} = value;
AuthorizationQuery {
Self {
client_id,
redirect_uri,
scope,
@ -40,4 +41,3 @@ impl From<LoginQuery> for AuthorizationQuery {
}
}
}

View file

@ -1,20 +1,14 @@
use super::{
encode,
ConsentPageTemplate,
AuthorizationQuery,
OidcResponse,
};
use askama::Template;
use oxide_auth::frontends::simple::request::{Body, Status};
use super::{AuthorizationQuery, ConsentPageTemplate, OidcResponse, encode};
/// A web consent solicitor form for the OIDC authentication flow.
///
/// Asks the resource owner for their consent to let a client access their data
/// on this server.
pub fn oidc_consent_form(
hostname: &str,
query: &AuthorizationQuery,
) -> OidcResponse {
#[must_use]
pub fn oidc_consent_form(hostname: &str, query: &AuthorizationQuery) -> OidcResponse {
// The target request route.
let route = "/_matrix/client/unstable/org.matrix.msc2964/authorize";
let nonce = rand::random::<u64>().to_string();
@ -30,12 +24,7 @@ pub fn oidc_consent_form(
}
/// Render the html contents of the user consent page.
fn consent_page(
hostname: &str,
query: &AuthorizationQuery,
route: &str,
nonce: &str,
) -> String {
fn consent_page(hostname: &str, query: &AuthorizationQuery, route: &str, nonce: &str) -> String {
let template = ConsentPageTemplate {
nonce,
hostname,

View file

@ -1,80 +1,78 @@
use super::OidcRequest;
use axum::{
http::{header::InvalidHeaderValue, StatusCode},
response::{IntoResponse, Response},
http::{StatusCode, header::InvalidHeaderValue},
response::{IntoResponse, Response},
};
use oxide_auth::frontends::{dev::OAuthError, simple::endpoint::Error};
use super::OidcRequest;
#[derive(Debug)]
/// The error type for Oxide Auth operations
pub enum OidcError {
/// Errors occuring in Endpoint operations
Endpoint(OAuthError),
/// Errors occuring in Endpoint operations
Header(InvalidHeaderValue),
/// Errors with the request encoding
Encoding,
/// Request body could not be parsed as a form
Form,
/// Request query was absent or could not be parsed
Query,
/// Request query was absent or could not be parsed
Body,
/// The Authorization header was invalid
Authorization,
/// General internal server error
InternalError(Option<String>),
/// Errors occurring in Endpoint operations
Endpoint(OAuthError),
/// Errors occurring in Endpoint operations
Header(InvalidHeaderValue),
/// Errors with the request encoding
Encoding,
/// Request body could not be parsed as a form
Form,
/// Request query was absent or could not be parsed
Query,
/// Request query was absent or could not be parsed
Body,
/// The Authorization header was invalid
Authorization,
/// General internal server error
InternalError(Option<String>),
}
impl std::fmt::Display for OidcError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
OidcError::Endpoint(ref e) => write!(f, "Endpoint, {}", e),
OidcError::Header(ref e) => write!(f, "Couldn't set header, {}", e),
OidcError::Encoding => write!(f, "Error decoding request"),
OidcError::Form => write!(f, "Request is not a form"),
OidcError::Query => write!(f, "No query present"),
OidcError::Body => write!(f, "No body present"),
OidcError::Authorization => write!(f, "Request has invalid Authorization headers"),
OidcError::InternalError(None) => write!(f, "An internal server error occured"),
OidcError::InternalError(Some(ref e)) => write!(f, "An internal server error occured: {}", e),
}
}
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
| Self::Endpoint(ref e) => write!(f, "Endpoint, {e}"),
| Self::Header(ref e) => write!(f, "Couldn't set header, {e}"),
| Self::Encoding => write!(f, "Error decoding request"),
| Self::Form => write!(f, "Request is not a form"),
| Self::Query => write!(f, "No query present"),
| Self::Body => write!(f, "No body present"),
| Self::Authorization => write!(f, "Request has invalid Authorization headers"),
| Self::InternalError(None) => write!(f, "An internal server error occurred"),
| Self::InternalError(Some(ref e)) =>
write!(f, "An internal server error occurred: {e}"),
}
}
}
impl std::error::Error for OidcError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
OidcError::Endpoint(ref e) => e.source(),
OidcError::Header(ref e) => e.source(),
_ => None,
}
}
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
| Self::Endpoint(ref e) => e.source(),
| Self::Header(ref e) => e.source(),
| _ => None,
}
}
}
impl IntoResponse for OidcError {
fn into_response(self) -> Response {
(StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response()
}
fn into_response(self) -> Response {
(StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response()
}
}
impl From<Error<OidcRequest>> for OidcError {
fn from(e: Error<OidcRequest>) -> Self {
match e {
Error::Web(e) => e,
Error::OAuth(e) => e.into(),
}
}
fn from(e: Error<OidcRequest>) -> Self {
match e {
| Error::Web(e) => e,
| Error::OAuth(e) => e.into(),
}
}
}
impl From<OAuthError> for OidcError {
fn from(e: OAuthError) -> Self {
OidcError::Endpoint(e)
}
fn from(e: OAuthError) -> Self { Self::Endpoint(e) }
}
impl From<InvalidHeaderValue> for OidcError {
fn from(e: InvalidHeaderValue) -> Self {
Self::Header(e)
}
fn from(e: InvalidHeaderValue) -> Self { Self::Header(e) }
}

View file

@ -1,17 +1,13 @@
use super::{
AuthorizationQuery,
LoginPageTemplate,
OidcRequest,
OidcResponse,
};
use std::str::FromStr;
use askama::Template;
use oxide_auth::{
endpoint::QueryParameter,
frontends::simple::request::{Body, Status},
};
use url::Url;
use std::str::FromStr;
use super::{AuthorizationQuery, LoginPageTemplate, OidcRequest, OidcResponse};
/// The set of query parameters a client needs to get authorization.
#[derive(serde::Deserialize, Debug, Clone)]
@ -38,40 +34,40 @@ impl TryFrom<OidcRequest> for LoginQuery {
let body = value.body().expect("body in OidcRequest");
let Some(username) = body.unique_value("username") else {
return Err(LoginError("missing field: username".to_string()));
return Err(LoginError("missing field: username".to_owned()));
};
let Some(password) = body.unique_value("password") else {
return Err(LoginError("missing field: password".to_string()));
return Err(LoginError("missing field: password".to_owned()));
};
let Some(client_id) = body.unique_value("client_id") else {
return Err(LoginError("missing field: client_id".to_string()));
return Err(LoginError("missing field: client_id".to_owned()));
};
let Some(redirect_uri) = body.unique_value("redirect_uri") else {
return Err(LoginError("missing field: redirect_uri".to_string()));
return Err(LoginError("missing field: redirect_uri".to_owned()));
};
let Some(scope) = body.unique_value("scope") else {
return Err(LoginError("missing field: scope".to_string()));
return Err(LoginError("missing field: scope".to_owned()));
};
let Some(state) = body.unique_value("state") else {
return Err(LoginError("missing field: state".to_string()));
return Err(LoginError("missing field: state".to_owned()));
};
let Some(code_challenge) = body.unique_value("code_challenge") else {
return Err(LoginError("missing field: code_challenge".to_string()));
return Err(LoginError("missing field: code_challenge".to_owned()));
};
let Some(code_challenge_method) = body.unique_value("code_challenge_method") else {
return Err(LoginError("missing field: code_challenge_method".to_string()));
return Err(LoginError("missing field: code_challenge_method".to_owned()));
};
let Some(response_type) = body.unique_value("response_type") else {
return Err(LoginError("missing field: response_type".to_string()));
return Err(LoginError("missing field: response_type".to_owned()));
};
let Some(response_mode) = body.unique_value("response_mode") else {
return Err(LoginError("missing field: response_mode".to_string()));
return Err(LoginError("missing field: response_mode".to_owned()));
};
let Ok(redirect_uri) = Url::from_str(&redirect_uri) else {
return Err(LoginError("invalid field: redirect_uri".to_string()));
return Err(LoginError("invalid field: redirect_uri".to_owned()));
};
Ok(LoginQuery {
Ok(Self {
username: username.to_string(),
password: password.to_string(),
client_id: client_id.to_string(),
@ -89,10 +85,8 @@ impl TryFrom<OidcRequest> for LoginQuery {
/// A web login form for the OIDC authentication flow.
///
/// The returned `OidcResponse` handles CSP headers to allow that form.
pub fn oidc_login_form(
hostname: &str,
query: &AuthorizationQuery,
) -> OidcResponse {
#[must_use]
pub fn oidc_login_form(hostname: &str, query: &AuthorizationQuery) -> OidcResponse {
// The target request route.
let route = "/_matrix/client/unstable/org.matrix.msc2964/login";
let nonce = rand::random::<u64>().to_string();
@ -108,12 +102,7 @@ pub fn oidc_login_form(
}
/// Render the html contents of the login page.
fn login_page(
hostname: &str,
query: &AuthorizationQuery,
route: &str,
nonce: &str,
) -> String {
fn login_page(hostname: &str, query: &AuthorizationQuery, route: &str, nonce: &str) -> String {
let template = LoginPageTemplate {
nonce,
hostname,

View file

@ -1,11 +1,13 @@
use super::{OidcError, OidcResponse};
use oxide_auth::endpoint::{NormalizedParameter, QueryParameter, WebRequest};
use std::borrow::Cow;
use async_trait::async_trait;
use axum::{
extract::{Form, FromRequest, FromRequestParts, Query, Request},
http::header,
};
use std::borrow::Cow;
use oxide_auth::endpoint::{NormalizedParameter, QueryParameter, WebRequest};
use super::{OidcError, OidcResponse};
/// An OIDC authentication request.
///
@ -16,84 +18,79 @@ use std::borrow::Cow;
/// [oxide-auth-axum]: https://docs.rs/oxide-auth-axum
#[derive(Clone, Debug)]
pub struct OidcRequest {
pub(crate) auth: Option<String>,
pub(crate) query: Option<NormalizedParameter>,
pub(crate) body: Option<NormalizedParameter>,
pub(crate) auth: Option<String>,
pub(crate) query: Option<NormalizedParameter>,
pub(crate) body: Option<NormalizedParameter>,
}
impl OidcRequest {
/// Fetch the authorization header from the request
pub fn authorization_header(&self) -> Option<&str> {
self.auth.as_deref()
}
/// Fetch the authorization header from the request
#[must_use]
pub fn authorization_header(&self) -> Option<&str> { self.auth.as_deref() }
/// Fetch the query for this request
pub fn query(&self) -> Option<&NormalizedParameter> {
self.query.as_ref()
}
/// Fetch the query for this request
#[must_use]
pub fn query(&self) -> Option<&NormalizedParameter> { self.query.as_ref() }
/// Fetch the query mutably
pub fn query_mut(&mut self) -> Option<&mut NormalizedParameter> {
self.query.as_mut()
}
/// Fetch the query mutably
pub fn query_mut(&mut self) -> Option<&mut NormalizedParameter> { self.query.as_mut() }
/// Fetch the body of the request
pub fn body(&self) -> Option<&NormalizedParameter> {
self.body.as_ref()
}
/// Fetch the body of the request
#[must_use]
pub fn body(&self) -> Option<&NormalizedParameter> { self.body.as_ref() }
}
impl WebRequest for OidcRequest {
type Error = OidcError;
type Response = OidcResponse;
type Error = OidcError;
type Response = OidcResponse;
fn query(&mut self) -> Result<Cow<'_, dyn QueryParameter + 'static>, Self::Error> {
self.query
.as_ref()
.map(|q| Cow::Borrowed(q as &dyn QueryParameter))
.ok_or(OidcError::Query)
}
fn query(&mut self) -> Result<Cow<'_, dyn QueryParameter + 'static>, Self::Error> {
self.query
.as_ref()
.map(|q| Cow::Borrowed(q as &dyn QueryParameter))
.ok_or(OidcError::Query)
}
fn urlbody(&mut self) -> Result<Cow<'_, dyn QueryParameter + 'static>, Self::Error> {
self.body
.as_ref()
.map(|b| Cow::Borrowed(b as &dyn QueryParameter))
.ok_or(OidcError::Body)
}
fn urlbody(&mut self) -> Result<Cow<'_, dyn QueryParameter + 'static>, Self::Error> {
self.body
.as_ref()
.map(|b| Cow::Borrowed(b as &dyn QueryParameter))
.ok_or(OidcError::Body)
}
fn authheader(&mut self) -> Result<Option<Cow<'_, str>>, Self::Error> {
Ok(self.auth.as_deref().map(Cow::Borrowed))
}
fn authheader(&mut self) -> Result<Option<Cow<'_, str>>, Self::Error> {
Ok(self.auth.as_deref().map(Cow::Borrowed))
}
}
#[async_trait]
impl<S> FromRequest<S> for OidcRequest
where
S: Send + Sync,
S: Send + Sync,
{
type Rejection = OidcError;
type Rejection = OidcError;
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
let mut all_auth = req.headers().get_all(header::AUTHORIZATION).iter();
let optional = all_auth.next();
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
let mut all_auth = req.headers().get_all(header::AUTHORIZATION).iter();
let optional = all_auth.next();
let auth = if all_auth.next().is_some() {
return Err(OidcError::Authorization);
} else {
optional.and_then(|hv| hv.to_str().ok().map(str::to_owned))
};
let auth = if all_auth.next().is_some() {
return Err(OidcError::Authorization);
} else {
optional.and_then(|hv| hv.to_str().ok().map(str::to_owned))
};
let (mut parts, body) = req.into_parts();
let query = Query::from_request_parts(&mut parts, state)
.await
.ok()
.map(|q: Query<NormalizedParameter>| q.0);
let (mut parts, body) = req.into_parts();
let query = Query::from_request_parts(&mut parts, state)
.await
.ok()
.map(|q: Query<NormalizedParameter>| q.0);
let req = Request::from_parts(parts, body);
let body = Form::from_request(req, state)
.await
.ok()
.map(|b: Form<NormalizedParameter>| b.0);
let req = Request::from_parts(parts, body);
let body = Form::from_request(req, state)
.await
.ok()
.map(|b: Form<NormalizedParameter>| b.0);
// If the query is empty and the body has a request, copy it over
// because login forms are POST requests but OAuth flow expects
@ -110,6 +107,6 @@ where
},
};
Ok(Self { auth, query, body })
}
Ok(Self { auth, query, body })
}
}

View file

@ -1,15 +1,16 @@
use super::{oidc_consent_form, LoginQuery, OidcError, OidcRequest};
use axum::{
body::Body,
http::{Response, header},
response::IntoResponse,
};
use oxide_auth::{
endpoint::{OwnerConsent, OwnerSolicitor, Solicitation, WebRequest, WebResponse},
frontends::simple::request::{Body as OAuthRequestBody, Status},
};
use axum::{
body::Body,
http::{header, Response},
response::IntoResponse,
};
use url::Url;
use super::{LoginQuery, OidcError, OidcRequest, oidc_consent_form};
/// A Web response that can be processed by the OIDC authentication flow before
/// being sent over.
#[derive(Default, Clone, Debug)]
@ -22,9 +23,9 @@ pub struct OidcResponse {
}
impl OidcResponse {
/// Instanciate from a response body. Used to send login or consent forms.
/// Instantiate from a response body. Used to send login or consent forms.
pub fn from_body(body: &str) -> Result<Self, OidcError> {
let mut result = OidcResponse::default();
let mut result = Self::default();
result.body_text(body)?;
Ok(result)
@ -33,17 +34,19 @@ impl OidcResponse {
impl IntoResponse for OidcResponse {
fn into_response(self) -> Response<Body> {
let body = self.body.expect("body").as_str().to_string();
let response = Response::builder()
let body = self.body.expect("body").as_str().to_owned();
Response::builder()
.header(header::CONTENT_TYPE, "text/html")
.header(
header::CONTENT_SECURITY_POLICY,
format!("default-src 'nonce-{}'; form-action https://eon.presentmatter.one/;", self.nonce)
format!(
"default-src 'nonce-{}'; form-action https://eon.presentmatter.one/;",
self.nonce
),
)
.body(body.into())
.unwrap();
response
.unwrap()
}
}
@ -62,10 +65,7 @@ impl OwnerSolicitor<OidcRequest> for OidcResponse {
.try_into()
.expect("login query from OidcRequest");
OwnerConsent::InProgress(oidc_consent_form(
hostname,
&query.into(),
))
OwnerConsent::InProgress(oidc_consent_form(hostname, &query.into()))
}
}
@ -80,39 +80,40 @@ impl WebResponse for OidcResponse {
Ok(())
}
/// A response which will redirect the user-agent to which the response is issued.
fn redirect(&mut self, url: Url) -> Result<(), Self::Error> {
self.status = Status::Redirect;
self.location = Some(url);
self.www_authenticate = None;
Ok(())
}
/// A response which will redirect the user-agent to which the response is
/// issued.
fn redirect(&mut self, url: Url) -> Result<(), Self::Error> {
self.status = Status::Redirect;
self.location = Some(url);
self.www_authenticate = None;
Ok(())
}
/// Set the response status to 400.
fn client_error(&mut self) -> Result<(), Self::Error> {
self.status = Status::BadRequest;
self.location = None;
self.www_authenticate = None;
Ok(())
}
/// Set the response status to 400.
fn client_error(&mut self) -> Result<(), Self::Error> {
self.status = Status::BadRequest;
self.location = None;
self.www_authenticate = None;
Ok(())
}
/// Set the response status to 401 and add a `WWW-Authenticate` header.
fn unauthorized(&mut self, header_value: &str) -> Result<(), Self::Error> {
self.status = Status::Unauthorized;
self.location = None;
self.www_authenticate = Some(header_value.to_owned());
Ok(())
}
/// Set the response status to 401 and add a `WWW-Authenticate` header.
fn unauthorized(&mut self, header_value: &str) -> Result<(), Self::Error> {
self.status = Status::Unauthorized;
self.location = None;
self.www_authenticate = Some(header_value.to_owned());
Ok(())
}
/// A pure text response with no special media type set.
fn body_text(&mut self, text: &str) -> Result<(), Self::Error> {
self.body = Some(OAuthRequestBody::Text(text.to_owned()));
Ok(())
}
/// A pure text response with no special media type set.
fn body_text(&mut self, text: &str) -> Result<(), Self::Error> {
self.body = Some(OAuthRequestBody::Text(text.to_owned()));
Ok(())
}
/// Json repsonse data, with media type `aplication/json.
fn body_json(&mut self, data: &str) -> Result<(), Self::Error> {
self.body = Some(OAuthRequestBody::Json(data.to_owned()));
Ok(())
}
/// Json response data, with media type `aplication/json.
fn body_json(&mut self, data: &str) -> Result<(), Self::Error> {
self.body = Some(OAuthRequestBody::Json(data.to_owned()));
Ok(())
}
}