Merge pull request #26 from PiMaDaum/feature/CEL-filter-implementation

feature/CEL filter implementation
This commit is contained in:
Samuel Lorch 2023-02-12 20:55:58 +01:00 committed by GitHub
commit 7fe89a34b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 333 additions and 2 deletions

View file

@ -19,6 +19,12 @@ var listCmd = &cobra.Command{
func init() { func init() {
rootCmd.AddCommand(listCmd) rootCmd.AddCommand(listCmd)
listCmd.PersistentFlags().BoolP("json", "j", false, "Output JSON") 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(resource.ResourceListCmd)
listCmd.AddCommand(folder.FolderListCmd) listCmd.AddCommand(folder.FolderListCmd)
listCmd.AddCommand(group.GroupListCmd) listCmd.AddCommand(group.GroupListCmd)

56
folder/filter.go Normal file
View file

@ -0,0 +1,56 @@
package folder
import (
"context"
"fmt"
"github.com/google/cel-go/cel"
"github.com/passbolt/go-passbolt-cli/util"
"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),
}
// 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
}
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{
"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
}

View file

@ -51,6 +51,10 @@ func FolderList(cmd *cobra.Command, args []string) error {
if err != nil { if err != nil {
return err return err
} }
celFilter, err := cmd.Flags().GetString("filter")
if err != nil {
return err
}
ctx := util.GetContext() ctx := util.GetContext()
cmd.SilenceUsage = true cmd.SilenceUsage = true
@ -69,6 +73,11 @@ func FolderList(cmd *cobra.Command, args []string) error {
return fmt.Errorf("Listing Folder: %w", err) return fmt.Errorf("Listing Folder: %w", err)
} }
folders, err = filterFolders(&folders, celFilter, ctx)
if err != nil {
return err
}
if jsonOutput { if jsonOutput {
outputFolders := []FolderJsonOutput{} outputFolders := []FolderJsonOutput{}
for i := range folders { for i := range folders {

5
go.mod
View file

@ -20,10 +20,12 @@ require (
github.com/ProtonMail/gopenpgp/v2 v2.5.0 // indirect github.com/ProtonMail/gopenpgp/v2 v2.5.0 // indirect
github.com/aead/argon2 v0.0.0-20180111183520-a87724528b07 // indirect github.com/aead/argon2 v0.0.0-20180111183520-a87724528b07 // indirect
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // 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/cloudflare/circl v1.3.1 // indirect
github.com/containerd/console v1.0.3 // indirect github.com/containerd/console v1.0.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/fsnotify/fsnotify v1.6.0 // 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/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.3.0 // indirect github.com/google/uuid v1.3.0 // indirect
github.com/gookit/color v1.5.2 // indirect github.com/gookit/color v1.5.2 // indirect
@ -42,11 +44,14 @@ require (
github.com/spf13/cast v1.5.0 // indirect github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // 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/subosito/gotenv v1.4.1 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/crypto v0.4.0 // indirect golang.org/x/crypto v0.4.0 // indirect
golang.org/x/sys v0.3.0 // indirect golang.org/x/sys v0.3.0 // indirect
golang.org/x/text v0.5.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/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect

13
go.sum
View file

@ -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/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 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= 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/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= 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= 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
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 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.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/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 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/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.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.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 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/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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.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.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-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 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 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=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 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 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU=
github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= 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.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.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 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
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 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-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-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.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 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
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 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.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 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 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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=

54
group/filter.go Normal file
View file

@ -0,0 +1,54 @@
package group
import (
"context"
"fmt"
"github.com/google/cel-go/cel"
"github.com/passbolt/go-passbolt-cli/util"
"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
}
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{
"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
}

View file

@ -51,6 +51,10 @@ func GroupList(cmd *cobra.Command, args []string) error {
if err != nil { if err != nil {
return err return err
} }
celFilter, err := cmd.Flags().GetString("filter")
if err != nil {
return err
}
ctx := util.GetContext() ctx := util.GetContext()
@ -69,6 +73,11 @@ func GroupList(cmd *cobra.Command, args []string) error {
return fmt.Errorf("Listing Group: %w", err) return fmt.Errorf("Listing Group: %w", err)
} }
groups, err = filterGroups(&groups, celFilter, ctx)
if err != nil {
return err
}
if jsonOutput { if jsonOutput {
outputGroups := []GroupJsonOutput{} outputGroups := []GroupJsonOutput{}
for i := range groups { for i := range groups {

80
resource/filter.go Normal file
View file

@ -0,0 +1,80 @@
package resource
import (
"context"
"fmt"
"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"
)
// 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),
}
// 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
}
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{
"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
}

View file

@ -28,10 +28,8 @@ var ResourceListCmd = &cobra.Command{
func init() { func init() {
ResourceListCmd.Flags().Bool("favorite", false, "Resources that are marked as favorite") ResourceListCmd.Flags().Bool("favorite", false, "Resources that are marked as favorite")
ResourceListCmd.Flags().Bool("own", false, "Resources that are owned by me") ResourceListCmd.Flags().Bool("own", false, "Resources that are owned by me")
ResourceListCmd.Flags().StringP("group", "g", "", "Resources that are shared with group") 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("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") 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")
} }
@ -63,6 +61,10 @@ func ResourceList(cmd *cobra.Command, args []string) error {
if err != nil { if err != nil {
return err return err
} }
celFilter, err := cmd.Flags().GetString("filter")
if err != nil {
return err
}
ctx := util.GetContext() ctx := util.GetContext()
@ -83,6 +85,11 @@ func ResourceList(cmd *cobra.Command, args []string) error {
return fmt.Errorf("Listing Resource: %w", err) return fmt.Errorf("Listing Resource: %w", err)
} }
resources, err = filterResources(&resources, celFilter, ctx, client)
if err != nil {
return err
}
if jsonOutput { if jsonOutput {
outputResources := []ResourceJsonOutput{} outputResources := []ResourceJsonOutput{}
for i := range resources { for i := range resources {

60
user/filter.go Normal file
View file

@ -0,0 +1,60 @@
package user
import (
"context"
"fmt"
"github.com/google/cel-go/cel"
"github.com/passbolt/go-passbolt-cli/util"
"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
}
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{
"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
}

View file

@ -62,6 +62,10 @@ func UserList(cmd *cobra.Command, args []string) error {
if err != nil { if err != nil {
return err return err
} }
celFilter, err := cmd.Flags().GetString("filter")
if err != nil {
return err
}
ctx := util.GetContext() ctx := util.GetContext()
@ -82,6 +86,11 @@ func UserList(cmd *cobra.Command, args []string) error {
return fmt.Errorf("Listing User: %w", err) return fmt.Errorf("Listing User: %w", err)
} }
users, err = filterUsers(&users, celFilter, ctx)
if err != nil {
return err
}
if jsonOutput { if jsonOutput {
outputUsers := []UserJsonOutput{} outputUsers := []UserJsonOutput{}
for i := range users { for i := range users {

23
util/cel.go Normal file
View file

@ -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
}