Context does not cross the wasm ABI, so requireAdmin's auth.GetUserFromContext only sees claims if the guest rebuilds them from the host-stamped X-Bn-Verified-* headers. 2.0.3 never mounted auth.TrustedHeaderMiddleware, so every admin endpoint (settings save, event-types, test, rotate) returned 401 for real admins — the settings panel could not store the Cal.com API key. Mount the middleware on the router and pin the contract with a header-driven test (viewer headers must reach requireAdmin and 403, never 401): the existing admin tests injected claims straight into context, which is exactly the path wasm does not have. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
64 lines
2.6 KiB
Go
64 lines
2.6 KiB
Go
package main
|
||
|
||
import (
|
||
"net/http"
|
||
"net/http/httptest"
|
||
"testing"
|
||
|
||
"git.dev.alexdunmow.com/block/pluginsdk/auth"
|
||
"git.dev.alexdunmow.com/block/pluginsdk/plugin"
|
||
"github.com/go-chi/chi/v5"
|
||
"github.com/google/uuid"
|
||
)
|
||
|
||
// TestRouterMatchesAbsolutePluginPaths pins the wasm routing contract: the
|
||
// host forwards the FULL request path (/api/plugins/calcomblock/...) to the
|
||
// guest with no prefix strip, so every route must be registered absolute.
|
||
// Relative registrations 404 the plugin's entire HTTP surface at runtime —
|
||
// exactly the regression that shipped in 2.0.0–2.0.2.
|
||
func TestRouterMatchesAbsolutePluginPaths(t *testing.T) {
|
||
router, ok := NewCalcomRouter(plugin.CoreServices{}).(chi.Routes)
|
||
if !ok {
|
||
t.Fatal("NewCalcomRouter no longer returns a chi router")
|
||
}
|
||
|
||
for _, tc := range []struct{ method, path string }{
|
||
{http.MethodGet, "/api/plugins/calcomblock/reset"},
|
||
{http.MethodGet, "/api/plugins/calcomblock/slots"},
|
||
{http.MethodGet, "/api/plugins/calcomblock/date-grid"},
|
||
{http.MethodPost, "/api/plugins/calcomblock/book"},
|
||
{http.MethodPost, "/api/plugins/calcomblock/cancel"},
|
||
{http.MethodGet, "/api/plugins/calcomblock/event-types"},
|
||
} {
|
||
rctx := chi.NewRouteContext()
|
||
if !router.Match(rctx, tc.method, tc.path) {
|
||
t.Errorf("%s %s does not match any route — must be registered at the absolute plugin path", tc.method, tc.path)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestRouterRebuildsClaimsFromTrustedHeaders pins the wasm identity contract:
|
||
// context does not cross the ABI, so the ONLY way requireAdmin can see the
|
||
// caller is for the router to rebuild claims from the host-stamped
|
||
// X-Bn-Verified-* headers (auth.TrustedHeaderMiddleware). 2.0.3 shipped
|
||
// without it — every admin endpoint 401'd for real admins. A viewer-role
|
||
// header must reach requireAdmin and be rejected 403 (claims seen), never
|
||
// 401 (claims lost); no handler runs, so empty CoreServices is safe.
|
||
func TestRouterRebuildsClaimsFromTrustedHeaders(t *testing.T) {
|
||
router := NewCalcomRouter(plugin.CoreServices{})
|
||
|
||
req := httptest.NewRequest(http.MethodGet, "/api/plugins/calcomblock/settings", nil)
|
||
req.Header.Set(auth.HeaderVerifiedUserID, uuid.NewString())
|
||
req.Header.Set(auth.HeaderVerifiedEmail, "viewer@example.test")
|
||
req.Header.Set(auth.HeaderVerifiedRole, "viewer")
|
||
rec := httptest.NewRecorder()
|
||
router.ServeHTTP(rec, req)
|
||
|
||
if rec.Code == http.StatusUnauthorized {
|
||
t.Fatalf("viewer trusted headers yielded 401 — TrustedHeaderMiddleware is not mounted, claims never reach requireAdmin")
|
||
}
|
||
if rec.Code != http.StatusForbidden {
|
||
t.Errorf("viewer trusted headers: expected 403 from requireAdmin, got %d (body %q)", rec.Code, rec.Body.String())
|
||
}
|
||
}
|