rustfs_policy/
error.rs

1// Copyright 2024 RustFS Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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
138// impl From<jsonwebtoken::errors::Error> for Error {
139//     fn from(e: jsonwebtoken::errors::Error) -> Self {
140//         Error::JWTError(e)
141//     }
142// }
143
144impl From<regex::Error> for Error {
145    fn from(e: regex::Error) -> Self {
146        Error::other(e)
147    }
148}
149
150// pub fn is_err_no_such_user(e: &Error) -> bool {
151//     matches!(e, Error::NoSuchUser(_))
152// }
153
154pub 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        // Test conversion from crypto::Error - use an actual variant
214        let crypto_error = rustfs_crypto::Error::ErrUnexpectedHeader;
215        let policy_error: Error = crypto_error.into();
216
217        match policy_error {
218            Error::CryptoError(_) => {
219                // Verify the conversion worked
220                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        // Create an invalid JWT to generate a JWT error
238        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                // Verify the conversion worked
251                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        // Test conversion from serde_json::Error
260        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        // Create an invalid date to generate a ComponentRange error
277        let time_result = Date::from_calendar_date(2023, Month::January, 32); // Invalid day
278        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        // Create an invalid regex to generate a regex error (unclosed bracket)
297        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        // Test helper functions for error type checking
314        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}