From 7bd20849497c6f92b6db768b2a471bbfafe43763 Mon Sep 17 00:00:00 2001 From: liewstar <2935437378@qq.com> Date: Fri, 10 Jan 2025 00:07:54 +0800 Subject: [PATCH 1/3] feat: support dynamic struct creation for parameters --- cmd/enforce.go | 141 ++++++++++++++++++++------------------ cmd/enforce_test.go | 5 ++ test/abac_rule_model.conf | 11 +++ test/abac_rule_policy.csv | 2 + 4 files changed, 94 insertions(+), 65 deletions(-) create mode 100644 test/abac_rule_model.conf create mode 100644 test/abac_rule_policy.csv diff --git a/cmd/enforce.go b/cmd/enforce.go index e961a30..0743fbe 100644 --- a/cmd/enforce.go +++ b/cmd/enforce.go @@ -16,6 +16,10 @@ package cmd import ( "encoding/json" + "reflect" + "regexp" + "strconv" + "strings" "github.com/casbin/casbin/v2" "github.com/spf13/cobra" @@ -26,83 +30,90 @@ type ResponseBody struct { Explain []string `json:"explain"` } -// enforceExCmd represents the enforceEx command. -var enforceExCmd = &cobra.Command{ - Use: "enforceEx", - Short: "Test if a 'subject' can access a 'object' with a given 'action' based on the policy", - Long: `Test if a 'subject' can access a 'object' with a given 'action' based on the policy`, - Run: func(cmd *cobra.Command, args []string) { - modelPath, _ := cmd.Flags().GetString("model") - policyPath, _ := cmd.Flags().GetString("policy") - - e, err := casbin.NewEnforcer(modelPath, policyPath) - if err != nil { - panic(err) - } +// Function to handle enforcement results +func handleEnforceResult(cmd *cobra.Command, res bool, explain []string, err error) { + if err != nil { + cmd.PrintErrf("Error during enforcement: %v\n", err) + return + } + + response := ResponseBody{ + Allow: res, + Explain: explain, + } + + encoder := json.NewEncoder(cmd.OutOrStdout()) + encoder.SetEscapeHTML(false) + encoder.Encode(response) +} - params := make([]interface{}, len(args)) - for i, v := range args { - params[i] = v +// Function to parse parameters and execute policy check +func executeEnforce(cmd *cobra.Command, args []string, isEnforceEx bool) { + modelPath, _ := cmd.Flags().GetString("model") + policyPath, _ := cmd.Flags().GetString("policy") + + e, err := casbin.NewEnforcer(modelPath, policyPath) + if err != nil { + panic(err) + } + + // Define regex pattern to match format like {field: value} + paramRegex := regexp.MustCompile(`{\s*"?(\w+)"?\s*:\s*(\d+)\s*}`) + + params := make([]interface{}, len(args)) + for i, v := range args { + // Using regex pattern to match parameters + if matches := paramRegex.FindStringSubmatch(v); len(matches) == 3 { + fieldName := matches[1] + valueStr := matches[2] + + // Convert value to integer + if val, err := strconv.Atoi(valueStr); err == nil { + // Dynamically create struct type + structType := reflect.StructOf([]reflect.StructField{ + { + Name: strings.Title(fieldName), + Type: reflect.TypeOf(0), + }, + }) + + // Create struct instance and set value + structValue := reflect.New(structType).Elem() + structValue.Field(0).SetInt(int64(val)) + + params[i] = structValue.Interface() + continue + } } + params[i] = v + } + if isEnforceEx { res, explain, err := e.EnforceEx(params...) - if err != nil { - cmd.PrintErrf("Error during enforcement: %v\n", err) - return - } - - response := ResponseBody{ - Allow: res, - Explain: explain, - } - - jsonResponse, err := json.Marshal(response) - if err != nil { - cmd.PrintErrf("Error marshaling JSON: %v\n", err) - return - } - - cmd.Println(string(jsonResponse)) - }, + handleEnforceResult(cmd, res, explain, err) + } else { + res, err := e.Enforce(params...) + handleEnforceResult(cmd, res, []string{}, err) + } } -// enforceCmd represents the enforce command. +// enforceCmd represents the enforce command var enforceCmd = &cobra.Command{ Use: "enforce", Short: "Test if a 'subject' can access a 'object' with a given 'action' based on the policy", Long: `Test if a 'subject' can access a 'object' with a given 'action' based on the policy`, Run: func(cmd *cobra.Command, args []string) { - modelPath, _ := cmd.Flags().GetString("model") - policyPath, _ := cmd.Flags().GetString("policy") - - e, err := casbin.NewEnforcer(modelPath, policyPath) - if err != nil { - panic(err) - } - - params := make([]interface{}, len(args)) - for i, v := range args { - params[i] = v - } - - res, err := e.Enforce(params...) - if err != nil { - cmd.PrintErrf("Error during enforcement: %v\n", err) - return - } - - response := ResponseBody{ - Allow: res, - Explain: []string{}, - } - - jsonResponse, err := json.Marshal(response) - if err != nil { - cmd.PrintErrf("Error marshaling response: %v\n", err) - return - } + executeEnforce(cmd, args, false) + }, +} - cmd.Println(string(jsonResponse)) +// enforceExCmd represents the enforceEx command +var enforceExCmd = &cobra.Command{ + Use: "enforceEx", + Short: "Test if a 'subject' can access a 'object' with a given 'action' based on the policy", + Long: `Test if a 'subject' can access a 'object' with a given 'action' based on the policy`, + Run: func(cmd *cobra.Command, args []string) { + executeEnforce(cmd, args, true) }, } diff --git a/cmd/enforce_test.go b/cmd/enforce_test.go index 9525049..3be9f76 100644 --- a/cmd/enforce_test.go +++ b/cmd/enforce_test.go @@ -42,4 +42,9 @@ func Test_enforceExCmd(t *testing.T) { domainArgs := []string{"enforceEx", "-m", "../test/rbac_with_domains_model.conf", "-p", "../test/rbac_with_domains_policy.csv"} assertExecuteCommand(t, rootCmd, "{\"allow\":true,\"explain\":[\"admin\",\"domain1\",\"data1\",\"read\"]}\n", append(domainArgs, "alice", "domain1", "data1", "read")...) + + // Test ABAC rule + abacArgs := []string{"enforceEx", "-m", "../test/abac_rule_model.conf", "-p", "../test/abac_rule_policy.csv"} + assertExecuteCommand(t, rootCmd, "{\"allow\":true,\"explain\":[\"r.sub.Age > 18\",\"/data1\",\"read\"]}\n", append(abacArgs, "{\"Age\":30}", "/data1", "read")...) + assertExecuteCommand(t, rootCmd, "{\"allow\":false,\"explain\":[]}\n", append(abacArgs, "{\"Age\":15}", "/data1", "read")...) } diff --git a/test/abac_rule_model.conf b/test/abac_rule_model.conf new file mode 100644 index 0000000..eebfc13 --- /dev/null +++ b/test/abac_rule_model.conf @@ -0,0 +1,11 @@ +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub_rule, obj, act + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = eval(p.sub_rule) && r.obj == p.obj && r.act == p.act diff --git a/test/abac_rule_policy.csv b/test/abac_rule_policy.csv new file mode 100644 index 0000000..be29ded --- /dev/null +++ b/test/abac_rule_policy.csv @@ -0,0 +1,2 @@ +p, r.sub.Age > 18, /data1, read +p, r.sub.Age < 60, /data2, write From af6aa6b64db10867d089fe87197d90215bb67400 Mon Sep 17 00:00:00 2001 From: liewstar <2935437378@qq.com> Date: Fri, 10 Jan 2025 00:16:27 +0800 Subject: [PATCH 2/3] fix: replace deprecated strings.Title with cases.Title --- cmd/enforce.go | 20 +++++++++++--------- go.mod | 1 + go.sum | 1 + 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/cmd/enforce.go b/cmd/enforce.go index 0743fbe..a3b0b0d 100644 --- a/cmd/enforce.go +++ b/cmd/enforce.go @@ -19,10 +19,11 @@ import ( "reflect" "regexp" "strconv" - "strings" "github.com/casbin/casbin/v2" "github.com/spf13/cobra" + "golang.org/x/text/cases" + "golang.org/x/text/language" ) type ResponseBody struct { @@ -30,7 +31,7 @@ type ResponseBody struct { Explain []string `json:"explain"` } -// Function to handle enforcement results +// Function to handle enforcement results. func handleEnforceResult(cmd *cobra.Command, res bool, explain []string, err error) { if err != nil { cmd.PrintErrf("Error during enforcement: %v\n", err) @@ -47,7 +48,7 @@ func handleEnforceResult(cmd *cobra.Command, res bool, explain []string, err err encoder.Encode(response) } -// Function to parse parameters and execute policy check +// Function to parse parameters and execute policy check. func executeEnforce(cmd *cobra.Command, args []string, isEnforceEx bool) { modelPath, _ := cmd.Flags().GetString("model") policyPath, _ := cmd.Flags().GetString("policy") @@ -57,27 +58,28 @@ func executeEnforce(cmd *cobra.Command, args []string, isEnforceEx bool) { panic(err) } - // Define regex pattern to match format like {field: value} + // Define regex pattern to match format like {field: value}. paramRegex := regexp.MustCompile(`{\s*"?(\w+)"?\s*:\s*(\d+)\s*}`) params := make([]interface{}, len(args)) for i, v := range args { - // Using regex pattern to match parameters + // Using regex pattern to match parameters. if matches := paramRegex.FindStringSubmatch(v); len(matches) == 3 { fieldName := matches[1] valueStr := matches[2] - // Convert value to integer + // Convert value to integer. if val, err := strconv.Atoi(valueStr); err == nil { - // Dynamically create struct type + // Dynamically create struct type. + caser := cases.Title(language.English) structType := reflect.StructOf([]reflect.StructField{ { - Name: strings.Title(fieldName), + Name: caser.String(fieldName), Type: reflect.TypeOf(0), }, }) - // Create struct instance and set value + // Create struct instance and set value. structValue := reflect.New(structType).Elem() structValue.Field(0).SetInt(int64(val)) diff --git a/go.mod b/go.mod index 04e3478..d4e0ad8 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.19 require ( github.com/casbin/casbin/v2 v2.97.0 github.com/spf13/cobra v1.8.1 + golang.org/x/text v0.3.0 ) require ( diff --git a/go.sum b/go.sum index 7fef443..d051a2d 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 9724b64dc55d89493c2974754a7bbd4e883e4a8c Mon Sep 17 00:00:00 2001 From: liewstar <2935437378@qq.com> Date: Fri, 10 Jan 2025 00:19:16 +0800 Subject: [PATCH 3/3] style: add periods to comments to fix godot linter errors --- cmd/enforce.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/enforce.go b/cmd/enforce.go index a3b0b0d..5ae8d0c 100644 --- a/cmd/enforce.go +++ b/cmd/enforce.go @@ -99,21 +99,21 @@ func executeEnforce(cmd *cobra.Command, args []string, isEnforceEx bool) { } } -// enforceCmd represents the enforce command +// enforceCmd represents the enforce command. var enforceCmd = &cobra.Command{ Use: "enforce", - Short: "Test if a 'subject' can access a 'object' with a given 'action' based on the policy", - Long: `Test if a 'subject' can access a 'object' with a given 'action' based on the policy`, + Short: "Test if a 'subject' can access a 'object' with a given 'action' based on the policy.", + Long: `Test if a 'subject' can access a 'object' with a given 'action' based on the policy.`, Run: func(cmd *cobra.Command, args []string) { executeEnforce(cmd, args, false) }, } -// enforceExCmd represents the enforceEx command +// enforceExCmd represents the enforceEx command. var enforceExCmd = &cobra.Command{ Use: "enforceEx", - Short: "Test if a 'subject' can access a 'object' with a given 'action' based on the policy", - Long: `Test if a 'subject' can access a 'object' with a given 'action' based on the policy`, + Short: "Test if a 'subject' can access a 'object' with a given 'action' based on the policy.", + Long: `Test if a 'subject' can access a 'object' with a given 'action' based on the policy.`, Run: func(cmd *cobra.Command, args []string) { executeEnforce(cmd, args, true) }, @@ -123,13 +123,13 @@ func init() { rootCmd.AddCommand(enforceExCmd) rootCmd.AddCommand(enforceCmd) - enforceExCmd.Flags().StringP("model", "m", "", "Path to the model file") + enforceExCmd.Flags().StringP("model", "m", "", "Path to the model file.") _ = enforceExCmd.MarkFlagRequired("model") - enforceExCmd.Flags().StringP("policy", "p", "", "Path to the policy file") + enforceExCmd.Flags().StringP("policy", "p", "", "Path to the policy file.") _ = enforceExCmd.MarkFlagRequired("policy") - enforceCmd.Flags().StringP("model", "m", "", "Path to the model file") + enforceCmd.Flags().StringP("model", "m", "", "Path to the model file.") _ = enforceCmd.MarkFlagRequired("model") - enforceCmd.Flags().StringP("policy", "p", "", "Path to the policy file") + enforceCmd.Flags().StringP("policy", "p", "", "Path to the policy file.") _ = enforceCmd.MarkFlagRequired("policy") }