use {
http::status::StatusCode,
scratchstack_errors::ServiceError,
std::{
error::Error,
fmt::{Display, Formatter, Result as FmtResult},
io::Error as IOError,
},
};
const ERR_CODE_EXPIRED_TOKEN: &str = "ExpiredToken";
const ERR_CODE_INTERNAL_FAILURE: &str = "InternalFailure";
const ERR_CODE_INVALID_CONTENT_TYPE: &str = "InvalidContentType";
const ERR_CODE_INVALID_BODY_ENCODING: &str = "InvalidBodyEncoding";
const ERR_CODE_INVALID_CLIENT_TOKEN_ID: &str = "InvalidClientTokenId";
const ERR_CODE_INCOMPLETE_SIGNATURE: &str = "IncompleteSignature";
const ERR_CODE_INVALID_REQUEST_METHOD: &str = "InvalidRequestMethod";
const ERR_CODE_INVALID_URI_PATH: &str = "InvalidURIPath";
const ERR_CODE_MALFORMED_QUERY_STRING: &str = "MalformedQueryString";
const ERR_CODE_MISSING_AUTHENTICATION_TOKEN: &str = "MissingAuthenticationToken";
const ERR_CODE_SIGNATURE_DOES_NOT_MATCH: &str = "SignatureDoesNotMatch";
#[derive(Debug)]
#[non_exhaustive]
pub enum SignatureError {
ExpiredToken( String),
IO(IOError),
InternalServiceError(Box<dyn Error + Send + Sync>),
InvalidBodyEncoding( String),
InvalidClientTokenId( String),
InvalidContentType( String),
InvalidRequestMethod( String),
IncompleteSignature( String),
InvalidURIPath( String),
MalformedQueryString( String),
MissingAuthenticationToken( String),
SignatureDoesNotMatch(Option< String>),
}
impl SignatureError {
fn error_code(&self) -> &'static str {
match self {
Self::ExpiredToken(_) => ERR_CODE_EXPIRED_TOKEN,
Self::IO(_) | Self::InternalServiceError(_) => ERR_CODE_INTERNAL_FAILURE,
Self::InvalidBodyEncoding(_) => ERR_CODE_INVALID_BODY_ENCODING,
Self::InvalidClientTokenId(_) => ERR_CODE_INVALID_CLIENT_TOKEN_ID,
Self::InvalidContentType(_) => ERR_CODE_INVALID_CONTENT_TYPE,
Self::InvalidRequestMethod(_) => ERR_CODE_INVALID_REQUEST_METHOD,
Self::IncompleteSignature(_) => ERR_CODE_INCOMPLETE_SIGNATURE,
Self::InvalidURIPath(_) => ERR_CODE_INVALID_URI_PATH,
Self::MalformedQueryString(_) => ERR_CODE_MALFORMED_QUERY_STRING,
Self::MissingAuthenticationToken(_) => ERR_CODE_MISSING_AUTHENTICATION_TOKEN,
Self::SignatureDoesNotMatch(_) => ERR_CODE_SIGNATURE_DOES_NOT_MATCH,
}
}
fn http_status(&self) -> StatusCode {
match self {
Self::IncompleteSignature(_)
| Self::InvalidBodyEncoding(_)
| Self::InvalidRequestMethod(_)
| Self::InvalidURIPath(_)
| Self::MalformedQueryString(_)
| Self::MissingAuthenticationToken(_) => StatusCode::BAD_REQUEST,
Self::IO(_) | Self::InternalServiceError(_) => StatusCode::INTERNAL_SERVER_ERROR,
_ => StatusCode::FORBIDDEN,
}
}
}
impl ServiceError for SignatureError {
fn error_code(&self) -> &'static str {
SignatureError::error_code(self)
}
fn http_status(&self) -> StatusCode {
SignatureError::http_status(self)
}
}
impl Display for SignatureError {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match self {
Self::ExpiredToken(msg) => f.write_str(msg),
Self::IO(ref e) => Display::fmt(e, f),
Self::InternalServiceError(ref e) => Display::fmt(e, f),
Self::InvalidBodyEncoding(msg) => f.write_str(msg),
Self::InvalidClientTokenId(msg) => f.write_str(msg),
Self::InvalidContentType(msg) => f.write_str(msg),
Self::InvalidRequestMethod(msg) => f.write_str(msg),
Self::IncompleteSignature(msg) => f.write_str(msg),
Self::InvalidURIPath(msg) => f.write_str(msg),
Self::MalformedQueryString(msg) => f.write_str(msg),
Self::MissingAuthenticationToken(msg) => f.write_str(msg),
Self::SignatureDoesNotMatch(msg) => {
if let Some(msg) = msg {
f.write_str(msg)
} else {
Ok(())
}
}
}
}
}
impl Error for SignatureError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::IO(ref e) => Some(e),
_ => None,
}
}
}
impl From<IOError> for SignatureError {
fn from(e: IOError) -> SignatureError {
SignatureError::IO(e)
}
}
impl From<Box<dyn Error + Send + Sync>> for SignatureError {
fn from(e: Box<dyn Error + Send + Sync>) -> SignatureError {
match e.downcast::<SignatureError>() {
Ok(sig_err) => *sig_err,
Err(e) => SignatureError::InternalServiceError(e),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum KeyLengthError {
TooLong,
TooShort,
}
impl Display for KeyLengthError {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match self {
KeyLengthError::TooLong => f.write_str("Key too long"),
KeyLengthError::TooShort => f.write_str("Key too short"),
}
}
}
impl Error for KeyLengthError {}
#[cfg(test)]
mod tests {
use {crate::SignatureError, std::error::Error};
#[test_log::test]
fn test_from() {
let utf8_error = Box::new(String::from_utf8(b"\x80".to_vec()).unwrap_err());
let e: SignatureError = (utf8_error as Box<dyn Error + Send + Sync + 'static>).into();
assert_eq!(e.error_code(), "InternalFailure");
assert_eq!(e.http_status(), 500);
let e = SignatureError::MalformedQueryString("foo".to_string());
let e2 = SignatureError::from(Box::new(e) as Box<dyn Error + Send + Sync + 'static>);
assert_eq!(e2.to_string(), "foo");
assert_eq!(e2.error_code(), "MalformedQueryString");
let e = SignatureError::InvalidContentType("Invalid content type: image/jpeg".to_string());
assert_eq!(e.error_code(), "InvalidContentType");
assert_eq!(e.http_status(), 403); assert_eq!(format!("{}", e), "Invalid content type: image/jpeg");
let e = SignatureError::InvalidRequestMethod("Invalid request method: DELETE".to_string());
assert_eq!(e.error_code(), "InvalidRequestMethod");
assert_eq!(e.http_status(), 400);
assert_eq!(format!("{}", e), "Invalid request method: DELETE");
}
}