#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum AuthFailureKind {
MissingCredential,
InvalidToken,
InvalidApiKey,
EmptyPrincipal,
InsufficientScope,
}
impl AuthFailureKind {
pub fn body_string(self) -> &'static str {
match self {
Self::MissingCredential => "missing_credential",
Self::InvalidToken => "invalid_token",
Self::InvalidApiKey => "invalid_api_key",
Self::EmptyPrincipal => "empty_principal",
Self::InsufficientScope => "insufficient_scope",
}
}
pub fn bearer_rfc6750_code(self) -> Option<&'static str> {
match self {
Self::MissingCredential => Some("invalid_request"),
Self::InvalidToken | Self::EmptyPrincipal => Some("invalid_token"),
Self::InsufficientScope => Some("insufficient_scope"),
Self::InvalidApiKey => None,
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum MiddlewareError {
Unauthenticated(AuthFailureKind),
HttpChallenge(AuthFailureKind),
Forbidden(AuthFailureKind),
Internal(String),
}
impl MiddlewareError {
pub(crate) fn precedence(&self) -> u8 {
match self {
Self::Forbidden(_) => 3,
Self::HttpChallenge(_) => 2,
Self::Unauthenticated(_) => 1,
Self::Internal(_) => 0, }
}
pub fn http_status(&self) -> u16 {
match self {
Self::Unauthenticated(_) | Self::HttpChallenge(_) => 401,
Self::Forbidden(_) => 403,
Self::Internal(_) => 500,
}
}
pub fn kind(&self) -> Option<AuthFailureKind> {
match self {
Self::Unauthenticated(k) | Self::HttpChallenge(k) | Self::Forbidden(k) => Some(*k),
Self::Internal(_) => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn body_strings_are_stable() {
assert_eq!(
AuthFailureKind::MissingCredential.body_string(),
"missing_credential"
);
assert_eq!(AuthFailureKind::InvalidToken.body_string(), "invalid_token");
assert_eq!(
AuthFailureKind::InvalidApiKey.body_string(),
"invalid_api_key"
);
assert_eq!(
AuthFailureKind::EmptyPrincipal.body_string(),
"empty_principal"
);
assert_eq!(
AuthFailureKind::InsufficientScope.body_string(),
"insufficient_scope"
);
}
#[test]
fn bearer_rfc6750_mapping() {
assert_eq!(
AuthFailureKind::MissingCredential.bearer_rfc6750_code(),
Some("invalid_request")
);
assert_eq!(
AuthFailureKind::InvalidToken.bearer_rfc6750_code(),
Some("invalid_token")
);
assert_eq!(
AuthFailureKind::EmptyPrincipal.bearer_rfc6750_code(),
Some("invalid_token")
);
assert_eq!(
AuthFailureKind::InsufficientScope.bearer_rfc6750_code(),
Some("insufficient_scope")
);
assert_eq!(AuthFailureKind::InvalidApiKey.bearer_rfc6750_code(), None);
}
#[test]
fn http_status_codes() {
assert_eq!(
MiddlewareError::Unauthenticated(AuthFailureKind::MissingCredential).http_status(),
401
);
assert_eq!(
MiddlewareError::HttpChallenge(AuthFailureKind::InvalidToken).http_status(),
401
);
assert_eq!(
MiddlewareError::Forbidden(AuthFailureKind::InsufficientScope).http_status(),
403
);
assert_eq!(MiddlewareError::Internal("x".into()).http_status(), 500);
}
}