1use crate::policy;
16
17pub type Result<T> = core::result::Result<T, Error>;
18
19#[derive(thiserror::Error, Debug)]
20pub enum Error {
21 #[error(transparent)]
22 PolicyError(#[from] policy::Error),
23
24 #[error("{0}")]
25 StringError(String),
26
27 #[error("crypto: {0}")]
28 CryptoError(#[from] rustfs_crypto::Error),
29
30 #[error("user '{0}' does not exist")]
31 NoSuchUser(String),
32
33 #[error("account '{0}' does not exist")]
34 NoSuchAccount(String),
35
36 #[error("service account '{0}' does not exist")]
37 NoSuchServiceAccount(String),
38
39 #[error("temp account '{0}' does not exist")]
40 NoSuchTempAccount(String),
41
42 #[error("group '{0}' does not exist")]
43 NoSuchGroup(String),
44
45 #[error("policy does not exist")]
46 NoSuchPolicy,
47
48 #[error("policy in use")]
49 PolicyInUse,
50
51 #[error("group not empty")]
52 GroupNotEmpty,
53
54 #[error("invalid arguments specified")]
55 InvalidArgument,
56
57 #[error("not initialized")]
58 IamSysNotInitialized,
59
60 #[error("invalid service type: {0}")]
61 InvalidServiceType(String),
62
63 #[error("malformed credential")]
64 ErrCredMalformed,
65
66 #[error("CredNotInitialized")]
67 CredNotInitialized,
68
69 #[error("invalid access key length")]
70 InvalidAccessKeyLength,
71
72 #[error("invalid secret key length")]
73 InvalidSecretKeyLength,
74
75 #[error("access key contains reserved characters =,")]
76 ContainsReservedChars,
77
78 #[error("group name contains reserved characters =,")]
79 GroupNameContainsReservedChars,
80
81 #[error("jwt err {0}")]
82 JWTError(#[from] jsonwebtoken::errors::Error),
83
84 #[error("no access key")]
85 NoAccessKey,
86
87 #[error("invalid token")]
88 InvalidToken,
89
90 #[error("invalid access_key")]
91 InvalidAccessKey,
92 #[error("action not allowed")]
93 IAMActionNotAllowed,
94
95 #[error("invalid expiration")]
96 InvalidExpiration,
97
98 #[error("no secret key with access key")]
99 NoSecretKeyWithAccessKey,
100
101 #[error("no access key with secret key")]
102 NoAccessKeyWithSecretKey,
103
104 #[error("policy too large")]
105 PolicyTooLarge,
106
107 #[error("io error: {0}")]
108 Io(std::io::Error),
109}
110
111impl Error {
112 pub fn other<E>(error: E) -> Self
113 where
114 E: Into<Box<dyn std::error::Error + Send + Sync>>,
115 {
116 Error::Io(std::io::Error::other(error))
117 }
118}
119
120impl From<std::io::Error> for Error {
121 fn from(e: std::io::Error) -> Self {
122 Error::Io(e)
123 }
124}
125
126impl From<time::error::ComponentRange> for Error {
127 fn from(e: time::error::ComponentRange) -> Self {
128 Error::other(e)
129 }
130}
131
132impl From<serde_json::Error> for Error {
133 fn from(e: serde_json::Error) -> Self {
134 Error::other(e)
135 }
136}
137
138impl From<regex::Error> for Error {
145 fn from(e: regex::Error) -> Self {
146 Error::other(e)
147 }
148}
149
150pub fn is_err_no_such_policy(err: &Error) -> bool {
155 matches!(err, Error::NoSuchPolicy)
156}
157
158pub fn is_err_no_such_user(err: &Error) -> bool {
159 matches!(err, Error::NoSuchUser(_))
160}
161
162pub fn is_err_no_such_account(err: &Error) -> bool {
163 matches!(err, Error::NoSuchAccount(_))
164}
165
166pub fn is_err_no_such_temp_account(err: &Error) -> bool {
167 matches!(err, Error::NoSuchTempAccount(_))
168}
169
170pub fn is_err_no_such_group(err: &Error) -> bool {
171 matches!(err, Error::NoSuchGroup(_))
172}
173
174pub fn is_err_no_such_service_account(err: &Error) -> bool {
175 matches!(err, Error::NoSuchServiceAccount(_))
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use std::io::{Error as IoError, ErrorKind};
182
183 #[test]
184 fn test_policy_error_from_io_error() {
185 let io_error = IoError::new(ErrorKind::PermissionDenied, "permission denied");
186 let policy_error: Error = io_error.into();
187
188 match policy_error {
189 Error::Io(inner_io) => {
190 assert_eq!(inner_io.kind(), ErrorKind::PermissionDenied);
191 assert!(inner_io.to_string().contains("permission denied"));
192 }
193 _ => panic!("Expected Io variant"),
194 }
195 }
196
197 #[test]
198 fn test_policy_error_other_function() {
199 let custom_error = "Custom policy error";
200 let policy_error = Error::other(custom_error);
201
202 match policy_error {
203 Error::Io(io_error) => {
204 assert!(io_error.to_string().contains(custom_error));
205 assert_eq!(io_error.kind(), ErrorKind::Other);
206 }
207 _ => panic!("Expected Io variant"),
208 }
209 }
210
211 #[test]
212 fn test_policy_error_from_crypto_error() {
213 let crypto_error = rustfs_crypto::Error::ErrUnexpectedHeader;
215 let policy_error: Error = crypto_error.into();
216
217 match policy_error {
218 Error::CryptoError(_) => {
219 assert!(policy_error.to_string().contains("crypto"));
221 }
222 _ => panic!("Expected CryptoError variant"),
223 }
224 }
225
226 #[test]
227 fn test_policy_error_from_jwt_error() {
228 use jsonwebtoken::{Algorithm, DecodingKey, Validation, decode};
229 use serde::{Deserialize, Serialize};
230
231 #[derive(Debug, Serialize, Deserialize)]
232 struct Claims {
233 sub: String,
234 exp: usize,
235 }
236
237 let invalid_token = "invalid.jwt.token";
239 let key = DecodingKey::from_secret(b"secret");
240 let validation = Validation::new(Algorithm::HS256);
241
242 let jwt_result = decode::<Claims>(invalid_token, &key, &validation);
243 assert!(jwt_result.is_err());
244
245 let jwt_error = jwt_result.unwrap_err();
246 let policy_error: Error = jwt_error.into();
247
248 match policy_error {
249 Error::JWTError(_) => {
250 assert!(policy_error.to_string().contains("jwt err"));
252 }
253 _ => panic!("Expected JWTError variant"),
254 }
255 }
256
257 #[test]
258 fn test_policy_error_from_serde_json() {
259 let invalid_json = r#"{"invalid": json}"#;
261 let json_error = serde_json::from_str::<serde_json::Value>(invalid_json).unwrap_err();
262 let policy_error: Error = json_error.into();
263
264 match policy_error {
265 Error::Io(io_error) => {
266 assert_eq!(io_error.kind(), ErrorKind::Other);
267 }
268 _ => panic!("Expected Io variant"),
269 }
270 }
271
272 #[test]
273 fn test_policy_error_from_time_component_range() {
274 use time::{Date, Month};
275
276 let time_result = Date::from_calendar_date(2023, Month::January, 32); assert!(time_result.is_err());
279
280 let time_error = time_result.unwrap_err();
281 let policy_error: Error = time_error.into();
282
283 match policy_error {
284 Error::Io(io_error) => {
285 assert_eq!(io_error.kind(), ErrorKind::Other);
286 }
287 _ => panic!("Expected Io variant"),
288 }
289 }
290
291 #[test]
292 #[allow(clippy::invalid_regex)]
293 fn test_policy_error_from_regex_error() {
294 use regex::Regex;
295
296 let regex_result = Regex::new("[");
298 assert!(regex_result.is_err());
299
300 let regex_error = regex_result.unwrap_err();
301 let policy_error: Error = regex_error.into();
302
303 match policy_error {
304 Error::Io(io_error) => {
305 assert_eq!(io_error.kind(), ErrorKind::Other);
306 }
307 _ => panic!("Expected Io variant"),
308 }
309 }
310
311 #[test]
312 fn test_helper_functions() {
313 assert!(is_err_no_such_policy(&Error::NoSuchPolicy));
315 assert!(!is_err_no_such_policy(&Error::NoSuchUser("test".to_string())));
316
317 assert!(is_err_no_such_user(&Error::NoSuchUser("test".to_string())));
318 assert!(!is_err_no_such_user(&Error::NoSuchAccount("test".to_string())));
319
320 assert!(is_err_no_such_account(&Error::NoSuchAccount("test".to_string())));
321 assert!(!is_err_no_such_account(&Error::NoSuchUser("test".to_string())));
322
323 assert!(is_err_no_such_temp_account(&Error::NoSuchTempAccount("test".to_string())));
324 assert!(!is_err_no_such_temp_account(&Error::NoSuchAccount("test".to_string())));
325
326 assert!(is_err_no_such_group(&Error::NoSuchGroup("test".to_string())));
327 assert!(!is_err_no_such_group(&Error::NoSuchUser("test".to_string())));
328
329 assert!(is_err_no_such_service_account(&Error::NoSuchServiceAccount("test".to_string())));
330 assert!(!is_err_no_such_service_account(&Error::NoSuchAccount("test".to_string())));
331 }
332
333 #[test]
334 fn test_error_display_format() {
335 let test_cases = vec![
336 (Error::NoSuchUser("testuser".to_string()), "user 'testuser' does not exist"),
337 (Error::NoSuchAccount("testaccount".to_string()), "account 'testaccount' does not exist"),
338 (
339 Error::NoSuchServiceAccount("service1".to_string()),
340 "service account 'service1' does not exist",
341 ),
342 (Error::NoSuchTempAccount("temp1".to_string()), "temp account 'temp1' does not exist"),
343 (Error::NoSuchGroup("group1".to_string()), "group 'group1' does not exist"),
344 (Error::NoSuchPolicy, "policy does not exist"),
345 (Error::PolicyInUse, "policy in use"),
346 (Error::GroupNotEmpty, "group not empty"),
347 (Error::InvalidArgument, "invalid arguments specified"),
348 (Error::IamSysNotInitialized, "not initialized"),
349 (Error::InvalidServiceType("invalid".to_string()), "invalid service type: invalid"),
350 (Error::ErrCredMalformed, "malformed credential"),
351 (Error::CredNotInitialized, "CredNotInitialized"),
352 (Error::InvalidAccessKeyLength, "invalid access key length"),
353 (Error::InvalidSecretKeyLength, "invalid secret key length"),
354 (Error::ContainsReservedChars, "access key contains reserved characters =,"),
355 (Error::GroupNameContainsReservedChars, "group name contains reserved characters =,"),
356 (Error::NoAccessKey, "no access key"),
357 (Error::InvalidToken, "invalid token"),
358 (Error::InvalidAccessKey, "invalid access_key"),
359 (Error::IAMActionNotAllowed, "action not allowed"),
360 (Error::InvalidExpiration, "invalid expiration"),
361 (Error::NoSecretKeyWithAccessKey, "no secret key with access key"),
362 (Error::NoAccessKeyWithSecretKey, "no access key with secret key"),
363 (Error::PolicyTooLarge, "policy too large"),
364 ];
365
366 for (error, expected_message) in test_cases {
367 assert_eq!(error.to_string(), expected_message);
368 }
369 }
370
371 #[test]
372 fn test_string_error_variant() {
373 let custom_message = "Custom error message";
374 let error = Error::StringError(custom_message.to_string());
375 assert_eq!(error.to_string(), custom_message);
376 }
377}