Skip to content

Commit

Permalink
eacl: Process numeric matchers in access rules' validator
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
cthulhu-rider committed Feb 13, 2024
1 parent 9c34769 commit ce9130f
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 21 deletions.
62 changes: 44 additions & 18 deletions eacl/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package eacl

import (
"bytes"
"math/big"
)

// Validator is a tool that calculates
Expand All @@ -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
Expand Down Expand Up @@ -56,13 +60,21 @@ 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())
if !ok {
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
Expand All @@ -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
Expand Down Expand Up @@ -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()
},
}
89 changes: 86 additions & 3 deletions eacl/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
}
}
}

0 comments on commit ce9130f

Please sign in to comment.