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