@@ -5,6 +5,7 @@ use bitwarden_core::{
55} ;
66use bitwarden_crypto:: { CryptoError , Decryptable , EncString , IdentifyKey , KeyStoreContext } ;
77use serde:: { Deserialize , Serialize } ;
8+ use serde_repr:: { Deserialize_repr , Serialize_repr } ;
89use uuid:: Uuid ;
910#[ cfg( feature = "wasm" ) ]
1011use { tsify:: Tsify , wasm_bindgen:: prelude:: * } ;
@@ -24,6 +25,8 @@ pub struct Collection {
2425 pub hide_passwords : bool ,
2526 pub read_only : bool ,
2627 pub manage : bool ,
28+ pub default_user_collection_email : Option < String > ,
29+ pub r#type : CollectionType ,
2730}
2831
2932#[ allow( missing_docs) ]
@@ -39,6 +42,24 @@ pub struct CollectionView {
3942 pub hide_passwords : bool ,
4043 pub read_only : bool ,
4144 pub manage : bool ,
45+ pub r#type : CollectionType ,
46+ }
47+
48+ /// Type of collection
49+ #[ derive( Serialize_repr , Deserialize_repr , Debug , Clone , Eq , PartialEq ) ]
50+ #[ cfg_attr( feature = "uniffi" , derive( uniffi:: Enum ) ) ]
51+ #[ cfg_attr(
52+ feature = "wasm" ,
53+ derive( tsify:: Tsify ) ,
54+ tsify( into_wasm_abi, from_wasm_abi)
55+ ) ]
56+ #[ repr( u8 ) ]
57+ pub enum CollectionType {
58+ /// Default collection type. Can be assigned by an organization to user(s) or group(s)
59+ SharedCollection = 0 ,
60+ /// Default collection assigned to a user for an organization that has
61+ /// OrganizationDataOwnership (formerly PersonalOwnership) policy enabled.
62+ DefaultUserCollection = 1 ,
4263}
4364
4465#[ allow( missing_docs) ]
@@ -48,14 +69,21 @@ impl Decryptable<KeyIds, SymmetricKeyId, CollectionView> for Collection {
4869 ctx : & mut KeyStoreContext < KeyIds > ,
4970 key : SymmetricKeyId ,
5071 ) -> Result < CollectionView , CryptoError > {
72+ let name = self
73+ . default_user_collection_email
74+ . as_ref ( )
75+ . unwrap_or ( & self . name . decrypt ( ctx, key) ?)
76+ . clone ( ) ;
77+
5178 Ok ( CollectionView {
5279 id : self . id ,
5380 organization_id : self . organization_id ,
54- name : self . name . decrypt ( ctx , key ) . ok ( ) . unwrap_or_default ( ) ,
81+ name,
5582 external_id : self . external_id . clone ( ) ,
5683 hide_passwords : self . hide_passwords ,
5784 read_only : self . read_only ,
5885 manage : self . manage ,
86+ r#type : self . r#type . clone ( ) ,
5987 } )
6088 }
6189}
@@ -73,6 +101,8 @@ impl TryFrom<CollectionDetailsResponseModel> for Collection {
73101 hide_passwords : collection. hide_passwords . unwrap_or ( false ) ,
74102 read_only : collection. read_only . unwrap_or ( false ) ,
75103 manage : collection. manage . unwrap_or ( false ) ,
104+ default_user_collection_email : collection. default_user_collection_email ,
105+ r#type : require ! ( collection. r#type) . into ( ) ,
76106 } )
77107 }
78108}
@@ -103,3 +133,132 @@ impl TreeItem for CollectionView {
103133
104134 const DELIMITER : char = '/' ;
105135}
136+
137+ impl From < bitwarden_api_api:: models:: CollectionType > for CollectionType {
138+ fn from ( collection_type : bitwarden_api_api:: models:: CollectionType ) -> Self {
139+ match collection_type {
140+ bitwarden_api_api:: models:: CollectionType :: SharedCollection => Self :: SharedCollection ,
141+ bitwarden_api_api:: models:: CollectionType :: DefaultUserCollection => {
142+ Self :: DefaultUserCollection
143+ }
144+ }
145+ }
146+ }
147+
148+ #[ cfg( test) ]
149+ mod tests {
150+ use bitwarden_core:: key_management:: { KeyIds , SymmetricKeyId } ;
151+ use bitwarden_crypto:: { KeyStore , PrimitiveEncryptable , SymmetricCryptoKey } ;
152+ use uuid:: Uuid ;
153+
154+ use super :: * ;
155+
156+ const ORGANIZATION_ID : & str = "12345678-1234-1234-1234-123456789012" ;
157+ const COLLECTION_ID : & str = "87654321-4321-4321-4321-210987654321" ;
158+
159+ // Helper function to create a test key store with a symmetric key
160+ fn create_test_key_store ( ) -> KeyStore < KeyIds > {
161+ let store = KeyStore :: < KeyIds > :: default ( ) ;
162+ let key = SymmetricCryptoKey :: make_aes256_cbc_hmac_key ( ) ;
163+ let org_id = Uuid :: parse_str ( ORGANIZATION_ID ) . unwrap ( ) ;
164+
165+ #[ allow( deprecated) ]
166+ store
167+ . context_mut ( )
168+ . set_symmetric_key ( SymmetricKeyId :: Organization ( org_id) , key)
169+ . unwrap ( ) ;
170+
171+ store
172+ }
173+
174+ #[ test]
175+ fn test_decrypt_with_name_only ( ) {
176+ let store = create_test_key_store ( ) ;
177+ let mut ctx = store. context ( ) ;
178+ let org_id = Uuid :: parse_str ( ORGANIZATION_ID ) . unwrap ( ) ;
179+ let key = SymmetricKeyId :: Organization ( org_id) ;
180+
181+ let collection_name: & str = "Collection Name" ;
182+
183+ let collection = Collection {
184+ id : Some ( Uuid :: parse_str ( COLLECTION_ID ) . unwrap ( ) ) ,
185+ organization_id : org_id,
186+ name : collection_name. encrypt ( & mut ctx, key) . unwrap ( ) ,
187+ external_id : Some ( "external-id" . to_string ( ) ) ,
188+ hide_passwords : true ,
189+ read_only : false ,
190+ manage : true ,
191+ default_user_collection_email : None ,
192+ r#type : CollectionType :: SharedCollection ,
193+ } ;
194+
195+ let decrypted = collection. decrypt ( & mut ctx, key) . unwrap ( ) ;
196+
197+ assert_eq ! ( decrypted. name, collection_name) ;
198+ }
199+
200+ #[ test]
201+ fn test_decrypt_with_default_user_collection_email ( ) {
202+ let store = create_test_key_store ( ) ;
203+ let mut ctx = store. context ( ) ;
204+ let org_id = Uuid :: parse_str ( ORGANIZATION_ID ) . unwrap ( ) ;
205+ let key = SymmetricKeyId :: Organization ( org_id) ;
206+
207+ let collection_name: & str = "Collection Name" ;
208+ let default_user_collection_email = String :: from ( "test-user@bitwarden.com" ) ;
209+
210+ let collection = Collection {
211+ id : Some ( Uuid :: parse_str ( COLLECTION_ID ) . unwrap ( ) ) ,
212+ organization_id : org_id,
213+ name : collection_name. encrypt ( & mut ctx, key) . unwrap ( ) ,
214+ external_id : None ,
215+ hide_passwords : false ,
216+ read_only : true ,
217+ manage : false ,
218+ default_user_collection_email : Some ( default_user_collection_email. clone ( ) ) ,
219+ r#type : CollectionType :: SharedCollection ,
220+ } ;
221+
222+ let decrypted = collection. decrypt ( & mut ctx, key) . unwrap ( ) ;
223+
224+ assert_ne ! ( decrypted. name, collection_name) ;
225+ assert_eq ! ( decrypted. name, default_user_collection_email) ;
226+ }
227+
228+ #[ test]
229+ fn test_decrypt_all_fields_preserved ( ) {
230+ let store = create_test_key_store ( ) ;
231+ let mut ctx = store. context ( ) ;
232+ let org_id = Uuid :: parse_str ( ORGANIZATION_ID ) . unwrap ( ) ;
233+ let key = SymmetricKeyId :: Organization ( org_id) ;
234+
235+ let collection_id = Some ( Uuid :: parse_str ( COLLECTION_ID ) . unwrap ( ) ) ;
236+ let external_id = Some ( "external-test-id" . to_string ( ) ) ;
237+ let collection_name: & str = "Collection Name" ;
238+ let collection_type = CollectionType :: SharedCollection ;
239+
240+ let collection = Collection {
241+ id : collection_id,
242+ organization_id : org_id,
243+ name : collection_name. encrypt ( & mut ctx, key) . unwrap ( ) ,
244+ external_id : external_id. clone ( ) ,
245+ hide_passwords : true ,
246+ read_only : true ,
247+ manage : true ,
248+ default_user_collection_email : None ,
249+ r#type : collection_type. clone ( ) ,
250+ } ;
251+
252+ let decrypted = collection. decrypt ( & mut ctx, key) . unwrap ( ) ;
253+
254+ // Verify all fields are correctly transferred
255+ assert_eq ! ( decrypted. id, collection. id) ;
256+ assert_eq ! ( decrypted. organization_id, collection. organization_id) ;
257+ assert_eq ! ( decrypted. name, collection_name) ;
258+ assert_eq ! ( decrypted. external_id, external_id) ;
259+ assert_eq ! ( decrypted. hide_passwords, collection. hide_passwords) ;
260+ assert_eq ! ( decrypted. read_only, collection. read_only) ;
261+ assert_eq ! ( decrypted. manage, collection. manage) ;
262+ assert_eq ! ( decrypted. r#type, collection_type) ;
263+ }
264+ }
0 commit comments