From e9582729ad66479cf3d2c07aa019a8ae0afd81f7 Mon Sep 17 00:00:00 2001 From: PiMaDaum Date: Sat, 28 Jan 2023 21:04:54 +0100 Subject: [PATCH 01/14] add google/cel-go as new dependency --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index d1da2e7..ac2cb7c 100644 --- a/go.mod +++ b/go.mod @@ -24,6 +24,7 @@ require ( github.com/containerd/console v1.0.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/google/cel-go v0.13.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gookit/color v1.5.2 // indirect diff --git a/go.sum b/go.sum index c950058..94f47d1 100644 --- a/go.sum +++ b/go.sum @@ -125,6 +125,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/cel-go v0.13.0 h1:z+8OBOcmh7IeKyqwT/6IlnMvy621fYUqnTVPEdegGlU= +github.com/google/cel-go v0.13.0/go.mod h1:K2hpQgEjDp18J76a2DKFRlPBPpgRZgi6EbnpDgIhJ8s= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= From a0b7b7daafcaf6c45be9945f79e1796793004077 Mon Sep 17 00:00:00 2001 From: PiMaDaum Date: Sun, 29 Jan 2023 01:18:07 +0100 Subject: [PATCH 02/14] Set deendency of antlr/antlr4/runtime/Go/antlr --- go.mod | 4 ++++ go.sum | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/go.mod b/go.mod index ac2cb7c..fc5efc2 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/ProtonMail/gopenpgp/v2 v2.5.0 // indirect github.com/aead/argon2 v0.0.0-20180111183520-a87724528b07 // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect + github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect github.com/cloudflare/circl v1.3.1 // indirect github.com/containerd/console v1.0.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect @@ -43,11 +44,14 @@ require ( github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect github.com/subosito/gotenv v1.4.1 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/crypto v0.4.0 // indirect golang.org/x/sys v0.3.0 // indirect golang.org/x/text v0.5.0 // indirect + google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 94f47d1..613842b 100644 --- a/go.sum +++ b/go.sum @@ -66,6 +66,8 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmH github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves= +github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -123,6 +125,7 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/cel-go v0.13.0 h1:z+8OBOcmh7IeKyqwT/6IlnMvy621fYUqnTVPEdegGlU= @@ -136,6 +139,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= @@ -241,6 +245,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -552,6 +558,8 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c h1:QgY/XxIAIeccR+Ca/rDdKubLIU9rcJ3xfy1DC/Wd2Oo= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -578,6 +586,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= From 5a464f48da5153c4a2cbcc8cdad7a2f0a6547c53 Mon Sep 17 00:00:00 2001 From: PiMaDaum Date: Sun, 29 Jan 2023 01:18:55 +0100 Subject: [PATCH 03/14] Implement filtering over CEL on list resources --- resource/list.go | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/resource/list.go b/resource/list.go index ad1d9ac..0c03cd8 100644 --- a/resource/list.go +++ b/resource/list.go @@ -8,6 +8,9 @@ import ( "time" "github.com/alessio/shellescape" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" "github.com/passbolt/go-passbolt-cli/util" "github.com/passbolt/go-passbolt/api" "github.com/passbolt/go-passbolt/helper" @@ -25,9 +28,21 @@ var ResourceListCmd = &cobra.Command{ RunE: ResourceList, } +var CelEnvOptions = []cel.EnvOption{ + cel.Variable("ID", cel.StringType), + cel.Variable("FolderParentID", cel.StringType), + cel.Variable("Name", cel.StringType), + cel.Variable("Username", cel.StringType), + cel.Variable("URI", cel.StringType), + cel.Variable("Password", cel.StringType), + cel.Variable("Description", cel.StringType), + cel.Variable("CreatedTimestamp", cel.TimestampType), + cel.Variable("ModifiedTimestamp", cel.TimestampType)} + func init() { ResourceListCmd.Flags().Bool("favorite", false, "Resources that are marked as favorite") ResourceListCmd.Flags().Bool("own", false, "Resources that are owned by me") + ResourceListCmd.Flags().String("filter", "", "Define a CEl expression as filter for resources.") ResourceListCmd.Flags().StringP("group", "g", "", "Resources that are shared with group") ResourceListCmd.Flags().StringArrayP("folder", "f", []string{}, "Resources that are in folder") @@ -63,6 +78,10 @@ func ResourceList(cmd *cobra.Command, args []string) error { if err != nil { return err } + celFilter, err := cmd.Flags().GetString("filter") + if err != nil { + return err + } ctx := util.GetContext() @@ -83,6 +102,11 @@ func ResourceList(cmd *cobra.Command, args []string) error { return fmt.Errorf("Listing Resource: %w", err) } + resources, err = filterResources(&resources, celFilter, ctx, client) + if err != nil { + return err + } + if jsonOutput { outputResources := []ResourceJsonOutput{} for i := range resources { @@ -152,3 +176,66 @@ func ResourceList(cmd *cobra.Command, args []string) error { } return nil } + +func filterResources(resources *[]api.Resource, celCmd string, ctx context.Context, client *api.Client) ([]api.Resource, error) { + if celCmd == "" { + return *resources, nil + } + + env, err := cel.NewEnv(CelEnvOptions...) + if err != nil { + return nil, err + } + + ast, issue := env.Compile(celCmd) + if issue.Err() != nil { + return nil, issue.Err() + } + + program, err := env.Program(ast) + if err != nil { + return nil, err + } + + filteredResources := []api.Resource{} + for _, resource := range *resources { + val, _, err := program.ContextEval(ctx, map[string]any{ + "Id": resource.ID, + "FolderParentID": resource.FolderParentID, + "Name": resource.Name, + "Username": resource.Username, + "URI": resource.URI, + "Password": func() ref.Val { + _, _, _, _, pass, _, err := helper.GetResource(ctx, client, resource.ID) + if err != nil { + fmt.Printf("Get Resource %v", err) + return types.String("") + } + return types.String(pass) + }, + "Description": func() ref.Val { + _, _, _, _, _, descr, err := helper.GetResource(ctx, client, resource.ID) + if err != nil { + fmt.Printf("Get Resource %v", err) + return types.String("") + } + return types.String(descr) + }, + "CreatedTimestamp": resource.Created.Time, + "ModifiedTimestamp": resource.Modified.Time, + }) + + if err != nil { + return nil, err + } + + if val.Value() == true { + filteredResources = append(filteredResources, resource) + } + } + + if len(filteredResources) == 0 { + return nil, fmt.Errorf("No such Resources found with filter %v!", celCmd) + } + return filteredResources, nil +} From 48604aefd010da8731bb2f2d0579e25d8a34107b Mon Sep 17 00:00:00 2001 From: PiMaDaum Date: Sun, 29 Jan 2023 20:14:21 +0100 Subject: [PATCH 04/14] Make slice of cel environment options private --- resource/list.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resource/list.go b/resource/list.go index 0c03cd8..719239c 100644 --- a/resource/list.go +++ b/resource/list.go @@ -28,7 +28,7 @@ var ResourceListCmd = &cobra.Command{ RunE: ResourceList, } -var CelEnvOptions = []cel.EnvOption{ +var celEnvOptions = []cel.EnvOption{ cel.Variable("ID", cel.StringType), cel.Variable("FolderParentID", cel.StringType), cel.Variable("Name", cel.StringType), @@ -182,7 +182,7 @@ func filterResources(resources *[]api.Resource, celCmd string, ctx context.Conte return *resources, nil } - env, err := cel.NewEnv(CelEnvOptions...) + env, err := cel.NewEnv(celEnvOptions...) if err != nil { return nil, err } From 3d395a11ed7a593c2a71bab4a29767894e602ef5 Mon Sep 17 00:00:00 2001 From: PiMaDaum Date: Sun, 29 Jan 2023 21:06:44 +0100 Subject: [PATCH 05/14] Implement an builtin function to parse timestamps for filter expressions --- resource/list.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/resource/list.go b/resource/list.go index 719239c..df246c2 100644 --- a/resource/list.go +++ b/resource/list.go @@ -37,7 +37,23 @@ var celEnvOptions = []cel.EnvOption{ cel.Variable("Password", cel.StringType), cel.Variable("Description", cel.StringType), cel.Variable("CreatedTimestamp", cel.TimestampType), - cel.Variable("ModifiedTimestamp", cel.TimestampType)} + cel.Variable("ModifiedTimestamp", cel.TimestampType), + cel.Function("parseTimestamp", + cel.Overload("parse_timestamp_string", + []*cel.Type{cel.StringType}, + cel.TimestampType, + cel.UnaryBinding(func(timeStampInput ref.Val) ref.Val { + timeStampString := fmt.Sprintf("%s", timeStampInput.Value()) + timeStamp, err := time.Parse(`2006-01-02+15:04:05`, timeStampString) + if err != nil { + fmt.Printf("Error while parsing timestamp: %v\n", err) + } + return types.Timestamp{Time: timeStamp} + }, + ), + ), + ), +} func init() { ResourceListCmd.Flags().Bool("favorite", false, "Resources that are marked as favorite") From 9bdc4a96ca0178c7eb2b5bae8fe9d0f5d0ca92b8 Mon Sep 17 00:00:00 2001 From: PiMaDaum Date: Sun, 29 Jan 2023 21:36:45 +0100 Subject: [PATCH 06/14] create filter flag description --- resource/list.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/resource/list.go b/resource/list.go index df246c2..999dd85 100644 --- a/resource/list.go +++ b/resource/list.go @@ -58,7 +58,11 @@ var celEnvOptions = []cel.EnvOption{ func init() { ResourceListCmd.Flags().Bool("favorite", false, "Resources that are marked as favorite") ResourceListCmd.Flags().Bool("own", false, "Resources that are owned by me") - ResourceListCmd.Flags().String("filter", "", "Define a CEl expression as filter for resources.") + ResourceListCmd.Flags().String("filter", "", + "Define a CEl expression as filter for resources. In the expression, all available columns can be used (see -c/--column).\n"+ + "See also CEl specifications under https://github.com/google/cel-spec."+ + "\nExample:\n\t--filter '(Name == \"SomeName\" || matches(Name, \"RegExpr\")) && URI.startsWith(\"https://auth.\")'\n"+ + "Available builtin functions:\n\tparseTimestamp(string): It parses a string to an timestamp like the layout '2006-01-02+15:04:05' (see golangs time.Parse documentation)") ResourceListCmd.Flags().StringP("group", "g", "", "Resources that are shared with group") ResourceListCmd.Flags().StringArrayP("folder", "f", []string{}, "Resources that are in folder") From e5bb6eceffd3248537b74c8441a17623068b52c3 Mon Sep 17 00:00:00 2001 From: PiMaDaum Date: Wed, 1 Feb 2023 21:11:06 +0100 Subject: [PATCH 07/14] Outsource the resources filter function to seperate file --- resource/filter.go | 105 +++++++++++++++++++++++++++++++++++++++++++++ resource/list.go | 93 --------------------------------------- 2 files changed, 105 insertions(+), 93 deletions(-) create mode 100644 resource/filter.go diff --git a/resource/filter.go b/resource/filter.go new file mode 100644 index 0000000..f694f59 --- /dev/null +++ b/resource/filter.go @@ -0,0 +1,105 @@ +package resource + +import ( + "context" + "fmt" + "time" + + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/passbolt/go-passbolt/api" + "github.com/passbolt/go-passbolt/helper" +) + +// Environments for CEl +var celEnvOptions = []cel.EnvOption{ + cel.Variable("ID", cel.StringType), + cel.Variable("FolderParentID", cel.StringType), + cel.Variable("Name", cel.StringType), + cel.Variable("Username", cel.StringType), + cel.Variable("URI", cel.StringType), + cel.Variable("Password", cel.StringType), + cel.Variable("Description", cel.StringType), + cel.Variable("CreatedTimestamp", cel.TimestampType), + cel.Variable("ModifiedTimestamp", cel.TimestampType), + cel.Function("parseTimestamp", + cel.Overload("parse_timestamp_string", + []*cel.Type{cel.StringType}, + cel.TimestampType, + cel.UnaryBinding(func(timeStampInput ref.Val) ref.Val { + timeStampString := fmt.Sprintf("%s", timeStampInput.Value()) + timeStamp, err := time.Parse(time.R, timeStampString) + if err != nil { + fmt.Printf("Error while parsing timestamp: %v\n", err) + } + return types.Timestamp{Time: timeStamp} + }, + ), + ), + ), +} + +// Filters the slice resources by invoke CEL program for each resource +func filterResources(resources *[]api.Resource, celCmd string, ctx context.Context, client *api.Client) ([]api.Resource, error) { + if celCmd == "" { + return *resources, nil + } + + env, err := cel.NewEnv(celEnvOptions...) + if err != nil { + return nil, err + } + + ast, issue := env.Compile(celCmd) + if issue.Err() != nil { + return nil, issue.Err() + } + + program, err := env.Program(ast) + if err != nil { + return nil, err + } + + filteredResources := []api.Resource{} + for _, resource := range *resources { + val, _, err := program.ContextEval(ctx, map[string]any{ + "Id": resource.ID, + "FolderParentID": resource.FolderParentID, + "Name": resource.Name, + "Username": resource.Username, + "URI": resource.URI, + "Password": func() ref.Val { + _, _, _, _, pass, _, err := helper.GetResource(ctx, client, resource.ID) + if err != nil { + fmt.Printf("Get Resource %v", err) + return types.String("") + } + return types.String(pass) + }, + "Description": func() ref.Val { + _, _, _, _, _, descr, err := helper.GetResource(ctx, client, resource.ID) + if err != nil { + fmt.Printf("Get Resource %v", err) + return types.String("") + } + return types.String(descr) + }, + "CreatedTimestamp": resource.Created.Time, + "ModifiedTimestamp": resource.Modified.Time, + }) + + if err != nil { + return nil, err + } + + if val.Value() == true { + filteredResources = append(filteredResources, resource) + } + } + + if len(filteredResources) == 0 { + return nil, fmt.Errorf("No such Resources found with filter %v!", celCmd) + } + return filteredResources, nil +} diff --git a/resource/list.go b/resource/list.go index 999dd85..26c3387 100644 --- a/resource/list.go +++ b/resource/list.go @@ -8,9 +8,6 @@ import ( "time" "github.com/alessio/shellescape" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" "github.com/passbolt/go-passbolt-cli/util" "github.com/passbolt/go-passbolt/api" "github.com/passbolt/go-passbolt/helper" @@ -28,33 +25,6 @@ var ResourceListCmd = &cobra.Command{ RunE: ResourceList, } -var celEnvOptions = []cel.EnvOption{ - cel.Variable("ID", cel.StringType), - cel.Variable("FolderParentID", cel.StringType), - cel.Variable("Name", cel.StringType), - cel.Variable("Username", cel.StringType), - cel.Variable("URI", cel.StringType), - cel.Variable("Password", cel.StringType), - cel.Variable("Description", cel.StringType), - cel.Variable("CreatedTimestamp", cel.TimestampType), - cel.Variable("ModifiedTimestamp", cel.TimestampType), - cel.Function("parseTimestamp", - cel.Overload("parse_timestamp_string", - []*cel.Type{cel.StringType}, - cel.TimestampType, - cel.UnaryBinding(func(timeStampInput ref.Val) ref.Val { - timeStampString := fmt.Sprintf("%s", timeStampInput.Value()) - timeStamp, err := time.Parse(`2006-01-02+15:04:05`, timeStampString) - if err != nil { - fmt.Printf("Error while parsing timestamp: %v\n", err) - } - return types.Timestamp{Time: timeStamp} - }, - ), - ), - ), -} - func init() { ResourceListCmd.Flags().Bool("favorite", false, "Resources that are marked as favorite") ResourceListCmd.Flags().Bool("own", false, "Resources that are owned by me") @@ -196,66 +166,3 @@ func ResourceList(cmd *cobra.Command, args []string) error { } return nil } - -func filterResources(resources *[]api.Resource, celCmd string, ctx context.Context, client *api.Client) ([]api.Resource, error) { - if celCmd == "" { - return *resources, nil - } - - env, err := cel.NewEnv(celEnvOptions...) - if err != nil { - return nil, err - } - - ast, issue := env.Compile(celCmd) - if issue.Err() != nil { - return nil, issue.Err() - } - - program, err := env.Program(ast) - if err != nil { - return nil, err - } - - filteredResources := []api.Resource{} - for _, resource := range *resources { - val, _, err := program.ContextEval(ctx, map[string]any{ - "Id": resource.ID, - "FolderParentID": resource.FolderParentID, - "Name": resource.Name, - "Username": resource.Username, - "URI": resource.URI, - "Password": func() ref.Val { - _, _, _, _, pass, _, err := helper.GetResource(ctx, client, resource.ID) - if err != nil { - fmt.Printf("Get Resource %v", err) - return types.String("") - } - return types.String(pass) - }, - "Description": func() ref.Val { - _, _, _, _, _, descr, err := helper.GetResource(ctx, client, resource.ID) - if err != nil { - fmt.Printf("Get Resource %v", err) - return types.String("") - } - return types.String(descr) - }, - "CreatedTimestamp": resource.Created.Time, - "ModifiedTimestamp": resource.Modified.Time, - }) - - if err != nil { - return nil, err - } - - if val.Value() == true { - filteredResources = append(filteredResources, resource) - } - } - - if len(filteredResources) == 0 { - return nil, fmt.Errorf("No such Resources found with filter %v!", celCmd) - } - return filteredResources, nil -} From 4e5983dc4327a929e73fc756a954ec5079dd2337 Mon Sep 17 00:00:00 2001 From: PiMaDaum Date: Wed, 1 Feb 2023 21:16:28 +0100 Subject: [PATCH 08/14] Remove the overloaded parseTimestamp function --- resource/filter.go | 16 ---------------- resource/list.go | 4 ++-- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/resource/filter.go b/resource/filter.go index f694f59..22c2550 100644 --- a/resource/filter.go +++ b/resource/filter.go @@ -3,7 +3,6 @@ package resource import ( "context" "fmt" - "time" "github.com/google/cel-go/cel" "github.com/google/cel-go/common/types" @@ -23,21 +22,6 @@ var celEnvOptions = []cel.EnvOption{ cel.Variable("Description", cel.StringType), cel.Variable("CreatedTimestamp", cel.TimestampType), cel.Variable("ModifiedTimestamp", cel.TimestampType), - cel.Function("parseTimestamp", - cel.Overload("parse_timestamp_string", - []*cel.Type{cel.StringType}, - cel.TimestampType, - cel.UnaryBinding(func(timeStampInput ref.Val) ref.Val { - timeStampString := fmt.Sprintf("%s", timeStampInput.Value()) - timeStamp, err := time.Parse(time.R, timeStampString) - if err != nil { - fmt.Printf("Error while parsing timestamp: %v\n", err) - } - return types.Timestamp{Time: timeStamp} - }, - ), - ), - ), } // Filters the slice resources by invoke CEL program for each resource diff --git a/resource/list.go b/resource/list.go index 26c3387..322b80c 100644 --- a/resource/list.go +++ b/resource/list.go @@ -31,8 +31,8 @@ func init() { ResourceListCmd.Flags().String("filter", "", "Define a CEl expression as filter for resources. In the expression, all available columns can be used (see -c/--column).\n"+ "See also CEl specifications under https://github.com/google/cel-spec."+ - "\nExample:\n\t--filter '(Name == \"SomeName\" || matches(Name, \"RegExpr\")) && URI.startsWith(\"https://auth.\")'\n"+ - "Available builtin functions:\n\tparseTimestamp(string): It parses a string to an timestamp like the layout '2006-01-02+15:04:05' (see golangs time.Parse documentation)") + "\nExamples:\n\t--filter '(Name == \"SomeName\" || matches(Name, \"RegExpr\")) && URI.startsWith(\"https://auth.\")'\n"+ + "\t--filter 'Username == \"User\" && CreatedTimestamp > timestamp(\"2022-06-10T00:00:00.000-00:00\")'") ResourceListCmd.Flags().StringP("group", "g", "", "Resources that are shared with group") ResourceListCmd.Flags().StringArrayP("folder", "f", []string{}, "Resources that are in folder") From 5ee20771a823de7e50b251d6ddb859d4642d8a00 Mon Sep 17 00:00:00 2001 From: PiMaDaum Date: Sun, 5 Feb 2023 19:38:58 +0100 Subject: [PATCH 09/14] Define filter argument as persistent flag in list command --- cmd/list.go | 6 ++++++ resource/list.go | 7 ------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cmd/list.go b/cmd/list.go index 90124b2..7e94658 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -19,6 +19,12 @@ var listCmd = &cobra.Command{ func init() { rootCmd.AddCommand(listCmd) listCmd.PersistentFlags().BoolP("json", "j", false, "Output JSON") + listCmd.PersistentFlags().String("filter", "", + "Define a CEl expression as filter for any list commands. In the expression, all available columns of subcommand can be used (see -c/--column).\n"+ + "See also CEl specifications under https://github.com/google/cel-spec.\n"+ + "Examples:\n"+ + "\t--filter '(Name == \"SomeName\" || matches(Name, \"RegExpr\")) && URI.startsWith(\"https://auth.\")'\n"+ + "\t--filter 'Username == \"User\" && CreatedTimestamp > timestamp(\"2022-06-10T00:00:00.000-00:00\")'") listCmd.AddCommand(resource.ResourceListCmd) listCmd.AddCommand(folder.FolderListCmd) listCmd.AddCommand(group.GroupListCmd) diff --git a/resource/list.go b/resource/list.go index 322b80c..a283d56 100644 --- a/resource/list.go +++ b/resource/list.go @@ -28,15 +28,8 @@ var ResourceListCmd = &cobra.Command{ func init() { ResourceListCmd.Flags().Bool("favorite", false, "Resources that are marked as favorite") ResourceListCmd.Flags().Bool("own", false, "Resources that are owned by me") - ResourceListCmd.Flags().String("filter", "", - "Define a CEl expression as filter for resources. In the expression, all available columns can be used (see -c/--column).\n"+ - "See also CEl specifications under https://github.com/google/cel-spec."+ - "\nExamples:\n\t--filter '(Name == \"SomeName\" || matches(Name, \"RegExpr\")) && URI.startsWith(\"https://auth.\")'\n"+ - "\t--filter 'Username == \"User\" && CreatedTimestamp > timestamp(\"2022-06-10T00:00:00.000-00:00\")'") - ResourceListCmd.Flags().StringP("group", "g", "", "Resources that are shared with group") ResourceListCmd.Flags().StringArrayP("folder", "f", []string{}, "Resources that are in folder") - ResourceListCmd.Flags().StringArrayP("column", "c", []string{"ID", "FolderParentID", "Name", "Username", "URI"}, "Columns to return, possible Columns:\nID, FolderParentID, Name, Username, URI, Password, Description, CreatedTimestamp, ModifiedTimestamp") } From 7db321f130e62410529c31a6bce258f7caba4ba4 Mon Sep 17 00:00:00 2001 From: PiMaDaum Date: Sun, 5 Feb 2023 21:27:40 +0100 Subject: [PATCH 10/14] Implement CEL filter in list folder command --- folder/filter.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ folder/list.go | 9 +++++++ 2 files changed, 73 insertions(+) create mode 100644 folder/filter.go diff --git a/folder/filter.go b/folder/filter.go new file mode 100644 index 0000000..8cd3202 --- /dev/null +++ b/folder/filter.go @@ -0,0 +1,64 @@ +package folder + +import ( + "context" + "fmt" + + "github.com/google/cel-go/cel" + "github.com/passbolt/go-passbolt/api" +) + +// Environments for CEl +var celEnvOptions = []cel.EnvOption{ + cel.Variable("ID", cel.StringType), + cel.Variable("FolderParentID", cel.StringType), + cel.Variable("Name", cel.StringType), + cel.Variable("CreatedTimestamp", cel.TimestampType), + cel.Variable("ModifiedTimestamp", cel.TimestampType), +} + +func filterFolders(folders *[]api.Folder, celCmd string, ctx context.Context) ([]api.Folder, error) { + if celCmd == "" { + return *folders, nil + } + + env, err := cel.NewEnv(celEnvOptions...) + if err != nil { + return nil, err + } + + ast, issue := env.Compile(celCmd) + if issue.Err() != nil { + return nil, issue.Err() + } + + program, err := env.Program(ast) + if err != nil { + return nil, err + } + + filteredFolders := []api.Folder{} + for _, folder := range *folders { + val, _, err := program.ContextEval(ctx, map[string]any{ + "ID": folder.ID, + "FolderParentID": folder.FolderParentID, + "Name": folder.Name, + "CreatedTimestamp": folder.Created.Time, + "ModifiedTimestamp": folder.Modified.Time, + }) + + if err != nil { + return nil, err + } + + if val.Value() == true { + filteredFolders = append(filteredFolders, folder) + } + } + + if len(filteredFolders) == 0 { + return nil, fmt.Errorf("No such folders found with filter %v!", celCmd) + } + + return filteredFolders, nil +} diff --git a/folder/list.go b/folder/list.go index be92a52..6bea8f1 100644 --- a/folder/list.go +++ b/folder/list.go @@ -51,6 +51,10 @@ func FolderList(cmd *cobra.Command, args []string) error { if err != nil { return err } + celFilter, err := cmd.Flags().GetString("filter") + if err != nil { + return err + } ctx := util.GetContext() cmd.SilenceUsage = true @@ -69,6 +73,11 @@ func FolderList(cmd *cobra.Command, args []string) error { return fmt.Errorf("Listing Folder: %w", err) } + folders, err = filterFolders(&folders, celFilter, ctx) + if err != nil { + return err + } + if jsonOutput { outputFolders := []FolderJsonOutput{} for i := range folders { From 20e3a72c92244e269b3b0a4ab440643e71ee63e9 Mon Sep 17 00:00:00 2001 From: PiMaDaum Date: Sun, 5 Feb 2023 21:30:48 +0100 Subject: [PATCH 11/14] Add comment to filterFolders function --- folder/filter.go | 1 + 1 file changed, 1 insertion(+) diff --git a/folder/filter.go b/folder/filter.go index 8cd3202..3414da5 100644 --- a/folder/filter.go +++ b/folder/filter.go @@ -17,6 +17,7 @@ var celEnvOptions = []cel.EnvOption{ cel.Variable("ModifiedTimestamp", cel.TimestampType), } +// Filters the slice folders by invoke CEL program for each folder func filterFolders(folders *[]api.Folder, celCmd string, ctx context.Context) ([]api.Folder, error) { if celCmd == "" { return *folders, nil From 6562aeab950ab1df9951026f58ac4009631939e6 Mon Sep 17 00:00:00 2001 From: PiMaDaum Date: Sun, 5 Feb 2023 21:42:32 +0100 Subject: [PATCH 12/14] Implement CEL filter in list group command --- group/filter.go | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ group/list.go | 9 +++++++ 2 files changed, 72 insertions(+) create mode 100644 group/filter.go diff --git a/group/filter.go b/group/filter.go new file mode 100644 index 0000000..0da4d01 --- /dev/null +++ b/group/filter.go @@ -0,0 +1,63 @@ +package group + +import ( + "context" + "fmt" + + "github.com/google/cel-go/cel" + "github.com/passbolt/go-passbolt/api" +) + +// Environments for CEl +var celEnvOptions = []cel.EnvOption{ + cel.Variable("ID", cel.StringType), + cel.Variable("Name", cel.StringType), + cel.Variable("CreatedTimestamp", cel.TimestampType), + cel.Variable("ModifiedTimestamp", cel.TimestampType), +} + +// Filters the slice groups by invoke CEL program for each group +func filterGroups(groups *[]api.Group, celCmd string, ctx context.Context) ([]api.Group, error) { + if celCmd == "" { + return *groups, nil + } + + env, err := cel.NewEnv(celEnvOptions...) + if err != nil { + return nil, err + } + + ast, issue := env.Compile(celCmd) + if issue.Err() != nil { + return nil, issue.Err() + } + + program, err := env.Program(ast) + if err != nil { + return nil, err + } + + filteredGroups := []api.Group{} + for _, group := range *groups { + val, _, err := program.ContextEval(ctx, map[string]any{ + "ID": group.ID, + "Name": group.Name, + "CreatedTimestamp": group.Created.Time, + "ModifiedTimestamp": group.Modified.Time, + }) + + if err != nil { + return nil, err + } + + if val.Value() == true { + filteredGroups = append(filteredGroups, group) + } + } + + if len(filteredGroups) == 0 { + return nil, fmt.Errorf("No such groups found with filter %v!", celCmd) + } + + return filteredGroups, nil +} diff --git a/group/list.go b/group/list.go index 6faa9c5..96afe52 100644 --- a/group/list.go +++ b/group/list.go @@ -51,6 +51,10 @@ func GroupList(cmd *cobra.Command, args []string) error { if err != nil { return err } + celFilter, err := cmd.Flags().GetString("filter") + if err != nil { + return err + } ctx := util.GetContext() @@ -69,6 +73,11 @@ func GroupList(cmd *cobra.Command, args []string) error { return fmt.Errorf("Listing Group: %w", err) } + groups, err = filterGroups(&groups, celFilter, ctx) + if err != nil { + return err + } + if jsonOutput { outputGroups := []GroupJsonOutput{} for i := range groups { From f30590588e38169e770a9495b8ce069f4b27d21e Mon Sep 17 00:00:00 2001 From: PiMaDaum Date: Sun, 5 Feb 2023 21:57:01 +0100 Subject: [PATCH 13/14] Implement CEL filter in list user command --- user/filter.go | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++ user/list.go | 9 +++++++ 2 files changed, 78 insertions(+) create mode 100644 user/filter.go diff --git a/user/filter.go b/user/filter.go new file mode 100644 index 0000000..e355e82 --- /dev/null +++ b/user/filter.go @@ -0,0 +1,69 @@ +package user + +import ( + "context" + "fmt" + + "github.com/google/cel-go/cel" + "github.com/passbolt/go-passbolt/api" +) + +// Environments for CEl +var celEnvOptions = []cel.EnvOption{ + cel.Variable("ID", cel.StringType), + cel.Variable("Username", cel.StringType), + cel.Variable("FirstName", cel.StringType), + cel.Variable("LastName", cel.StringType), + cel.Variable("Role", cel.StringType), + cel.Variable("CreatedTimestamp", cel.TimestampType), + cel.Variable("ModifiedTimestamp", cel.TimestampType), +} + +// Filters the slice users by invoke CEL program for each user +func filterUsers(users *[]api.User, celCmd string, ctx context.Context) ([]api.User, error) { + if celCmd == "" { + return *users, nil + } + + env, err := cel.NewEnv(celEnvOptions...) + if err != nil { + return nil, err + } + + ast, issue := env.Compile(celCmd) + if issue.Err() != nil { + return nil, issue.Err() + } + + program, err := env.Program(ast) + if err != nil { + return nil, err + } + + filteredUsers := []api.User{} + for _, user := range *users { + val, _, err := program.ContextEval(ctx, map[string]any{ + "ID": user.ID, + "Username": user.Username, + "FirstName": user.Profile.FirstName, + "LastName": user.Profile.LastName, + "Role": user.Role.Name, + "CreatedTimestamp": user.Created.Time, + "ModifiedTimestamp": user.Modified.Time, + }) + + if err != nil { + return nil, err + } + + if val.Value() == true { + filteredUsers = append(filteredUsers, user) + } + } + + if len(filteredUsers) == 0 { + return nil, fmt.Errorf("No such users found with filter %v!", celCmd) + } + + return filteredUsers, nil +} diff --git a/user/list.go b/user/list.go index 32ff273..c1f3eb1 100644 --- a/user/list.go +++ b/user/list.go @@ -62,6 +62,10 @@ func UserList(cmd *cobra.Command, args []string) error { if err != nil { return err } + celFilter, err := cmd.Flags().GetString("filter") + if err != nil { + return err + } ctx := util.GetContext() @@ -82,6 +86,11 @@ func UserList(cmd *cobra.Command, args []string) error { return fmt.Errorf("Listing User: %w", err) } + users, err = filterUsers(&users, celFilter, ctx) + if err != nil { + return err + } + if jsonOutput { outputUsers := []UserJsonOutput{} for i := range users { From 0a542bfdba984634a2e160ee30fb94c7615f5773 Mon Sep 17 00:00:00 2001 From: PiMaDaum Date: Sun, 5 Feb 2023 22:19:54 +0100 Subject: [PATCH 14/14] Initialize the CEL program in a util function --- folder/filter.go | 15 +++------------ group/filter.go | 15 +++------------ resource/filter.go | 15 +++------------ user/filter.go | 15 +++------------ util/cel.go | 23 +++++++++++++++++++++++ 5 files changed, 35 insertions(+), 48 deletions(-) create mode 100644 util/cel.go diff --git a/folder/filter.go b/folder/filter.go index 3414da5..afcd433 100644 --- a/folder/filter.go +++ b/folder/filter.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/google/cel-go/cel" + "github.com/passbolt/go-passbolt-cli/util" "github.com/passbolt/go-passbolt/api" ) @@ -23,24 +24,14 @@ func filterFolders(folders *[]api.Folder, celCmd string, ctx context.Context) ([ return *folders, nil } - env, err := cel.NewEnv(celEnvOptions...) - if err != nil { - return nil, err - } - - ast, issue := env.Compile(celCmd) - if issue.Err() != nil { - return nil, issue.Err() - } - - program, err := env.Program(ast) + program, err := util.InitCELProgram(celCmd, celEnvOptions...) if err != nil { return nil, err } filteredFolders := []api.Folder{} for _, folder := range *folders { - val, _, err := program.ContextEval(ctx, map[string]any{ + val, _, err := (*program).ContextEval(ctx, map[string]any{ "ID": folder.ID, "FolderParentID": folder.FolderParentID, "Name": folder.Name, diff --git a/group/filter.go b/group/filter.go index 0da4d01..0b85d67 100644 --- a/group/filter.go +++ b/group/filter.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/google/cel-go/cel" + "github.com/passbolt/go-passbolt-cli/util" "github.com/passbolt/go-passbolt/api" ) @@ -22,24 +23,14 @@ func filterGroups(groups *[]api.Group, celCmd string, ctx context.Context) ([]ap return *groups, nil } - env, err := cel.NewEnv(celEnvOptions...) - if err != nil { - return nil, err - } - - ast, issue := env.Compile(celCmd) - if issue.Err() != nil { - return nil, issue.Err() - } - - program, err := env.Program(ast) + program, err := util.InitCELProgram(celCmd, celEnvOptions...) if err != nil { return nil, err } filteredGroups := []api.Group{} for _, group := range *groups { - val, _, err := program.ContextEval(ctx, map[string]any{ + val, _, err := (*program).ContextEval(ctx, map[string]any{ "ID": group.ID, "Name": group.Name, "CreatedTimestamp": group.Created.Time, diff --git a/resource/filter.go b/resource/filter.go index 22c2550..2cc1f31 100644 --- a/resource/filter.go +++ b/resource/filter.go @@ -7,6 +7,7 @@ import ( "github.com/google/cel-go/cel" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" + "github.com/passbolt/go-passbolt-cli/util" "github.com/passbolt/go-passbolt/api" "github.com/passbolt/go-passbolt/helper" ) @@ -30,24 +31,14 @@ func filterResources(resources *[]api.Resource, celCmd string, ctx context.Conte return *resources, nil } - env, err := cel.NewEnv(celEnvOptions...) - if err != nil { - return nil, err - } - - ast, issue := env.Compile(celCmd) - if issue.Err() != nil { - return nil, issue.Err() - } - - program, err := env.Program(ast) + program, err := util.InitCELProgram(celCmd, celEnvOptions...) if err != nil { return nil, err } filteredResources := []api.Resource{} for _, resource := range *resources { - val, _, err := program.ContextEval(ctx, map[string]any{ + val, _, err := (*program).ContextEval(ctx, map[string]any{ "Id": resource.ID, "FolderParentID": resource.FolderParentID, "Name": resource.Name, diff --git a/user/filter.go b/user/filter.go index e355e82..973fce7 100644 --- a/user/filter.go +++ b/user/filter.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/google/cel-go/cel" + "github.com/passbolt/go-passbolt-cli/util" "github.com/passbolt/go-passbolt/api" ) @@ -25,24 +26,14 @@ func filterUsers(users *[]api.User, celCmd string, ctx context.Context) ([]api.U return *users, nil } - env, err := cel.NewEnv(celEnvOptions...) - if err != nil { - return nil, err - } - - ast, issue := env.Compile(celCmd) - if issue.Err() != nil { - return nil, issue.Err() - } - - program, err := env.Program(ast) + program, err := util.InitCELProgram(celCmd, celEnvOptions...) if err != nil { return nil, err } filteredUsers := []api.User{} for _, user := range *users { - val, _, err := program.ContextEval(ctx, map[string]any{ + val, _, err := (*program).ContextEval(ctx, map[string]any{ "ID": user.ID, "Username": user.Username, "FirstName": user.Profile.FirstName, diff --git a/util/cel.go b/util/cel.go new file mode 100644 index 0000000..1e41f95 --- /dev/null +++ b/util/cel.go @@ -0,0 +1,23 @@ +package util + +import "github.com/google/cel-go/cel" + +// InitCELProgram - Initialize a CEL program with given CEL command and a set of environments +func InitCELProgram(celCmd string, options ...cel.EnvOption) (*cel.Program, error) { + env, err := cel.NewEnv(options...) + if err != nil { + return nil, err + } + + ast, issue := env.Compile(celCmd) + if issue.Err() != nil { + return nil, issue.Err() + } + + program, err := env.Program(ast) + if err != nil { + return nil, err + } + + return &program, nil +}