1use std::ffi::OsString;
6pub type Result<T> = std::result::Result<T, Error>;
7pub type ApiResult<T> = std::result::Result<T, LoginsApiError>;
9
10pub use error_support::{breadcrumb, handle_error, report_error};
11pub use error_support::{debug, error, info, trace, warn};
12
13use db_crypto::DbCryptoApiError;
14use error_support::{ErrorHandling, GetErrorHandling};
15use jwcrypto::JwCryptoError;
16
17#[derive(Debug, thiserror::Error)]
19pub enum LoginsApiError {
20 #[error("NSS not initialized")]
21 NSSUninitialized,
22
23 #[error("NSS error during authentication: {reason}")]
24 NSSAuthenticationError { reason: String },
25
26 #[error("error during authentication: {reason}")]
27 AuthenticationError { reason: String },
28
29 #[error("authentication cancelled")]
30 AuthenticationCanceled,
31
32 #[error("Invalid login: {reason}")]
33 InvalidRecord { reason: String },
34
35 #[error("No record with guid exists (when one was required): {reason:?}")]
36 NoSuchRecord { reason: String },
37
38 #[error("Encryption key is missing.")]
39 MissingKey,
40
41 #[error("Encryption key is not valid.")]
42 InvalidKey,
43
44 #[error("encryption failed: {reason}")]
45 EncryptionFailed { reason: String },
46
47 #[error("decryption failed: {reason}")]
48 DecryptionFailed { reason: String },
49
50 #[error("{reason}")]
51 Interrupted { reason: String },
52
53 #[error("Unexpected Error: {reason}")]
54 UnexpectedLoginsApiError { reason: String },
55}
56
57#[derive(Debug, thiserror::Error)]
61pub enum Error {
62 #[error("Database is closed")]
63 DatabaseClosed,
64
65 #[error("Malformed incoming record")]
66 MalformedIncomingRecord,
67
68 #[error("Invalid login: {0}")]
69 InvalidLogin(#[from] InvalidLogin),
70
71 #[error("The `sync_status` column in DB has an illegal value: {0}")]
72 BadSyncStatus(u8),
73
74 #[error("No record with guid exists (when one was required): {0:?}")]
75 NoSuchRecord(String),
76
77 #[error("The logins tables are not empty")]
79 NonEmptyTable,
80
81 #[error("encryption failed: {0:?}")]
82 EncryptionFailed(String),
83
84 #[error("decryption failed: {0:?}")]
85 DecryptionFailed(String),
86
87 #[error("Error parsing JSON data: {0}")]
88 JsonError(#[from] serde_json::Error),
89
90 #[error("Error executing SQL: {0}")]
91 SqlError(#[from] rusqlite::Error),
92
93 #[error("Error parsing URL: {0}")]
94 UrlParseError(#[from] url::ParseError),
95
96 #[error("Invalid path: {0:?}")]
97 InvalidPath(OsString),
98
99 #[error("CryptoError({0})")]
100 CryptoError(#[from] DbCryptoApiError),
101
102 #[error("JwCryptoError({0})")]
103 JwCryptoError(#[from] JwCryptoError),
104
105 #[error("{0}")]
106 Interrupted(#[from] interrupt_support::Interrupted),
107
108 #[error("IOError: {0}")]
109 IOError(#[from] std::io::Error),
110
111 #[error("Migration Error: {0}")]
112 MigrationError(String),
113
114 #[error("IncompatibleVersion: {0}")]
115 IncompatibleVersion(i64),
116}
117
118#[derive(Debug, thiserror::Error)]
120pub enum InvalidLogin {
121 #[error("Origin is empty")]
123 EmptyOrigin,
124 #[error("Password is empty")]
125 EmptyPassword,
126 #[error("Login already exists")]
127 DuplicateLogin,
128 #[error("Both `formActionOrigin` and `httpRealm` are present")]
129 BothTargets,
130 #[error("Neither `formActionOrigin` or `httpRealm` are present")]
131 NoTarget,
132 #[error("Login has illegal origin: {reason}")]
135 IllegalOrigin { reason: String },
136 #[error("Login has illegal field: {field_info}")]
137 IllegalFieldValue { field_info: String },
138}
139
140impl GetErrorHandling for Error {
143 type ExternalError = LoginsApiError;
144
145 fn get_error_handling(&self) -> ErrorHandling<Self::ExternalError> {
146 match self {
147 Self::InvalidLogin(why) => ErrorHandling::convert(LoginsApiError::InvalidRecord {
148 reason: why.to_string(),
149 }),
150 Self::MalformedIncomingRecord => {
151 ErrorHandling::convert(LoginsApiError::InvalidRecord {
152 reason: "invalid incoming record".to_string(),
153 })
154 }
155 Self::NoSuchRecord(guid) => ErrorHandling::convert(LoginsApiError::NoSuchRecord {
157 reason: guid.to_string(),
158 }),
159 Self::NonEmptyTable => {
163 ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
164 reason: "must be an empty DB to migrate".to_string(),
165 })
166 .report_error("logins-migration")
167 }
168 Self::Interrupted(_) => ErrorHandling::convert(LoginsApiError::Interrupted {
169 reason: self.to_string(),
170 }),
171 Error::SqlError(rusqlite::Error::SqliteFailure(err, _)) => match err.code {
172 rusqlite::ErrorCode::DatabaseCorrupt => {
173 ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
174 reason: self.to_string(),
175 })
176 .report_error("logins-db-corrupt")
177 }
178 rusqlite::ErrorCode::DiskFull => {
179 ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
180 reason: self.to_string(),
181 })
182 .report_error("logins-db-disk-full")
183 }
184 _ => ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
185 reason: self.to_string(),
186 })
187 .report_error("logins-unexpected"),
188 },
189 _ => ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
195 reason: self.to_string(),
196 })
197 .report_error("logins-unexpected"),
198 }
199 }
200}
201
202impl From<anyhow::Error> for LoginsApiError {
207 fn from(value: anyhow::Error) -> Self {
208 LoginsApiError::UnexpectedLoginsApiError {
209 reason: value.to_string(),
210 }
211 }
212}
213
214impl From<uniffi::UnexpectedUniFFICallbackError> for LoginsApiError {
215 fn from(error: uniffi::UnexpectedUniFFICallbackError) -> Self {
216 LoginsApiError::UnexpectedLoginsApiError {
217 reason: error.to_string(),
218 }
219 }
220}