mirror of
https://github.com/warmbly/warmbly.git
synced 2026-08-21 16:01:32 +00:00
99226338c9
New encryptedkeys.Store interface with three impls:
postgres - backend default, durable via PG
dynamodb - existing AWS path, also covers Scylla Alternator via
AWS_ENDPOINT_URL_DYNAMODB
http - worker-side adapter that talks to the backend's new
/api/v1/internal/dek/:userID endpoint, so workers never
connect directly to Postgres
The HTTP endpoint sits behind a new InternalAuthMiddleware that does
constant-time bearer-token compare against INTERNAL_API_TOKEN. Fail-
closed if the env var is unset.
cipher.Service now takes an encryptedkeys.Store instead of a Dynamo
repository. The old internal/repository/dynamo_user_encrypted_keys.go
is deleted (the file also had a pre-existing copy-paste bug using
EmailMessageMapTable in Get/Del that's gone with it).
New migration 38 adds user_encrypted_keys (user_id PK, encrypted_data_key,
created_at, updated_at).
20 tests cover HTTP round-trip, conflict semantics, factory selection,
middleware auth (fail-closed / wrong-scheme / timing-safe / happy path),
and DEK handler responses through gin's test harness.
64 lines
1.7 KiB
Go
64 lines
1.7 KiB
Go
package encryptedkeys
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgconn"
|
|
"github.com/warmbly/warmbly/internal/infrastructure/db"
|
|
)
|
|
|
|
// PostgresStore stores encrypted DEKs in the user_encrypted_keys table.
|
|
// Only the backend uses this impl directly; workers reach DEKs via the
|
|
// HTTP store (which proxies through the backend).
|
|
type PostgresStore struct {
|
|
db *db.DB
|
|
}
|
|
|
|
func NewPostgres(d *db.DB) *PostgresStore {
|
|
return &PostgresStore{db: d}
|
|
}
|
|
|
|
func (s *PostgresStore) Name() string { return "postgres" }
|
|
|
|
func (s *PostgresStore) Put(ctx context.Context, userID uuid.UUID, encryptedDEKB64 string) error {
|
|
const q = `
|
|
INSERT INTO user_encrypted_keys (user_id, encrypted_data_key)
|
|
VALUES ($1, $2)
|
|
`
|
|
_, err := s.db.Exec(ctx, q, userID, encryptedDEKB64)
|
|
if err != nil {
|
|
var pgErr *pgconn.PgError
|
|
if errors.As(err, &pgErr) && pgErr.Code == "23505" { // unique_violation
|
|
return ErrAlreadyExists
|
|
}
|
|
return fmt.Errorf("encryptedkeys.postgres: put: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *PostgresStore) Get(ctx context.Context, userID uuid.UUID) (string, error) {
|
|
const q = `SELECT encrypted_data_key FROM user_encrypted_keys WHERE user_id = $1`
|
|
var out string
|
|
err := s.db.QueryRow(ctx, q, userID).Scan(&out)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return "", nil
|
|
}
|
|
return "", fmt.Errorf("encryptedkeys.postgres: get: %w", err)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *PostgresStore) Delete(ctx context.Context, userID uuid.UUID) error {
|
|
const q = `DELETE FROM user_encrypted_keys WHERE user_id = $1`
|
|
_, err := s.db.Exec(ctx, q, userID)
|
|
if err != nil {
|
|
return fmt.Errorf("encryptedkeys.postgres: delete: %w", err)
|
|
}
|
|
return nil
|
|
}
|