@@ -17,6 +17,10 @@ describe('Sync Down', () => {
1717 username : string ,
1818 accountUid : Uint8Array ,
1919 }
20+ let anotherUserA : {
21+ username : string ,
22+ accountUid : Uint8Array ,
23+ }
2024 beforeAll ( async ( ) => {
2125 connectPlatform ( nodePlatform )
2226 dataKey = platform . getRandomBytes ( 32 )
@@ -25,6 +29,10 @@ describe('Sync Down', () => {
2529 username : 'keeper@keepersecurity.com' ,
2630 accountUid : platform . getRandomBytes ( 16 )
2731 }
32+ anotherUserA = {
33+ username : 'another@keepersecurity.com' ,
34+ accountUid : platform . getRandomBytes ( 16 )
35+ }
2836 } )
2937 describe ( 'Owned Records' , ( ) => {
3038 beforeEach ( ( ) => {
@@ -254,5 +262,132 @@ describe('Sync Down', () => {
254262 expect ( storage . delete ) . toHaveBeenCalledWith ( 'record' , webSafe64FromBytes ( recordUid ) )
255263 } )
256264 } )
265+ describe ( 'Directly-Shared Records' , ( ) => {
266+ it ( 'saves the record data when a record is direct-shared by other user' , async ( ) => {
267+ const decryptedRecordKey = platform . getRandomBytes ( 32 )
268+ const recordKey = await platform . aesGcmEncrypt ( decryptedRecordKey , auth . dataKey ! )
269+ const recordUid = platform . getRandomBytes ( 16 )
270+ const recordUidStr = webSafe64FromBytes ( recordUid )
271+ const decryptedRecordData = {
272+ title : 'test record' ,
273+ }
274+ const decodedRecordData = platform . stringToBytes ( JSON . stringify ( decryptedRecordData ) )
275+ const recordData = await platform . aesGcmEncrypt ( decodedRecordData , decryptedRecordKey )
276+ const userFolderRecord : Vault . IUserFolderRecord = {
277+ recordUid,
278+ revision : 1 ,
279+ }
280+ const recordMetadata : Vault . IRecordMetaData = {
281+ recordUid,
282+ recordKey,
283+ owner : false ,
284+ canEdit : true ,
285+ canShare : true ,
286+ recordKeyType : Records . RecordKeyType . ENCRYPTED_BY_DATA_KEY_GCM ,
287+ ownerUsername : anotherUserA . username ,
288+ ownerAccountUid : anotherUserA . accountUid ,
289+ }
290+ const record : Vault . IRecord = {
291+ recordUid,
292+ version : 3 ,
293+ data : recordData ,
294+ extra : new Uint8Array ( [ ] ) ,
295+ }
296+ syncDownResponseBuilder
297+ . addUserFolderRecord ( userFolderRecord )
298+ . addRecordMetadata ( recordMetadata )
299+ . addRecord ( record )
300+ mockSyncDownCommand . mockResolvedValue ( syncDownResponseBuilder . build ( ) )
301+ await syncDown ( {
302+ auth,
303+ storage,
304+ } )
305+ expect ( storage . put ) . toHaveBeenCalledWith (
306+ expect . objectContaining ( {
307+ kind : 'metadata' ,
308+ uid : recordUidStr ,
309+ owner : recordMetadata . owner ,
310+ ownerUsername : recordMetadata . ownerUsername ,
311+ } )
312+ )
313+ expect ( storage . put ) . toHaveBeenCalledWith (
314+ expect . objectContaining ( {
315+ kind : 'record' ,
316+ uid : recordUidStr ,
317+ data : decryptedRecordData ,
318+ } )
319+ )
320+ expect ( storage . addDependencies ) . toHaveBeenCalledWith ( {
321+ "" : new Set ( [ {
322+ kind : "record" ,
323+ "parentUid" : "" ,
324+ uid : recordUidStr ,
325+ } ] )
326+ } )
327+ } )
328+ it ( 'saves the record data when the direct-shared record is updated by another user or the user' , async ( ) => {
329+ const decryptedRecordKey = platform . getRandomBytes ( 32 )
330+ const recordKey = await platform . aesGcmEncrypt ( decryptedRecordKey , auth . dataKey ! )
331+ const recordUid = platform . getRandomBytes ( 16 )
332+ const recordUidStr = webSafe64FromBytes ( recordUid )
333+ await platform . unwrapKey ( recordKey , recordUidStr , 'data' , 'gcm' , 'aes' )
334+ const decryptedRecordData = {
335+ title : 'test record updated' ,
336+ }
337+ const decodedRecordData = platform . stringToBytes ( JSON . stringify ( decryptedRecordData ) )
338+ const recordData = await platform . aesGcmEncrypt ( decodedRecordData , decryptedRecordKey )
339+ const recordMetadata : Vault . IRecordMetaData = {
340+ recordUid,
341+ recordKey,
342+ owner : false ,
343+ canEdit : true ,
344+ canShare : true ,
345+ recordKeyType : Records . RecordKeyType . ENCRYPTED_BY_DATA_KEY_GCM ,
346+ ownerUsername : anotherUserA . username ,
347+ ownerAccountUid : anotherUserA . accountUid ,
348+ }
349+ const record : Vault . IRecord = {
350+ recordUid,
351+ version : 3 ,
352+ data : recordData ,
353+ extra : new Uint8Array ( [ ] ) ,
354+ }
355+ syncDownResponseBuilder
356+ . addRecord ( record )
357+ . addRecordMetadata ( recordMetadata )
358+ mockSyncDownCommand . mockResolvedValue ( syncDownResponseBuilder . build ( ) )
359+ await syncDown ( {
360+ auth,
361+ storage,
362+ } )
363+ expect ( storage . put ) . toHaveBeenCalledWith (
364+ expect . objectContaining ( {
365+ kind : 'metadata' ,
366+ uid : recordUidStr ,
367+ owner : recordMetadata . owner ,
368+ ownerUsername : recordMetadata . ownerUsername ,
369+ } )
370+ )
371+ expect ( storage . put ) . toHaveBeenCalledWith (
372+ expect . objectContaining ( {
373+ kind : 'record' ,
374+ uid : recordUidStr ,
375+ data : decryptedRecordData ,
376+ } )
377+ )
378+ } )
379+ it ( 'deletes the record data when the direct-shared record is unshared by another user or deleted by the user' , async ( ) => {
380+ const recordUid = platform . getRandomBytes ( 16 )
381+ syncDownResponseBuilder
382+ . addRemovedRecord ( recordUid )
383+ mockSyncDownCommand . mockResolvedValue ( syncDownResponseBuilder . build ( ) )
384+ await syncDown ( {
385+ auth,
386+ storage,
387+ } )
388+ expect ( storage . delete ) . toHaveBeenCalledWith ( 'record' , webSafe64FromBytes ( recordUid ) )
389+ } )
390+ it ( 'does nothing when the direct-shared record is deleted by another user' , ( ) => { } )
391+ } )
257392} )
258393
0 commit comments