From d7ed188f68aac3d4fee878886f53293061cdc058 Mon Sep 17 00:00:00 2001 From: Airat Arifullin Date: Wed, 22 May 2024 13:49:42 +0300 Subject: [PATCH] [#76] chain: Increase unit-test coverage for chain related types * Add more unit-test cases. Signed-off-by: Airat Arifullin --- pkg/chain/chain_test.go | 849 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 795 insertions(+), 54 deletions(-) diff --git a/pkg/chain/chain_test.go b/pkg/chain/chain_test.go index f9b115a..a5154eb 100644 --- a/pkg/chain/chain_test.go +++ b/pkg/chain/chain_test.go @@ -1,6 +1,8 @@ package chain import ( + "fmt" + "strings" "testing" "git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource/testutil" @@ -50,7 +52,7 @@ func TestEncodeDecode(t *testing.T) { require.Equal(t, expected, actual) } -func TestReturnFirstMatch(t *testing.T) { +func TestChainMatch(t *testing.T) { ch := Chain{ Rules: []Rule{ { @@ -87,9 +89,195 @@ func TestReturnFirstMatch(t *testing.T) { 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 TestCondSliceContainsMatch(t *testing.T) { +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" @@ -151,7 +339,7 @@ func TestCondSliceContainsMatch(t *testing.T) { } } -func TestNumericConditionsMatch(t *testing.T) { +func testNumericConditionsMatch(t *testing.T) { propKey := s3.PropertyKeyMaxKeys for _, tc := range []struct { @@ -227,61 +415,614 @@ func TestNumericConditionsMatch(t *testing.T) { } } -func TestInvalidNumericValues(t *testing.T) { - propKey := s3.PropertyKeyMaxKeys - propValues := []string{"", "invalid"} +func testStringConiditionsMatch(t *testing.T) { + propKey := fmt.Sprintf(common.PropertyKeyFormatFrostFSIDUserClaim, "some-tag") + val := "tag-value" - 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, + 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: "50", - } + Value: val, + }}, + }}} - 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) - } + 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) + }) +}