scratchstack_aws_signature/
error.rs1use {
2 http::status::StatusCode,
3 scratchstack_errors::ServiceError,
4 std::{
5 error::Error,
6 fmt::{Display, Formatter, Result as FmtResult},
7 io::Error as IOError,
8 },
9};
10
11const ERR_CODE_EXPIRED_TOKEN: &str = "ExpiredToken";
13
14const ERR_CODE_INTERNAL_FAILURE: &str = "InternalFailure";
16
17const ERR_CODE_INVALID_CONTENT_TYPE: &str = "InvalidContentType";
19
20const ERR_CODE_INVALID_BODY_ENCODING: &str = "InvalidBodyEncoding";
22
23const ERR_CODE_INVALID_CLIENT_TOKEN_ID: &str = "InvalidClientTokenId";
25
26const ERR_CODE_INCOMPLETE_SIGNATURE: &str = "IncompleteSignature";
28
29const ERR_CODE_INVALID_REQUEST_METHOD: &str = "InvalidRequestMethod";
31
32const ERR_CODE_INVALID_URI_PATH: &str = "InvalidURIPath";
34
35const ERR_CODE_MALFORMED_QUERY_STRING: &str = "MalformedQueryString";
37
38const ERR_CODE_MISSING_AUTHENTICATION_TOKEN: &str = "MissingAuthenticationToken";
40
41const ERR_CODE_SIGNATURE_DOES_NOT_MATCH: &str = "SignatureDoesNotMatch";
43
44#[derive(Debug)]
46#[non_exhaustive]
47pub enum SignatureError {
48 ExpiredToken(String),
50
51 IO(IOError),
53
54 InternalServiceError(Box<dyn Error + Send + Sync>),
56
57 InvalidBodyEncoding(String),
59
60 InvalidClientTokenId(String),
62
63 InvalidContentType(String),
65
66 InvalidRequestMethod(String),
68
69 IncompleteSignature(String),
75
76 InvalidURIPath(String),
79
80 MalformedQueryString(String),
86
87 MissingAuthenticationToken(String),
90
91 SignatureDoesNotMatch(Option<String>),
97}
98
99impl SignatureError {
100 fn error_code(&self) -> &'static str {
101 match self {
102 Self::ExpiredToken(_) => ERR_CODE_EXPIRED_TOKEN,
103 Self::IO(_) | Self::InternalServiceError(_) => ERR_CODE_INTERNAL_FAILURE,
104 Self::InvalidBodyEncoding(_) => ERR_CODE_INVALID_BODY_ENCODING,
105 Self::InvalidClientTokenId(_) => ERR_CODE_INVALID_CLIENT_TOKEN_ID,
106 Self::InvalidContentType(_) => ERR_CODE_INVALID_CONTENT_TYPE,
107 Self::InvalidRequestMethod(_) => ERR_CODE_INVALID_REQUEST_METHOD,
108 Self::IncompleteSignature(_) => ERR_CODE_INCOMPLETE_SIGNATURE,
109 Self::InvalidURIPath(_) => ERR_CODE_INVALID_URI_PATH,
110 Self::MalformedQueryString(_) => ERR_CODE_MALFORMED_QUERY_STRING,
111 Self::MissingAuthenticationToken(_) => ERR_CODE_MISSING_AUTHENTICATION_TOKEN,
112 Self::SignatureDoesNotMatch(_) => ERR_CODE_SIGNATURE_DOES_NOT_MATCH,
113 }
114 }
115
116 fn http_status(&self) -> StatusCode {
117 match self {
118 Self::IncompleteSignature(_)
119 | Self::InvalidBodyEncoding(_)
120 | Self::InvalidRequestMethod(_)
121 | Self::InvalidURIPath(_)
122 | Self::MalformedQueryString(_)
123 | Self::MissingAuthenticationToken(_) => StatusCode::BAD_REQUEST,
124 Self::IO(_) | Self::InternalServiceError(_) => StatusCode::INTERNAL_SERVER_ERROR,
125 _ => StatusCode::FORBIDDEN,
126 }
127 }
128}
129
130impl ServiceError for SignatureError {
131 fn error_code(&self) -> &'static str {
132 SignatureError::error_code(self)
133 }
134
135 fn http_status(&self) -> StatusCode {
136 SignatureError::http_status(self)
137 }
138}
139
140impl Display for SignatureError {
141 fn fmt(&self, f: &mut Formatter) -> FmtResult {
142 match self {
143 Self::ExpiredToken(msg) => f.write_str(msg),
144 Self::IO(ref e) => Display::fmt(e, f),
145 Self::InternalServiceError(ref e) => Display::fmt(e, f),
146 Self::InvalidBodyEncoding(msg) => f.write_str(msg),
147 Self::InvalidClientTokenId(msg) => f.write_str(msg),
148 Self::InvalidContentType(msg) => f.write_str(msg),
149 Self::InvalidRequestMethod(msg) => f.write_str(msg),
150 Self::IncompleteSignature(msg) => f.write_str(msg),
151 Self::InvalidURIPath(msg) => f.write_str(msg),
152 Self::MalformedQueryString(msg) => f.write_str(msg),
153 Self::MissingAuthenticationToken(msg) => f.write_str(msg),
154 Self::SignatureDoesNotMatch(msg) => {
155 if let Some(msg) = msg {
156 f.write_str(msg)
157 } else {
158 Ok(())
159 }
160 }
161 }
162 }
163}
164
165impl Error for SignatureError {
166 fn source(&self) -> Option<&(dyn Error + 'static)> {
167 match self {
168 Self::IO(ref e) => Some(e),
169 _ => None,
170 }
171 }
172}
173
174impl From<IOError> for SignatureError {
175 fn from(e: IOError) -> SignatureError {
176 SignatureError::IO(e)
177 }
178}
179
180impl From<Box<dyn Error + Send + Sync>> for SignatureError {
181 fn from(e: Box<dyn Error + Send + Sync>) -> SignatureError {
182 match e.downcast::<SignatureError>() {
183 Ok(sig_err) => *sig_err,
184 Err(e) => SignatureError::InternalServiceError(e),
185 }
186 }
187}
188
189#[derive(Clone, Copy, Debug, Eq, PartialEq)]
191pub struct KeyTooLongError;
192
193impl Display for KeyTooLongError {
194 fn fmt(&self, f: &mut Formatter) -> FmtResult {
195 f.write_str("Key too long")
196 }
197}
198
199impl Error for KeyTooLongError {}
200
201#[cfg(test)]
202mod tests {
203 use {crate::SignatureError, std::error::Error};
204
205 #[test_log::test]
206 fn test_from() {
207 let utf8_error = Box::new(String::from_utf8(b"\x80".to_vec()).unwrap_err());
209 let e: SignatureError = (utf8_error as Box<dyn Error + Send + Sync + 'static>).into();
210 assert_eq!(e.error_code(), "InternalFailure");
211 assert_eq!(e.http_status(), 500);
212
213 let e = SignatureError::MalformedQueryString("foo".to_string());
214 let e2 = SignatureError::from(Box::new(e) as Box<dyn Error + Send + Sync + 'static>);
215 assert_eq!(e2.to_string(), "foo");
216 assert_eq!(e2.error_code(), "MalformedQueryString");
217
218 let e = SignatureError::InvalidContentType("Invalid content type: image/jpeg".to_string());
219 assert_eq!(e.error_code(), "InvalidContentType");
220 assert_eq!(e.http_status(), 403); assert_eq!(format!("{}", e), "Invalid content type: image/jpeg");
222
223 let e = SignatureError::InvalidRequestMethod("Invalid request method: DELETE".to_string());
224 assert_eq!(e.error_code(), "InvalidRequestMethod");
225 assert_eq!(e.http_status(), 400);
226 assert_eq!(format!("{}", e), "Invalid request method: DELETE");
227 }
228}