package httpapi import ( "crypto/ed25519" "encoding/base64" "encoding/json" "io" "net/http" "net/http/httptest" "testing" "evobgp/internal/bundle" ) func TestLegacyCompatibilityNodePullAndAuth(t *testing.T) { srv, err := New(Options{SeedDemo: true, BundleSeedHex: testBundleSeed}) if err != nil { t.Fatal(err) } defer srv.Close() tenant, _, _, revision, speaker := srv.Store().DemoIDs() mustSetTestAPIKeys(t, srv, "legacy-node|"+tenant+"|node,operator|"+tenant+"|operator,other-node|other-tenant|node") if err := srv.Store().PublishRevisionForSpeaker(speaker, revision); err != nil { t.Fatal(err) } handler := srv.Handler() call := func(path, token string) (int, []byte, string) { t.Helper() req, err := http.NewRequest(http.MethodGet, path, nil) if err != nil { t.Fatal(err) } if token != "" { req.Header.Set("Authorization", "Bearer "+token) } w := httptest.NewRecorder() handler.ServeHTTP(w, req) resp := w.Result() defer func() { _ = resp.Body.Close() }() body, err := io.ReadAll(resp.Body) if err != nil { t.Fatal(err) } return resp.StatusCode, body, resp.Header.Get("Content-Type") } latestPath := "/v1/speakers/" + speaker + "/revisions/latest" bundlePath := "/v1/speakers/" + speaker + "/bundle/" + revision for _, path := range []string{latestPath, bundlePath} { for _, tc := range []struct { token string want int }{{"", http.StatusUnauthorized}, {"operator", http.StatusForbidden}, {"other-node", http.StatusForbidden}} { status, _, _ := call(path, tc.token) if status != tc.want { t.Fatalf("%s token %q: status %d, want %d", path, tc.token, status, tc.want) } } } status, raw, _ := call(latestPath, "legacy-node") if status != http.StatusOK { t.Fatalf("legacy latest: status %d: %s", status, raw) } var latest struct { RevisionID string `json:"revision_id"` PublishedAt string `json:"published_at"` } if err := json.Unmarshal(raw, &latest); err != nil { t.Fatal(err) } if latest.RevisionID != revision || latest.PublishedAt == "" { t.Fatalf("legacy latest response: %+v", latest) } status, raw, contentType := call("/v1/bundle/signing-public-key", "operator") if status != http.StatusOK || contentType != "application/json" { t.Fatalf("public key status %d, type %q: %s", status, contentType, raw) } var key struct { PublicKeyBase64 string `json:"public_key_base64"` } if err := json.Unmarshal(raw, &key); err != nil { t.Fatal(err) } pub, err := base64.StdEncoding.DecodeString(key.PublicKeyBase64) if err != nil || len(pub) != ed25519.PublicKeySize { t.Fatalf("legacy public key: len=%d err=%v", len(pub), err) } status, raw, contentType = call(bundlePath, "legacy-node") if status != http.StatusOK || contentType != "application/gzip" { t.Fatalf("legacy bundle: status %d, type %q: %s", status, contentType, raw) } verified, err := bundle.VerifyGzippedTar(raw, ed25519.PublicKey(pub)) if err != nil { t.Fatalf("legacy signed bundle verification: %v", err) } if verified.Manifest.RevisionID != revision || verified.Manifest.SpeakerID != speaker || len(verified.Files) == 0 { t.Fatalf("legacy bundle manifest: %+v", verified.Manifest) } }