@@ -5,6 +5,7 @@ use bitwarden_core::{
5
5
} ;
6
6
use bitwarden_crypto:: { CryptoError , Decryptable , EncString , IdentifyKey , KeyStoreContext } ;
7
7
use serde:: { Deserialize , Serialize } ;
8
+ use serde_repr:: { Deserialize_repr , Serialize_repr } ;
8
9
use uuid:: Uuid ;
9
10
#[ cfg( feature = "wasm" ) ]
10
11
use { tsify:: Tsify , wasm_bindgen:: prelude:: * } ;
@@ -24,6 +25,8 @@ pub struct Collection {
24
25
pub hide_passwords : bool ,
25
26
pub read_only : bool ,
26
27
pub manage : bool ,
28
+ pub default_user_collection_email : Option < String > ,
29
+ pub r#type : CollectionType ,
27
30
}
28
31
29
32
#[ allow( missing_docs) ]
@@ -39,6 +42,24 @@ pub struct CollectionView {
39
42
pub hide_passwords : bool ,
40
43
pub read_only : bool ,
41
44
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 ,
42
63
}
43
64
44
65
#[ allow( missing_docs) ]
@@ -48,14 +69,21 @@ impl Decryptable<KeyIds, SymmetricKeyId, CollectionView> for Collection {
48
69
ctx : & mut KeyStoreContext < KeyIds > ,
49
70
key : SymmetricKeyId ,
50
71
) -> 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
+
51
78
Ok ( CollectionView {
52
79
id : self . id ,
53
80
organization_id : self . organization_id ,
54
- name : self . name . decrypt ( ctx , key ) . ok ( ) . unwrap_or_default ( ) ,
81
+ name,
55
82
external_id : self . external_id . clone ( ) ,
56
83
hide_passwords : self . hide_passwords ,
57
84
read_only : self . read_only ,
58
85
manage : self . manage ,
86
+ r#type : self . r#type . clone ( ) ,
59
87
} )
60
88
}
61
89
}
@@ -73,6 +101,8 @@ impl TryFrom<CollectionDetailsResponseModel> for Collection {
73
101
hide_passwords : collection. hide_passwords . unwrap_or ( false ) ,
74
102
read_only : collection. read_only . unwrap_or ( false ) ,
75
103
manage : collection. manage . unwrap_or ( false ) ,
104
+ default_user_collection_email : collection. default_user_collection_email ,
105
+ r#type : require ! ( collection. r#type) . into ( ) ,
76
106
} )
77
107
}
78
108
}
@@ -103,3 +133,132 @@ impl TreeItem for CollectionView {
103
133
104
134
const DELIMITER : char = '/' ;
105
135
}
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 ( "[email protected] " ) ;
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