Skip to main content

rocketmq_error/
auth_error.rs

1// Copyright 2023 The RocketMQ Rust Authors
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
15//! Authentication error types.
16
17use thiserror::Error;
18
19/// Authentication error types.
20///
21/// This enum represents various authentication errors that can occur during
22/// the authentication process, including context building, credential validation,
23/// and signature verification.
24#[derive(Debug, Clone, Error)]
25pub enum AuthError {
26    /// Missing DateTime header in gRPC metadata
27    #[error("Missing DateTime header: {0}")]
28    MissingDateTime(String),
29
30    /// Invalid Authorization header format
31    #[error("Invalid Authorization header: {0}")]
32    InvalidAuthorizationHeader(String),
33
34    /// Invalid credential format or content
35    #[error("Invalid credential: {0}")]
36    InvalidCredential(String),
37
38    /// Invalid hex-encoded signature
39    #[error("Invalid hex signature: {0}")]
40    InvalidHexSignature(String),
41
42    /// Generic authentication context creation error
43    #[error("Failed to create authentication context: {0}")]
44    ContextCreationError(String),
45
46    /// User authentication failed
47    #[error("Authentication failed: {0}")]
48    AuthenticationFailed(String),
49
50    /// User not found
51    #[error("User not found: {0}")]
52    UserNotFound(String),
53
54    /// Invalid signature
55    #[error("Invalid signature: {0}")]
56    InvalidSignature(String),
57
58    /// Request timestamp is outside the configured replay-protection window
59    #[error(
60        "Request timestamp expired: request={request_timestamp_millis}, now={now_millis}, \
61         allowedSkewMillis={allowed_skew_millis}"
62    )]
63    RequestTimestampExpired {
64        request_timestamp_millis: i64,
65        now_millis: i64,
66        allowed_skew_millis: u64,
67    },
68
69    /// User status is not valid (disabled, deleted, etc.)
70    #[error("Invalid user status: {0}")]
71    InvalidUserStatus(String),
72
73    /// Generic error with custom message
74    #[error("Authentication error: {0}")]
75    Other(String),
76}
77
78impl From<String> for AuthError {
79    fn from(msg: String) -> Self {
80        AuthError::Other(msg)
81    }
82}
83
84impl From<&str> for AuthError {
85    fn from(msg: &str) -> Self {
86        AuthError::Other(msg.to_string())
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_auth_error_variants() {
96        let errors = vec![
97            AuthError::AuthenticationFailed("could not authenticate".to_string()),
98            AuthError::ContextCreationError("could not create context".to_string()),
99            AuthError::InvalidAuthorizationHeader("invalid authorization header".to_string()),
100            AuthError::InvalidCredential("invalid credential".to_string()),
101            AuthError::InvalidHexSignature("invalid hex signature".to_string()),
102            AuthError::InvalidSignature("invalid signature".to_string()),
103            AuthError::RequestTimestampExpired {
104                request_timestamp_millis: 1,
105                now_millis: 2,
106                allowed_skew_millis: 3,
107            },
108            AuthError::InvalidUserStatus("invalid user status".to_string()),
109            AuthError::MissingDateTime("missing date time".to_string()),
110            AuthError::Other("other error".to_string()),
111            AuthError::UserNotFound("user not found".to_string()),
112        ];
113
114        for error in errors {
115            let _msg = format!("{}", error);
116            let _debug = format!("{:?}", error);
117        }
118    }
119
120    #[test]
121    fn test_from_string_creates_other() {
122        let error: AuthError = String::from("custom error").into();
123
124        match error {
125            AuthError::Other(msg) => assert_eq!(msg, "custom error"),
126            _ => panic!("Expected AuthError::Other"),
127        }
128    }
129
130    #[test]
131    fn test_from_str_creates_other() {
132        let error: AuthError = "custom error".into();
133
134        match error {
135            AuthError::Other(msg) => assert_eq!(msg, "custom error"),
136            _ => panic!("Expected AuthError::Other"),
137        }
138    }
139}