1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-28 11:02:02 +03:00

Properly implement Display and FromStr for oauth2-types enums

Use SerializeDisplay and DeserializeFromStr derives.
Add tests for serialize and deserialize implemntations.
This commit is contained in:
Kévin Commaille
2022-09-12 15:11:47 +02:00
committed by Quentin Gliech
parent 94ba03a273
commit c4e495a84a
5 changed files with 413 additions and 98 deletions

View File

@ -14,8 +14,9 @@
use std::borrow::Cow;
use parse_display::{Display, FromStr};
use serde::{Deserialize, Serialize};
use serde_enum_str::{Deserialize_enum_str, Serialize_enum_str};
use serde_with::{DeserializeFromStr, SerializeDisplay};
/// A client error returned by an authorization server.
///
@ -55,8 +56,8 @@ impl From<ClientErrorCode> for ClientError {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize_enum_str, Deserialize_enum_str)]
#[serde(rename_all = "snake_case")]
#[derive(Debug, Clone, PartialEq, Eq, Display, FromStr, SerializeDisplay, DeserializeFromStr)]
#[display(style = "snake_case")]
pub enum ClientErrorCode {
/// `invalid_request`
///
@ -225,7 +226,7 @@ pub enum ClientErrorCode {
InvalidClientMetadata,
/// Another error code.
#[serde(other)]
#[display("{0}")]
Unknown(String),
}
@ -307,3 +308,195 @@ impl ClientErrorCode {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serialize_client_error_code() {
assert_eq!(
serde_json::to_string(&ClientErrorCode::InvalidRequest).unwrap(),
"\"invalid_request\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::InvalidClient).unwrap(),
"\"invalid_client\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::InvalidGrant).unwrap(),
"\"invalid_grant\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::UnauthorizedClient).unwrap(),
"\"unauthorized_client\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::UnsupportedGrantType).unwrap(),
"\"unsupported_grant_type\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::AccessDenied).unwrap(),
"\"access_denied\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::UnsupportedResponseType).unwrap(),
"\"unsupported_response_type\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::InvalidScope).unwrap(),
"\"invalid_scope\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::ServerError).unwrap(),
"\"server_error\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::TemporarilyUnavailable).unwrap(),
"\"temporarily_unavailable\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::InteractionRequired).unwrap(),
"\"interaction_required\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::LoginRequired).unwrap(),
"\"login_required\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::AccountSelectionRequired).unwrap(),
"\"account_selection_required\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::ConsentRequired).unwrap(),
"\"consent_required\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::InvalidRequestUri).unwrap(),
"\"invalid_request_uri\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::InvalidRequestObject).unwrap(),
"\"invalid_request_object\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::RequestNotSupported).unwrap(),
"\"request_not_supported\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::RequestUriNotSupported).unwrap(),
"\"request_uri_not_supported\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::RegistrationNotSupported).unwrap(),
"\"registration_not_supported\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::InvalidRedirectUri).unwrap(),
"\"invalid_redirect_uri\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::InvalidClientMetadata).unwrap(),
"\"invalid_client_metadata\""
);
assert_eq!(
serde_json::to_string(&ClientErrorCode::Unknown("unknown_error_code".to_owned()))
.unwrap(),
"\"unknown_error_code\""
);
}
#[test]
fn deserialize_client_error_code() {
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"invalid_request\"").unwrap(),
ClientErrorCode::InvalidRequest
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"invalid_client\"").unwrap(),
ClientErrorCode::InvalidClient
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"invalid_grant\"").unwrap(),
ClientErrorCode::InvalidGrant
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"unauthorized_client\"").unwrap(),
ClientErrorCode::UnauthorizedClient
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"unsupported_grant_type\"").unwrap(),
ClientErrorCode::UnsupportedGrantType
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"access_denied\"").unwrap(),
ClientErrorCode::AccessDenied
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"unsupported_response_type\"").unwrap(),
ClientErrorCode::UnsupportedResponseType
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"invalid_scope\"").unwrap(),
ClientErrorCode::InvalidScope
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"server_error\"").unwrap(),
ClientErrorCode::ServerError
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"temporarily_unavailable\"").unwrap(),
ClientErrorCode::TemporarilyUnavailable
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"interaction_required\"").unwrap(),
ClientErrorCode::InteractionRequired
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"login_required\"").unwrap(),
ClientErrorCode::LoginRequired
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"account_selection_required\"").unwrap(),
ClientErrorCode::AccountSelectionRequired
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"consent_required\"").unwrap(),
ClientErrorCode::ConsentRequired
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"invalid_request_uri\"").unwrap(),
ClientErrorCode::InvalidRequestUri
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"invalid_request_object\"").unwrap(),
ClientErrorCode::InvalidRequestObject
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"request_not_supported\"").unwrap(),
ClientErrorCode::RequestNotSupported
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"request_uri_not_supported\"").unwrap(),
ClientErrorCode::RequestUriNotSupported
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"registration_not_supported\"").unwrap(),
ClientErrorCode::RegistrationNotSupported
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"invalid_redirect_uri\"").unwrap(),
ClientErrorCode::InvalidRedirectUri
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"invalid_client_metadata\"").unwrap(),
ClientErrorCode::InvalidClientMetadata
);
assert_eq!(
serde_json::from_str::<ClientErrorCode>("\"unknown_error_code\"").unwrap(),
ClientErrorCode::Unknown("unknown_error_code".to_owned())
);
}
}

View File

@ -22,29 +22,36 @@ use mas_iana::{
PkceCodeChallengeMethod,
},
};
use parse_display::{Display, FromStr};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use serde_with::{skip_serializing_none, DeserializeFromStr, SerializeDisplay};
use thiserror::Error;
use url::Url;
use crate::requests::{Display, GrantType, Prompt, ResponseMode};
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[serde(rename_all = "lowercase")]
#[derive(
SerializeDisplay, DeserializeFromStr, Clone, Copy, PartialEq, Eq, Hash, Debug, Display, FromStr,
)]
#[display(style = "lowercase")]
pub enum ApplicationType {
Web,
Native,
}
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[serde(rename_all = "lowercase")]
#[derive(
SerializeDisplay, DeserializeFromStr, Clone, Copy, PartialEq, Eq, Hash, Debug, Display, FromStr,
)]
#[display(style = "lowercase")]
pub enum SubjectType {
Public,
Pairwise,
}
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[serde(rename_all = "lowercase")]
#[derive(
SerializeDisplay, DeserializeFromStr, Clone, Copy, PartialEq, Eq, Hash, Debug, Display, FromStr,
)]
#[display(style = "lowercase")]
pub enum ClaimType {
Normal,
Aggregated,
@ -918,7 +925,7 @@ mod tests {
};
use url::Url;
use super::{ProviderMetadata, ProviderMetadataVerificationError, SubjectType};
use super::*;
fn valid_provider_metadata() -> (ProviderMetadata, Url) {
let issuer = Url::parse("https://localhost").unwrap();
@ -1407,4 +1414,84 @@ mod tests {
Some(Url::parse("https://localhost/par?query#fragment").unwrap());
metadata.validate(&issuer).unwrap();
}
#[test]
fn serialize_application_type() {
assert_eq!(
serde_json::to_string(&ApplicationType::Web).unwrap(),
"\"web\""
);
assert_eq!(
serde_json::to_string(&ApplicationType::Native).unwrap(),
"\"native\""
);
}
#[test]
fn deserialize_application_type() {
assert_eq!(
serde_json::from_str::<ApplicationType>("\"web\"").unwrap(),
ApplicationType::Web
);
assert_eq!(
serde_json::from_str::<ApplicationType>("\"native\"").unwrap(),
ApplicationType::Native
);
}
#[test]
fn serialize_subject_type() {
assert_eq!(
serde_json::to_string(&SubjectType::Public).unwrap(),
"\"public\""
);
assert_eq!(
serde_json::to_string(&SubjectType::Pairwise).unwrap(),
"\"pairwise\""
);
}
#[test]
fn deserialize_subject_type() {
assert_eq!(
serde_json::from_str::<SubjectType>("\"public\"").unwrap(),
SubjectType::Public
);
assert_eq!(
serde_json::from_str::<SubjectType>("\"pairwise\"").unwrap(),
SubjectType::Pairwise
);
}
#[test]
fn serialize_claim_type() {
assert_eq!(
serde_json::to_string(&ClaimType::Normal).unwrap(),
"\"normal\""
);
assert_eq!(
serde_json::to_string(&ClaimType::Aggregated).unwrap(),
"\"aggregated\""
);
assert_eq!(
serde_json::to_string(&ClaimType::Distributed).unwrap(),
"\"distributed\""
);
}
#[test]
fn deserialize_claim_type() {
assert_eq!(
serde_json::from_str::<ClaimType>("\"normal\"").unwrap(),
ClaimType::Normal
);
assert_eq!(
serde_json::from_str::<ClaimType>("\"aggregated\"").unwrap(),
ClaimType::Aggregated
);
assert_eq!(
serde_json::from_str::<ClaimType>("\"distributed\"").unwrap(),
ClaimType::Distributed
);
}
}

View File

@ -22,8 +22,8 @@ use mas_iana::oauth::{
use parse_display::{Display, FromStr};
use serde::{Deserialize, Serialize};
use serde_with::{
formats::SpaceSeparator, serde_as, skip_serializing_none, DisplayFromStr, DurationSeconds,
StringWithSeparator, TimestampSeconds,
formats::SpaceSeparator, serde_as, skip_serializing_none, DeserializeFromStr, DisplayFromStr,
DurationSeconds, SerializeDisplay, StringWithSeparator, TimestampSeconds,
};
use url::Url;
@ -46,10 +46,10 @@ use crate::scope::Scope;
Copy,
Display,
FromStr,
Serialize,
Deserialize,
SerializeDisplay,
DeserializeFromStr,
)]
#[serde(rename_all = "snake_case")]
#[display(style = "snake_case")]
pub enum ResponseMode {
/// Authorization Response parameters are encoded in the query string added
/// to the `redirect_uri`.
@ -84,10 +84,10 @@ pub enum ResponseMode {
Copy,
Display,
FromStr,
Serialize,
Deserialize,
SerializeDisplay,
DeserializeFromStr,
)]
#[serde(rename_all = "snake_case")]
#[display(style = "snake_case")]
pub enum Display {
/// The Authorization Server should display the authentication and consent
/// UI consistent with a full User Agent page view.
@ -129,11 +129,10 @@ impl Default for Display {
Copy,
Display,
FromStr,
Serialize,
Deserialize,
SerializeDisplay,
DeserializeFromStr,
)]
#[display(style = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum Prompt {
/// The Authorization Server must not display any authentication or consent
/// user interface pages.
@ -366,10 +365,10 @@ pub struct ClientCredentialsGrant {
Copy,
Display,
FromStr,
Serialize,
Deserialize,
SerializeDisplay,
DeserializeFromStr,
)]
#[serde(rename_all = "snake_case")]
#[display(style = "snake_case")]
pub enum GrantType {
/// [`authorization_code`](https://www.rfc-editor.org/rfc/rfc6749#section-4.1)
AuthorizationCode,
@ -387,11 +386,11 @@ pub enum GrantType {
Password,
/// [`urn:ietf:params:oauth:grant-type:device_code`](https://www.rfc-editor.org/rfc/rfc8628)
#[serde(rename = "urn:ietf:params:oauth:grant-type:device_code")]
#[display("urn:ietf:params:oauth:grant-type:device_code")]
DeviceCode,
/// [`urn:openid:params:grant-type:ciba`](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html)
#[serde(rename = "urn:openid:params:grant-type:ciba")]
#[display("urn:openid:params:grant-type:ciba")]
ClientInitiatedBackchannelAuthentication,
}
@ -667,4 +666,106 @@ mod tests {
GrantType::ClientInitiatedBackchannelAuthentication
);
}
#[test]
fn serialize_response_mode() {
assert_eq!(
serde_json::to_string(&ResponseMode::Query).unwrap(),
"\"query\""
);
assert_eq!(
serde_json::to_string(&ResponseMode::Fragment).unwrap(),
"\"fragment\""
);
assert_eq!(
serde_json::to_string(&ResponseMode::FormPost).unwrap(),
"\"form_post\""
);
}
#[test]
fn deserialize_response_mode() {
assert_eq!(
serde_json::from_str::<ResponseMode>("\"query\"").unwrap(),
ResponseMode::Query
);
assert_eq!(
serde_json::from_str::<ResponseMode>("\"fragment\"").unwrap(),
ResponseMode::Fragment
);
assert_eq!(
serde_json::from_str::<ResponseMode>("\"form_post\"").unwrap(),
ResponseMode::FormPost
);
}
#[test]
fn serialize_display() {
assert_eq!(serde_json::to_string(&Display::Page).unwrap(), "\"page\"");
assert_eq!(serde_json::to_string(&Display::Popup).unwrap(), "\"popup\"");
assert_eq!(serde_json::to_string(&Display::Touch).unwrap(), "\"touch\"");
assert_eq!(serde_json::to_string(&Display::Wap).unwrap(), "\"wap\"");
}
#[test]
fn deserialize_display() {
assert_eq!(
serde_json::from_str::<Display>("\"page\"").unwrap(),
Display::Page
);
assert_eq!(
serde_json::from_str::<Display>("\"popup\"").unwrap(),
Display::Popup
);
assert_eq!(
serde_json::from_str::<Display>("\"touch\"").unwrap(),
Display::Touch
);
assert_eq!(
serde_json::from_str::<Display>("\"wap\"").unwrap(),
Display::Wap
);
}
#[test]
fn serialize_prompt() {
assert_eq!(serde_json::to_string(&Prompt::None).unwrap(), "\"none\"");
assert_eq!(serde_json::to_string(&Prompt::Login).unwrap(), "\"login\"");
assert_eq!(
serde_json::to_string(&Prompt::Consent).unwrap(),
"\"consent\""
);
assert_eq!(
serde_json::to_string(&Prompt::SelectAccount).unwrap(),
"\"select_account\""
);
assert_eq!(
serde_json::to_string(&Prompt::Create).unwrap(),
"\"create\""
);
}
#[test]
fn deserialize_prompt() {
assert_eq!(
serde_json::from_str::<Prompt>("\"none\"").unwrap(),
Prompt::None
);
assert_eq!(
serde_json::from_str::<Prompt>("\"login\"").unwrap(),
Prompt::Login
);
assert_eq!(
serde_json::from_str::<Prompt>("\"consent\"").unwrap(),
Prompt::Consent
);
assert_eq!(
serde_json::from_str::<Prompt>("\"select_account\"").unwrap(),
Prompt::SelectAccount
);
assert_eq!(
serde_json::from_str::<Prompt>("\"create\"").unwrap(),
Prompt::Create
);
}
}