fix(09): WR-05 refuse relation link and unlink the panel does not declare

This commit is contained in:
Jakub Zych
2026-10-01 21:07:11 +02:00
parent b8084080cb
commit 9bca815b1b
3 changed files with 52 additions and 1 deletions

View File

@@ -2,6 +2,8 @@ package cabana
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"git.golem15.com/golem15/summercms/modules/bouncer"
@@ -105,3 +107,37 @@ func TestNavigationDropsDeniedParentAndRepointsTarget(t *testing.T) {
t.Fatalf("navigation = %#v, want the parent to keep its own controller", nav)
}
}
// TestRelationMutationsFollowToolbarButtons pins WR-05: link and unlink are
// refused unless the relation's view panel declares them.
func TestRelationMutationsFollowToolbarButtons(t *testing.T) {
svc := phase09DeniedService()
cc := svc.reg.byID["acme.demo.widgets"]
super := &bouncer.Principal{ID: 1, Backend: true, IsSuperuser: true}
relation := func(buttons ...string) {
cc.Relations = map[string]*CompiledRelation{"editors": {Schema: &RelationSchema{Name: "editors", View: RelationPanel{ToolbarButtons: buttons}}}}
}
call := func(handler func(*service, http.ResponseWriter, *http.Request)) int {
rec := httptest.NewRecorder()
handler(svc, rec, phase09Request(super))
return rec.Code
}
relation("link")
if code := call((*service).relationUnlink); code != http.StatusForbidden {
t.Fatalf("unlink on a link-only relation = %d, want 403", code)
}
if code := call((*service).relationLink); code == http.StatusForbidden {
t.Fatal("link on a link-only relation was refused")
}
relation()
for name, handler := range map[string]func(*service, http.ResponseWriter, *http.Request){"link": (*service).relationLink, "unlink": (*service).relationUnlink} {
if code := call(handler); code != http.StatusForbidden {
t.Fatalf("%s on a relation with no buttons = %d, want 403", name, code)
}
}
relation("link", "unlink")
if code := call((*service).relationUnlink); code == http.StatusForbidden {
t.Fatal("unlink on a link|unlink relation was refused")
}
}