use thiserror::Error;
use crate::entry::ID;
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum AuthError {
#[error("Key not found: {key_name}")]
KeyNotFound {
key_name: String,
},
#[error("Invalid key format: {reason}")]
InvalidKeyFormat {
reason: String,
},
#[error("Key parsing failed")]
KeyParsingFailed {
#[source]
source: ed25519_dalek::SignatureError,
},
#[error("No auth configuration found")]
NoAuthConfiguration,
#[error("Invalid auth configuration: {reason}")]
InvalidAuthConfiguration {
reason: String,
},
#[error("Empty delegation path")]
EmptyDelegationPath,
#[error("Maximum delegation depth ({depth}) exceeded")]
DelegationDepthExceeded {
depth: usize,
},
#[error("Invalid delegation step: {reason}")]
InvalidDelegationStep {
reason: String,
},
#[error("Failed to load delegated tree {tree_id}")]
DelegatedTreeLoadFailed {
tree_id: String,
#[source]
source: Box<crate::Error>,
},
#[error(
"Invalid delegation tips for tree {tree_id}: claimed tips {claimed_tips:?} don't match"
)]
InvalidDelegationTips {
tree_id: String,
claimed_tips: Vec<ID>,
},
#[error("Cannot revoke non-key entry: {key_name}")]
CannotRevokeNonKey {
key_name: String,
},
#[error("Invalid signature")]
InvalidSignature,
#[error("Signature verification failed")]
SignatureVerificationFailed {
#[source]
source: ed25519_dalek::SignatureError,
},
#[error("Database required for {operation}")]
DatabaseRequired {
operation: String,
},
#[error("Invalid permission string: {value}")]
InvalidPermissionString {
value: String,
},
#[error("{permission_type} permission requires priority")]
PermissionRequiresPriority {
permission_type: String,
},
#[error("Invalid priority value: {value}")]
InvalidPriorityValue {
value: String,
},
#[error("Invalid key status: {value}")]
InvalidKeyStatus {
value: String,
},
#[error("Permission denied: {reason}")]
PermissionDenied {
reason: String,
},
#[error("Key already exists: {key_name}")]
KeyAlreadyExists {
key_name: String,
},
#[error(
"Key name '{key_name}' conflicts: existing key has pubkey '{existing_pubkey}', new key has pubkey '{new_pubkey}'"
)]
KeyNameConflict {
key_name: String,
existing_pubkey: String,
new_pubkey: String,
},
}
impl AuthError {
pub fn is_key_not_found(&self) -> bool {
matches!(self, AuthError::KeyNotFound { .. })
}
pub fn is_invalid_signature(&self) -> bool {
matches!(
self,
AuthError::InvalidSignature | AuthError::SignatureVerificationFailed { .. }
)
}
pub fn is_permission_denied(&self) -> bool {
matches!(self, AuthError::PermissionDenied { .. })
}
pub fn is_key_already_exists(&self) -> bool {
matches!(self, AuthError::KeyAlreadyExists { .. })
}
pub fn is_key_name_conflict(&self) -> bool {
matches!(self, AuthError::KeyNameConflict { .. })
}
pub fn is_configuration_error(&self) -> bool {
matches!(
self,
AuthError::NoAuthConfiguration
| AuthError::InvalidAuthConfiguration { .. }
| AuthError::InvalidKeyFormat { .. }
| AuthError::KeyParsingFailed { .. }
)
}
pub fn is_delegation_error(&self) -> bool {
matches!(
self,
AuthError::EmptyDelegationPath
| AuthError::DelegationDepthExceeded { .. }
| AuthError::InvalidDelegationStep { .. }
| AuthError::DelegatedTreeLoadFailed { .. }
| AuthError::InvalidDelegationTips { .. }
)
}
pub fn key_name(&self) -> Option<&str> {
match self {
AuthError::KeyNotFound { key_name: id } => Some(id),
_ => None,
}
}
}
impl From<AuthError> for crate::Error {
fn from(err: AuthError) -> Self {
crate::Error::Auth(err)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_helpers() {
let err = AuthError::KeyNotFound {
key_name: "test-key".to_string(),
};
assert!(err.is_key_not_found());
assert_eq!(err.key_name(), Some("test-key"));
let err = AuthError::InvalidSignature;
assert!(err.is_invalid_signature());
let err = AuthError::PermissionDenied {
reason: "test".to_string(),
};
assert!(err.is_permission_denied());
let err = AuthError::NoAuthConfiguration;
assert!(err.is_configuration_error());
let err = AuthError::EmptyDelegationPath;
assert!(err.is_delegation_error());
}
#[test]
fn test_error_conversion() {
let auth_err = AuthError::KeyNotFound {
key_name: "test".to_string(),
};
let err: crate::Error = auth_err.into();
match err {
crate::Error::Auth(AuthError::KeyNotFound { key_name: id }) => assert_eq!(id, "test"),
_ => panic!("Unexpected error variant"),
}
}
}