forked from casdoor/casdoor
feat: Enable ABAC support in /api/enforce and /api/batch-enforce
This commit is contained in:
@@ -57,7 +57,9 @@ func (c *ApiController) Enforce() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var request []string
|
// Accept both plain string arrays (["alice","data1","read"]) and mixed arrays
|
||||||
|
// with JSON objects ([{"DivisionGuid":"x"}, "resource", "read"]) for ABAC support.
|
||||||
|
var request []interface{}
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &request)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
@@ -74,8 +76,8 @@ func (c *ApiController) Enforce() {
|
|||||||
res := []bool{}
|
res := []bool{}
|
||||||
keyRes := []string{}
|
keyRes := []string{}
|
||||||
|
|
||||||
// type transformation
|
// Convert elements: JSON-object strings and maps become anonymous structs for ABAC.
|
||||||
interfaceRequest := util.StringToInterfaceArray(request)
|
interfaceRequest := util.InterfaceToEnforceArray(request)
|
||||||
|
|
||||||
enforceResult, err := enforcer.Enforce(interfaceRequest...)
|
enforceResult, err := enforcer.Enforce(interfaceRequest...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -197,7 +199,8 @@ func (c *ApiController) BatchEnforce() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var requests [][]string
|
// Accept both string arrays and mixed arrays with JSON objects for ABAC support.
|
||||||
|
var requests [][]interface{}
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &requests)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &requests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
@@ -214,8 +217,8 @@ func (c *ApiController) BatchEnforce() {
|
|||||||
res := [][]bool{}
|
res := [][]bool{}
|
||||||
keyRes := []string{}
|
keyRes := []string{}
|
||||||
|
|
||||||
// type transformation
|
// Convert elements: JSON-object strings and maps become anonymous structs for ABAC.
|
||||||
interfaceRequests := util.StringToInterfaceArray2d(requests)
|
interfaceRequests := util.InterfaceToEnforceArray2d(requests)
|
||||||
|
|
||||||
enforceResult, err := enforcer.BatchEnforce(interfaceRequests)
|
enforceResult, err := enforcer.BatchEnforce(interfaceRequests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -357,26 +357,27 @@ func removePolicies(permission *Permission) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Enforce(permission *Permission, request []string, permissionIds ...string) (bool, error) {
|
func Enforce(permission *Permission, request []interface{}, permissionIds ...string) (bool, error) {
|
||||||
enforcer, err := getPermissionEnforcer(permission, permissionIds...)
|
enforcer, err := getPermissionEnforcer(permission, permissionIds...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// type transformation
|
// Convert each element: JSON-object strings and maps become anonymous structs
|
||||||
interfaceRequest := util.StringToInterfaceArray(request)
|
// so Casbin can evaluate ABAC rules with dot-notation (e.g. r.sub.DivisionGuid).
|
||||||
|
interfaceRequest := util.InterfaceToEnforceArray(request)
|
||||||
|
|
||||||
return enforcer.Enforce(interfaceRequest...)
|
return enforcer.Enforce(interfaceRequest...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BatchEnforce(permission *Permission, requests [][]string, permissionIds ...string) ([]bool, error) {
|
func BatchEnforce(permission *Permission, requests [][]interface{}, permissionIds ...string) ([]bool, error) {
|
||||||
enforcer, err := getPermissionEnforcer(permission, permissionIds...)
|
enforcer, err := getPermissionEnforcer(permission, permissionIds...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// type transformation
|
// Convert each element in every row for ABAC support.
|
||||||
interfaceRequests := util.StringToInterfaceArray2d(requests)
|
interfaceRequests := util.InterfaceToEnforceArray2d(requests)
|
||||||
|
|
||||||
return enforcer.BatchEnforce(interfaceRequests)
|
return enforcer.BatchEnforce(interfaceRequests)
|
||||||
}
|
}
|
||||||
|
|||||||
32
util/json.go
32
util/json.go
@@ -67,3 +67,35 @@ func TryJsonToAnonymousStruct(j string) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InterfaceToEnforceValue converts a single request value for use in Casbin ABAC enforcement.
|
||||||
|
// - Strings that are valid JSON objects are converted to anonymous structs so Casbin can
|
||||||
|
// access their fields (e.g. r.sub.DivisionGuid).
|
||||||
|
// - Maps (map[string]interface{}) produced by direct JSON unmarshaling are re-marshaled and
|
||||||
|
// then converted to anonymous structs in the same way.
|
||||||
|
// - All other values are returned unchanged.
|
||||||
|
func InterfaceToEnforceValue(v interface{}) interface{} {
|
||||||
|
switch val := v.(type) {
|
||||||
|
case string:
|
||||||
|
jStruct, err := TryJsonToAnonymousStruct(val)
|
||||||
|
if err == nil {
|
||||||
|
return jStruct
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
case map[string]interface{}:
|
||||||
|
// The value was already decoded as a JSON object; re-encode it so we
|
||||||
|
// can reuse TryJsonToAnonymousStruct to produce a named-field struct
|
||||||
|
// that Casbin can evaluate with dot-notation (r.sub.Field).
|
||||||
|
jsonBytes, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
jStruct, err := TryJsonToAnonymousStruct(string(jsonBytes))
|
||||||
|
if err == nil {
|
||||||
|
return jStruct
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
default:
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -381,3 +381,25 @@ func StringToInterfaceArray2d(arrays [][]string) [][]interface{} {
|
|||||||
}
|
}
|
||||||
return interfaceArrays
|
return interfaceArrays
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InterfaceToEnforceArray converts a []interface{} request for use in Casbin ABAC enforcement.
|
||||||
|
// Each element is processed by InterfaceToEnforceValue: plain strings that are valid JSON
|
||||||
|
// objects and map values decoded directly from JSON are both converted to anonymous structs
|
||||||
|
// so Casbin can evaluate attribute-based rules with dot-notation (r.sub.Field).
|
||||||
|
func InterfaceToEnforceArray(array []interface{}) []interface{} {
|
||||||
|
result := make([]interface{}, len(array))
|
||||||
|
for i, elem := range array {
|
||||||
|
result[i] = InterfaceToEnforceValue(elem)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterfaceToEnforceArray2d applies InterfaceToEnforceArray to every row in a
|
||||||
|
// two-dimensional slice, for use with Casbin BatchEnforce.
|
||||||
|
func InterfaceToEnforceArray2d(arrays [][]interface{}) [][]interface{} {
|
||||||
|
result := make([][]interface{}, len(arrays))
|
||||||
|
for i, arr := range arrays {
|
||||||
|
result[i] = InterfaceToEnforceArray(arr)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user