Files
kms/testutil_test.go
zeekayandGitHub e9a83302c1 feat: cloud Mount() per HIP-0106 (#10)
* docs(llm): explicit Upstream attribution line

LICENSE-attested or repo-attested upstream is now called out at the
top of LLM.md alongside the project description, so downstream
audits (universe/docs/PRODUCTS.md) can rely on a single canonical
location for the OSS lineage statement.

* feat: cloud Mount() per HIP-0106

Adds pkg/kms.Mount(*zip.App, cloud.Deps) error so the unified Hanzo
Cloud binary can blank-import and register the in-process KMS
server alongside every other Hanzo subsystem.

- pkg/kms/mount.go: native /v1/kms/health + /v1/kms/readyz zip
  handlers and zip.AdaptNetHTTP wrap of the existing Embed mux at
  /v1/kms/* + /healthz. init() registers with cloud.Register("kms",
  10, …) so blank import wires the subsystem in.
- cmd/kmsd: rewritten as a HIP-0106 thin shim — cloud.LoadConfig +
  cloud.BuildDeps + zip.New + kms.Mount + app.Listen. The Embed
  surface is unchanged; only assembly moves through zip.
- schema/kms.zap: minimal Health ZAP interface; full secret/key
  surfaces land in follow-up PRs.

Standalone kmsd binary still ships via the existing Dockerfile and
CI workflow.

* refactor: collapse pkg/kms/ → root for clean import path

Subsystem code lives in package kms at repo root. Importers now use
`github.com/hanzoai/kms` (no /pkg/kms/ nesting). Standalone shim moved
to cmd/kms/ where applicable.

Per Hanzo Go-stdlib pattern: repo IS the package.

* refactor: flatten import path — github.com/hanzoai/cloud (drop /pkg/cloud)
2026-05-18 23:37:41 -07:00

92 lines
2.5 KiB
Go

// Shared test utilities. The package-level sharedTestJWT is bound during
// TestMain so every test can produce a signed JWT without re-spinning a
// JWKS server per test.
//
// This replaces the previous mintToken() unsigned-JWT helper. Post-patch
// the KMS authz layer requires real RS256 signatures; an unsigned token
// returns 401 (as Red F3 demands).
package kms
import (
"crypto/rand"
"crypto/rsa"
"encoding/base64"
"encoding/json"
"math/big"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
"github.com/golang-jwt/jwt/v5"
)
// shared fixtures bound in TestMain.
var (
sharedPriv *rsa.PrivateKey
sharedKid = "kms-shared-test-kid"
sharedJWKS *httptest.Server
sharedIssuer = "https://iam.test.hanzo.id"
sharedAudience = "kms"
)
func TestMain(m *testing.M) {
// Generate a single RSA key for the whole test binary.
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic("testutil: keygen: " + err.Error())
}
sharedPriv = priv
nB64 := base64.RawURLEncoding.EncodeToString(priv.N.Bytes())
eB64 := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(priv.E)).Bytes())
jwksJSON, _ := json.Marshal(map[string]any{
"keys": []map[string]string{
{"kty": "RSA", "kid": sharedKid, "use": "sig", "alg": "RS256", "n": nB64, "e": eB64},
},
})
sharedJWKS = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write(jwksJSON)
}))
os.Setenv("IAM_ISSUER", sharedIssuer)
os.Setenv("IAM_AUDIENCE", sharedAudience)
os.Setenv("IAM_KEYS_URL", sharedJWKS.URL)
os.Setenv("KMS_ENV", "dev")
applyAuthConfig(loadAuthConfig())
resetJWKSCacheForTest()
code := m.Run()
sharedJWKS.Close()
os.Exit(code)
}
// mintTestJWTSigned produces an RS256-signed JWT using the shared test
// keypair + kid + JWKS. Defaults iss/aud/exp/iat to the shared test
// fixtures unless the caller overrides them.
func mintTestJWTSigned(t *testing.T, claims jwt.MapClaims) string {
t.Helper()
if _, ok := claims["iss"]; !ok {
claims["iss"] = sharedIssuer
}
if _, ok := claims["aud"]; !ok {
claims["aud"] = sharedAudience
}
if _, ok := claims["exp"]; !ok {
claims["exp"] = time.Now().Add(10 * time.Minute).Unix()
}
if _, ok := claims["iat"]; !ok {
claims["iat"] = time.Now().Unix()
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
token.Header["kid"] = sharedKid
s, err := token.SignedString(sharedPriv)
if err != nil {
t.Fatalf("mintTestJWTSigned: %v", err)
}
return s
}