package chain 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" "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 TestReturnFirstMatch(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) }) } 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 TestInvalidNumericValues(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) } }) } }