Skip to main content

bitwarden_vault/cipher/cipher_client/
get.rs

1use bitwarden_core::key_management::KeySlotIds;
2use bitwarden_crypto::{CryptoError, KeyStore};
3use bitwarden_error::bitwarden_error;
4use bitwarden_state::repository::{Repository, RepositoryError};
5use thiserror::Error;
6
7use super::CiphersClient;
8use crate::{
9    Cipher, CipherId, CipherView, ItemNotFoundError,
10    cipher::cipher::{DecryptCipherListResult, DecryptCipherResult, StrictDecrypt},
11};
12
13#[allow(missing_docs)]
14#[bitwarden_error(flat)]
15#[derive(Debug, Error)]
16pub enum GetCipherError {
17    #[error(transparent)]
18    ItemNotFound(#[from] ItemNotFoundError),
19    #[error(transparent)]
20    Crypto(#[from] CryptoError),
21    #[error(transparent)]
22    Repository(#[from] RepositoryError),
23}
24
25async fn get_cipher(
26    store: &KeyStore<KeySlotIds>,
27    repository: &dyn Repository<Cipher>,
28    id: CipherId,
29    use_strict_decryption: bool,
30) -> Result<CipherView, GetCipherError> {
31    let cipher = repository.get(id).await?.ok_or(ItemNotFoundError)?;
32
33    Ok(if use_strict_decryption {
34        store.decrypt(&StrictDecrypt(cipher))?
35    } else {
36        store.decrypt(&cipher)?
37    })
38}
39
40async fn list_ciphers(
41    store: &KeyStore<KeySlotIds>,
42    repository: &dyn Repository<Cipher>,
43    use_strict_decryption: bool,
44) -> Result<DecryptCipherListResult, GetCipherError> {
45    let ciphers = repository.list().await?;
46    Ok(if use_strict_decryption {
47        let wrapped: Vec<StrictDecrypt<Cipher>> = ciphers.into_iter().map(StrictDecrypt).collect();
48        let (successes, failures) = store.decrypt_list_with_failures(&wrapped);
49        DecryptCipherListResult {
50            successes,
51            failures: failures.into_iter().map(|f| f.0.clone()).collect(),
52        }
53    } else {
54        let (successes, failures) = store.decrypt_list_with_failures(&ciphers);
55        DecryptCipherListResult {
56            successes,
57            failures: failures.into_iter().cloned().collect(),
58        }
59    })
60}
61
62async fn get_all_ciphers(
63    store: &KeyStore<KeySlotIds>,
64    repository: &dyn Repository<Cipher>,
65    use_strict_decryption: bool,
66) -> Result<DecryptCipherResult, GetCipherError> {
67    let ciphers = repository.list().await?;
68    Ok(if use_strict_decryption {
69        let wrapped: Vec<StrictDecrypt<Cipher>> = ciphers.into_iter().map(StrictDecrypt).collect();
70        let (successes, failures) = store.decrypt_list_with_failures(&wrapped);
71        DecryptCipherResult {
72            successes,
73            failures: failures.into_iter().map(|f| f.0.clone()).collect(),
74        }
75    } else {
76        let (successes, failures) = store.decrypt_list_with_failures(&ciphers);
77        DecryptCipherResult {
78            successes,
79            failures: failures.into_iter().cloned().collect(),
80        }
81    })
82}
83
84#[allow(deprecated)]
85#[bitwarden_ffi::wasm_export]
86impl CiphersClient {
87    /// Get all ciphers from state and decrypt them to [crate::CipherListView], returning both
88    /// successes and failures. This method will not fail when some ciphers fail to decrypt,
89    /// allowing for graceful handling of corrupted or problematic cipher data.
90    pub async fn list(&self) -> Result<DecryptCipherListResult, GetCipherError> {
91        let key_store = self.client.internal.get_key_store();
92        let repository = self.get_repository()?;
93
94        list_ciphers(
95            key_store,
96            repository.as_ref(),
97            self.is_strict_decrypt().await,
98        )
99        .await
100    }
101
102    /// Get all ciphers from state and decrypt them to full [CipherView], returning both
103    /// successes and failures. This method will not fail when some ciphers fail to decrypt,
104    /// allowing for graceful handling of corrupted or problematic cipher data.
105    pub async fn get_all(&self) -> Result<DecryptCipherResult, GetCipherError> {
106        let key_store = self.client.internal.get_key_store();
107        let repository = self.get_repository()?;
108
109        get_all_ciphers(
110            key_store,
111            repository.as_ref(),
112            self.is_strict_decrypt().await,
113        )
114        .await
115    }
116
117    /// Get [Cipher] by ID from state and decrypt it to a [CipherView].
118    pub async fn get(&self, cipher_id: CipherId) -> Result<CipherView, GetCipherError> {
119        let key_store = self.client.internal.get_key_store();
120        let repository = self.get_repository()?;
121
122        get_cipher(
123            key_store,
124            repository.as_ref(),
125            cipher_id,
126            self.is_strict_decrypt().await,
127        )
128        .await
129    }
130}