fix(09): WR-01 match wildcard required permissions and treat several codes as any, like Winter

This commit is contained in:
Jakub Zych
2026-10-01 20:58:16 +02:00
parent 1a878b371e
commit b4b8b5df64
4 changed files with 84 additions and 10 deletions

View File

@@ -121,9 +121,12 @@ func (r *Registry) Setting(code string) (*CompiledSetting, bool) {
return setting, ok && setting != nil
}
// Allows reports whether principal satisfies every required permission code.
// A nil principal fails. Superusers pass. An empty requirement list allows
// any authenticated principal. Grants ending in ".*" match by prefix.
// Allows reports whether principal satisfies any of the required permission
// codes, the way Winter's hasAnyAccess does. A nil principal fails. Superusers
// pass. An empty requirement list allows any authenticated principal. Both
// sides may use a wildcard: a grant ending in ".*" covers every code with that
// prefix, and a required code ending in ".*" (or starting with "*") is met by
// any granted code that matches it.
func Allows(principal *bouncer.Principal, required []string) bool {
if principal == nil {
return false
@@ -132,23 +135,43 @@ func Allows(principal *bouncer.Principal, required []string) bool {
return true
}
for _, code := range required {
if !granted(principal.PermissionGrants, code) {
return false
if granted(principal.PermissionGrants, code) {
return true
}
}
return true
return false
}
// granted ports Winter's User::hasPermission for one code. Only enabled
// grants are in the map, so the "(int) $value === 1" test is already applied.
func granted(grants map[string]bool, code string) bool {
switch {
case len(code) > 1 && strings.HasSuffix(code, "*"):
prefix := strings.TrimSuffix(code, "*")
for key, on := range grants {
if on && key != prefix && strings.HasPrefix(key, prefix) {
return true
}
}
return false
case len(code) > 1 && strings.HasPrefix(code, "*"):
suffix := strings.TrimPrefix(code, "*")
for key, on := range grants {
if on && key != suffix && strings.HasSuffix(key, suffix) {
return true
}
}
return false
}
if grants[code] {
return true
}
for key, on := range grants {
if !on || !strings.HasSuffix(key, ".*") {
if !on || len(key) < 2 || !strings.HasSuffix(key, "*") {
continue
}
prefix := strings.TrimSuffix(key, "*")
if strings.HasPrefix(code, prefix) {
if prefix != code && strings.HasPrefix(code, prefix) {
return true
}
}