test: extend the permission resolver table, the admin migration history and the API token refusals

- resolver: several groups, numbers stored as text, empty group sets, a code only at user level, booleans
- migrations: every id once, the history applied twice, the unique code after a down and an up
- personal API tokens: no application, no principal, invalid bodies, another user's token
This commit is contained in:
Jakub Zych
2026-10-05 15:23:23 +02:00
parent a848aa17d6
commit 7991b53979
3 changed files with 205 additions and 0 deletions

View File

@@ -125,6 +125,48 @@ func TestMergedPermissions(t *testing.T) {
merged: map[string]int{"blog.post": 1, "blog.edit": -1},
asks: []ask{{"blog.post", true}, {"blog.bad", false}, {"blog.edit", false}},
},
{
name: "several groups: the highest value of each code wins",
groups: []string{
`{"blog.post":-1,"blog.edit":1,"shop.buy":0}`,
`{"blog.post":0,"blog.edit":-1,"news.read":1}`,
`{"blog.post":1,"shop.buy":-1}`,
`{}`,
},
own: "NULL",
merged: map[string]int{"blog.post": 1, "blog.edit": 1, "shop.buy": 0, "news.read": 1},
asks: []ask{{"blog.post", true}, {"blog.edit", true}, {"shop.buy", false}, {"news.read", true}, {"news.*", true}},
},
{
name: "numbers stored as text count at both levels",
groups: []string{`{"blog.post":"1","blog.edit":"1","shop.buy":"-1"}`},
own: `{"blog.edit":"-1","shop.buy":"1","news.read":"0"}`,
merged: map[string]int{"blog.post": 1, "blog.edit": -1, "shop.buy": 1, "news.read": 0},
asks: []ask{{"blog.post", true}, {"blog.edit", false}, {"shop.buy", true}, {"news.read", false}},
},
{
name: "groups that store nothing leave the user's own permissions",
groups: []string{`{}`, ``, `[]`},
own: `{"blog.post":1}`,
merged: map[string]int{"blog.post": 1},
asks: []ask{{"blog.post", true}, {"blog.edit", false}},
},
{
name: "a code present only at user level is held or denied by that value alone",
groups: []string{`{"blog.post":1}`},
own: `{"only.allow":1,"only.deny":-1,"only.inherit":0}`,
merged: map[string]int{"blog.post": 1, "only.allow": 1, "only.deny": -1, "only.inherit": 0},
asks: []ask{{"only.allow", true}, {"only.deny", false}, {"only.inherit", false}, {"only.*", true}, {"blog.post", true}},
},
{
// 12.1-SECURITY-REVIEW.md, open item 5: the resolver reads
// numbers only. A boolean is not a grant here.
name: "a boolean value is not a grant",
groups: []string{`{"blog.post":true,"blog.edit":false}`},
own: `{"shop.buy":true}`,
merged: map[string]int{},
asks: []ask{{"blog.post", false}, {"shop.buy", false}},
},
}
for i, c := range cases {
t.Run(c.name, func(t *testing.T) {