policy-engine/pkg/chain/chain_test.go
Airat Arifullin d7ed188f68 [#76] chain: Increase unit-test coverage for chain related types
* Add more unit-test cases.

Signed-off-by: Airat Arifullin <aarifullin@yadro.com>
2024-05-22 15:08:11 +03:00

1028 lines
29 KiB
Go

package chain
import (
"fmt"
"strings"
"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"
"git.frostfs.info/TrueCloudLab/policy-engine/schema/s3"
"github.com/stretchr/testify/require"
)
func TestChainIDSerialization(t *testing.T) {
chainIDBytes := []byte{93, 236, 80, 138, 168, 3, 144, 92, 173, 141, 16, 42, 249, 90, 97, 109, 211, 169, 54, 163}
chain1 := &Chain{ID: ID(chainIDBytes)}
data := chain1.Bytes()
var chain2 Chain
err := chain2.DecodeBytes(data)
require.NoError(t, err)
require.Equal(t, chain1.ID, chain2.ID)
}
func TestEncodeDecode(t *testing.T) {
expected := Chain{
MatchType: MatchTypeFirstMatch,
Rules: []Rule{
{
Status: Allow,
Actions: Actions{Names: []string{
"native::PutObject",
}},
Resources: Resources{Names: []string{"*"}},
Condition: []Condition{
{
Op: CondStringEquals,
Key: "Name",
Value: "NNS",
},
},
},
},
}
data := expected.Bytes()
var actual Chain
require.NoError(t, actual.DecodeBytes(data))
require.Equal(t, expected, actual)
}
func TestChainMatch(t *testing.T) {
ch := Chain{
Rules: []Rule{
{
Status: Allow,
Actions: Actions{Names: []string{
native.MethodPutObject,
}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{},
},
{
Status: AccessDenied,
Actions: Actions{Names: []string{
native.MethodPutObject,
}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{},
},
},
}
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, nil)
t.Run("default match", func(t *testing.T) {
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, AccessDenied, st)
})
t.Run("return first match", func(t *testing.T) {
ch.MatchType = MatchTypeFirstMatch
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
})
t.Run("unknown match", func(t *testing.T) {
ch.MatchType = MatchType(255)
request := testutil.NewRequest(native.MethodGetObject, resource, nil)
require.PanicsWithValue(t, "unknown MatchType 255", func() {
ch.Match(request)
})
})
t.Run("no rule found", func(t *testing.T) {
ch.MatchType = MatchTypeFirstMatch
request := testutil.NewRequest(native.MethodGetObject, resource, nil)
st, found := ch.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
}
func TestAnyAllConditionMatch(t *testing.T) {
ch := Chain{
Rules: []Rule{
{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{
{
Op: CondIPAddress,
Kind: KindRequest,
Key: common.PropertyKeyFrostFSSourceIP,
Value: "192.92.1.1/20",
},
{
Op: CondStringEquals,
Kind: KindRequest,
Key: native.PropertyKeyActorRole,
Value: "owner",
},
{
Op: CondStringEquals,
Kind: KindResource,
Key: native.PropertyKeyObjectID,
Value: "79xGoKYwhyJQhrDNb7bHhY1WCvN6trHJPTjKkw24c6W9",
},
},
},
}}
t.Run("match by all conditions", func(t *testing.T) {
ch.Rules[0].Any = false
resource := testutil.NewResource(native.ResourceFormatRootContainers, map[string]string{
native.PropertyKeyObjectID: "79xGoKYwhyJQhrDNb7bHhY1WCvN6trHJPTjKkw24c6W9",
})
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
native.PropertyKeyActorRole: "owner",
})
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
common.PropertyKeyFrostFSSourceIP: "192.93.1.91",
})
st, found = ch.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
t.Run("match by any condition", func(t *testing.T) {
ch.Rules[0].Any = true
resource := testutil.NewResource(native.ResourceFormatRootContainers, map[string]string{
native.PropertyKeyObjectID: "79xGoKYwhyJQhrDNb7bHhY1WCvN6trHJPTjKkw24c6W9",
})
request := testutil.NewRequest(native.MethodPutObject, resource, nil)
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
native.PropertyKeyActorRole: "owner",
})
st, found = ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
resource = testutil.NewResource(native.ResourceFormatRootContainers, map[string]string{})
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
})
st, found = ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
common.PropertyKeyFrostFSSourceIP: "192.93.1.91",
})
st, found = ch.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
}
func TestConditionMatch(t *testing.T) {
t.Run("condition types", func(t *testing.T) {
t.Run("slice condition type", testCondSliceContainsMatch)
t.Run("numeric condition types", testNumericConditionsMatch)
t.Run("string condition types", testStringConiditionsMatch)
t.Run("ip conidition types", testIPConditionMatch)
t.Run("unknown condition type", func(t *testing.T) {
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: ConditionType(255),
Kind: KindRequest,
Key: common.PropertyKeyFrostFSSourceIP,
Value: "192.92.1.1/20",
}},
}}}
resource := testutil.NewResource(native.ResourceFormatRootContainers, map[string]string{
native.PropertyKeyObjectID: "79xGoKYwhyJQhrDNb7bHhY1WCvN6trHJPTjKkw24c6W9",
})
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
native.PropertyKeyActorRole: "owner",
})
require.PanicsWithValue(t, "unimplemented: 255", func() {
ch.Match(request)
})
})
})
t.Run("kind", func(t *testing.T) {
resource := testutil.NewResource(native.ResourceFormatRootContainers, map[string]string{
native.PropertyKeyObjectID: "79xGoKYwhyJQhrDNb7bHhY1WCvN6trHJPTjKkw24c6W9",
})
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
native.PropertyKeyActorRole: "owner",
})
t.Run("resource", func(t *testing.T) {
cond := Condition{
Op: CondStringEquals,
Kind: KindResource,
Key: native.PropertyKeyObjectID,
Value: "79xGoKYwhyJQhrDNb7bHhY1WCvN6trHJPTjKkw24c6W9",
}
found := cond.Match(request)
require.True(t, found)
})
t.Run("request", func(t *testing.T) {
cond := Condition{
Op: CondStringEquals,
Kind: KindRequest,
Key: native.PropertyKeyActorRole,
Value: "owner",
}
found := cond.Match(request)
require.True(t, found)
})
t.Run("unknown", func(t *testing.T) {
cond := Condition{
Op: CondStringEquals,
Kind: ConditionKindType(255),
Key: native.PropertyKeyActorRole,
Value: "owner",
}
require.PanicsWithValue(t, "unknown condition type: 255", func() {
cond.Match(request)
})
})
})
}
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,
Kind: KindRequest,
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())
})
}
}
func testNumericConditionsMatch(t *testing.T) {
propKey := s3.PropertyKeyMaxKeys
for _, tc := range []struct {
name string
conditions []Condition
value string
status Status
}{
{
name: "value from interval",
conditions: []Condition{
{
Op: CondNumericLessThan,
Kind: KindRequest,
Key: propKey,
Value: "100",
},
{
Op: CondNumericGreaterThan,
Kind: KindRequest,
Key: propKey,
Value: "80",
},
{
Op: CondNumericNotEquals,
Kind: KindRequest,
Key: propKey,
Value: "91",
},
},
value: "90",
status: Allow,
},
{
name: "border value",
conditions: []Condition{
{
Op: CondNumericEquals,
Kind: KindRequest,
Key: propKey,
Value: "50",
},
{
Op: CondNumericLessThanEquals,
Kind: KindRequest,
Key: propKey,
Value: "50",
},
{
Op: CondNumericGreaterThanEquals,
Kind: KindRequest,
Key: propKey,
Value: "50",
},
},
value: "50",
status: Allow,
},
} {
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})
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: tc.conditions,
}}}
st, _ := ch.Match(request)
require.Equal(t, tc.status.String(), st.String())
})
}
}
func testStringConiditionsMatch(t *testing.T) {
propKey := fmt.Sprintf(common.PropertyKeyFormatFrostFSIDUserClaim, "some-tag")
val := "tag-value"
t.Run(CondStringEquals.String(), func(t *testing.T) {
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: CondStringEquals,
Kind: KindRequest,
Key: propKey,
Value: val,
}},
}}}
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: val,
})
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: "distort_tag_value" + val,
})
st, found = ch.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
t.Run(CondStringNotEquals.String(), func(t *testing.T) {
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: CondStringNotEquals,
Kind: KindRequest,
Key: propKey,
Value: val,
}},
}}}
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: "distort_tag_value" + val,
})
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: val,
})
st, found = ch.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
t.Run(CondStringEqualsIgnoreCase.String(), func(t *testing.T) {
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: CondStringEqualsIgnoreCase,
Kind: KindRequest,
Key: propKey,
Value: val,
}},
}}}
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: strings.ToUpper(val),
})
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: strings.ToUpper("distort_tag_value" + val),
})
st, found = ch.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
t.Run(CondStringNotEqualsIgnoreCase.String(), func(t *testing.T) {
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: CondStringNotEqualsIgnoreCase,
Kind: KindRequest,
Key: propKey,
Value: val,
}},
}}}
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: strings.ToUpper("distort_tag_value" + val),
})
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: strings.ToUpper(val),
})
st, found = ch.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
t.Run(CondStringLike.String(), func(t *testing.T) {
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: CondStringLike,
Kind: KindRequest,
Key: propKey,
Value: val + "*",
}},
}}}
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: val + "suffix",
})
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: string([]byte(val)[:len(val)-1]), //cut last letter
})
st, found = ch.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
t.Run(CondStringNotLike.String(), func(t *testing.T) {
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: CondStringNotLike,
Kind: KindRequest,
Key: propKey,
Value: "prefix" + val + "*",
}},
}}}
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: val + "suffix",
})
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: "prefix" + val,
})
st, found = ch.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
t.Run(CondStringLessThan.String(), func(t *testing.T) {
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: CondStringLessThan,
Kind: KindRequest,
Key: propKey,
Value: val + "b",
}},
}}}
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: val + "a",
})
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: val + "c",
})
st, found = ch.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
t.Run(CondStringLessThanEquals.String(), func(t *testing.T) {
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: CondStringLessThanEquals,
Kind: KindRequest,
Key: propKey,
Value: val + "b",
}},
}}}
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: val + "a",
})
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: val + "b",
})
st, found = ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
})
t.Run(CondStringGreaterThan.String(), func(t *testing.T) {
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: CondStringGreaterThan,
Kind: KindRequest,
Key: propKey,
Value: val + "b",
}},
}}}
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: val + "c",
})
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: val + "b",
})
st, found = ch.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
t.Run(CondStringGreaterThanEquals.String(), func(t *testing.T) {
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: CondStringGreaterThanEquals,
Kind: KindRequest,
Key: propKey,
Value: val + "b",
}},
}}}
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: val + "c",
})
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
propKey: val + "b",
})
st, found = ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
})
}
func testIPConditionMatch(t *testing.T) {
t.Run(CondIPAddress.String(), func(t *testing.T) {
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: CondIPAddress,
Kind: KindRequest,
Key: common.PropertyKeyFrostFSSourceIP,
Value: "192.92.1.1/20",
}},
}}}
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
})
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
common.PropertyKeyFrostFSSourceIP: "192.93.1.91",
})
st, found = ch.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
t.Run(CondNotIPAddress.String(), func(t *testing.T) {
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: CondNotIPAddress,
Kind: KindRequest,
Key: common.PropertyKeyFrostFSSourceIP,
Value: "192.92.1.1/20",
}},
}}}
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
common.PropertyKeyFrostFSSourceIP: "192.93.1.91",
})
st, found := ch.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
})
st, found = ch.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
t.Run("invalid ip address condition value", func(t *testing.T) {
ch := Chain{Rules: []Rule{{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: CondIPAddress,
Kind: KindRequest,
Key: common.PropertyKeyFrostFSSourceIP,
Value: "192.92.1.1:33333",
}},
}}}
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
})
st, found := ch.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
t.Run("match ip", func(t *testing.T) {
cond := Condition{
Op: CondIPAddress,
Kind: KindRequest,
Key: common.PropertyKeyFrostFSSourceIP,
Value: "192.92.1.1/10",
}
require.NotPanics(t, func() {
cond.matchIP("192.92.1.91")
})
require.PanicsWithValue(t, "unimplemented: 10", func() {
cond.Op = CondNumericEquals
cond.matchIP("192.92.1.91")
})
})
}
func TestInvalidNumericValues(t *testing.T) {
t.Run("invalid request property", func(t *testing.T) {
propKey := s3.PropertyKeyMaxKeys
propValues := []string{"", "invalid"}
for _, tc := range []struct {
name string
conditionType ConditionType
match bool
}{
{
name: "NumericEquals condition",
conditionType: CondNumericEquals,
match: false,
},
{
name: "NumericNotEquals condition",
conditionType: CondNumericNotEquals,
match: true,
},
{
name: "NumericLessThan condition",
conditionType: CondNumericLessThan,
match: false,
},
{
name: "NumericLessThanEquals condition",
conditionType: CondNumericLessThanEquals,
match: false,
},
{
name: "NumericGreaterThan condition",
conditionType: CondNumericGreaterThan,
match: false,
},
{
name: "NumericGreaterThanEquals condition",
conditionType: CondNumericGreaterThanEquals,
match: false,
},
} {
t.Run(tc.name, func(t *testing.T) {
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
condition := Condition{
Op: tc.conditionType,
Kind: KindRequest,
Key: propKey,
Value: "50",
}
for _, propValue := range propValues {
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{propKey: propValue})
match := condition.Match(request)
require.Equal(t, tc.match, match)
}
})
}
})
t.Run("invalid condition numeric value", func(t *testing.T) {
propKey := s3.PropertyKeyMaxKeys
condition := Condition{
Kind: KindRequest,
Key: propKey,
Value: "invalid",
}
t.Run("match on CondNumericNotEquals", func(t *testing.T) {
condition.Op = CondNumericNotEquals
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{propKey: "50"})
match := condition.Match(request)
require.Equal(t, true, match)
})
t.Run("non-match on non-CondNumericNotEquals", func(t *testing.T) {
condition.Op = CondNumericEquals
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{propKey: "50"})
match := condition.Match(request)
require.Equal(t, false, match)
})
t.Run("match numeric", func(t *testing.T) {
cond := Condition{
Op: CondNumericLessThan,
Kind: KindRequest,
Key: s3.PropertyKeyMaxKeys,
Value: "5",
}
require.NotPanics(t, func() {
cond.matchNumeric("6")
})
require.PanicsWithValue(t, "unimplemented: 0", func() {
cond.Op = CondStringEquals
cond.matchNumeric("10")
})
})
})
}
func TestCondTypeStringification(t *testing.T) {
for _, pair := range []struct {
cond ConditionType
expectedString string
}{
{CondStringEquals, "StringEquals"},
{CondStringNotEquals, "StringNotEquals"},
{CondStringEqualsIgnoreCase, "StringEqualsIgnoreCase"},
{CondStringNotEqualsIgnoreCase, "StringNotEqualsIgnoreCase"},
{CondStringLike, "StringLike"},
{CondStringNotLike, "StringNotLike"},
{CondStringLessThan, "StringLessThan"},
{CondStringLessThanEquals, "StringLessThanEquals"},
{CondStringGreaterThan, "StringGreaterThan"},
{CondStringGreaterThanEquals, "StringGreaterThanEquals"},
{CondNumericEquals, "NumericEquals"},
{CondNumericNotEquals, "NumericNotEquals"},
{CondNumericLessThan, "NumericLessThan"},
{CondNumericLessThanEquals, "NumericLessThanEquals"},
{CondNumericGreaterThan, "NumericGreaterThan"},
{CondNumericGreaterThanEquals, "NumericGreaterThanEquals"},
{CondSliceContains, "SliceContains"},
{CondIPAddress, "IPAddress"},
{CondNotIPAddress, "NotIPAddress"},
{ConditionType(255), "unknown condition type"},
} {
require.Equal(t, pair.expectedString, pair.cond.String())
}
}
func TestRuleMatch(t *testing.T) {
rule := Rule{
Status: Allow,
Actions: Actions{Names: []string{native.MethodPutObject}},
Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
Condition: []Condition{{
Op: CondIPAddress,
Kind: KindRequest,
Key: common.PropertyKeyFrostFSSourceIP,
Value: "192.92.1.1/20",
}},
}
t.Run("match", func(t *testing.T) {
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
})
st, found := rule.Match(request)
require.True(t, found)
require.Equal(t, Allow, st)
})
t.Run("not matching resource name", func(t *testing.T) {
resource := testutil.NewResource(fmt.Sprintf(native.ResourceFormatNamespaceContainers, "namespicy"), nil)
request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
})
st, found := rule.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
t.Run("not matching action", func(t *testing.T) {
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodGetObject, resource, map[string]string{
common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
})
st, found := rule.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
t.Run("not matching condition", func(t *testing.T) {
resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
request := testutil.NewRequest(native.MethodGetObject, resource, map[string]string{
common.PropertyKeyFrostFSSourceIP: "192.93.1.91",
})
st, found := rule.Match(request)
require.False(t, found)
require.Equal(t, NoRuleFound, st)
})
}