fix(09): WR-17 merge an admin's own permissions over the role's, honouring denies

This commit is contained in:
Jakub Zych
2026-10-01 21:33:07 +02:00
parent 629fac4d29
commit 8479defe53
4 changed files with 100 additions and 1 deletions

View File

@@ -61,9 +61,44 @@ func (p BackendUsers) FindByID(ctx context.Context, id uint) (*bouncer.Principal
}
principal.PermissionGrants[code] = true
}
// The administrator's own permissions are applied last, over the role's
// and the code-declared role grants, as Winter's getMergedPermissions does.
principal.PermissionGrants = applyUserPermissions(principal.PermissionGrants, user.Permissions)
return principal, nil
}
// applyUserPermissions overlays an administrator's own backend_users.permissions
// onto the grants that come from the role, the way Winter's
// User::getMergedPermissions does: the user's value for a code replaces the
// role's, and only a value of 1 grants. A user-level -1 (or 0) therefore removes
// a permission the role grants, and a user-level 1 adds one the role does not.
// Winter compares codes exactly while merging, so a deny of one code never
// removes a wildcard grant such as "acme.*"; it removes that exact code.
func applyUserPermissions(grants map[string]bool, raw string) map[string]bool {
raw = strings.TrimSpace(raw)
if raw == "" || raw == "{}" || raw == "null" {
return grants
}
var decoded map[string]any
if err := json.Unmarshal([]byte(raw), &decoded); err != nil {
return grants
}
for code, value := range decoded {
if truthyGrant(value) {
if grants == nil {
grants = map[string]bool{}
}
grants[code] = true
continue
}
delete(grants, code)
}
if len(grants) == 0 {
return nil
}
return grants
}
func principalFrom(user BackendUser) *bouncer.Principal {
principal := &bouncer.Principal{
ID: user.ID,