@@ -490,3 +490,216 @@ func containsProvisionerDaemon(daemons []database.ProvisionerDaemon, name string
490
490
return d .Name == name
491
491
})
492
492
}
493
+
494
+ //nolint:paralleltest // It uses LockIDDBPurge.
495
+ func TestDeleteExpiredOAuth2ProviderAppCodes (t * testing.T ) {
496
+ ctx ,cancel := context .WithTimeout (context .Background (),testutil .WaitShort )
497
+ defer cancel ()
498
+
499
+ clk := quartz .NewMock (t )
500
+ db ,_ := dbtestutil .NewDB (t ,dbtestutil .WithDumpOnFailure ())
501
+ logger := slogtest .Make (t ,& slogtest.Options {IgnoreErrors :true })
502
+
503
+ now := dbtime .Now ()
504
+ clk .Set (now ).MustWait (ctx )
505
+
506
+ // Create test data
507
+ user := dbgen .User (t ,db , database.User {})
508
+ app := dbgen .OAuth2ProviderApp (t ,db , database.OAuth2ProviderApp {
509
+ Name :fmt .Sprintf ("test-codes-%d" ,time .Now ().UnixNano ()),
510
+ })
511
+
512
+ // Create expired authorization code (should be deleted)
513
+ expiredCode := dbgen .OAuth2ProviderAppCode (t ,db , database.OAuth2ProviderAppCode {
514
+ ExpiresAt :now .Add (- 1 * time .Hour ),// Expired 1 hour ago
515
+ AppID :app .ID ,
516
+ UserID :user .ID ,
517
+ SecretPrefix : []byte (fmt .Sprintf ("expired-%d" ,time .Now ().UnixNano ())),
518
+ })
519
+
520
+ // Create non-expired authorization code (should be retained)
521
+ validCode := dbgen .OAuth2ProviderAppCode (t ,db , database.OAuth2ProviderAppCode {
522
+ ExpiresAt :now .Add (1 * time .Hour ),// Expires in 1 hour
523
+ AppID :app .ID ,
524
+ UserID :user .ID ,
525
+ SecretPrefix : []byte (fmt .Sprintf ("valid-%d" ,time .Now ().UnixNano ())),
526
+ })
527
+
528
+ // Verify codes exist initially
529
+ _ ,err := db .GetOAuth2ProviderAppCodeByID (ctx ,expiredCode .ID )
530
+ require .NoError (t ,err )
531
+ _ ,err = db .GetOAuth2ProviderAppCodeByID (ctx ,validCode .ID )
532
+ require .NoError (t ,err )
533
+
534
+ // Run cleanup
535
+ done := awaitDoTick (ctx ,t ,clk )
536
+ closer := dbpurge .New (ctx ,logger ,db ,clk )
537
+ defer closer .Close ()
538
+ <- done
539
+
540
+ // Verify expired code is deleted
541
+ _ ,err = db .GetOAuth2ProviderAppCodeByID (ctx ,expiredCode .ID )
542
+ require .Error (t ,err )
543
+ require .ErrorIs (t ,err ,sql .ErrNoRows )
544
+
545
+ // Verify non-expired code is retained
546
+ _ ,err = db .GetOAuth2ProviderAppCodeByID (ctx ,validCode .ID )
547
+ require .NoError (t ,err )
548
+ }
549
+
550
+ //nolint:paralleltest // It uses LockIDDBPurge.
551
+ func TestDeleteExpiredOAuth2ProviderAppTokens (t * testing.T ) {
552
+ ctx ,cancel := context .WithTimeout (context .Background (),testutil .WaitShort )
553
+ defer cancel ()
554
+
555
+ clk := quartz .NewMock (t )
556
+ db ,_ := dbtestutil .NewDB (t ,dbtestutil .WithDumpOnFailure ())
557
+ logger := slogtest .Make (t ,& slogtest.Options {IgnoreErrors :true })
558
+
559
+ now := dbtime .Now ()
560
+ clk .Set (now ).MustWait (ctx )
561
+
562
+ // Create test data
563
+ user := dbgen .User (t ,db , database.User {})
564
+ app := dbgen .OAuth2ProviderApp (t ,db , database.OAuth2ProviderApp {
565
+ Name :fmt .Sprintf ("test-tokens-%d" ,time .Now ().UnixNano ()),
566
+ })
567
+ appSecret := dbgen .OAuth2ProviderAppSecret (t ,db , database.OAuth2ProviderAppSecret {
568
+ AppID :app .ID ,
569
+ })
570
+
571
+ // Create API keys for the tokens
572
+ expiredAPIKey ,_ := dbgen .APIKey (t ,db , database.APIKey {
573
+ UserID :user .ID ,
574
+ ExpiresAt :now .Add (- 1 * time .Hour ),
575
+ })
576
+ validAPIKey ,_ := dbgen .APIKey (t ,db , database.APIKey {
577
+ UserID :user .ID ,
578
+ ExpiresAt :now .Add (24 * time .Hour ),// Valid for 24 hours
579
+ })
580
+
581
+ // Create expired access token (should be deleted)
582
+ expiredToken := dbgen .OAuth2ProviderAppToken (t ,db , database.OAuth2ProviderAppToken {
583
+ ExpiresAt :now .Add (- 1 * time .Hour ),// Expired 1 hour ago
584
+ AppSecretID :appSecret .ID ,
585
+ APIKeyID :expiredAPIKey .ID ,
586
+ UserID :user .ID ,
587
+ HashPrefix : []byte (fmt .Sprintf ("expired-%d" ,time .Now ().UnixNano ())),
588
+ })
589
+
590
+ // Create non-expired access token (should be retained)
591
+ validToken := dbgen .OAuth2ProviderAppToken (t ,db , database.OAuth2ProviderAppToken {
592
+ ExpiresAt :now .Add (1 * time .Hour ),// Expires in 1 hour
593
+ AppSecretID :appSecret .ID ,
594
+ APIKeyID :validAPIKey .ID ,
595
+ UserID :user .ID ,
596
+ HashPrefix : []byte (fmt .Sprintf ("valid-%d" ,time .Now ().UnixNano ())),
597
+ })
598
+
599
+ // Verify tokens exist initially
600
+ _ ,err := db .GetOAuth2ProviderAppTokenByPrefix (ctx ,expiredToken .HashPrefix )
601
+ require .NoError (t ,err )
602
+ _ ,err = db .GetOAuth2ProviderAppTokenByPrefix (ctx ,validToken .HashPrefix )
603
+ require .NoError (t ,err )
604
+
605
+ // Run cleanup
606
+ done := awaitDoTick (ctx ,t ,clk )
607
+ closer := dbpurge .New (ctx ,logger ,db ,clk )
608
+ defer closer .Close ()
609
+ <- done
610
+
611
+ // Verify expired token is deleted
612
+ _ ,err = db .GetOAuth2ProviderAppTokenByPrefix (ctx ,expiredToken .HashPrefix )
613
+ require .Error (t ,err )
614
+ require .ErrorIs (t ,err ,sql .ErrNoRows )
615
+
616
+ // Verify non-expired token is retained
617
+ _ ,err = db .GetOAuth2ProviderAppTokenByPrefix (ctx ,validToken .HashPrefix )
618
+ require .NoError (t ,err )
619
+ }
620
+
621
+ //nolint:paralleltest // It uses LockIDDBPurge.
622
+ func TestDeleteExpiredOAuth2ProviderDeviceCodes (t * testing.T ) {
623
+ ctx ,cancel := context .WithTimeout (context .Background (),testutil .WaitShort )
624
+ defer cancel ()
625
+
626
+ clk := quartz .NewMock (t )
627
+ db ,_ := dbtestutil .NewDB (t ,dbtestutil .WithDumpOnFailure ())
628
+ logger := slogtest .Make (t ,& slogtest.Options {IgnoreErrors :true })
629
+
630
+ now := dbtime .Now ()
631
+ clk .Set (now ).MustWait (ctx )
632
+
633
+ // Create test data
634
+ app := dbgen .OAuth2ProviderApp (t ,db , database.OAuth2ProviderApp {
635
+ Name :fmt .Sprintf ("test-device-%d" ,time .Now ().UnixNano ()),
636
+ })
637
+
638
+ nanoTime := time .Now ().UnixNano ()
639
+
640
+ // Create expired device code with pending status (should be deleted)
641
+ expiredPendingCode := dbgen .OAuth2ProviderDeviceCode (t ,db , database.OAuth2ProviderDeviceCode {
642
+ ExpiresAt :now .Add (- 1 * time .Hour ),// Expired 1 hour ago
643
+ ClientID :app .ID ,
644
+ Status :database .OAuth2DeviceStatusPending ,
645
+ DeviceCodePrefix :fmt .Sprintf ("EP%06d" ,nanoTime % 1000000 ),
646
+ UserCode :fmt .Sprintf ("EP%06d" ,nanoTime % 1000000 ),
647
+ DeviceCodeHash :fmt .Appendf (nil ,"hash-exp-pending-%d" ,nanoTime ),
648
+ })
649
+
650
+ // Create non-expired device code with pending status (should be retained)
651
+ validPendingCode := dbgen .OAuth2ProviderDeviceCode (t ,db , database.OAuth2ProviderDeviceCode {
652
+ ExpiresAt :now .Add (1 * time .Hour ),// Expires in 1 hour
653
+ ClientID :app .ID ,
654
+ Status :database .OAuth2DeviceStatusPending ,
655
+ DeviceCodePrefix :fmt .Sprintf ("VP%06d" , (nanoTime + 1 )% 1000000 ),
656
+ UserCode :fmt .Sprintf ("VP%06d" , (nanoTime + 1 )% 1000000 ),
657
+ DeviceCodeHash :fmt .Appendf (nil ,"hash-val-pending-%d" ,nanoTime + 1 ),
658
+ })
659
+
660
+ // Create expired device code with authorized status (should be deleted - all expired codes are deleted)
661
+ expiredAuthorizedCode := dbgen .OAuth2ProviderDeviceCode (t ,db , database.OAuth2ProviderDeviceCode {
662
+ ExpiresAt :now .Add (- 1 * time .Hour ),// Expired 1 hour ago
663
+ ClientID :app .ID ,
664
+ DeviceCodePrefix :fmt .Sprintf ("EA%06d" , (nanoTime + 2 )% 1000000 ),
665
+ UserCode :fmt .Sprintf ("EA%06d" , (nanoTime + 2 )% 1000000 ),
666
+ DeviceCodeHash :fmt .Appendf (nil ,"hash-exp-auth-%d" ,nanoTime + 2 ),
667
+ })
668
+
669
+ // Create a user and authorize the device code
670
+ user := dbgen .User (t ,db , database.User {})
671
+ expiredAuthorizedCode ,err := db .UpdateOAuth2ProviderDeviceCodeAuthorization (ctx , database.UpdateOAuth2ProviderDeviceCodeAuthorizationParams {
672
+ ID :expiredAuthorizedCode .ID ,
673
+ UserID : uuid.NullUUID {UUID :user .ID ,Valid :true },
674
+ Status :database .OAuth2DeviceStatusAuthorized ,
675
+ })
676
+ require .NoError (t ,err )
677
+
678
+ // Verify device codes exist initially
679
+ _ ,err = db .GetOAuth2ProviderDeviceCodeByID (ctx ,expiredPendingCode .ID )
680
+ require .NoError (t ,err )
681
+ _ ,err = db .GetOAuth2ProviderDeviceCodeByID (ctx ,validPendingCode .ID )
682
+ require .NoError (t ,err )
683
+ _ ,err = db .GetOAuth2ProviderDeviceCodeByID (ctx ,expiredAuthorizedCode .ID )
684
+ require .NoError (t ,err )
685
+
686
+ // Run cleanup
687
+ done := awaitDoTick (ctx ,t ,clk )
688
+ closer := dbpurge .New (ctx ,logger ,db ,clk )
689
+ defer closer .Close ()
690
+ <- done
691
+
692
+ // Verify expired pending device code is deleted
693
+ _ ,err = db .GetOAuth2ProviderDeviceCodeByID (ctx ,expiredPendingCode .ID )
694
+ require .Error (t ,err )
695
+ require .ErrorIs (t ,err ,sql .ErrNoRows )
696
+
697
+ // Verify non-expired pending device code is retained
698
+ _ ,err = db .GetOAuth2ProviderDeviceCodeByID (ctx ,validPendingCode .ID )
699
+ require .NoError (t ,err )
700
+
701
+ // Verify expired authorized device code is deleted (all expired codes are deleted)
702
+ _ ,err = db .GetOAuth2ProviderDeviceCodeByID (ctx ,expiredAuthorizedCode .ID )
703
+ require .Error (t ,err )
704
+ require .ErrorIs (t ,err ,sql .ErrNoRows )
705
+ }