@@ -352,6 +352,115 @@ func TestSessionsStoreMigration(t *testing.T) {
352352				return  getBoltStoreSessions (t , store )
353353			},
354354		},
355+ 		{
356+ 			name : "multiple sessions with the same ID" ,
357+ 			populateDB : func (t  * testing.T , store  * BoltStore ,
358+ 				_  accounts.Store ) []* Session  {
359+ 
360+ 				// We first add one session which has no other 
361+ 				// session with same ID, to test that this is 
362+ 				// correctly migrated, and included in the 
363+ 				// migration result. 
364+ 				sess1 , err  :=  store .NewSession (
365+ 					ctx , "session1" , TypeMacaroonAdmin ,
366+ 					time .Unix (1000 , 0 ), "" ,
367+ 				)
368+ 				require .NoError (t , err )
369+ 
370+ 				sess2 , err  :=  store .NewSession (
371+ 					ctx , "session2" , TypeMacaroonAdmin ,
372+ 					time .Unix (1000 , 0 ), "" ,
373+ 				)
374+ 				require .NoError (t , err )
375+ 
376+ 				// Then add two sessions with the same ID, to 
377+ 				// test that only the latest session is included 
378+ 				// in the migration result. 
379+ 				sess3 , err  :=  store .NewSession (
380+ 					ctx , "session3" , TypeMacaroonAdmin ,
381+ 					time .Unix (1000 , 0 ), "" ,
382+ 				)
383+ 				require .NoError (t , err )
384+ 
385+ 				// During the addition of the session linking 
386+ 				// functionality, logic was added in the 
387+ 				// NewSession function to ensure we can't create 
388+ 				// multiple sessions with the same ID. Therefore 
389+ 				// we need to manually override the ID of 
390+ 				// the second session to match the first 
391+ 				// session, to simulate such a scenario that 
392+ 				// could occur prior to the addition of that 
393+ 				// logic. 
394+ 				// We also need to update the CreatedAt time 
395+ 				// as the execution of this function is too 
396+ 				// fast for the CreatedAt time of sess2 and 
397+ 				// sess3 to differ. 
398+ 				err  =  updateSessionIDAndCreatedAt (
399+ 					store , sess3 .ID , sess2 .MacaroonRootKey ,
400+ 					sess2 .CreatedAt .Add (time .Minute ),
401+ 				)
402+ 				require .NoError (t , err )
403+ 
404+ 				// Finally, we add three sessions with the same 
405+ 				// ID, to test we can handle more than two 
406+ 				// sessions with the same ID. 
407+ 				sess4 , err  :=  store .NewSession (
408+ 					ctx , "session4" , TypeMacaroonAdmin ,
409+ 					time .Unix (1000 , 0 ), "" ,
410+ 				)
411+ 				require .NoError (t , err )
412+ 
413+ 				sess5 , err  :=  store .NewSession (
414+ 					ctx , "session5" , TypeMacaroonAdmin ,
415+ 					time .Unix (1000 , 0 ), "" ,
416+ 				)
417+ 				require .NoError (t , err )
418+ 
419+ 				sess6 , err  :=  store .NewSession (
420+ 					ctx , "session6" , TypeMacaroonAdmin ,
421+ 					time .Unix (1000 , 0 ), "" ,
422+ 				)
423+ 				require .NoError (t , err )
424+ 
425+ 				err  =  updateSessionIDAndCreatedAt (
426+ 					store , sess5 .ID , sess4 .MacaroonRootKey ,
427+ 					sess2 .CreatedAt .Add (time .Minute ),
428+ 				)
429+ 				require .NoError (t , err )
430+ 
431+ 				err  =  updateSessionIDAndCreatedAt (
432+ 					store , sess6 .ID , sess4 .MacaroonRootKey ,
433+ 					sess2 .CreatedAt .Add (time .Minute * 2 ),
434+ 				)
435+ 				require .NoError (t , err )
436+ 
437+ 				// Now fetch the updated sessions from the kv 
438+ 				// store, so that we are sure that the new IDs 
439+ 				// have really been persisted in the DB. 
440+ 				kvSessions  :=  getBoltStoreSessions (t , store )
441+ 				require .Len (t , kvSessions , 6 )
442+ 
443+ 				getSessionByName  :=  func (name  string ) * Session  {
444+ 					for  _ , session  :=  range  kvSessions  {
445+ 						if  session .Label  ==  name  {
446+ 							return  session 
447+ 						}
448+ 					}
449+ 
450+ 					t .Fatalf ("session %s not found" , name )
451+ 					return  nil 
452+ 				}
453+ 
454+ 				// When multiple sessions with the same ID 
455+ 				// exist, we expect only the session with the 
456+ 				// latest creation time to be migrated. 
457+ 				return  []* Session {
458+ 					getSessionByName (sess1 .Label ),
459+ 					getSessionByName (sess3 .Label ),
460+ 					getSessionByName (sess6 .Label ),
461+ 				}
462+ 			},
463+ 		},
355464		{
356465			name :       "randomized sessions" ,
357466			populateDB : randomizedSessions ,
@@ -803,3 +912,44 @@ func shiftStateUnsafe(db *BoltStore, id ID, dest State) error {
803912		return  putSession (sessionBucket , session )
804913	})
805914}
915+ 
916+ // updateSessionIDAndCreatedAt can be used to update the ID, the GroupID, 
917+ // the MacaroonRootKey and the CreatedAt time a session in the BoltStore. 
918+ // 
919+ // NOTE: this function should only be used for testing purposes. Also note that 
920+ // we pass the macaroon root key to set the new session ID, as the 
921+ // DeserializeSession function derives the session ID from the 
922+ // session.MacaroonRootKey. 
923+ func  updateSessionIDAndCreatedAt (db  * BoltStore , oldID  ID , newIdRootKey  uint64 ,
924+ 	newCreatedAt  time.Time ) error  {
925+ 
926+ 	newId  :=  IDFromMacRootKeyID (newIdRootKey )
927+ 
928+ 	if  oldID  ==  newId  {
929+ 		return  fmt .Errorf ("can't update session ID to the same ID: %s" ,
930+ 			oldID )
931+ 	}
932+ 
933+ 	return  db .Update (func (tx  * bbolt.Tx ) error  {
934+ 		// Get the main session bucket. 
935+ 		sessionBkt , err  :=  getBucket (tx , sessionBucketKey )
936+ 		if  err  !=  nil  {
937+ 			return  err 
938+ 		}
939+ 
940+ 		// Look up the session using the old ID. 
941+ 		sess , err  :=  getSessionByID (sessionBkt , oldID )
942+ 		if  err  !=  nil  {
943+ 			return  err 
944+ 		}
945+ 
946+ 		// Update the session. 
947+ 		sess .ID  =  newId 
948+ 		sess .GroupID  =  newId 
949+ 		sess .MacaroonRootKey  =  newIdRootKey 
950+ 		sess .CreatedAt  =  newCreatedAt 
951+ 
952+ 		// Write it back under the same key (local pubkey). 
953+ 		return  putSession (sessionBkt , sess )
954+ 	})
955+ }
0 commit comments