Skip to main content

authy/
error.rs

1use serde::Serialize;
2use thiserror::Error;
3
4#[derive(Error, Debug)]
5pub enum AuthyError {
6    #[error("Vault not initialized. Run `authy init` first.")]
7    VaultNotInitialized,
8
9    #[error("Vault already initialized at {0}")]
10    VaultAlreadyExists(String),
11
12    #[error("Secret not found: {0}")]
13    SecretNotFound(String),
14
15    #[error("Secret already exists: {0} (use --force to overwrite)")]
16    SecretAlreadyExists(String),
17
18    #[error("Policy not found: {0}")]
19    PolicyNotFound(String),
20
21    #[error("Policy already exists: {0}")]
22    PolicyAlreadyExists(String),
23
24    #[error("Access denied: secret '{secret}' not allowed by scope '{scope}'")]
25    AccessDenied { secret: String, scope: String },
26
27    #[error("Authentication failed: {0}")]
28    AuthFailed(String),
29
30    #[error("Invalid session token")]
31    InvalidToken,
32
33    #[error("Session token expired")]
34    TokenExpired,
35
36    #[error("Session token revoked")]
37    #[allow(dead_code)]
38    TokenRevoked,
39
40    #[error("Session not found: {0}")]
41    SessionNotFound(String),
42
43    #[error("Write operations require master key authentication (tokens are read-only)")]
44    TokenReadOnly,
45
46    #[error("Run-only mode: secret values cannot be read directly. Use `authy run` to inject secrets into a subprocess.")]
47    RunOnly,
48
49    #[error("Encryption error: {0}")]
50    Encryption(String),
51
52    #[error("Decryption error: {0}")]
53    Decryption(String),
54
55    #[error("Serialization error: {0}")]
56    Serialization(String),
57
58    #[error("Audit chain integrity violation at entry {0}")]
59    AuditChainBroken(usize),
60
61    #[error("Invalid keyfile: {0}")]
62    InvalidKeyfile(String),
63
64    #[error("IO error: {0}")]
65    Io(#[from] std::io::Error),
66
67    #[error("{0}")]
68    Other(String),
69}
70
71impl AuthyError {
72    /// Return a typed exit code for this error category.
73    pub fn exit_code(&self) -> i32 {
74        match self {
75            AuthyError::VaultNotInitialized => 7,
76            AuthyError::VaultAlreadyExists(_) => 5,
77            AuthyError::SecretNotFound(_) => 3,
78            AuthyError::SecretAlreadyExists(_) => 5,
79            AuthyError::PolicyNotFound(_) => 3,
80            AuthyError::PolicyAlreadyExists(_) => 5,
81            AuthyError::AccessDenied { .. } => 4,
82            AuthyError::AuthFailed(_) => 2,
83            AuthyError::InvalidToken => 6,
84            AuthyError::TokenExpired => 6,
85            AuthyError::TokenRevoked => 6,
86            AuthyError::SessionNotFound(_) => 3,
87            AuthyError::TokenReadOnly => 4,
88            AuthyError::RunOnly => 4,
89            AuthyError::Encryption(_) => 1,
90            AuthyError::Decryption(_) => 2,
91            AuthyError::Serialization(_) => 1,
92            AuthyError::AuditChainBroken(_) => 1,
93            AuthyError::InvalidKeyfile(_) => 2,
94            AuthyError::Io(_) => 1,
95            AuthyError::Other(_) => 1,
96        }
97    }
98
99    /// Return a string error code identifier.
100    pub fn error_code(&self) -> &'static str {
101        match self {
102            AuthyError::VaultNotInitialized => "vault_not_initialized",
103            AuthyError::VaultAlreadyExists(_) => "already_exists",
104            AuthyError::SecretNotFound(_) => "not_found",
105            AuthyError::SecretAlreadyExists(_) => "already_exists",
106            AuthyError::PolicyNotFound(_) => "not_found",
107            AuthyError::PolicyAlreadyExists(_) => "already_exists",
108            AuthyError::AccessDenied { .. } => "access_denied",
109            AuthyError::AuthFailed(_) => "auth_failed",
110            AuthyError::InvalidToken => "invalid_token",
111            AuthyError::TokenExpired => "token_expired",
112            AuthyError::TokenRevoked => "token_revoked",
113            AuthyError::SessionNotFound(_) => "not_found",
114            AuthyError::TokenReadOnly => "token_read_only",
115            AuthyError::RunOnly => "run_only",
116            AuthyError::Encryption(_) => "encryption_error",
117            AuthyError::Decryption(_) => "decryption_error",
118            AuthyError::Serialization(_) => "serialization_error",
119            AuthyError::AuditChainBroken(_) => "audit_chain_broken",
120            AuthyError::InvalidKeyfile(_) => "invalid_keyfile",
121            AuthyError::Io(_) => "io_error",
122            AuthyError::Other(_) => "error",
123        }
124    }
125}
126
127/// JSON error response for --json mode.
128#[derive(Serialize)]
129pub struct JsonError {
130    pub error: JsonErrorDetail,
131}
132
133#[derive(Serialize)]
134pub struct JsonErrorDetail {
135    pub code: String,
136    pub message: String,
137    pub exit_code: i32,
138}
139
140impl JsonError {
141    pub fn from_error(e: &AuthyError) -> Self {
142        Self {
143            error: JsonErrorDetail {
144                code: e.error_code().to_string(),
145                message: e.to_string(),
146                exit_code: e.exit_code(),
147            },
148        }
149    }
150}
151
152pub type Result<T> = std::result::Result<T, AuthyError>;