package cabana import ( "encoding/json" "errors" "net/http" "net/http/httptest" "strings" "testing" "git.golem15.com/golem15/summercms/modules/bouncer" "git.golem15.com/golem15/summercms/modules/pact" ) type orderController struct { perms []string } func (orderController) ID() string { return "acme.demo.widgets" } func (orderController) ModelName() string { return "Widget" } func (orderController) ConfigDir() string { return "controllers/widgets" } func (c orderController) RequiredPermissions() []string { return c.perms } func TestAuthorizationOrder(t *testing.T) { svc := &service{reg: &Registry{byID: map[string]*CompiledController{ "acme.demo.widgets": { Controller: orderController{perms: []string{"acme.demo.access"}}, List: &ListSchema{RecordsPerPage: 20, Columns: []ListColumn{}}, }, }}} t.Run("permission before schema", func(t *testing.T) { called := 0 rec := httptest.NewRecorder() req := controllerRequest(&bouncer.Principal{ID: 4}) svc.protect(rec, req, func(*CompiledController) { called++ }) if rec.Code != http.StatusForbidden { t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String()) } if called != 0 { t.Fatal("schema or database callback ran before permission denial") } assertErrorCode(t, rec.Body.Bytes(), "forbidden") }) t.Run("superuser reaches handler", func(t *testing.T) { called := 0 rec := httptest.NewRecorder() req := controllerRequest(&bouncer.Principal{ID: 1, IsSuperuser: true}) svc.protect(rec, req, func(*CompiledController) { called++ }) if called != 1 { t.Fatalf("superuser callback count=%d, want 1", called) } }) t.Run("unknown controller is not queried", func(t *testing.T) { called := 0 rec := httptest.NewRecorder() req := controllerRequest(&bouncer.Principal{ID: 1, IsSuperuser: true}) req.SetPathValue("controller", "missing") svc.protect(rec, req, func(*CompiledController) { called++ }) if rec.Code != http.StatusNotFound || called != 0 { t.Fatalf("status=%d called=%d", rec.Code, called) } }) } func TestSecretRedaction(t *testing.T) { const secret = "summercms-test-only-admin-hs256-secret" const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIn0.signature" rec := httptest.NewRecorder() writeUnauthenticated(rec, errors.New(secret+" "+token)) body := rec.Body.String() if strings.Contains(body, secret) || strings.Contains(body, token) || strings.Contains(body, "eyJ") { t.Fatalf("unauthorized body leaked credential material: %s", body) } assertErrorCode(t, rec.Body.Bytes(), "unauthenticated") } func controllerRequest(principal *bouncer.Principal) *http.Request { req := httptest.NewRequest(http.MethodGet, adminAPI("/acme/demo/widgets"), nil) req.SetPathValue("vendor", "acme") req.SetPathValue("plugin", "demo") req.SetPathValue("controller", "widgets") if principal != nil { principal.Backend = true req = req.WithContext(bouncer.WithUser(req.Context(), principal)) } return req } func assertErrorCode(t *testing.T, raw []byte, code string) { t.Helper() var body struct { Error struct { Code string `json:"code"` } `json:"error"` } if err := json.Unmarshal(raw, &body); err != nil { t.Fatal(err) } if body.Error.Code != code { t.Fatalf("error code=%q, want %s; body %s", body.Error.Code, code, raw) } } var _ pact.AdminPermissioned = orderController{}