feat: /v1/role list, get, create, update and delete. Reassign members on delete. Write-time validation (ValidateGrants, duplicates, typed keys against real content types). Name uniqueness (role_name_taken), role_locked, role_has_members, role_conflict on version mismatch. No-escalation: nobody grants a permission they don't hold, and roles.edit_owner_only guards the Owner role. Activity events for role changes. Typed-key exposure in /auth/me. Cleanup of typed rows when a content type is deleted. | Backend only. Will carry the deferred ListGrants decision.
This commit is contained in:
1 parent
a043f7f41a
commit
094df63a7f
51 files changed
+4822
-115
No files matched your search
@@ -0,0 +1,44 @@
|
||||
package role
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func NormalizeName(name string) (string, error) {
|
||||
trimmed := strings.TrimSpace(name)
|
||||
if trimmed == "" {
|
||||
return "", ErrInvalidRoleName
|
||||
}
|
||||
return trimmed, nil
|
||||
}
|
||||
|
||||
func SameName(left string, right string) bool {
|
||||
return strings.EqualFold(strings.TrimSpace(left), strings.TrimSpace(right))
|
||||
}
|
||||
|
||||
func ValidateGrantTypes(grants []Grant, allowedTypeKeys []string) error {
|
||||
for _, grant := range grants {
|
||||
if !isTypedKey(grant.Key) {
|
||||
continue
|
||||
}
|
||||
_, typeKey, err := ParseTypedKey(grant.Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !slices.Contains(allowedTypeKeys, typeKey) {
|
||||
return ErrUnknownContentType
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TypedKeys(typeKey string) []string {
|
||||
keys := []string{}
|
||||
for _, permission := range catalogue {
|
||||
if permission.PerType() {
|
||||
keys = append(keys, permission.Key+typeSeparator+typeKey)
|
||||
}
|
||||
}
|
||||
return keys
|
||||
}
|
||||
Reference in new issue
Block a user