ProofedMesh/services/attest-reconciler/internal/attest/http_signer.go

76 lines
2.1 KiB
Go
Raw Normal View History

package attest
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"time"
)
// HTTPSigner calls POST /api/v1/sign/dsse on the Signing Service (ADR-0005).
// Wire shape matches admin-api/internal/attest.HTTPSigner so the two services
// are drop-in interchangeable against the same Signing Service deployment.
type HTTPSigner struct {
BaseURL string
Client *http.Client
TokenSource TokenSource
}
type signRequest struct {
KeyRef string `json:"key_ref"`
Statement Statement `json:"statement"`
}
type signResponse struct {
Envelope Envelope `json:"envelope"`
ContentHash string `json:"content_hash"`
SignedAt time.Time `json:"signed_at"`
}
func (s HTTPSigner) SignDSSE(ctx context.Context, keyRef string, stmt Statement) (Envelope, string, error) {
if s.BaseURL == "" {
return Envelope{}, "", errors.New("attest.HTTPSigner: BaseURL is empty")
}
cli := s.Client
if cli == nil {
cli = &http.Client{Timeout: 10 * time.Second}
}
body, err := json.Marshal(signRequest{KeyRef: keyRef, Statement: stmt})
if err != nil {
return Envelope{}, "", fmt.Errorf("marshal: %w", err)
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, s.BaseURL+"/api/v1/sign/dsse", bytes.NewReader(body))
if err != nil {
return Envelope{}, "", err
}
req.Header.Set("Content-Type", "application/json")
if s.TokenSource != nil {
tok, err := s.TokenSource.Token(ctx)
if err != nil {
return Envelope{}, "", fmt.Errorf("token: %w", err)
}
req.Header.Set("Authorization", "Bearer "+tok)
}
resp, err := cli.Do(req)
if err != nil {
return Envelope{}, "", err
}
defer resp.Body.Close()
raw, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
if resp.StatusCode != http.StatusOK {
return Envelope{}, "", fmt.Errorf("signer: status %d: %s", resp.StatusCode, string(raw))
}
var out signResponse
if err := json.Unmarshal(raw, &out); err != nil {
return Envelope{}, "", fmt.Errorf("decode: %w", err)
}
if out.ContentHash == "" {
return Envelope{}, "", errors.New("signer: response missing content_hash")
}
return out.Envelope, out.ContentHash, nil
}