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