[#33] pkg/chain: Support CondSliceContains condition
DCO action / DCO (pull_request) Successful in 1m4s Details
Tests and linters / Tests (1.21) (pull_request) Successful in 1m3s Details
Tests and linters / Tests (1.20) (pull_request) Successful in 1m18s Details
Tests and linters / Staticcheck (pull_request) Successful in 1m23s Details
Tests and linters / Tests with -race (pull_request) Successful in 1m34s Details
Tests and linters / Lint (pull_request) Successful in 2m8s Details

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
pull/38/head
Denis Kirillov 2023-12-20 13:17:15 +03:00
parent 3128352693
commit 0f053c2417
6 changed files with 91 additions and 0 deletions

1
go.mod
View File

@ -6,6 +6,7 @@ require (
git.frostfs.info/TrueCloudLab/frostfs-contract v0.18.1-0.20231129062201-a1b61d394958
github.com/nspcc-dev/neo-go v0.103.0
github.com/stretchr/testify v1.8.4
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
)
require (

2
go.sum
View File

@ -31,6 +31,8 @@ github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74/go.mod h1
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=

View File

@ -50,6 +50,9 @@ const (
CondArnLike string = "ArnLike"
CondArnNotEquals string = "ArnNotEquals"
CondArnNotLike string = "ArnNotLike"
// Custom condition operators.
CondSliceContains string = "SliceContains"
)
const (
@ -191,6 +194,8 @@ func getConditionTypeAndConverter(op string) (chain.ConditionType, convertFuncti
return chain.CondStringLike, noConvertFunction, nil
case op == CondNotIPAddress:
return chain.CondStringNotLike, noConvertFunction, nil
case op == CondSliceContains:
return chain.CondSliceContains, noConvertFunction, nil
default:
return 0, nil, fmt.Errorf("unsupported condition operator: '%s'", op)
}

View File

@ -7,6 +7,7 @@ import (
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource"
"git.frostfs.info/TrueCloudLab/policy-engine/util"
"golang.org/x/exp/slices"
)
// ID is the ID of rule chain.
@ -103,6 +104,8 @@ const (
CondNumericLessThanEquals
CondNumericGreaterThan
CondNumericGreaterThanEquals
CondSliceContains
)
func (c ConditionType) String() string {
@ -139,11 +142,21 @@ func (c ConditionType) String() string {
return "NumericGreaterThan"
case CondNumericGreaterThanEquals:
return "NumericGreaterThanEquals"
case CondSliceContains:
return "SliceContains"
default:
return "unknown condition type"
}
}
const condSliceContainsDelimiter = "\x00"
// FormCondSliceContainsValue builds value for ObjectResource or ObjectRequest property
// that can be matched by CondSliceContains condition.
func FormCondSliceContainsValue(values []string) string {
return strings.Join(values, condSliceContainsDelimiter)
}
func (c *Condition) Match(req resource.Request) bool {
var val string
switch c.Object {
@ -178,6 +191,8 @@ func (c *Condition) Match(req resource.Request) bool {
return val > c.Value
case CondStringGreaterThanEquals:
return val >= c.Value
case CondSliceContains:
return slices.Contains(strings.Split(val, condSliceContainsDelimiter), c.Value)
}
}

View File

@ -4,6 +4,7 @@ import (
"testing"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource/testutil"
"git.frostfs.info/TrueCloudLab/policy-engine/schema/common"
"git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
"github.com/stretchr/testify/require"
)
@ -73,3 +74,65 @@ func TestReturnFirstMatch(t *testing.T) {
require.Equal(t, Allow, st)
})
}
func TestCondSliceContainsMatch(t *testing.T) {
propKey := common.PropertyKeyFrostFSIDGroupID
groupID := "1"
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: CondSliceContains,
Object: ObjectRequest,
Key: propKey,
Value: groupID,
}},
}}}
for _, tc := range []struct {
name string
value string
status Status
}{
{
name: "simple value",
value: groupID,
status: Allow,
},
{
name: "simple value by func",
value: FormCondSliceContainsValue([]string{groupID}),
status: Allow,
},
{
name: "multiple values by func",
value: FormCondSliceContainsValue([]string{groupID, "2", "3"}),
status: Allow,
},
{
name: "simple mismatched",
value: "3",
status: NoRuleFound,
},
{
name: "multiple mismatched",
value: FormCondSliceContainsValue([]string{"11", "12"}),
status: NoRuleFound,
},
{
name: "comma correct handling mismatched",
value: "1,11",
status: NoRuleFound,
},
} {
t.Run(tc.name, func(t *testing.T) {
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{propKey: tc.value})
st, _ := ch.Match(request)
require.Equal(t, tc.status.String(), st.String())
})
}
}

View File

@ -0,0 +1,5 @@
package common
const (
PropertyKeyFrostFSIDGroupID = "frostfsid:groupID"
)