fix(10.1): WR-05 drop widgets the admin may not run from the form schema

formSchema now filters type: widget fields by the action's permissions,
the same D-12 filtering listSchema applies to toolbarActions, so an admin
without the action permission no longer gets a button that always
answers 403, and the action name is not revealed. The filtered fields are
a new slice, so the cached schema is never modified.
This commit is contained in:
Jakub Zych
2026-09-29 09:58:27 +02:00
parent 0bdb6ebcac
commit 7b72bf4be4
3 changed files with 43 additions and 1 deletions

View File

@@ -435,6 +435,33 @@ func TestPhase101Actions(t *testing.T) {
t.Fatalf("limited toolbarActions = %+v err=%v", list.Data.ToolbarActions, err)
}
env.expect(t, http.StatusOK, http.MethodGet, "/acme/demo/gadgets/partials/stats", "", "limited")
// WR-05: the form schema offers no widget whose action the admin
// cannot run, and keeps every other field; an admin who may run it
// still gets the widget.
widgets := func(token string) (widgets, others []string) {
t.Helper()
rec := env.expect(t, http.StatusOK, http.MethodGet, "/acme/demo/gadgets/schema/form", "", token)
var form cabana.Envelope[cabana.FormView]
if err := json.Unmarshal(rec.Body.Bytes(), &form); err != nil {
t.Fatal(err)
}
for _, field := range form.Data.Fields {
if field.Type == "widget" {
widgets = append(widgets, field.Name+":"+field.Action)
} else {
others = append(others, field.Name)
}
}
return widgets, others
}
limitedWidgets, limitedOthers := widgets("limited")
fullWidgets, fullOthers := widgets("bearer")
if len(limitedWidgets) != 0 || strings.Contains(strings.Join(limitedOthers, ","), "lookup") {
t.Fatalf("limited form widgets = %v", limitedWidgets)
}
if !reflect.DeepEqual(fullWidgets, []string{"lookup:lookup"}) || !reflect.DeepEqual(limitedOthers, fullOthers) {
t.Fatalf("full widgets = %v, others limited %v full %v", fullWidgets, limitedOthers, fullOthers)
}
if calls := env.spy.take(); len(calls) != 0 {
t.Fatalf("action ran for a denied admin: %+v", calls)
}