91 lines
3 KiB
Go
91 lines
3 KiB
Go
|
|
// Package store defines the persistence contract for approval_sessions,
|
||
|
|
// approval_previews, and approvals. Phase 1 ships an in-memory impl; the
|
||
|
|
// Postgres schema is in db/trust/migrations/0004_approval_sessions.*.sql
|
||
|
|
// and wired via a skeleton Postgres type (see postgres.go) so the HTTP
|
||
|
|
// layer stays unchanged when the real pgxpool wiring lands.
|
||
|
|
package store
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"basicstack.de/proofedmesh/services/approval-service/internal/signer"
|
||
|
|
)
|
||
|
|
|
||
|
|
// ErrNotFound is returned by lookup methods when the row does not exist
|
||
|
|
// or has expired.
|
||
|
|
var ErrNotFound = errors.New("store: not found")
|
||
|
|
|
||
|
|
// ErrAlreadyConsumed is returned by ConsumePreview when the preview has
|
||
|
|
// already been consumed — the confirm endpoint MUST reject a replay.
|
||
|
|
var ErrAlreadyConsumed = errors.New("store: preview already consumed")
|
||
|
|
|
||
|
|
// Session is a per-browser-login approval session. active_until bounds
|
||
|
|
// how long previews may be created against it.
|
||
|
|
type Session struct {
|
||
|
|
ID string
|
||
|
|
UserSub string
|
||
|
|
ActiveUntil time.Time
|
||
|
|
CreatedAt time.Time
|
||
|
|
}
|
||
|
|
|
||
|
|
// Preview is a server-rendered approval payload that has not yet been
|
||
|
|
// confirmed. ContentCanonical lets the confirm endpoint recompute
|
||
|
|
// H_content deterministically from the stored row.
|
||
|
|
type Preview struct {
|
||
|
|
ID string
|
||
|
|
SessionID string
|
||
|
|
UserSub string
|
||
|
|
ContentHash string
|
||
|
|
ContentCanonical []byte
|
||
|
|
Salt []byte
|
||
|
|
Nonce []byte
|
||
|
|
VerificationDigits string
|
||
|
|
ExpiresAt time.Time
|
||
|
|
CreatedAt time.Time
|
||
|
|
ConsumedAt *time.Time
|
||
|
|
}
|
||
|
|
|
||
|
|
// TLogStatus is the three-valued state of the Transparency-Log submit.
|
||
|
|
type TLogStatus string
|
||
|
|
|
||
|
|
const (
|
||
|
|
TLogPending TLogStatus = "attestation-pending"
|
||
|
|
TLogLogged TLogStatus = "logged"
|
||
|
|
TLogFailed TLogStatus = "failed"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Approval is the confirmed, DSSE-signed approval record. If TLogStatus
|
||
|
|
// is TLogPending/TLogFailed, log_index is nil and the attest-reconciler
|
||
|
|
// (or a future worker) will replay the Transparency-Log submit against
|
||
|
|
// the server's idempotent /api/v1/log/entries contract.
|
||
|
|
type Approval struct {
|
||
|
|
ID string
|
||
|
|
PreviewID string
|
||
|
|
SessionID string
|
||
|
|
UserSub string
|
||
|
|
ContentHash string
|
||
|
|
Envelope signer.Envelope
|
||
|
|
AttestationRef string
|
||
|
|
TLogStatus TLogStatus
|
||
|
|
LogIndex *int64
|
||
|
|
TLogError string
|
||
|
|
ConfirmedAt time.Time
|
||
|
|
LoggedAt *time.Time
|
||
|
|
}
|
||
|
|
|
||
|
|
// Store is the full persistence surface the HTTP service depends on.
|
||
|
|
type Store interface {
|
||
|
|
CreateSession(ctx context.Context, s Session) error
|
||
|
|
GetSession(ctx context.Context, id string) (Session, error)
|
||
|
|
|
||
|
|
SavePreview(ctx context.Context, p Preview) error
|
||
|
|
GetPreview(ctx context.Context, id string) (Preview, error)
|
||
|
|
ConsumePreview(ctx context.Context, id string, now time.Time) (Preview, error)
|
||
|
|
|
||
|
|
SaveApproval(ctx context.Context, a Approval) error
|
||
|
|
MarkApprovalLogged(ctx context.Context, id string, logIndex int64, loggedAt time.Time) error
|
||
|
|
MarkApprovalTLogError(ctx context.Context, id string, errorMsg string) error
|
||
|
|
}
|