From 76f322861a8e94956181db7a519ad37fb35216b2 Mon Sep 17 00:00:00 2001 From: iderr Date: Mon, 4 Aug 2025 07:51:25 -0400 Subject: [PATCH] feat: Refactor GetFilteredPolicies to support multiple filters via POST (#4037) --- controllers/enforcer.go | 34 ++++++----------- object/enforcer.go | 81 +++++++++++++++++++++++++++++++++++++++++ routers/router.go | 2 +- 3 files changed, 94 insertions(+), 23 deletions(-) diff --git a/controllers/enforcer.go b/controllers/enforcer.go index b569b1fd..41569db1 100644 --- a/controllers/enforcer.go +++ b/controllers/enforcer.go @@ -17,7 +17,6 @@ package controllers import ( "encoding/json" "fmt" - "strings" "github.com/beego/beego/utils/pagination" "github.com/casdoor/casdoor/object" @@ -200,37 +199,28 @@ func (c *ApiController) GetPolicies() { // GetFilteredPolicies // @Title GetFilteredPolicies // @Tag Enforcer API -// @Description get filtered policies +// @Description get filtered policies with support for multiple filters via POST body // @Param id query string true "The id ( owner/name ) of enforcer" -// @Param ptype query string false "Policy type, default is 'p'" -// @Param fieldIndex query int false "Field index for filtering" -// @Param fieldValues query string false "Field values for filtering, comma-separated" +// @Param body body []object.Filter true "Array of filter objects for multiple filters" // @Success 200 {array} xormadapter.CasbinRule -// @router /get-filtered-policies [get] +// @router /get-filtered-policies [post] func (c *ApiController) GetFilteredPolicies() { id := c.Input().Get("id") - ptype := c.Input().Get("ptype") - fieldIndexStr := c.Input().Get("fieldIndex") - fieldValuesStr := c.Input().Get("fieldValues") - if ptype == "" { - ptype = "p" - } - - fieldIndex := util.ParseInt(fieldIndexStr) - - var fieldValues []string - if fieldValuesStr != "" { - fieldValues = strings.Split(fieldValuesStr, ",") - } - - policies, err := object.GetFilteredPolicies(id, ptype, fieldIndex, fieldValues...) + var filters []object.Filter + err := json.Unmarshal(c.Ctx.Input.RequestBody, &filters) if err != nil { c.ResponseError(err.Error()) return } - c.ResponseOk(policies) + filteredPolicies, err := object.GetFilteredPoliciesMulti(id, filters) + if err != nil { + c.ResponseError(err.Error()) + return + } + + c.ResponseOk(filteredPolicies) } // UpdatePolicy diff --git a/object/enforcer.go b/object/enforcer.go index 7ebde50d..f515f08b 100644 --- a/object/enforcer.go +++ b/object/enforcer.go @@ -206,6 +206,13 @@ func GetPolicies(id string) ([]*xormadapter.CasbinRule, error) { return res, nil } +// Filter represents filter criteria with optional policy type +type Filter struct { + Ptype string `json:"ptype,omitempty"` + FieldIndex int `json:"fieldIndex"` + FieldValues []string `json:"fieldValues"` +} + func GetFilteredPolicies(id string, ptype string, fieldIndex int, fieldValues ...string) ([]*xormadapter.CasbinRule, error) { enforcer, err := GetInitializedEnforcer(id) if err != nil { @@ -236,6 +243,80 @@ func GetFilteredPolicies(id string, ptype string, fieldIndex int, fieldValues .. return res, nil } +// GetFilteredPoliciesMulti applies multiple filters to policies +// Doing this in our loop is more efficient than using GetFilteredGroupingPolicy / GetFilteredPolicy which +// iterates over all policies again and again +func GetFilteredPoliciesMulti(id string, filters []Filter) ([]*xormadapter.CasbinRule, error) { + // Get all policies first + allPolicies, err := GetPolicies(id) + if err != nil { + return nil, err + } + + // Filter policies based on multiple criteria + var filteredPolicies []*xormadapter.CasbinRule + if len(filters) == 0 { + // No filters, return all policies + return allPolicies, nil + } else { + for _, policy := range allPolicies { + matchesAllFilters := true + for _, filter := range filters { + // If no policy type is specified, set it to the default policy type + if filter.Ptype == "" { + filter.Ptype = "p" + } + // Check policy type + if policy.Ptype != filter.Ptype { + matchesAllFilters = false + break + } + + // Check if field index is valid (0-5 for V0-V5) + if filter.FieldIndex < 0 || filter.FieldIndex > 5 { + matchesAllFilters = false + break + } + + fieldValue := "" + switch filter.FieldIndex { + case 0: + fieldValue = policy.V0 + case 1: + fieldValue = policy.V1 + case 2: + fieldValue = policy.V2 + case 3: + fieldValue = policy.V3 + case 4: + fieldValue = policy.V4 + case 5: + fieldValue = policy.V5 + } + + found := false + // Check if field value is in the list of expected values + for _, expectedValue := range filter.FieldValues { + if fieldValue == expectedValue { + found = true + break + } + } + if !found { + matchesAllFilters = false + break + } + } + + if matchesAllFilters { + filteredPolicies = append(filteredPolicies, policy) + } + } + } + + return filteredPolicies, nil +} + func UpdatePolicy(id string, ptype string, oldPolicy []string, newPolicy []string) (bool, error) { enforcer, err := GetInitializedEnforcer(id) if err != nil { diff --git a/routers/router.go b/routers/router.go index a14ccf45..3b3c0d68 100644 --- a/routers/router.go +++ b/routers/router.go @@ -160,7 +160,7 @@ func initAPI() { beego.Router("/api/add-adapter", &controllers.ApiController{}, "POST:AddAdapter") beego.Router("/api/delete-adapter", &controllers.ApiController{}, "POST:DeleteAdapter") beego.Router("/api/get-policies", &controllers.ApiController{}, "GET:GetPolicies") - beego.Router("/api/get-filtered-policies", &controllers.ApiController{}, "GET:GetFilteredPolicies") + beego.Router("/api/get-filtered-policies", &controllers.ApiController{}, "POST:GetFilteredPolicies") beego.Router("/api/update-policy", &controllers.ApiController{}, "POST:UpdatePolicy") beego.Router("/api/add-policy", &controllers.ApiController{}, "POST:AddPolicy") beego.Router("/api/remove-policy", &controllers.ApiController{}, "POST:RemovePolicy")