bitwarden_vault/
collection_client.rs

1use std::collections::HashMap;
2
3use bitwarden_collections::{
4    collection::{Collection, CollectionId, CollectionView},
5    tree::{NodeItem, Tree},
6};
7use bitwarden_core::Client;
8use serde::{Deserialize, Serialize};
9#[cfg(feature = "wasm")]
10use tsify::Tsify;
11#[cfg(feature = "wasm")]
12use wasm_bindgen::prelude::wasm_bindgen;
13
14use crate::DecryptError;
15
16#[allow(missing_docs)]
17#[cfg_attr(feature = "wasm", wasm_bindgen)]
18#[derive(Clone)]
19pub struct CollectionsClient {
20    pub(crate) client: Client,
21}
22
23#[cfg_attr(feature = "wasm", wasm_bindgen)]
24impl CollectionsClient {
25    #[allow(missing_docs)]
26    pub fn decrypt(&self, collection: Collection) -> Result<CollectionView, DecryptError> {
27        let key_store = self.client.internal.get_key_store();
28        let view = key_store.decrypt(&collection)?;
29        Ok(view)
30    }
31
32    #[allow(missing_docs)]
33    pub fn decrypt_list(
34        &self,
35        collections: Vec<Collection>,
36    ) -> Result<Vec<CollectionView>, DecryptError> {
37        let key_store = self.client.internal.get_key_store();
38        let views = key_store.decrypt_list(&collections)?;
39        Ok(views)
40    }
41
42    ///
43    /// Returns the vector of CollectionView objects in a tree structure based on its implemented
44    /// path().
45    pub fn get_collection_tree(&self, collections: Vec<CollectionView>) -> CollectionViewTree {
46        CollectionViewTree {
47            tree: Tree::from_items(collections),
48        }
49    }
50}
51
52#[cfg_attr(feature = "wasm", wasm_bindgen)]
53pub struct CollectionViewTree {
54    tree: Tree<CollectionView>,
55}
56
57#[cfg_attr(feature = "wasm", wasm_bindgen)]
58pub struct CollectionViewNodeItem {
59    node_item: NodeItem<CollectionView>,
60}
61
62#[cfg_attr(
63    feature = "wasm",
64    derive(Tsify, Serialize, Deserialize),
65    tsify(into_wasm_abi, from_wasm_abi)
66)]
67#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
68pub struct AncestorMap {
69    pub ancestors: HashMap<CollectionId, String>,
70}
71
72#[cfg_attr(feature = "wasm", wasm_bindgen)]
73impl CollectionViewNodeItem {
74    pub fn get_item(&self) -> CollectionView {
75        self.node_item.item.clone()
76    }
77
78    pub fn get_parent(&self) -> Option<CollectionView> {
79        self.node_item.parent.clone()
80    }
81
82    pub fn get_children(&self) -> Vec<CollectionView> {
83        self.node_item.children.clone()
84    }
85
86    pub fn get_ancestors(&self) -> AncestorMap {
87        AncestorMap {
88            ancestors: self
89                .node_item
90                .ancestors
91                .iter()
92                .map(|(&uuid, name)| (CollectionId::new(uuid), name.clone()))
93                .collect(),
94        }
95    }
96}
97
98#[cfg_attr(feature = "wasm", wasm_bindgen)]
99impl CollectionViewTree {
100    pub fn get_item_for_view(
101        &self,
102        collection_view: CollectionView,
103    ) -> Option<CollectionViewNodeItem> {
104        self.tree
105            .get_item_by_id(collection_view.id.unwrap_or_default().into())
106            .map(|n| CollectionViewNodeItem { node_item: n })
107    }
108
109    pub fn get_root_items(&self) -> Vec<CollectionViewNodeItem> {
110        self.tree
111            .get_root_items()
112            .into_iter()
113            .map(|n| CollectionViewNodeItem { node_item: n })
114            .collect()
115    }
116
117    pub fn get_flat_items(&self) -> Vec<CollectionViewNodeItem> {
118        self.tree
119            .get_flat_items()
120            .into_iter()
121            .map(|n| CollectionViewNodeItem { node_item: n })
122            .collect()
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use bitwarden_collections::collection::CollectionType;
129    use bitwarden_core::client::test_accounts::test_bitwarden_com_account;
130
131    use super::*;
132    use crate::VaultClientExt;
133
134    #[tokio::test]
135    async fn test_decrypt_list() {
136        let client = Client::init_test_account(test_bitwarden_com_account()).await;
137
138        let dec = client.vault().collections().decrypt_list(vec![Collection {
139            id: Some("66c5ca57-0868-4c7e-902f-b181009709c0".parse().unwrap()),
140            organization_id: "1bc9ac1e-f5aa-45f2-94bf-b181009709b8".parse().unwrap(),
141            name: "2.EI9Km5BfrIqBa1W+WCccfA==|laWxNnx+9H3MZww4zm7cBSLisjpi81zreaQntRhegVI=|x42+qKFf5ga6DIL0OW5pxCdLrC/gm8CXJvf3UASGteI=".parse().unwrap(),
142            external_id: None,
143            hide_passwords: false,
144            read_only: false,
145            manage: false,
146            default_user_collection_email: None,
147            r#type: CollectionType::SharedCollection,
148        }]).unwrap();
149
150        assert_eq!(dec[0].name, "Default collection");
151    }
152
153    #[tokio::test]
154    async fn test_decrypt() {
155        let client = Client::init_test_account(test_bitwarden_com_account()).await;
156
157        let dec = client.vault().collections().decrypt(Collection {
158            id: Some("66c5ca57-0868-4c7e-902f-b181009709c0".parse().unwrap()),
159            organization_id: "1bc9ac1e-f5aa-45f2-94bf-b181009709b8".parse().unwrap(),
160            name: "2.EI9Km5BfrIqBa1W+WCccfA==|laWxNnx+9H3MZww4zm7cBSLisjpi81zreaQntRhegVI=|x42+qKFf5ga6DIL0OW5pxCdLrC/gm8CXJvf3UASGteI=".parse().unwrap(),
161            external_id: None,
162            hide_passwords: false,
163            read_only: false,
164            manage: false,
165            default_user_collection_email: None,
166            r#type: CollectionType::SharedCollection,
167        }).unwrap();
168
169        assert_eq!(dec.name, "Default collection");
170    }
171}