fix: rebuild admin claims from trusted headers — v2.0.4
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>
This commit is contained in:
parent
7c7bceb9d7
commit
3967fa955a
@ -265,6 +265,11 @@ func NewCalcomHandler(settings *SettingsManager, limiter *RateLimiter, appURL st
|
||||
// and encrypt the Cal.com API key + webhook secret.
|
||||
func NewCalcomRouter(deps plugin.CoreServices) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
// Context does not cross the wasm ABI: requireAdmin's claims exist only if
|
||||
// the guest rebuilds them from the host-stamped X-Bn-Verified-* headers.
|
||||
// Without this middleware every admin endpoint 401s for real admins (the
|
||||
// settings panel could not save the API key — 2.0.3 regression).
|
||||
r.Use(auth.TrustedHeaderMiddleware)
|
||||
// Settings persistence goes through the SDK settings capabilities: under the
|
||||
// wasm sandbox the plugin's Postgres role cannot touch the CMS `settings`
|
||||
// table (public schema) directly, so the host performs the DB work.
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
name = "calcomblock"
|
||||
display_name = "Cal.com Booking"
|
||||
scope = "@ninja"
|
||||
version = "2.0.3"
|
||||
version = "2.0.4"
|
||||
description = "Embeddable Cal.com booking calendar block with custom styling, timezone-aware slot windowing, honeypot + captcha + rate-limited public booking endpoints, and webhook receiver."
|
||||
kind = "plugin"
|
||||
categories = ["forms"]
|
||||
|
||||
@ -2,10 +2,13 @@ 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
|
||||
@ -33,3 +36,28 @@ func TestRouterMatchesAbsolutePluginPaths(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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())
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user