1use thiserror::Error as ThisError;
7
8use crate::Error;
9use crate::entry::ID;
10
11#[non_exhaustive]
20#[derive(Debug, ThisError)]
21pub enum AuthError {
22 #[error("Key not found: {key_name}")]
24 KeyNotFound {
25 key_name: String,
27 },
28
29 #[error("Invalid key format: {reason}")]
31 InvalidKeyFormat {
32 reason: String,
34 },
35
36 #[error("Key parsing failed: {reason}")]
38 KeyParsingFailed {
39 reason: String,
41 },
42
43 #[error("No auth configuration found")]
45 NoAuthConfiguration,
46
47 #[error("Invalid auth configuration: {reason}")]
49 InvalidAuthConfiguration {
50 reason: String,
52 },
53
54 #[error("Empty delegation path")]
56 EmptyDelegationPath,
57
58 #[error("Maximum delegation depth ({depth}) exceeded")]
60 DelegationDepthExceeded {
61 depth: usize,
63 },
64
65 #[error("Invalid delegation step: {reason}")]
67 InvalidDelegationStep {
68 reason: String,
70 },
71
72 #[error("Failed to load delegated tree {tree_id}")]
74 DelegatedTreeLoadFailed {
75 tree_id: String,
77 #[source]
79 source: Box<Error>,
80 },
81
82 #[error(
84 "Invalid delegation tips for tree {tree_id}: claimed tips {claimed_tips:?} don't match"
85 )]
86 InvalidDelegationTips {
87 tree_id: String,
89 claimed_tips: Vec<ID>,
91 },
92
93 #[error("Delegation not found for tree: {tree_id}")]
95 DelegationNotFound {
96 tree_id: String,
98 },
99
100 #[error("Cannot revoke non-key entry: {key_name}")]
102 CannotRevokeNonKey {
103 key_name: String,
105 },
106
107 #[error("Malformed entry: {reason}")]
109 MalformedEntry {
110 reason: &'static str,
112 },
113
114 #[error("Invalid signature")]
116 InvalidSignature,
117
118 #[error("Signature verification failed: {reason}")]
120 SignatureVerificationFailed {
121 reason: String,
123 },
124
125 #[error("Database required for {operation}")]
127 DatabaseRequired {
128 operation: String,
130 },
131
132 #[error("Invalid permission string: {value}")]
134 InvalidPermissionString {
135 value: String,
137 },
138
139 #[error("{permission_type} permission requires priority")]
141 PermissionRequiresPriority {
142 permission_type: String,
144 },
145
146 #[error("Invalid priority value: {value}")]
148 InvalidPriorityValue {
149 value: String,
151 },
152
153 #[error("Invalid key status: {value}")]
155 InvalidKeyStatus {
156 value: String,
158 },
159
160 #[error("Permission denied: {reason}")]
162 PermissionDenied {
163 reason: String,
165 },
166
167 #[error("Key already exists: {key_name}")]
169 KeyAlreadyExists {
170 key_name: String,
172 },
173
174 #[error(
176 "Key name '{key_name}' conflicts: existing key has pubkey '{existing_pubkey}', new key has pubkey '{new_pubkey}'"
177 )]
178 KeyNameConflict {
179 key_name: String,
181 existing_pubkey: String,
183 new_pubkey: String,
185 },
186
187 #[error("Signing key mismatch: {reason}")]
189 SigningKeyMismatch {
190 reason: String,
192 },
193}
194
195impl AuthError {
196 pub fn is_not_found(&self) -> bool {
198 matches!(
199 self,
200 AuthError::KeyNotFound { .. } | AuthError::DelegationNotFound { .. }
201 )
202 }
203
204 pub fn is_invalid_signature(&self) -> bool {
206 matches!(
207 self,
208 AuthError::InvalidSignature | AuthError::SignatureVerificationFailed { .. }
209 )
210 }
211
212 pub fn is_permission_denied(&self) -> bool {
214 matches!(self, AuthError::PermissionDenied { .. })
215 }
216
217 pub fn is_key_already_exists(&self) -> bool {
219 matches!(self, AuthError::KeyAlreadyExists { .. })
220 }
221
222 pub fn is_key_name_conflict(&self) -> bool {
224 matches!(self, AuthError::KeyNameConflict { .. })
225 }
226
227 pub fn is_configuration_error(&self) -> bool {
229 matches!(
230 self,
231 AuthError::NoAuthConfiguration
232 | AuthError::InvalidAuthConfiguration { .. }
233 | AuthError::InvalidKeyFormat { .. }
234 | AuthError::KeyParsingFailed { .. }
235 )
236 }
237
238 pub fn is_delegation_error(&self) -> bool {
240 matches!(
241 self,
242 AuthError::EmptyDelegationPath
243 | AuthError::DelegationDepthExceeded { .. }
244 | AuthError::InvalidDelegationStep { .. }
245 | AuthError::DelegatedTreeLoadFailed { .. }
246 | AuthError::InvalidDelegationTips { .. }
247 | AuthError::DelegationNotFound { .. }
248 )
249 }
250
251 pub fn key_name(&self) -> Option<&str> {
253 match self {
254 AuthError::KeyNotFound { key_name: id } => Some(id),
255 _ => None,
256 }
257 }
258}
259
260impl From<AuthError> for Error {
262 fn from(err: AuthError) -> Self {
263 Error::Auth(err)
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn test_error_helpers() {
274 let err = AuthError::KeyNotFound {
275 key_name: "test-key".to_string(),
276 };
277 assert!(err.is_not_found());
278 assert_eq!(err.key_name(), Some("test-key"));
279
280 let err = AuthError::InvalidSignature;
281 assert!(err.is_invalid_signature());
282
283 let err = AuthError::PermissionDenied {
284 reason: "test".to_string(),
285 };
286 assert!(err.is_permission_denied());
287
288 let err = AuthError::NoAuthConfiguration;
289 assert!(err.is_configuration_error());
290
291 let err = AuthError::EmptyDelegationPath;
292 assert!(err.is_delegation_error());
293 }
294
295 #[test]
296 fn test_error_conversion() {
297 let auth_err = AuthError::KeyNotFound {
298 key_name: "test".to_string(),
299 };
300 let err: Error = auth_err.into();
301 match err {
302 Error::Auth(AuthError::KeyNotFound { key_name: id }) => assert_eq!(id, "test"),
303 _ => panic!("Unexpected error variant"),
304 }
305 }
306}