ProofedMesh/services/signing-service/pkg/httpapi/httpapi.go
CTO Agent 5d338dd725
All checks were successful
validate / Rego (opa fmt + opa test) (push) Successful in 13s
validate / JSON Schema (ajv) (push) Successful in 19s
validate / Go (vet + test) (push) Successful in 22s
signing-service: public pkg/httpapi test surface + admin-api interop test (DEV-599)
- services/signing-service/pkg/httpapi exposes NewTestHandler so sibling
  modules can build the real signing-service in-process (same pattern
  transparency-log already uses).
- services/admin-api/internal/attest/interop_signing_test.go drives the real
  attest.HTTPSigner against the real signing-service handler and asserts the
  end-to-end ADR-0005 round-trip: envelope shape, keyid, content_hash
  self-consistency across canonicalisers, Ed25519 signature verification
  with the server's public key, and canonical-payload byte equality.

Closes the "Interop-Test: admin-api attest.HTTPSigner-Client erfolgreich
gegen diesen Server" acceptance criterion from DEV-599.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 18:11:40 +00:00

62 lines
2.5 KiB
Go

// Package httpapi exposes the signing-service's HTTP contract as a public
// package so sibling modules (admin-api interop tests, future verifier
// services) can build a real, in-process signing-service against which to
// run end-to-end checks without duplicating ADR-0005 wire assembly.
//
// Production builds reach the handler through cmd/signing-service/main.go,
// which wires the same internal/httpapi.Server with Pocket-ID JWKS, OPA, and
// OpenBao backends. The signing-service module itself does not expose those
// prod backends through this package; this is the test/interop surface only.
package httpapi
import (
"crypto/ed25519"
"net/http"
"time"
internalhttpapi "basicstack.de/proofedmesh/services/signing-service/internal/httpapi"
"basicstack.de/proofedmesh/services/signing-service/internal/policy"
"basicstack.de/proofedmesh/services/signing-service/internal/signer"
"basicstack.de/proofedmesh/services/signing-service/internal/token"
)
// TestServerConfig configures the in-memory signing-service for interop tests.
// All fields are required; the factory panics on empty maps so a silent typo
// in a sibling module's test does not look like a successful setup.
type TestServerConfig struct {
// Keys maps key_ref → Ed25519 private key. The matching public key must
// be held by the test so it can independently verify the returned
// envelope signatures.
Keys map[string]ed25519.PrivateKey
// Tokens maps bearer token → subject (the Pocket-ID "sub" claim). The
// handler returns 401 for any token not in this map.
Tokens map[string]string
// Now is injected so SignedAt is deterministic in tests. Optional.
Now func() time.Time
}
// NewTestHandler constructs the full signing-service HTTP handler with the
// in-memory signer, AllowAll policy, and static token validator. The handler
// implements the ADR-0005 POST /api/v1/sign/dsse contract and exposes
// GET /healthz. It is safe to wrap in an httptest.Server.
func NewTestHandler(cfg TestServerConfig) http.Handler {
if len(cfg.Keys) == 0 {
panic("signing-service/pkg/httpapi: TestServerConfig.Keys must not be empty")
}
if len(cfg.Tokens) == 0 {
panic("signing-service/pkg/httpapi: TestServerConfig.Tokens must not be empty")
}
s := signer.NewInMemory()
for ref, priv := range cfg.Keys {
s.AddKey(ref, priv)
}
srv := internalhttpapi.Server{
Signer: s,
Policy: policy.AllowAll{},
Validator: token.Static(cfg.Tokens),
Now: cfg.Now,
}
mux := http.NewServeMux()
srv.Mount(mux)
return mux
}