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

Add variants for unknown values on mas-iana types

Remove the Copy derive and mark enums as non-exhaustive.
This commit is contained in:
Kévin Commaille
2022-09-13 18:32:56 +02:00
committed by Quentin Gliech
parent 9e3b3567b2
commit 80d317f23c
25 changed files with 414 additions and 292 deletions

View File

@ -33,7 +33,7 @@ use crate::{
/// An enum for types that accept either an [`OAuthClientAuthenticationMethod`]
/// or an [`OAuthAccessTokenType`].
#[derive(
SerializeDisplay, DeserializeFromStr, Clone, Copy, PartialEq, Eq, Hash, Debug, Display, FromStr,
SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug, Display, FromStr,
)]
pub enum AuthenticationMethodOrAccessTokenType {
/// An authentication method.
@ -49,9 +49,9 @@ impl AuthenticationMethodOrAccessTokenType {
/// Get the authentication method of this
/// `AuthenticationMethodOrAccessTokenType`.
#[must_use]
pub fn authentication_method(&self) -> Option<OAuthClientAuthenticationMethod> {
pub fn authentication_method(&self) -> Option<&OAuthClientAuthenticationMethod> {
match self {
Self::AuthenticationMethod(m) => Some(*m),
Self::AuthenticationMethod(m) => Some(m),
Self::AccessTokenType(_) => None,
}
}
@ -59,10 +59,10 @@ impl AuthenticationMethodOrAccessTokenType {
/// Get the access token type of this
/// `AuthenticationMethodOrAccessTokenType`.
#[must_use]
pub fn access_token_type(&self) -> Option<OAuthAccessTokenType> {
pub fn access_token_type(&self) -> Option<&OAuthAccessTokenType> {
match self {
Self::AuthenticationMethod(_) => None,
Self::AccessTokenType(t) => Some(*t),
Self::AccessTokenType(t) => Some(t),
}
}
}
@ -470,8 +470,14 @@ impl ProviderMetadata {
validate_signing_alg_values_supported(
"token_endpoint",
&metadata.token_endpoint_auth_signing_alg_values_supported,
&metadata.token_endpoint_auth_methods_supported,
metadata
.token_endpoint_auth_signing_alg_values_supported
.iter()
.flatten(),
metadata
.token_endpoint_auth_methods_supported
.iter()
.flatten(),
)?;
if let Some(url) = &metadata.revocation_endpoint {
@ -480,8 +486,14 @@ impl ProviderMetadata {
validate_signing_alg_values_supported(
"revocation_endpoint",
&metadata.revocation_endpoint_auth_signing_alg_values_supported,
&metadata.revocation_endpoint_auth_methods_supported,
metadata
.revocation_endpoint_auth_signing_alg_values_supported
.iter()
.flatten(),
metadata
.revocation_endpoint_auth_methods_supported
.iter()
.flatten(),
)?;
if let Some(url) = &metadata.introspection_endpoint {
@ -500,8 +512,11 @@ impl ProviderMetadata {
});
validate_signing_alg_values_supported(
"introspection_endpoint",
&metadata.introspection_endpoint_auth_signing_alg_values_supported,
&introspection_methods,
metadata
.introspection_endpoint_auth_signing_alg_values_supported
.iter()
.flatten(),
introspection_methods.into_iter().flatten(),
)?;
if let Some(url) = &metadata.userinfo_endpoint {
@ -957,24 +972,32 @@ fn validate_url(
/// - The algorithm values must not contain `none`,
/// - If the `client_secret_jwt` or `private_key_jwt` authentication methods are
/// supported, the values must be present.
fn validate_signing_alg_values_supported(
fn validate_signing_alg_values_supported<'a>(
endpoint: &'static str,
values: &Option<Vec<JsonWebSignatureAlg>>,
methods: &Option<Vec<OAuthClientAuthenticationMethod>>,
values: impl Iterator<Item = &'a JsonWebSignatureAlg>,
mut methods: impl Iterator<Item = &'a OAuthClientAuthenticationMethod>,
) -> Result<(), ProviderMetadataVerificationError> {
if let Some(values) = values {
if values.contains(&JsonWebSignatureAlg::None) {
let mut no_values = true;
for value in values {
if *value == JsonWebSignatureAlg::None {
return Err(ProviderMetadataVerificationError::SigningAlgValuesWithNone(
endpoint,
));
}
} else if methods.iter().flatten().any(|method| {
matches!(
method,
OAuthClientAuthenticationMethod::ClientSecretJwt
| OAuthClientAuthenticationMethod::PrivateKeyJwt
)
}) {
no_values = false;
}
if no_values
&& methods.any(|method| {
matches!(
method,
OAuthClientAuthenticationMethod::ClientSecretJwt
| OAuthClientAuthenticationMethod::PrivateKeyJwt
)
})
{
return Err(ProviderMetadataVerificationError::MissingAuthSigningAlgValues(endpoint));
}

View File

@ -33,6 +33,9 @@ pub enum CodeChallengeError {
#[error("challenge verification failed")]
VerificationFailed,
#[error("unknown challenge method")]
UnknownChallengeMethod,
}
fn validate_verifier(verifier: &str) -> Result<(), CodeChallengeError> {
@ -61,7 +64,7 @@ pub trait CodeChallengeMethodExt {
///
/// Returns an error if the verifier did not adhere to the rules defined by
/// the RFC in terms of length and allowed characters
fn compute_challenge(self, verifier: &str) -> Result<Cow<'_, str>, CodeChallengeError>;
fn compute_challenge<'a>(&self, verifier: &'a str) -> Result<Cow<'a, str>, CodeChallengeError>;
/// Verify that a given verifier is valid for the given challenge
///
@ -70,7 +73,7 @@ pub trait CodeChallengeMethodExt {
/// Returns an error if the verifier did not match the challenge, or if the
/// verifier did not adhere to the rules defined by the RFC in terms of
/// length and allowed characters
fn verify(self, challenge: &str, verifier: &str) -> Result<(), CodeChallengeError>
fn verify(&self, challenge: &str, verifier: &str) -> Result<(), CodeChallengeError>
where
Self: Sized,
{
@ -83,7 +86,7 @@ pub trait CodeChallengeMethodExt {
}
impl CodeChallengeMethodExt for PkceCodeChallengeMethod {
fn compute_challenge(self, verifier: &str) -> Result<Cow<'_, str>, CodeChallengeError> {
fn compute_challenge<'a>(&self, verifier: &'a str) -> Result<Cow<'a, str>, CodeChallengeError> {
validate_verifier(verifier)?;
let challenge = match self {
@ -95,6 +98,7 @@ impl CodeChallengeMethodExt for PkceCodeChallengeMethod {
let verifier = BASE64URL_NOPAD.encode(&hash);
verifier.into()
}
_ => return Err(CodeChallengeError::UnknownChallengeMethod),
};
Ok(challenge)

View File

@ -42,13 +42,13 @@ pub const DEFAULT_GRANT_TYPES: &[GrantType] = &[GrantType::AuthorizationCode];
pub const DEFAULT_APPLICATION_TYPE: ApplicationType = ApplicationType::Web;
pub const DEFAULT_TOKEN_AUTH_METHOD: OAuthClientAuthenticationMethod =
OAuthClientAuthenticationMethod::ClientSecretBasic;
pub const DEFAULT_TOKEN_AUTH_METHOD: &OAuthClientAuthenticationMethod =
&OAuthClientAuthenticationMethod::ClientSecretBasic;
pub const DEFAULT_SIGNING_ALGORITHM: JsonWebSignatureAlg = JsonWebSignatureAlg::Rs256;
pub const DEFAULT_SIGNING_ALGORITHM: &JsonWebSignatureAlg = &JsonWebSignatureAlg::Rs256;
pub const DEFAULT_ENCRYPTION_ENC_ALGORITHM: JsonWebEncryptionEnc =
JsonWebEncryptionEnc::A128CbcHs256;
pub const DEFAULT_ENCRYPTION_ENC_ALGORITHM: &JsonWebEncryptionEnc =
&JsonWebEncryptionEnc::A128CbcHs256;
/// A collection of localized variants.
///
@ -463,7 +463,7 @@ impl ClientMetadata {
));
}
if self.token_endpoint_auth_method() == OAuthClientAuthenticationMethod::PrivateKeyJwt
if *self.token_endpoint_auth_method() == OAuthClientAuthenticationMethod::PrivateKeyJwt
&& self.jwks_uri.is_none()
&& self.jwks.is_none()
{
@ -486,26 +486,26 @@ impl ClientMetadata {
));
}
if self.id_token_signed_response_alg() == JsonWebSignatureAlg::None
if *self.id_token_signed_response_alg() == JsonWebSignatureAlg::None
&& response_types.iter().any(ResponseType::has_id_token)
{
return Err(ClientMetadataVerificationError::IdTokenSigningAlgNone);
}
if self.id_token_encrypted_response_enc.is_some() {
self.id_token_encrypted_response_alg.ok_or(
self.id_token_encrypted_response_alg.as_ref().ok_or(
ClientMetadataVerificationError::MissingEncryptionAlg("id_token"),
)?;
}
if self.userinfo_encrypted_response_enc.is_some() {
self.userinfo_encrypted_response_alg.ok_or(
self.userinfo_encrypted_response_alg.as_ref().ok_or(
ClientMetadataVerificationError::MissingEncryptionAlg("userinfo"),
)?;
}
if self.request_object_encryption_enc.is_some() {
self.request_object_encryption_alg.ok_or(
self.request_object_encryption_alg.as_ref().ok_or(
ClientMetadataVerificationError::MissingEncryptionAlg("request_object"),
)?;
}
@ -522,7 +522,7 @@ impl ClientMetadata {
}
if self.introspection_encrypted_response_enc.is_some() {
self.introspection_encrypted_response_alg.ok_or(
self.introspection_encrypted_response_alg.as_ref().ok_or(
ClientMetadataVerificationError::MissingEncryptionAlg("introspection"),
)?;
}
@ -542,9 +542,12 @@ impl ClientMetadata {
/// [authorization endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1
#[must_use]
pub fn response_types(&self) -> Vec<ResponseType> {
self.response_types
.clone()
.unwrap_or_else(|| DEFAULT_RESPONSE_TYPES.map(ResponseType::from).into())
self.response_types.clone().unwrap_or_else(|| {
DEFAULT_RESPONSE_TYPES
.into_iter()
.filter_map(|t| ResponseType::try_from(t).ok())
.collect()
})
}
/// Array of [OAuth 2.0 `grant_type` values] that the client can use at the
@ -578,8 +581,9 @@ impl ClientMetadata {
///
/// [token endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.2
#[must_use]
pub fn token_endpoint_auth_method(&self) -> OAuthClientAuthenticationMethod {
pub fn token_endpoint_auth_method(&self) -> &OAuthClientAuthenticationMethod {
self.token_endpoint_auth_method
.as_ref()
.unwrap_or(DEFAULT_TOKEN_AUTH_METHOD)
}
@ -594,8 +598,9 @@ impl ClientMetadata {
///
/// [JWS]: http://tools.ietf.org/html/draft-ietf-jose-json-web-signature
#[must_use]
pub fn id_token_signed_response_alg(&self) -> JsonWebSignatureAlg {
pub fn id_token_signed_response_alg(&self) -> &JsonWebSignatureAlg {
self.id_token_signed_response_alg
.as_ref()
.unwrap_or(DEFAULT_SIGNING_ALGORITHM)
}
@ -610,11 +615,12 @@ impl ClientMetadata {
#[must_use]
pub fn id_token_encrypted_response(
&self,
) -> Option<(JsonWebEncryptionAlg, JsonWebEncryptionEnc)> {
self.id_token_encrypted_response_alg.map(|alg| {
) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
self.id_token_encrypted_response_alg.as_ref().map(|alg| {
(
alg,
self.id_token_encrypted_response_enc
.as_ref()
.unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
)
})
@ -631,11 +637,12 @@ impl ClientMetadata {
#[must_use]
pub fn userinfo_encrypted_response(
&self,
) -> Option<(JsonWebEncryptionAlg, JsonWebEncryptionEnc)> {
self.userinfo_encrypted_response_alg.map(|alg| {
) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
self.userinfo_encrypted_response_alg.as_ref().map(|alg| {
(
alg,
self.userinfo_encrypted_response_enc
.as_ref()
.unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
)
})
@ -652,11 +659,12 @@ impl ClientMetadata {
#[must_use]
pub fn request_object_encryption(
&self,
) -> Option<(JsonWebEncryptionAlg, JsonWebEncryptionEnc)> {
self.request_object_encryption_alg.map(|alg| {
) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
self.request_object_encryption_alg.as_ref().map(|alg| {
(
alg,
self.request_object_encryption_enc
.as_ref()
.unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
)
})
@ -705,14 +713,17 @@ impl ClientMetadata {
#[must_use]
pub fn introspection_encrypted_response(
&self,
) -> Option<(JsonWebEncryptionAlg, JsonWebEncryptionEnc)> {
self.introspection_encrypted_response_alg.map(|alg| {
(
alg,
self.introspection_encrypted_response_enc
.unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
)
})
) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
self.introspection_encrypted_response_alg
.as_ref()
.map(|alg| {
(
alg,
self.introspection_encrypted_response_enc
.as_ref()
.unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
)
})
}
}