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) -> Result<DecryptCipherListResult, GetCipherError> {
47    let ciphers = repository.list().await?;
48    let (successes, failures) = store.decrypt_list_with_failures(&ciphers);
49    Ok(DecryptCipherListResult {
50        successes,
51        failures: failures.into_iter().cloned().collect(),
52    })
53}
54
55async fn get_all_ciphers(
56    store: &KeyStore<KeySlotIds>,
57    repository: &dyn Repository<Cipher>,
58) -> Result<DecryptCipherResult, GetCipherError> {
59    let ciphers = repository.list().await?;
60    let (successes, failures) = store.decrypt_list_with_failures(&ciphers);
61    Ok(DecryptCipherResult {
62        successes,
63        failures: failures.into_iter().cloned().collect(),
64    })
65}
66
67#[cfg_attr(feature = "wasm", wasm_bindgen)]
68impl CiphersClient {
69    /// Get all ciphers from state and decrypt them to [crate::CipherListView], returning both
70    /// successes and failures. This method will not fail when some ciphers fail to decrypt,
71    /// allowing for graceful handling of corrupted or problematic cipher data.
72    pub async fn list(&self) -> Result<DecryptCipherListResult, GetCipherError> {
73        let key_store = self.client.internal.get_key_store();
74        let repository = self.get_repository()?;
75
76        list_ciphers(key_store, repository.as_ref()).await
77    }
78
79    /// Get all ciphers from state and decrypt them to full [CipherView], returning both
80    /// successes and failures. This method will not fail when some ciphers fail to decrypt,
81    /// allowing for graceful handling of corrupted or problematic cipher data.
82    pub async fn get_all(&self) -> Result<DecryptCipherResult, GetCipherError> {
83        let key_store = self.client.internal.get_key_store();
84        let repository = self.get_repository()?;
85
86        get_all_ciphers(key_store, repository.as_ref()).await
87    }
88
89    /// Get [Cipher] by ID from state and decrypt it to a [CipherView].
90    pub async fn get(&self, cipher_id: &str) -> Result<CipherView, GetCipherError> {
91        let key_store = self.client.internal.get_key_store();
92        let repository = self.get_repository()?;
93
94        get_cipher(
95            key_store,
96            repository.as_ref(),
97            cipher_id,
98            self.is_strict_decrypt().await,
99        )
100        .await
101    }
102}