From 51515358f7ffa5466032a893f16e661cd5ddc017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Mon, 17 Oct 2022 13:50:33 +0200 Subject: [PATCH] Make more enum types accept unknown values --- .../src/oauth2/authorization/callback.rs | 20 +++++++++++-------- .../src/oauth2/authorization/complete.rs | 12 ++++++----- .../handlers/src/oauth2/authorization/mod.rs | 14 ++++++------- crates/oauth2-types/src/requests.rs | 18 ++++++++++++++--- 4 files changed, 40 insertions(+), 24 deletions(-) diff --git a/crates/handlers/src/oauth2/authorization/callback.rs b/crates/handlers/src/oauth2/authorization/callback.rs index f028567e..5b32a671 100644 --- a/crates/handlers/src/oauth2/authorization/callback.rs +++ b/crates/handlers/src/oauth2/authorization/callback.rs @@ -41,12 +41,15 @@ pub struct CallbackDestination { } #[derive(Debug, Error)] -pub enum InvalidRedirectUriError { +pub enum IntoCallbackDestinationError { #[error("Redirect URI can't have a fragment")] - FragmentNotAllowed, + RedirectUriFragmentNotAllowed, #[error("Existing query parameters are not valid")] - InvalidQueryParams(#[from] serde_urlencoded::de::Error), + RedirectUriInvalidQueryParams(#[from] serde_urlencoded::de::Error), + + #[error("Requested response_mode is not supported")] + UnsupportedResponseMode, } #[derive(Debug, Error)] @@ -59,11 +62,11 @@ pub enum CallbackDestinationError { } impl TryFrom<&AuthorizationGrant> for CallbackDestination { - type Error = InvalidRedirectUriError; + type Error = IntoCallbackDestinationError; fn try_from(value: &AuthorizationGrant) -> Result { Self::try_new( - value.response_mode, + &value.response_mode, value.redirect_uri.clone(), value.state.clone(), ) @@ -72,12 +75,12 @@ impl TryFrom<&AuthorizationGrant> for CallbackDestination impl CallbackDestination { pub fn try_new( - mode: ResponseMode, + mode: &ResponseMode, mut redirect_uri: Url, state: Option, - ) -> Result { + ) -> Result { if redirect_uri.fragment().is_some() { - return Err(InvalidRedirectUriError::FragmentNotAllowed); + return Err(IntoCallbackDestinationError::RedirectUriFragmentNotAllowed); } let mode = match mode { @@ -95,6 +98,7 @@ impl CallbackDestination { } ResponseMode::Fragment => CallbackDestinationMode::Fragment, ResponseMode::FormPost => CallbackDestinationMode::FormPost, + _ => return Err(IntoCallbackDestinationError::UnsupportedResponseMode), }; Ok(Self { diff --git a/crates/handlers/src/oauth2/authorization/complete.rs b/crates/handlers/src/oauth2/authorization/complete.rs index 8f416ec7..644a8c7f 100644 --- a/crates/handlers/src/oauth2/authorization/complete.rs +++ b/crates/handlers/src/oauth2/authorization/complete.rs @@ -39,7 +39,9 @@ use oauth2_types::requests::{AccessTokenResponse, AuthorizationResponse}; use sqlx::{PgPool, Postgres, Transaction}; use thiserror::Error; -use super::callback::{CallbackDestination, CallbackDestinationError, InvalidRedirectUriError}; +use super::callback::{ + CallbackDestination, CallbackDestinationError, IntoCallbackDestinationError, +}; #[derive(Debug, Error)] pub enum RouteError { @@ -90,8 +92,8 @@ impl From for RouteError { } } -impl From for RouteError { - fn from(e: InvalidRedirectUriError) -> Self { +impl From for RouteError { + fn from(e: IntoCallbackDestinationError) -> Self { Self::Internal(Box::new(e)) } } @@ -175,8 +177,8 @@ impl From for GrantCompletionError { } } -impl From for GrantCompletionError { - fn from(e: InvalidRedirectUriError) -> Self { +impl From for GrantCompletionError { + fn from(e: IntoCallbackDestinationError) -> Self { Self::Internal(Box::new(e)) } } diff --git a/crates/handlers/src/oauth2/authorization/mod.rs b/crates/handlers/src/oauth2/authorization/mod.rs index 7d8931e4..ad5a347b 100644 --- a/crates/handlers/src/oauth2/authorization/mod.rs +++ b/crates/handlers/src/oauth2/authorization/mod.rs @@ -58,8 +58,8 @@ pub enum RouteError { #[error("could not find client")] ClientNotFound, - #[error("invalid redirect uri")] - InvalidRedirectUri(#[from] self::callback::InvalidRedirectUriError), + #[error("invalid parameters")] + IntoCallbackDestination(#[from] self::callback::IntoCallbackDestinationError), #[error("invalid redirect uri")] UnknownRedirectUri(#[from] mas_data_model::InvalidRedirectUriError), @@ -78,11 +78,9 @@ impl IntoResponse for RouteError { RouteError::ClientNotFound => { (StatusCode::BAD_REQUEST, "could not find client").into_response() } - RouteError::InvalidRedirectUri(e) => ( - StatusCode::BAD_REQUEST, - format!("Invalid redirect URI ({})", e), - ) - .into_response(), + RouteError::IntoCallbackDestination(e) => { + (StatusCode::BAD_REQUEST, e.to_string()).into_response() + } RouteError::UnknownRedirectUri(e) => ( StatusCode::BAD_REQUEST, format!("Invalid redirect URI ({})", e), @@ -175,7 +173,7 @@ pub(crate) async fn get( // Now we have a proper callback destination to go to on error let callback_destination = CallbackDestination::try_new( - response_mode, + &response_mode, redirect_uri.clone(), params.auth.state.clone(), )?; diff --git a/crates/oauth2-types/src/requests.rs b/crates/oauth2-types/src/requests.rs index a44a4168..dfe33fd4 100644 --- a/crates/oauth2-types/src/requests.rs +++ b/crates/oauth2-types/src/requests.rs @@ -41,13 +41,13 @@ use crate::{response_type::ResponseType, scope::Scope}; PartialOrd, Ord, Clone, - Copy, Display, FromStr, SerializeDisplay, DeserializeFromStr, )] #[display(style = "snake_case")] +#[non_exhaustive] pub enum ResponseMode { /// Authorization Response parameters are encoded in the query string added /// to the `redirect_uri`. @@ -65,6 +65,10 @@ pub enum ResponseMode { /// /// Defined in [OAuth 2.0 Form Post Response Mode](https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html). FormPost, + + /// An unknown value. + #[display("{0}")] + Unknown(String), } /// Value that specifies how the Authorization Server displays the @@ -79,13 +83,13 @@ pub enum ResponseMode { PartialOrd, Ord, Clone, - Copy, Display, FromStr, SerializeDisplay, DeserializeFromStr, )] #[display(style = "snake_case")] +#[non_exhaustive] pub enum Display { /// The Authorization Server should display the authentication and consent /// UI consistent with a full User Agent page view. @@ -104,6 +108,10 @@ pub enum Display { /// The Authorization Server should display the authentication and consent /// UI consistent with a "feature phone" type display. Wap, + + /// An unknown value. + #[display("{0}")] + Unknown(String), } impl Default for Display { @@ -124,13 +132,13 @@ impl Default for Display { PartialOrd, Ord, Clone, - Copy, Display, FromStr, SerializeDisplay, DeserializeFromStr, )] #[display(style = "snake_case")] +#[non_exhaustive] pub enum Prompt { /// The Authorization Server must not display any authentication or consent /// user interface pages. @@ -157,6 +165,10 @@ pub enum Prompt { /// /// Defined in [Initiating User Registration via OpenID Connect](https://openid.net/specs/openid-connect-prompt-create-1_0.html). Create, + + /// An unknown value. + #[display("{0}")] + Unknown(String), } /// The body of a request to the [Authorization Endpoint].