github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/caveats/env.go (about) 1 package caveats 2 3 import ( 4 "fmt" 5 6 "github.com/authzed/cel-go/cel" 7 8 "github.com/authzed/spicedb/pkg/caveats/types" 9 core "github.com/authzed/spicedb/pkg/proto/core/v1" 10 ) 11 12 // Environment defines the evaluation environment for a caveat. 13 type Environment struct { 14 variables map[string]types.VariableType 15 } 16 17 // NewEnvironment creates and returns a new environment for compiling a caveat. 18 func NewEnvironment() *Environment { 19 return &Environment{ 20 variables: map[string]types.VariableType{}, 21 } 22 } 23 24 // EnvForVariables returns a new environment constructed for the given variables. 25 func EnvForVariables(vars map[string]types.VariableType) (*Environment, error) { 26 e := NewEnvironment() 27 for varName, varType := range vars { 28 err := e.AddVariable(varName, varType) 29 if err != nil { 30 return nil, err 31 } 32 } 33 return e, nil 34 } 35 36 // MustEnvForVariables returns a new environment constructed for the given variables 37 // or panics. 38 func MustEnvForVariables(vars map[string]types.VariableType) *Environment { 39 env, err := EnvForVariables(vars) 40 if err != nil { 41 panic(err) 42 } 43 return env 44 } 45 46 // AddVariable adds a variable with the given type to the environment. 47 func (e *Environment) AddVariable(name string, varType types.VariableType) error { 48 if _, ok := e.variables[name]; ok { 49 return fmt.Errorf("variable `%s` already exists", name) 50 } 51 52 e.variables[name] = varType 53 return nil 54 } 55 56 // EncodedParametersTypes returns the map of encoded parameters for the environment. 57 func (e *Environment) EncodedParametersTypes() map[string]*core.CaveatTypeReference { 58 return types.EncodeParameterTypes(e.variables) 59 } 60 61 // asCelEnvironment converts the exported Environment into an internal CEL environment. 62 func (e *Environment) asCelEnvironment() (*cel.Env, error) { 63 opts := make([]cel.EnvOption, 0, len(e.variables)+len(types.CustomTypes)+2) 64 65 // Add the custom types and functions. 66 for _, customTypeOpts := range types.CustomTypes { 67 opts = append(opts, customTypeOpts...) 68 } 69 opts = append(opts, types.CustomMethodsOnTypes...) 70 71 // Set options. 72 // DefaultUTCTimeZone: ensure all timestamps are evaluated at UTC 73 opts = append(opts, cel.DefaultUTCTimeZone(true)) 74 75 // OptionalTypes: enable optional typing syntax, e.g. `sometype?.foo` 76 // See: https://github.com/google/cel-spec/wiki/proposal-246 77 opts = append(opts, cel.OptionalTypes(cel.OptionalTypesVersion(0))) 78 79 // EnableMacroCallTracking: enables tracking of call macros so when we call AstToString we get 80 // back out the expected expressions. 81 // See: https://github.com/authzed/cel-go/issues/474 82 opts = append(opts, cel.EnableMacroCallTracking()) 83 84 // ParserExpressionSizeLimit: disable the size limit for codepoints in expressions. 85 // This has to be disabled due to us padding out the whitespace in expression parsing based on 86 // schema size. We instead do our own expression size check in the Compile method. 87 // TODO(jschorr): Remove this once the whitespace hack is removed. 88 opts = append(opts, cel.ParserExpressionSizeLimit(-1)) 89 90 for name, varType := range e.variables { 91 opts = append(opts, cel.Variable(name, varType.CelType())) 92 } 93 return cel.NewEnv(opts...) 94 }