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 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 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#[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>;