package role import ( "errors" "strings" ) 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") ErrMissingPermission = errors.New("missing permission") ) 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"` } func (r Role) scopeOf(key string) Scope { for _, grant := range r.Grants { if grant.Key == key { return grant.Scope } } return ScopeNone } func (r Role) Allows(key string, owned bool) bool { permission, err := Lookup(key) if err != nil { return false } if permission.OwnerOnly && !r.Locked { return false } return scopeAllows(permission.Kind, r.scopeOf(key), owned) } func (r Role) AllowsFor(key string, typeKey string, owned bool) bool { if typeKey == "" { return r.Allows(key, owned) } composite := key + typeSeparator + typeKey base, _, err := ParseTypedKey(composite) if err != nil { return false } permission, _ := Lookup(base) for _, grant := range r.Grants { if grant.Key == composite { return scopeAllows(permission.Kind, grant.Scope, owned) } } return r.Allows(key, owned) } func scopeAllows(kind Kind, scope Scope, owned bool) bool { switch scope { case ScopeAll: return true case ScopeOwn: return kind == KindScoped && owned default: return false } } 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 { 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) { return ErrInvalidScope } } return nil } func scopeValidFor(kind Kind, scope Scope) bool { if kind == KindToggle { return scope == ScopeAll } return scope == ScopeOwn || scope == ScopeAll }