Skip to content
This repository has been archived by the owner on Jul 12, 2023. It is now read-only.

Commit

Permalink
Add more rotation tests (#1759)
Browse files Browse the repository at this point in the history
  • Loading branch information
sethvargo authored Feb 3, 2021
1 parent 1f5b85b commit 3855ada
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 56 deletions.
68 changes: 64 additions & 4 deletions pkg/controller/rotation/handle_token_rotate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"time"

"github.com/google/exposure-notifications-server/pkg/keys"
"github.com/google/exposure-notifications-verification-server/internal/envstest"
"github.com/google/exposure-notifications-verification-server/internal/project"
"github.com/google/exposure-notifications-verification-server/pkg/config"
"github.com/google/exposure-notifications-verification-server/pkg/database"
Expand All @@ -32,8 +33,6 @@ func TestHandleRotate(t *testing.T) {

ctx := project.TestContext(t)

db, _ := testDatabaseInstance.NewDatabase(t, nil)

keyManager := keys.TestKeyManager(t)
keyManagerSigner, ok := keyManager.(keys.SigningKeyManager)
if !ok {
Expand All @@ -46,18 +45,21 @@ func TestHandleRotate(t *testing.T) {
t.Fatal(err)
}

config := &config.RotationConfig{
cfg := &config.RotationConfig{
TokenSigning: config.TokenSigningConfig{
TokenSigningKeys: []string{tokenSigningKey},
TokenSigningKeyIDs: []string{"v1"},
},
TokenSigningKeyMaxAge: 30 * time.Second,
}
c := New(config, db, keyManagerSigner, h)

t.Run("rotates", func(t *testing.T) {
t.Parallel()

db, _ := testDatabaseInstance.NewDatabase(t, nil)

c := New(cfg, db, keyManagerSigner, h)

// Rotating should create a new key since none exists.
{
r, err := http.NewRequest("GET", "/", nil)
Expand Down Expand Up @@ -135,4 +137,62 @@ func TestHandleRotate(t *testing.T) {
}
}
})

t.Run("too_early", func(t *testing.T) {
t.Parallel()

db, _ := testDatabaseInstance.NewDatabase(t, nil)

cfg := &config.RotationConfig{
TokenSigning: config.TokenSigningConfig{
TokenSigningKeys: []string{tokenSigningKey},
TokenSigningKeyIDs: []string{"v1"},
},
TokenSigningKeyMaxAge: 30 * time.Second,
MinTTL: 5 * time.Minute,
}

c := New(cfg, db, keyManagerSigner, h)

r, err := http.NewRequest("GET", "/", nil)
if err != nil {
t.Fatal(err)
}
r = r.Clone(ctx)

w := httptest.NewRecorder()

c.HandleRotate().ServeHTTP(w, r)
if got, want := w.Code, 200; got != want {
t.Errorf("expected %d to be %d", got, want)
}

// again
c.HandleRotate().ServeHTTP(w, r)
if got, want := w.Code, 200; got != want {
t.Errorf("expected %d to be %d", got, want)
}
})

t.Run("database_error", func(t *testing.T) {
t.Parallel()

db, _ := testDatabaseInstance.NewDatabase(t, nil)
db.SetRawDB(envstest.NewFailingDatabase())

c := New(cfg, db, keyManagerSigner, h)
r, err := http.NewRequest("GET", "/", nil)
if err != nil {
t.Fatal(err)
}
r = r.Clone(ctx)

w := httptest.NewRecorder()

c.HandleRotate().ServeHTTP(w, r)

if got, want := w.Code, 500; got != want {
t.Errorf("expected %d to be %d", got, want)
}
})
}
170 changes: 118 additions & 52 deletions pkg/controller/rotation/handle_verification_rotate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"time"

"github.com/google/exposure-notifications-server/pkg/keys"
"github.com/google/exposure-notifications-verification-server/internal/envstest"
"github.com/google/exposure-notifications-verification-server/internal/project"
"github.com/google/exposure-notifications-verification-server/pkg/config"
"github.com/google/exposure-notifications-verification-server/pkg/database"
Expand All @@ -33,18 +34,6 @@ func TestHandleVerificationRotation(t *testing.T) {

ctx := project.TestContext(t)

db, _ := testDatabaseInstance.NewDatabase(t, nil)

realm := database.NewRealmWithDefaults("state")
realm.AutoRotateCertificateKey = true
realm.UseRealmCertificateKey = true
realm.CertificateIssuer = "iss"
realm.CertificateAudience = "aud"
realm.CertificateDuration = database.FromDuration(time.Second)
if err := db.SaveRealm(realm, database.SystemTest); err != nil {
t.Fatal(err)
}

keyManager := keys.TestKeyManager(t)
keyManagerSigner, ok := keyManager.(keys.SigningKeyManager)
if !ok {
Expand All @@ -56,61 +45,138 @@ func TestHandleVerificationRotation(t *testing.T) {
t.Fatal(err)
}

config := &config.RotationConfig{
VerificationSigningKeyMaxAge: 10 * time.Second,
VerificationActivationDelay: 2 * time.Second,
MinTTL: time.Microsecond,
}
c := New(config, db, keyManagerSigner, h)

// create the initial signing key version, which will make it active.
if _, err := realm.CreateSigningKeyVersion(ctx, db, database.SystemTest); err != nil {
t.Fatal(err)
}
// Initial state - 1 active signing key.
checkKeys(t, db, realm, 1, 0)

// Wait the max age, and run the test.
time.Sleep(config.VerificationSigningKeyMaxAge + time.Second)
invokeRotate(t, ctx, c)
// There should be 2 keys on the realm now, the older one should still be the active one.
checkKeys(t, db, realm, 2, 1)

// Wait long enough for the activation delay.
time.Sleep(config.VerificationActivationDelay + time.Second)
invokeRotate(t, ctx, c)
// There should still be 2 signing keys, but now the first one should be active.
checkKeys(t, db, realm, 2, 0)

// Wait long enough for original key to be deleted.
time.Sleep(config.VerificationActivationDelay + time.Second)
invokeRotate(t, ctx, c)
// Original key should be destroyed, only 1 key and it's active now.
checkKeys(t, db, realm, 1, 0)
t.Run("rotates", func(t *testing.T) {
t.Parallel()

db, _ := testDatabaseInstance.NewDatabase(t, nil)

realm := database.NewRealmWithDefaults("state")
realm.AutoRotateCertificateKey = true
realm.UseRealmCertificateKey = true
realm.CertificateIssuer = "iss"
realm.CertificateAudience = "aud"
realm.CertificateDuration = database.FromDuration(time.Second)
if err := db.SaveRealm(realm, database.SystemTest); err != nil {
t.Fatal(err)
}

cfg := &config.RotationConfig{
VerificationSigningKeyMaxAge: 10 * time.Second,
VerificationActivationDelay: 2 * time.Second,
MinTTL: time.Microsecond,
}
c := New(cfg, db, keyManagerSigner, h)

// create the initial signing key version, which will make it active.
if _, err := realm.CreateSigningKeyVersion(ctx, db, database.SystemTest); err != nil {
t.Fatal(err)
}
// Initial state - 1 active signing key.
checkKeys(t, db, realm, 1, 0)

// Wait the max age, and run the test.
time.Sleep(cfg.VerificationSigningKeyMaxAge + time.Second)
invokeRotate(t, ctx, c)
// There should be 2 keys on the realm now, the older one should still be the active one.
checkKeys(t, db, realm, 2, 1)

// Wait long enough for the activation delay.
time.Sleep(cfg.VerificationActivationDelay + time.Second)
invokeRotate(t, ctx, c)
// There should still be 2 signing keys, but now the first one should be active.
checkKeys(t, db, realm, 2, 0)

// Wait long enough for original key to be deleted.
time.Sleep(cfg.VerificationActivationDelay + time.Second)
invokeRotate(t, ctx, c)
// Original key should be destroyed, only 1 key and it's active now.
checkKeys(t, db, realm, 1, 0)
})

t.Run("too_early", func(t *testing.T) {
t.Parallel()

db, _ := testDatabaseInstance.NewDatabase(t, nil)

cfg := &config.RotationConfig{
VerificationSigningKeyMaxAge: 2 * time.Second,
VerificationActivationDelay: 1 * time.Second,
MinTTL: 5 * time.Minute,
}

c := New(cfg, db, keyManagerSigner, h)

r, err := http.NewRequest("GET", "/", nil)
if err != nil {
t.Fatal(err)
}
r = r.Clone(ctx)

w := httptest.NewRecorder()

c.HandleVerificationRotate().ServeHTTP(w, r)
if got, want := w.Code, 200; got != want {
t.Errorf("expected %d to be %d", got, want)
}

// again
c.HandleVerificationRotate().ServeHTTP(w, r)
if got, want := w.Code, 200; got != want {
t.Errorf("expected %d to be %d", got, want)
}
})

t.Run("database_error", func(t *testing.T) {
t.Parallel()

db, _ := testDatabaseInstance.NewDatabase(t, nil)
db.SetRawDB(envstest.NewFailingDatabase())

cfg := &config.RotationConfig{
VerificationSigningKeyMaxAge: 2 * time.Second,
VerificationActivationDelay: 1 * time.Second,
MinTTL: time.Microsecond,
}
c := New(cfg, db, keyManagerSigner, h)

r, err := http.NewRequest("GET", "/", nil)
if err != nil {
t.Fatal(err)
}
r = r.Clone(ctx)

w := httptest.NewRecorder()

c.HandleVerificationRotate().ServeHTTP(w, r)

if got, want := w.Code, 500; got != want {
t.Errorf("expected %d to be %d", got, want)
}
})
}

func checkKeys(t testing.TB, db *database.Database, realm *database.Realm, count, active int) {
t.Helper()
func checkKeys(tb testing.TB, db *database.Database, realm *database.Realm, count, active int) {
tb.Helper()

keys, err := realm.ListSigningKeys(db)
if err != nil {
t.Fatalf("listing signing keys: %v", err)
tb.Fatalf("listing signing keys: %v", err)
}

if l := len(keys); l != count {
t.Fatalf("expected key count wrong, want: %v got: %v", count, l)
tb.Fatalf("expected key count wrong, want: %v got: %v", count, l)
}
if !keys[active].Active {
t.Fatalf("expected active key (%v) is not active", active)
tb.Fatalf("expected active key (%v) is not active", active)
}
}

func invokeRotate(t testing.TB, ctx context.Context, c *Controller) {
t.Helper()
func invokeRotate(tb testing.TB, ctx context.Context, c *Controller) {
tb.Helper()

r, err := http.NewRequest("GET", "/", nil)
if err != nil {
t.Fatal(err)
tb.Fatal(err)
}
r = r.Clone(ctx)

Expand All @@ -119,6 +185,6 @@ func invokeRotate(t testing.TB, ctx context.Context, c *Controller) {
c.HandleVerificationRotate().ServeHTTP(w, r)

if w.Code != http.StatusOK {
t.Fatalf("invoke didn't return success, status: %v", w.Code)
tb.Fatalf("invoke didn't return success, status: %v", w.Code)
}
}

0 comments on commit 3855ada

Please sign in to comment.