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) }) }