From ce9130fe7826f91833233cc7e74b31b2e990dc52 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 13 Feb 2024 22:54:51 +0400 Subject: [PATCH] eacl: Process numeric matchers in access rules' validator Handle handle recently added numeric `MatchType` values by `Validator` to support integer comparisons. Non-decimal filter and/or header value used with numeric matcher is considered as mismatching rule. Signed-off-by: Leonard Lyubich --- eacl/validator.go | 62 ++++++++++++++++++++--------- eacl/validator_test.go | 89 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 130 insertions(+), 21 deletions(-) diff --git a/eacl/validator.go b/eacl/validator.go index 86a2cdff..638f32ca 100644 --- a/eacl/validator.go +++ b/eacl/validator.go @@ -2,6 +2,7 @@ package eacl import ( "bytes" + "math/big" ) // Validator is a tool that calculates @@ -25,6 +26,9 @@ func NewValidator() *Validator { // // If no matching table entry is found or some filters are missing, // ActionAllow is returned and the second return value is false. +// +// Note that if some rule imposes requirements on the format of values (like +// numeric), but they do not comply with it - such a rule does not match. func (v *Validator) CalculateAction(unit *ValidationUnit) (Action, bool) { for _, record := range unit.table.Records() { // check type of operation @@ -56,6 +60,7 @@ func (v *Validator) CalculateAction(unit *ValidationUnit) (Action, bool) { // - negative value if the headers of at least one filter cannot be obtained. func matchFilters(hdrSrc TypedHeaderSource, filters []Filter) int { matched := 0 + var nv, nf big.Int for _, filter := range filters { headers, ok := hdrSrc.HeadersOfType(filter.From()) @@ -63,6 +68,13 @@ func matchFilters(hdrSrc TypedHeaderSource, filters []Filter) int { return -1 } + m := filter.Matcher() + if m == MatchNumGT || m == MatchNumGE || m == MatchNumLT || m == MatchNumLE { + if _, ok = nf.SetString(filter.Value(), 10); !ok { + continue + } + } + // get headers of filtering type for _, header := range headers { // prevent NPE @@ -75,15 +87,40 @@ func matchFilters(hdrSrc TypedHeaderSource, filters []Filter) int { continue } - // get match function - matchFn, ok := mMatchFns[filter.Matcher()] - if !ok { - continue - } - // check match - if !matchFn(header, &filter) { + switch m { + default: // incl. MatchNotPresent continue + case MatchStringEqual: + if header.Value() != filter.Value() { + continue + } + case MatchStringNotEqual: + if header.Value() == filter.Value() { + continue + } + case MatchNumGT, MatchNumGE, MatchNumLT, MatchNumLE: + // TODO: big math simplifies coding but almost always not efficient + // enough, try to optimize + if _, ok = nv.SetString(header.Value(), 10); !ok { + continue + } + switch nf.Cmp(&nv) { + default: + continue // should never happen but just in case + case -1: + if m == MatchNumGT || m == MatchNumGE { + continue + } + case 0: + if m == MatchNumGT || m == MatchNumLT { + continue + } + case 1: + if m == MatchNumLT || m == MatchNumLE { + continue + } + } } // increment match counter @@ -123,14 +160,3 @@ func targetMatches(unit *ValidationUnit, record *Record) bool { return false } - -// Maps match type to corresponding function. -var mMatchFns = map[Match]func(Header, *Filter) bool{ - MatchStringEqual: func(header Header, filter *Filter) bool { - return header.Value() == filter.Value() - }, - - MatchStringNotEqual: func(header Header, filter *Filter) bool { - return header.Value() != filter.Value() - }, -} diff --git a/eacl/validator_test.go b/eacl/validator_test.go index 98a74dc2..e9c22906 100644 --- a/eacl/validator_test.go +++ b/eacl/validator_test.go @@ -19,10 +19,10 @@ func checkAction(t *testing.T, expected Action, v *Validator, vu *ValidationUnit require.Equal(t, expected, action) } -func checkDefaultAction(t *testing.T, v *Validator, vu *ValidationUnit) { +func checkDefaultAction(t *testing.T, v *Validator, vu *ValidationUnit, msgAndArgs ...any) { action, ok := v.CalculateAction(vu) - require.False(t, ok) - require.Equal(t, ActionAllow, action) + require.False(t, ok, msgAndArgs) + require.Equal(t, ActionAllow, action, msgAndArgs...) } func TestFilterMatch(t *testing.T) { @@ -283,3 +283,86 @@ func newValidationUnit(role Role, key []byte, table *Table) *ValidationUnit { WithSenderKey(key). WithEACLTable(table) } + +var allNumMatchers = []Match{MatchNumGT, MatchNumGE, MatchNumLT, MatchNumLE} + +func TestNumericRules(t *testing.T) { + for _, tc := range []struct { + m Match + f string + h string + exp bool + }{ + // > + {MatchNumGT, "non-decimal", "0", false}, + {MatchNumGT, "0", "non-decimal", false}, + {MatchNumGT, "-1", "-2", true}, + {MatchNumGT, "0", "0", false}, + {MatchNumGT, "0", "-1", true}, + {MatchNumGT, "1", "0", true}, + {MatchNumGT, "111111111111111111111111111111", "111111111111111111111111111110", true}, // more than 64-bit + {MatchNumGT, "111111111111111111111111111111", "111111111111111111111111111111", false}, + {MatchNumGT, "-111111111111111111111111111110", "-111111111111111111111111111111", true}, + {MatchNumGT, "-2", "-1", false}, + {MatchNumGT, "-1", "0", false}, + {MatchNumGT, "0", "1", false}, + {MatchNumGT, "111111111111111111111111111110", "111111111111111111111111111111", false}, + {MatchNumGT, "-111111111111111111111111111111", "-111111111111111111111111111110", false}, + // >= + {MatchNumGE, "non-decimal", "0", false}, + {MatchNumGE, "0", "non-decimal", false}, + {MatchNumGE, "-1", "-2", true}, + {MatchNumGE, "0", "0", true}, + {MatchNumGE, "0", "-1", true}, + {MatchNumGE, "1", "0", true}, + {MatchNumGE, "111111111111111111111111111111", "111111111111111111111111111110", true}, + {MatchNumGE, "111111111111111111111111111111", "111111111111111111111111111111", true}, + {MatchNumGE, "-111111111111111111111111111110", "-111111111111111111111111111111", true}, + {MatchNumGE, "-2", "-1", false}, + {MatchNumGE, "-1", "0", false}, + {MatchNumGE, "0", "1", false}, + {MatchNumGE, "111111111111111111111111111110", "111111111111111111111111111111", false}, + {MatchNumGE, "-111111111111111111111111111111", "-111111111111111111111111111110", false}, + // < + {MatchNumLT, "non-decimal", "0", false}, + {MatchNumLT, "0", "non-decimal", false}, + {MatchNumLT, "-1", "-2", false}, + {MatchNumLT, "0", "0", false}, + {MatchNumLT, "0", "-1", false}, + {MatchNumLT, "1", "0", false}, + {MatchNumLT, "111111111111111111111111111111", "111111111111111111111111111110", false}, + {MatchNumLT, "111111111111111111111111111111", "111111111111111111111111111111", false}, + {MatchNumLT, "-111111111111111111111111111110", "-111111111111111111111111111111", false}, + {MatchNumLT, "-2", "-1", true}, + {MatchNumLT, "-1", "0", true}, + {MatchNumLT, "0", "1", true}, + {MatchNumLT, "111111111111111111111111111110", "111111111111111111111111111111", true}, + {MatchNumLT, "-111111111111111111111111111111", "-111111111111111111111111111110", true}, + // <= + {MatchNumLE, "non-decimal", "0", false}, + {MatchNumLE, "0", "non-decimal", false}, + {MatchNumLE, "-1", "-2", false}, + {MatchNumLE, "0", "0", true}, + {MatchNumLE, "0", "-1", false}, + {MatchNumLE, "1", "0", false}, + {MatchNumLE, "111111111111111111111111111111", "111111111111111111111111111110", false}, + {MatchNumLE, "111111111111111111111111111111", "111111111111111111111111111111", true}, + {MatchNumLE, "-111111111111111111111111111110", "-111111111111111111111111111111", false}, + {MatchNumLE, "-2", "-1", true}, + {MatchNumLE, "-1", "0", true}, + {MatchNumLE, "0", "1", true}, + {MatchNumLE, "111111111111111111111111111110", "111111111111111111111111111111", true}, + {MatchNumLE, "-111111111111111111111111111111", "-111111111111111111111111111110", true}, + } { + var rec Record + rec.AddObjectAttributeFilter(tc.m, "any_key", tc.f) + hs := headers{obj: makeHeaders("any_key", tc.h)} + + v := matchFilters(hs, rec.filters) + if tc.exp { + require.Zero(t, v, tc) + } else { + require.Positive(t, v, tc) + } + } +}