Jesús Pérez 4b92aa764a
Some checks failed
Build and Test / Validate Setup (push) Has been cancelled
Build and Test / Build (darwin-amd64) (push) Has been cancelled
Build and Test / Build (darwin-arm64) (push) Has been cancelled
Build and Test / Build (linux-amd64) (push) Has been cancelled
Build and Test / Build (windows-amd64) (push) Has been cancelled
Build and Test / Build (linux-arm64) (push) Has been cancelled
Build and Test / Security Audit (push) Has been cancelled
Build and Test / Package Results (push) Has been cancelled
Build and Test / Quality Gate (push) Has been cancelled
implements a production-ready bootstrap installer with comprehensive error handling, version-agnostic archive extraction, and clear user messaging. All improvements follow DRY principles using symlink-based architecture for single-source-of-truth maintenance
2025-12-11 22:04:54 +00:00

223 lines
7.0 KiB
Rust

//! Error types for the authentication plugin.
//!
//! This module provides structured error handling with specific error kinds
//! for different failure scenarios in authentication operations.
use std::fmt;
/// Enum representing different kinds of authentication errors.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AuthErrorKind {
/// Failed to authenticate with invalid credentials
InvalidCredentials,
/// Token has expired
TokenExpired,
/// Token format is invalid
InvalidToken,
/// Failed to verify token signature
SignatureVerificationFailed,
/// Keyring operation failed
KeyringError,
/// Network or HTTP request failed
NetworkError,
/// Server returned an error response
ServerError,
/// MFA verification failed
MfaFailed,
/// User not found
UserNotFound,
/// Session not found or expired
SessionNotFound,
/// Configuration error
ConfigurationError,
/// Internal error
InternalError,
}
impl fmt::Display for AuthErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidCredentials => write!(f, "invalid credentials"),
Self::TokenExpired => write!(f, "token expired"),
Self::InvalidToken => write!(f, "invalid token format"),
Self::SignatureVerificationFailed => write!(f, "signature verification failed"),
Self::KeyringError => write!(f, "keyring operation failed"),
Self::NetworkError => write!(f, "network error"),
Self::ServerError => write!(f, "server error"),
Self::MfaFailed => write!(f, "MFA verification failed"),
Self::UserNotFound => write!(f, "user not found"),
Self::SessionNotFound => write!(f, "session not found"),
Self::ConfigurationError => write!(f, "configuration error"),
Self::InternalError => write!(f, "internal error"),
}
}
}
/// Structured error type for authentication operations.
///
/// Provides detailed error information including:
/// - Error kind for programmatic handling
/// - Context message for additional details
/// - Optional source error for error chaining
#[derive(Debug)]
pub struct AuthError {
/// The kind of error that occurred
pub kind: AuthErrorKind,
/// Additional context about the error
pub context: String,
/// Optional underlying error
pub source: Option<Box<dyn std::error::Error + Send + Sync>>,
}
impl AuthError {
/// Creates a new AuthError with the specified kind and context.
///
/// # Arguments
///
/// * `kind` - The type of authentication error
/// * `context` - Additional context describing the error
///
/// # Example
///
/// ```
/// use nu_plugin_auth::error::{AuthError, AuthErrorKind};
///
/// let error = AuthError::new(
/// AuthErrorKind::InvalidCredentials,
/// "Username or password is incorrect"
/// );
/// ```
pub fn new(kind: AuthErrorKind, context: impl Into<String>) -> Self {
Self {
kind,
context: context.into(),
source: None,
}
}
/// Creates an AuthError with an underlying source error.
///
/// # Arguments
///
/// * `kind` - The type of authentication error
/// * `context` - Additional context describing the error
/// * `source` - The underlying error that caused this error
pub fn with_source(
kind: AuthErrorKind,
context: impl Into<String>,
source: impl std::error::Error + Send + Sync + 'static,
) -> Self {
Self {
kind,
context: context.into(),
source: Some(Box::new(source)),
}
}
/// Creates an invalid credentials error.
pub fn invalid_credentials(context: impl Into<String>) -> Self {
Self::new(AuthErrorKind::InvalidCredentials, context)
}
/// Creates a token expired error.
pub fn token_expired(context: impl Into<String>) -> Self {
Self::new(AuthErrorKind::TokenExpired, context)
}
/// Creates an invalid token error.
pub fn invalid_token(context: impl Into<String>) -> Self {
Self::new(AuthErrorKind::InvalidToken, context)
}
/// Creates a keyring error.
pub fn keyring_error(context: impl Into<String>) -> Self {
Self::new(AuthErrorKind::KeyringError, context)
}
/// Creates a network error.
pub fn network_error(context: impl Into<String>) -> Self {
Self::new(AuthErrorKind::NetworkError, context)
}
/// Creates a server error.
pub fn server_error(context: impl Into<String>) -> Self {
Self::new(AuthErrorKind::ServerError, context)
}
/// Creates an MFA failed error.
pub fn mfa_failed(context: impl Into<String>) -> Self {
Self::new(AuthErrorKind::MfaFailed, context)
}
/// Creates a configuration error.
pub fn configuration_error(context: impl Into<String>) -> Self {
Self::new(AuthErrorKind::ConfigurationError, context)
}
}
impl fmt::Display for AuthError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.kind, self.context)?;
if let Some(ref source) = self.source {
write!(f, " (caused by: {})", source)?;
}
Ok(())
}
}
impl std::error::Error for AuthError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source
.as_ref()
.map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
}
}
impl From<AuthError> for nu_protocol::LabeledError {
fn from(err: AuthError) -> Self {
nu_protocol::LabeledError::new(err.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let error = AuthError::new(
AuthErrorKind::InvalidCredentials,
"username admin not found",
);
assert!(error.to_string().contains("invalid credentials"));
assert!(error.to_string().contains("username admin not found"));
}
#[test]
fn test_error_with_source() {
let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let error = AuthError::with_source(
AuthErrorKind::KeyringError,
"failed to read keyring",
io_error,
);
assert!(error.to_string().contains("caused by"));
}
#[test]
fn test_error_kind_display() {
assert_eq!(AuthErrorKind::InvalidCredentials.to_string(), "invalid credentials");
assert_eq!(AuthErrorKind::TokenExpired.to_string(), "token expired");
assert_eq!(AuthErrorKind::KeyringError.to_string(), "keyring operation failed");
}
#[test]
fn test_convenience_constructors() {
let error = AuthError::invalid_credentials("bad password");
assert_eq!(error.kind, AuthErrorKind::InvalidCredentials);
let error = AuthError::network_error("connection refused");
assert_eq!(error.kind, AuthErrorKind::NetworkError);
}
}