package role import ( "errors" "strings" "time" ) var ( ErrRoleNotFound = errors.New("role not found") ErrUnknownPermission = errors.New("unknown permission") ErrRoleConflict = errors.New("role was modified concurrently") ErrInvalidScope = errors.New("invalid scope for permission") ErrOwnerOnlyGrant = errors.New("owner-only permission granted to a non-owner role") ErrDuplicateGrant = errors.New("permission granted more than once") ErrMissingPermission = errors.New("missing permission") ErrRoleHasMembers = errors.New("role still has members") ErrRoleNameTaken = errors.New("role name already used") ErrRoleLocked = errors.New("role is locked") ErrBuiltInRole = errors.New("role is built in") ErrBuiltInName = errors.New("built-in role cannot be renamed") ErrOwnRole = errors.New("cannot change the role you hold") ErrInvalidRoleName = errors.New("invalid role name") ErrUnknownContentType = errors.New("unknown content type") ErrNotBuiltIn = errors.New("role is not built in") ErrInvalidReassign = errors.New("invalid reassign target") ErrReassignTargetNotFound = errors.New("reassign target not found") ErrEscalation = errors.New("grants more than the caller holds") ErrUnknownRole = errors.New("unknown role") ) type Scope string const ( ScopeNone Scope = "none" ScopeOwn Scope = "own" ScopeAll Scope = "all" ) type Grant struct { Key string `json:"key"` Scope Scope `json:"scope"` } type Role struct { ID uint32 `json:"id"` Name string `json:"name"` Description string `json:"description"` BuiltIn bool `json:"built_in"` Locked bool `json:"locked"` Version uint32 `json:"version"` Grants []Grant `json:"grants"` UpdatedAt time.Time `json:"-"` UpdatedBy *uint32 `json:"-"` } func resolveGrantKey(key string) (Permission, error) { if strings.Contains(key, typeSeparator) { base, _, err := ParseTypedKey(key) if err != nil { return Permission{}, err } return Lookup(base) } return Lookup(key) } func ValidateGrants(locked bool, grants []Grant) error { seen := make(map[string]bool, len(grants)) for _, grant := range grants { permission, err := resolveGrantKey(grant.Key) if err != nil { return err } if permission.OwnerOnly && !locked { return ErrOwnerOnlyGrant } if !scopeValidFor(permission.Kind, grant.Scope, isTypedKey(grant.Key)) { return ErrInvalidScope } if seen[grant.Key] { return ErrDuplicateGrant } seen[grant.Key] = true } return nil } func isTypedKey(key string) bool { return strings.Contains(key, typeSeparator) } func scopeValidFor(kind Kind, scope Scope, typed bool) bool { if typed && scope == ScopeNone { return true } if kind == KindToggle { return scope == ScopeAll } return scope == ScopeOwn || scope == ScopeAll }