Skip to content

Commit 7fe89a3

Browse files
authored
Merge pull request #26 from PiMaDaum/feature/CEL-filter-implementation
feature/CEL filter implementation
2 parents 0aa3926 + 0a542bf commit 7fe89a3

File tree

12 files changed

+333
-2
lines changed

12 files changed

+333
-2
lines changed

cmd/list.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ var listCmd = &cobra.Command{
1919
func init() {
2020
rootCmd.AddCommand(listCmd)
2121
listCmd.PersistentFlags().BoolP("json", "j", false, "Output JSON")
22+
listCmd.PersistentFlags().String("filter", "",
23+
"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"+
24+
"See also CEl specifications under https://github.com/google/cel-spec.\n"+
25+
"Examples:\n"+
26+
"\t--filter '(Name == \"SomeName\" || matches(Name, \"RegExpr\")) && URI.startsWith(\"https://auth.\")'\n"+
27+
"\t--filter 'Username == \"User\" && CreatedTimestamp > timestamp(\"2022-06-10T00:00:00.000-00:00\")'")
2228
listCmd.AddCommand(resource.ResourceListCmd)
2329
listCmd.AddCommand(folder.FolderListCmd)
2430
listCmd.AddCommand(group.GroupListCmd)

folder/filter.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package folder
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/google/cel-go/cel"
8+
"github.com/passbolt/go-passbolt-cli/util"
9+
"github.com/passbolt/go-passbolt/api"
10+
)
11+
12+
// Environments for CEl
13+
var celEnvOptions = []cel.EnvOption{
14+
cel.Variable("ID", cel.StringType),
15+
cel.Variable("FolderParentID", cel.StringType),
16+
cel.Variable("Name", cel.StringType),
17+
cel.Variable("CreatedTimestamp", cel.TimestampType),
18+
cel.Variable("ModifiedTimestamp", cel.TimestampType),
19+
}
20+
21+
// Filters the slice folders by invoke CEL program for each folder
22+
func filterFolders(folders *[]api.Folder, celCmd string, ctx context.Context) ([]api.Folder, error) {
23+
if celCmd == "" {
24+
return *folders, nil
25+
}
26+
27+
program, err := util.InitCELProgram(celCmd, celEnvOptions...)
28+
if err != nil {
29+
return nil, err
30+
}
31+
32+
filteredFolders := []api.Folder{}
33+
for _, folder := range *folders {
34+
val, _, err := (*program).ContextEval(ctx, map[string]any{
35+
"ID": folder.ID,
36+
"FolderParentID": folder.FolderParentID,
37+
"Name": folder.Name,
38+
"CreatedTimestamp": folder.Created.Time,
39+
"ModifiedTimestamp": folder.Modified.Time,
40+
})
41+
42+
if err != nil {
43+
return nil, err
44+
}
45+
46+
if val.Value() == true {
47+
filteredFolders = append(filteredFolders, folder)
48+
}
49+
}
50+
51+
if len(filteredFolders) == 0 {
52+
return nil, fmt.Errorf("No such folders found with filter %v!", celCmd)
53+
}
54+
55+
return filteredFolders, nil
56+
}

folder/list.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ func FolderList(cmd *cobra.Command, args []string) error {
5151
if err != nil {
5252
return err
5353
}
54+
celFilter, err := cmd.Flags().GetString("filter")
55+
if err != nil {
56+
return err
57+
}
5458

5559
ctx := util.GetContext()
5660
cmd.SilenceUsage = true
@@ -69,6 +73,11 @@ func FolderList(cmd *cobra.Command, args []string) error {
6973
return fmt.Errorf("Listing Folder: %w", err)
7074
}
7175

76+
folders, err = filterFolders(&folders, celFilter, ctx)
77+
if err != nil {
78+
return err
79+
}
80+
7281
if jsonOutput {
7382
outputFolders := []FolderJsonOutput{}
7483
for i := range folders {

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ require (
2020
github.com/ProtonMail/gopenpgp/v2 v2.5.0 // indirect
2121
github.com/aead/argon2 v0.0.0-20180111183520-a87724528b07 // indirect
2222
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
23+
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
2324
github.com/cloudflare/circl v1.3.1 // indirect
2425
github.com/containerd/console v1.0.3 // indirect
2526
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
2627
github.com/fsnotify/fsnotify v1.6.0 // indirect
28+
github.com/google/cel-go v0.13.0 // indirect
2729
github.com/google/go-querystring v1.1.0 // indirect
2830
github.com/google/uuid v1.3.0 // indirect
2931
github.com/gookit/color v1.5.2 // indirect
@@ -42,11 +44,14 @@ require (
4244
github.com/spf13/cast v1.5.0 // indirect
4345
github.com/spf13/jwalterweatherman v1.1.0 // indirect
4446
github.com/spf13/pflag v1.0.5 // indirect
47+
github.com/stoewer/go-strcase v1.2.0 // indirect
4548
github.com/subosito/gotenv v1.4.1 // indirect
4649
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
4750
golang.org/x/crypto v0.4.0 // indirect
4851
golang.org/x/sys v0.3.0 // indirect
4952
golang.org/x/text v0.5.0 // indirect
53+
google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect
54+
google.golang.org/protobuf v1.28.1 // indirect
5055
gopkg.in/ini.v1 v1.67.0 // indirect
5156
gopkg.in/yaml.v2 v2.4.0 // indirect
5257
gopkg.in/yaml.v3 v3.0.1 // indirect

go.sum

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmH
6666
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
6767
github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
6868
github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
69+
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves=
70+
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
6971
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
7072
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
7173
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -123,8 +125,11 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
123125
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
124126
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
125127
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
128+
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
126129
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
127130
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
131+
github.com/google/cel-go v0.13.0 h1:z+8OBOcmh7IeKyqwT/6IlnMvy621fYUqnTVPEdegGlU=
132+
github.com/google/cel-go v0.13.0/go.mod h1:K2hpQgEjDp18J76a2DKFRlPBPpgRZgi6EbnpDgIhJ8s=
128133
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
129134
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
130135
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -134,6 +139,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
134139
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
135140
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
136141
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
142+
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
137143
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
138144
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
139145
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
@@ -239,6 +245,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
239245
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
240246
github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU=
241247
github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As=
248+
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
249+
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
242250
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
243251
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
244252
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -550,6 +558,8 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
550558
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
551559
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
552560
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
561+
google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c h1:QgY/XxIAIeccR+Ca/rDdKubLIU9rcJ3xfy1DC/Wd2Oo=
562+
google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo=
553563
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
554564
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
555565
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -576,6 +586,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
576586
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
577587
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
578588
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
589+
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
590+
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
591+
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
579592
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
580593
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
581594
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=

group/filter.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package group
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/google/cel-go/cel"
8+
"github.com/passbolt/go-passbolt-cli/util"
9+
"github.com/passbolt/go-passbolt/api"
10+
)
11+
12+
// Environments for CEl
13+
var celEnvOptions = []cel.EnvOption{
14+
cel.Variable("ID", cel.StringType),
15+
cel.Variable("Name", cel.StringType),
16+
cel.Variable("CreatedTimestamp", cel.TimestampType),
17+
cel.Variable("ModifiedTimestamp", cel.TimestampType),
18+
}
19+
20+
// Filters the slice groups by invoke CEL program for each group
21+
func filterGroups(groups *[]api.Group, celCmd string, ctx context.Context) ([]api.Group, error) {
22+
if celCmd == "" {
23+
return *groups, nil
24+
}
25+
26+
program, err := util.InitCELProgram(celCmd, celEnvOptions...)
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
filteredGroups := []api.Group{}
32+
for _, group := range *groups {
33+
val, _, err := (*program).ContextEval(ctx, map[string]any{
34+
"ID": group.ID,
35+
"Name": group.Name,
36+
"CreatedTimestamp": group.Created.Time,
37+
"ModifiedTimestamp": group.Modified.Time,
38+
})
39+
40+
if err != nil {
41+
return nil, err
42+
}
43+
44+
if val.Value() == true {
45+
filteredGroups = append(filteredGroups, group)
46+
}
47+
}
48+
49+
if len(filteredGroups) == 0 {
50+
return nil, fmt.Errorf("No such groups found with filter %v!", celCmd)
51+
}
52+
53+
return filteredGroups, nil
54+
}

group/list.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ func GroupList(cmd *cobra.Command, args []string) error {
5151
if err != nil {
5252
return err
5353
}
54+
celFilter, err := cmd.Flags().GetString("filter")
55+
if err != nil {
56+
return err
57+
}
5458

5559
ctx := util.GetContext()
5660

@@ -69,6 +73,11 @@ func GroupList(cmd *cobra.Command, args []string) error {
6973
return fmt.Errorf("Listing Group: %w", err)
7074
}
7175

76+
groups, err = filterGroups(&groups, celFilter, ctx)
77+
if err != nil {
78+
return err
79+
}
80+
7281
if jsonOutput {
7382
outputGroups := []GroupJsonOutput{}
7483
for i := range groups {

resource/filter.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package resource
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/google/cel-go/cel"
8+
"github.com/google/cel-go/common/types"
9+
"github.com/google/cel-go/common/types/ref"
10+
"github.com/passbolt/go-passbolt-cli/util"
11+
"github.com/passbolt/go-passbolt/api"
12+
"github.com/passbolt/go-passbolt/helper"
13+
)
14+
15+
// Environments for CEl
16+
var celEnvOptions = []cel.EnvOption{
17+
cel.Variable("ID", cel.StringType),
18+
cel.Variable("FolderParentID", cel.StringType),
19+
cel.Variable("Name", cel.StringType),
20+
cel.Variable("Username", cel.StringType),
21+
cel.Variable("URI", cel.StringType),
22+
cel.Variable("Password", cel.StringType),
23+
cel.Variable("Description", cel.StringType),
24+
cel.Variable("CreatedTimestamp", cel.TimestampType),
25+
cel.Variable("ModifiedTimestamp", cel.TimestampType),
26+
}
27+
28+
// Filters the slice resources by invoke CEL program for each resource
29+
func filterResources(resources *[]api.Resource, celCmd string, ctx context.Context, client *api.Client) ([]api.Resource, error) {
30+
if celCmd == "" {
31+
return *resources, nil
32+
}
33+
34+
program, err := util.InitCELProgram(celCmd, celEnvOptions...)
35+
if err != nil {
36+
return nil, err
37+
}
38+
39+
filteredResources := []api.Resource{}
40+
for _, resource := range *resources {
41+
val, _, err := (*program).ContextEval(ctx, map[string]any{
42+
"Id": resource.ID,
43+
"FolderParentID": resource.FolderParentID,
44+
"Name": resource.Name,
45+
"Username": resource.Username,
46+
"URI": resource.URI,
47+
"Password": func() ref.Val {
48+
_, _, _, _, pass, _, err := helper.GetResource(ctx, client, resource.ID)
49+
if err != nil {
50+
fmt.Printf("Get Resource %v", err)
51+
return types.String("")
52+
}
53+
return types.String(pass)
54+
},
55+
"Description": func() ref.Val {
56+
_, _, _, _, _, descr, err := helper.GetResource(ctx, client, resource.ID)
57+
if err != nil {
58+
fmt.Printf("Get Resource %v", err)
59+
return types.String("")
60+
}
61+
return types.String(descr)
62+
},
63+
"CreatedTimestamp": resource.Created.Time,
64+
"ModifiedTimestamp": resource.Modified.Time,
65+
})
66+
67+
if err != nil {
68+
return nil, err
69+
}
70+
71+
if val.Value() == true {
72+
filteredResources = append(filteredResources, resource)
73+
}
74+
}
75+
76+
if len(filteredResources) == 0 {
77+
return nil, fmt.Errorf("No such Resources found with filter %v!", celCmd)
78+
}
79+
return filteredResources, nil
80+
}

resource/list.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,8 @@ var ResourceListCmd = &cobra.Command{
2828
func init() {
2929
ResourceListCmd.Flags().Bool("favorite", false, "Resources that are marked as favorite")
3030
ResourceListCmd.Flags().Bool("own", false, "Resources that are owned by me")
31-
3231
ResourceListCmd.Flags().StringP("group", "g", "", "Resources that are shared with group")
3332
ResourceListCmd.Flags().StringArrayP("folder", "f", []string{}, "Resources that are in folder")
34-
3533
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")
3634
}
3735

@@ -63,6 +61,10 @@ func ResourceList(cmd *cobra.Command, args []string) error {
6361
if err != nil {
6462
return err
6563
}
64+
celFilter, err := cmd.Flags().GetString("filter")
65+
if err != nil {
66+
return err
67+
}
6668

6769
ctx := util.GetContext()
6870

@@ -83,6 +85,11 @@ func ResourceList(cmd *cobra.Command, args []string) error {
8385
return fmt.Errorf("Listing Resource: %w", err)
8486
}
8587

88+
resources, err = filterResources(&resources, celFilter, ctx, client)
89+
if err != nil {
90+
return err
91+
}
92+
8693
if jsonOutput {
8794
outputResources := []ResourceJsonOutput{}
8895
for i := range resources {

0 commit comments

Comments
 (0)