mirror of
https://github.com/OMGeeky/yup-oauth2.git
synced 2026-01-11 13:17:21 +01:00
All the same functionality can be tested through the publicly exposed API providing more extensive coverage.
257 lines
7.5 KiB
Rust
257 lines
7.5 KiB
Rust
//! Module containing various error types.
|
|
|
|
use std::borrow::Cow;
|
|
use std::error::Error as StdError;
|
|
use std::fmt;
|
|
use std::io;
|
|
|
|
use serde::Deserialize;
|
|
|
|
/// Error returned by the authorization server.
|
|
/// https://tools.ietf.org/html/rfc6749#section-5.2
|
|
/// https://tools.ietf.org/html/rfc8628#section-3.5
|
|
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
|
pub struct AuthError {
|
|
/// Error code from the server.
|
|
pub error: AuthErrorCode,
|
|
/// Human-readable text providing additional information.
|
|
pub error_description: Option<String>,
|
|
/// A URI identifying a human-readable web page with information about the error.
|
|
pub error_uri: Option<String>,
|
|
}
|
|
|
|
impl fmt::Display for AuthError {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(f, "{}", &self.error.as_str())?;
|
|
if let Some(desc) = &self.error_description {
|
|
write!(f, ": {}", desc)?;
|
|
}
|
|
if let Some(uri) = &self.error_uri {
|
|
write!(f, "; See {} for more info", uri)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
impl StdError for AuthError {}
|
|
|
|
/// The error code returned by the authorization server.
|
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
pub enum AuthErrorCode {
|
|
/// invalid_request
|
|
InvalidRequest,
|
|
/// invalid_client
|
|
InvalidClient,
|
|
/// invalid_grant
|
|
InvalidGrant,
|
|
/// unauthorized_client
|
|
UnauthorizedClient,
|
|
/// unsupported_grant_type
|
|
UnsupportedGrantType,
|
|
/// invalid_scope
|
|
InvalidScope,
|
|
/// access_denied
|
|
AccessDenied,
|
|
/// expired_token
|
|
ExpiredToken,
|
|
/// other error
|
|
Other(String),
|
|
}
|
|
|
|
impl AuthErrorCode {
|
|
/// The error code as a &str
|
|
pub fn as_str(&self) -> &str {
|
|
match self {
|
|
AuthErrorCode::InvalidRequest => "invalid_request",
|
|
AuthErrorCode::InvalidClient => "invalid_client",
|
|
AuthErrorCode::InvalidGrant => "invalid_grant",
|
|
AuthErrorCode::UnauthorizedClient => "unauthorized_client",
|
|
AuthErrorCode::UnsupportedGrantType => "unsupported_grant_type",
|
|
AuthErrorCode::InvalidScope => "invalid_scope",
|
|
AuthErrorCode::AccessDenied => "access_denied",
|
|
AuthErrorCode::ExpiredToken => "expired_token",
|
|
AuthErrorCode::Other(s) => s.as_str(),
|
|
}
|
|
}
|
|
|
|
fn from_string<'a>(s: impl Into<Cow<'a, str>>) -> AuthErrorCode {
|
|
let s = s.into();
|
|
match s.as_ref() {
|
|
"invalid_request" => AuthErrorCode::InvalidRequest,
|
|
"invalid_client" => AuthErrorCode::InvalidClient,
|
|
"invalid_grant" => AuthErrorCode::InvalidGrant,
|
|
"unauthorized_client" => AuthErrorCode::UnauthorizedClient,
|
|
"unsupported_grant_type" => AuthErrorCode::UnsupportedGrantType,
|
|
"invalid_scope" => AuthErrorCode::InvalidScope,
|
|
"access_denied" => AuthErrorCode::AccessDenied,
|
|
"expired_token" => AuthErrorCode::ExpiredToken,
|
|
_ => AuthErrorCode::Other(s.into_owned()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<String> for AuthErrorCode {
|
|
fn from(s: String) -> Self {
|
|
AuthErrorCode::from_string(s)
|
|
}
|
|
}
|
|
|
|
impl<'a> From<&'a str> for AuthErrorCode {
|
|
fn from(s: &str) -> Self {
|
|
AuthErrorCode::from_string(s)
|
|
}
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for AuthErrorCode {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
struct V;
|
|
impl<'de> serde::de::Visitor<'de> for V {
|
|
type Value = AuthErrorCode;
|
|
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
f.write_str("any string")
|
|
}
|
|
fn visit_string<E: serde::de::Error>(self, value: String) -> Result<Self::Value, E> {
|
|
Ok(value.into())
|
|
}
|
|
fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
|
|
Ok(value.into())
|
|
}
|
|
}
|
|
deserializer.deserialize_string(V)
|
|
}
|
|
}
|
|
|
|
/// A helper type to deserialize either an AuthError or another piece of data.
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(untagged)]
|
|
pub(crate) enum AuthErrorOr<T> {
|
|
AuthError(AuthError),
|
|
Data(T),
|
|
}
|
|
|
|
impl<T> AuthErrorOr<T> {
|
|
pub(crate) fn into_result(self) -> Result<T, AuthError> {
|
|
match self {
|
|
AuthErrorOr::AuthError(err) => Result::Err(err),
|
|
AuthErrorOr::Data(value) => Result::Ok(value),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Encapsulates all possible results of the `token(...)` operation
|
|
#[derive(Debug)]
|
|
pub enum Error {
|
|
/// Indicates connection failure
|
|
HttpError(hyper::Error),
|
|
/// The server returned an error.
|
|
AuthError(AuthError),
|
|
/// Error while decoding a JSON response.
|
|
JSONError(serde_json::Error),
|
|
/// Error within user input.
|
|
UserError(String),
|
|
/// A lower level IO error.
|
|
LowLevelError(io::Error),
|
|
}
|
|
|
|
impl From<hyper::Error> for Error {
|
|
fn from(error: hyper::Error) -> Error {
|
|
Error::HttpError(error)
|
|
}
|
|
}
|
|
|
|
impl From<AuthError> for Error {
|
|
fn from(value: AuthError) -> Error {
|
|
Error::AuthError(value)
|
|
}
|
|
}
|
|
|
|
impl From<serde_json::Error> for Error {
|
|
fn from(value: serde_json::Error) -> Error {
|
|
Error::JSONError(value)
|
|
}
|
|
}
|
|
|
|
impl From<io::Error> for Error {
|
|
fn from(value: io::Error) -> Error {
|
|
Error::LowLevelError(value)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Error {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
match *self {
|
|
Error::HttpError(ref err) => err.fmt(f),
|
|
Error::AuthError(ref err) => err.fmt(f),
|
|
Error::JSONError(ref e) => {
|
|
write!(
|
|
f,
|
|
"JSON Error; this might be a bug with unexpected server responses! {}",
|
|
e
|
|
)?;
|
|
Ok(())
|
|
}
|
|
Error::UserError(ref s) => s.fmt(f),
|
|
Error::LowLevelError(ref e) => e.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl StdError for Error {
|
|
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
|
match *self {
|
|
Error::HttpError(ref err) => Some(err),
|
|
Error::AuthError(ref err) => Some(err),
|
|
Error::JSONError(ref err) => Some(err),
|
|
Error::LowLevelError(ref err) => Some(err),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_auth_error_code_deserialize() {
|
|
assert_eq!(
|
|
AuthErrorCode::InvalidRequest,
|
|
serde_json::from_str(r#""invalid_request""#).unwrap()
|
|
);
|
|
assert_eq!(
|
|
AuthErrorCode::InvalidClient,
|
|
serde_json::from_str(r#""invalid_client""#).unwrap()
|
|
);
|
|
assert_eq!(
|
|
AuthErrorCode::InvalidGrant,
|
|
serde_json::from_str(r#""invalid_grant""#).unwrap()
|
|
);
|
|
assert_eq!(
|
|
AuthErrorCode::UnauthorizedClient,
|
|
serde_json::from_str(r#""unauthorized_client""#).unwrap()
|
|
);
|
|
assert_eq!(
|
|
AuthErrorCode::UnsupportedGrantType,
|
|
serde_json::from_str(r#""unsupported_grant_type""#).unwrap()
|
|
);
|
|
assert_eq!(
|
|
AuthErrorCode::InvalidScope,
|
|
serde_json::from_str(r#""invalid_scope""#).unwrap()
|
|
);
|
|
assert_eq!(
|
|
AuthErrorCode::AccessDenied,
|
|
serde_json::from_str(r#""access_denied""#).unwrap()
|
|
);
|
|
assert_eq!(
|
|
AuthErrorCode::ExpiredToken,
|
|
serde_json::from_str(r#""expired_token""#).unwrap()
|
|
);
|
|
assert_eq!(
|
|
AuthErrorCode::Other("undefined".to_owned()),
|
|
serde_json::from_str(r#""undefined""#).unwrap()
|
|
);
|
|
}
|
|
}
|