From 0ca77cec0af74b4304c2ccf805ee3dea642cad37 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Wed, 14 Jul 2021 13:31:36 -0500 Subject: [PATCH] postgres: remove KeyStore implementation and tests Signed-off-by: Hank Donnay --- notifier/postgres/e2e_test.go | 2 +- notifier/postgres/keystore.go | 185 ------------------ notifier/postgres/keystore_test.go | 158 --------------- .../postgres/notification_pagination_test.go | 2 +- notifier/postgres/teststore.go | 8 +- 5 files changed, 6 insertions(+), 349 deletions(-) delete mode 100644 notifier/postgres/keystore.go delete mode 100644 notifier/postgres/keystore_test.go diff --git a/notifier/postgres/e2e_test.go b/notifier/postgres/e2e_test.go index 7bcf5ac8d3..b53930d6ee 100644 --- a/notifier/postgres/e2e_test.go +++ b/notifier/postgres/e2e_test.go @@ -37,7 +37,7 @@ func TestE2E(t *testing.T) { }, } ctx := context.Background() - db, store, _, teardown := TestStore(ctx, t) + db, store, teardown := TestStore(ctx, t) defer teardown() e := e2e{ notificaitonID: notificationID, diff --git a/notifier/postgres/keystore.go b/notifier/postgres/keystore.go deleted file mode 100644 index 5b2a663fef..0000000000 --- a/notifier/postgres/keystore.go +++ /dev/null @@ -1,185 +0,0 @@ -package postgres - -import ( - "context" - "crypto/rsa" - "crypto/x509" - "fmt" - "time" - - "github.com/google/uuid" - "github.com/jackc/pgx/v4" - "github.com/jackc/pgx/v4/pgxpool" - "github.com/quay/zlog" - "go.opentelemetry.io/otel/baggage" - "go.opentelemetry.io/otel/label" - - clairerror "github.com/quay/clair/v4/clair-error" - "github.com/quay/clair/v4/notifier" -) - -var _ notifier.KeyStore = (*KeyStore)(nil) - -// KeyStore implements the notifier.KeyStore interface. -// Stored public keys are RSA encoded in PKIX ASN.1 DER form. -type KeyStore struct { - pool *pgxpool.Pool -} - -func NewKeyStore(pool *pgxpool.Pool) *KeyStore { - return &KeyStore{ - pool: pool, - } -} - -func (k *KeyStore) Keys(ctx context.Context) ([]notifier.Key, error) { - ctx = baggage.ContextWithValues(ctx, - label.String("component", "notifier/postgres/KeyStore.Keys"), - ) - const ( - query = `SELECT id, expiration, pub_key FROM key WHERE expiration > CURRENT_TIMESTAMP;` - ) - - rows, err := k.pool.Query(ctx, query) - defer rows.Close() - if err != nil { - return nil, err - } - - type tmp struct { - id uuid.UUID - exp time.Time - der []byte - } - tmps := []tmp{} - - for rows.Next() { - var t tmp - err = rows.Scan(&t.id, &t.exp, &t.der) - if err != nil { - return nil, err - } - tmps = append(tmps, t) - } - rows.Close() - zlog.Debug(ctx). - Int("len", len(tmps)). - Msg("discovered keys") - - // process tmp keys, rows are closed so time taken here - // won't starve conn pool. - keys := make([]notifier.Key, 0, len(tmps)) - for _, t := range tmps { - pub, err := derToRSAPublic(t.der) - if err != nil { - return nil, err - } - keys = append(keys, notifier.Key{ - ID: t.id, - Expiration: t.exp, - Public: pub, - }) - } - - return keys, nil -} - -func (k *KeyStore) KeyByID(ctx context.Context, ID uuid.UUID) (notifier.Key, error) { - const ( - query = `SELECT id, expiration, pub_key FROM key WHERE id = $1 AND expiration > CURRENT_TIMESTAMP;` - ) - - var key notifier.Key - der := []byte{} - - row := k.pool.QueryRow(ctx, query, ID) - err := row.Scan(&key.ID, &key.Expiration, &der) - switch err { - case pgx.ErrNoRows: - return notifier.Key{}, clairerror.ErrKeyNotFound{ID} - case nil: - // hop out - default: - // return unhandled error - return notifier.Key{}, err - } - - pub, err := derToRSAPublic(der) - if err != nil { - return notifier.Key{}, err - } - key.Public = pub - return key, nil -} - -func (k *KeyStore) PutKey(ctx context.Context, ID uuid.UUID, key *rsa.PublicKey, n time.Duration) error { - const ( - query = `INSERT INTO key (id, pub_key, expiration) VALUES ($1, $2, CURRENT_TIMESTAMP + $3)` - ) - der, err := x509.MarshalPKIXPublicKey(key) - if err != nil { - return fmt.Errorf("could not marshal provided public key to PKIX, ASN.1 DER form") - } - tag, err := k.pool.Exec(ctx, query, ID, der, n.String()) - if err != nil { - return err - } - if tag.RowsAffected() <= 0 { - return fmt.Errorf("insertion did not affect any rows") - } - return nil -} - -func (k *KeyStore) DeleteKey(ctx context.Context, ID uuid.UUID) error { - const ( - query = "DELETE FROM key WHERE id = $1" - ) - tag, err := k.pool.Exec(ctx, query, ID) - if err != nil { - return err - } - if tag.RowsAffected() <= 0 { - return clairerror.ErrKeyNotFound{ID} - } - return nil -} - -func (k *KeyStore) BumpExpiration(ctx context.Context, ID uuid.UUID, n time.Duration) error { - const ( - query = `UPDATE key SET expiration = CURRENT_TIMESTAMP + $1 WHERE id = $2;` - ) - tag, err := k.pool.Exec(ctx, query, n.String(), ID) - if err != nil { - return err - } - if tag.RowsAffected() <= 0 { - return clairerror.ErrKeyNotFound{ID} - } - return nil -} - -func (k *KeyStore) GC(ctx context.Context) (int64, error) { - const ( - query = `DELETE FROM key WHERE id = ANY(array(SELECT id FROM key WHERE CURRENT_TIMESTAMP > expiration ORDER BY expiration LIMIT 100));` - ) - tag, err := k.pool.Exec(ctx, query) - if err != nil { - return 0, fmt.Errorf("received error when deleting expired keys: %v", err) - } - return tag.RowsAffected(), nil -} - -// derToRSAPublic is a helper method converting a PKIX, ASN.1 DER form -// byte slice to a *rsa.PublicKey data structure. -func derToRSAPublic(der []byte) (*rsa.PublicKey, error) { - pub, err := x509.ParsePKIXPublicKey(der) - if err != nil { - return nil, err - } - var rsaPub *rsa.PublicKey - var ok bool - if rsaPub, ok = pub.(*rsa.PublicKey); !ok { - return nil, fmt.Errorf("could not type assert parsed PKIX public key to rsa.PublicKey") - } - return rsaPub, nil -} diff --git a/notifier/postgres/keystore_test.go b/notifier/postgres/keystore_test.go deleted file mode 100644 index e37af57fdd..0000000000 --- a/notifier/postgres/keystore_test.go +++ /dev/null @@ -1,158 +0,0 @@ -package postgres_test - -import ( - "context" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "errors" - "testing" - "time" - - "github.com/google/uuid" - "github.com/quay/claircore/test/integration" - "github.com/quay/zlog" - - clairerror "github.com/quay/clair/v4/clair-error" - "github.com/quay/clair/v4/notifier/keymanager" - "github.com/quay/clair/v4/notifier/postgres" -) - -func genKeyPair(t *testing.T, n int) (kps []keymanager.KeyPair) { - reader := rand.Reader - bitSize := 512 // low bitsize for test speed - for i := 0; i < n; i++ { - key, err := rsa.GenerateKey(reader, bitSize) - if err != nil { - t.Fatalf("failed to generate test key pair: %v", err) - } - - pub, err := x509.MarshalPKIXPublicKey(&key.PublicKey) - if err != nil { - t.Fatalf("failed to marshal public key to PKIX") - } - id := uuid.New() - - kps = append(kps, keymanager.KeyPair{ - ID: id, - Private: key, - Public: &key.PublicKey, - Der: pub, - }) - } - return kps -} - -// TestKeyStore is a parellel test harness. -func TestKeyStore(t *testing.T) { - t.Run("GC", testKeyStoreGC) - t.Run("KeyStore", testKeyStore) -} - -func testKeyStoreGC(t *testing.T) { - integration.Skip(t) - t.Parallel() - ctx := zlog.Test(context.Background(), t) - db, _, keystore, teardown := postgres.TestStore(ctx, t) - defer teardown() - - // put some expired keys - kps := genKeyPair(t, 200) - for _, kp := range kps { - err := keystore.PutKey(ctx, kp.ID, kp.Public, -5*time.Minute) - if err != nil { - t.Fatalf("failed to store key pair %+v: %v", kp, err) - } - } - - // comfirm we don't receive them with an all query - keys, err := keystore.Keys(ctx) - if err != nil { - t.Fatalf("failed to retrieve all keys: %v", err) - } - if len(keys) != 0 { - t.Errorf("got: %v want: %v", len(keys), 0) - } - - // run gc - var n int64 = -1 - var cycles int64 - for n != 0 { - n, err = keystore.GC(ctx) - if err != nil { - t.Fatalf("gc call failed: %v", err) - } - cycles++ - } - if cycles != 3 { - t.Errorf("got: %v want: %v", cycles, 2) - } - - // confirm no keys in db - var count int64 - query := "SELECT COUNT(*) FROM key;" - row := db.QueryRow(query) - err = row.Scan(&count) - if err != nil { - t.Fatalf("failed to receive count from db: %v", err) - } - if count != 0 { - t.Errorf("got: %v want: %v", count, 0) - } - -} - -func testKeyStore(t *testing.T) { - integration.Skip(t) - t.Parallel() - ctx := zlog.Test(context.Background(), t) - _, _, keystore, teardown := postgres.TestStore(ctx, t) - defer teardown() - - kps := genKeyPair(t, 10) - - // put em... - for _, kp := range kps { - err := keystore.PutKey(ctx, kp.ID, kp.Public, 5*time.Minute) - if err != nil { - t.Fatalf("failed to store key pair %+v: %v", kp, err) - } - } - // get em all... - keys, err := keystore.Keys(ctx) - if err != nil { - t.Errorf("failed to retrieve all keys: %v", err) - } - if len(keys) != len(kps) { - t.Errorf("got: %d, want: %d", len(keys), len(kps)) - } - - // get em by ids... - for _, kp := range kps { - key, err := keystore.KeyByID(ctx, kp.ID) - if err != nil { - t.Fatalf("failed to retrieve key by id %s: %v", kp.ID, err) - } - if key.ID != kp.ID { - t.Errorf("got: %s, want: %s", key.ID, kp.ID) - } - if key.Public.N.Cmp(kp.Public.N) != 0 { - t.Errorf("got: %X want: %X", key.Public.N, kp.Public.N) - } - if key.Public.E != key.Public.E { - t.Errorf("got: %v want: %v", key.Public.E, kp.Public.E) - } - } - // delete em all - for _, kp := range kps { - err := keystore.DeleteKey(ctx, kp.ID) - if err != nil { - t.Fatalf("failed to delete key pair %v: %v", kp.ID, err) - } - - _, err = keystore.KeyByID(ctx, kp.ID) - if !errors.As(err, &clairerror.ErrKeyNotFound{}) { - t.Errorf("got: %v, wanted: %v", err, clairerror.ErrKeyNotFound{kp.ID}) - } - } -} diff --git a/notifier/postgres/notification_pagination_test.go b/notifier/postgres/notification_pagination_test.go index 0323a263e6..27d982d893 100644 --- a/notifier/postgres/notification_pagination_test.go +++ b/notifier/postgres/notification_pagination_test.go @@ -73,7 +73,7 @@ func TestNotePagination(t *testing.T) { t.Parallel() ctx := context.Background() ctx = zlog.Test(ctx, t) - _, store, _, _ := TestStore(ctx, t) + _, store, _ := TestStore(ctx, t) noteID := uuid.New() updateID := uuid.New() diff --git a/notifier/postgres/teststore.go b/notifier/postgres/teststore.go index 21482f0213..7782a3e0b6 100644 --- a/notifier/postgres/teststore.go +++ b/notifier/postgres/teststore.go @@ -11,9 +11,10 @@ import ( "github.com/jackc/pgx/v4/pgxpool" _ "github.com/jackc/pgx/v4/stdlib" // Needed for sqlx.Open "github.com/jmoiron/sqlx" - "github.com/quay/clair/v4/notifier/migrations" "github.com/quay/claircore/test/integration" "github.com/remind101/migrate" + + "github.com/quay/clair/v4/notifier/migrations" ) const ( @@ -21,7 +22,7 @@ const ( DefaultDSN = `host=localhost port=5432 user=clair dbname=clair sslmode=disable` ) -func TestStore(ctx context.Context, t testing.TB) (*sqlx.DB, *Store, *KeyStore, func()) { +func TestStore(ctx context.Context, t testing.TB) (*sqlx.DB, *Store, func()) { if os.Getenv(integration.EnvPGConnString) == "" { os.Setenv(integration.EnvPGConnString, DefaultDSN) } @@ -57,8 +58,7 @@ func TestStore(ctx context.Context, t testing.TB) (*sqlx.DB, *Store, *KeyStore, } s := NewStore(pool) - ks := NewKeyStore(pool) - return sx, s, ks, func() { + return sx, s, func() { sx.Close() pool.Close() db.Close(ctx, t)