git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/eacl/validator_test.go (about)

     1  package eacl
     2  
     3  import (
     4  	"crypto/rand"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  )
     9  
    10  func checkAction(t *testing.T, expected Action, v *Validator, vu *ValidationUnit) {
    11  	action, ok := v.CalculateAction(vu)
    12  	require.True(t, ok)
    13  	require.Equal(t, expected, action)
    14  }
    15  
    16  func checkDefaultAction(t *testing.T, v *Validator, vu *ValidationUnit) {
    17  	action, ok := v.CalculateAction(vu)
    18  	require.False(t, ok)
    19  	require.Equal(t, ActionAllow, action)
    20  }
    21  
    22  func TestFilterMatch(t *testing.T) {
    23  	tgt := *NewTarget()
    24  	tgt.SetRole(RoleOthers)
    25  
    26  	t.Run("simple header match", func(t *testing.T) {
    27  		tb := NewTable()
    28  
    29  		r := newRecord(ActionDeny, OperationUnknown, tgt)
    30  		r.AddFilter(HeaderFromObject, MatchStringEqual, "a", "xxx")
    31  		tb.AddRecord(r)
    32  
    33  		r = newRecord(ActionDeny, OperationUnknown, tgt)
    34  		r.AddFilter(HeaderFromRequest, MatchStringNotEqual, "b", "yyy")
    35  		tb.AddRecord(r)
    36  
    37  		tb.AddRecord(newRecord(ActionAllow, OperationUnknown, tgt))
    38  
    39  		v := NewValidator()
    40  		vu := newValidationUnit(RoleOthers, nil, tb)
    41  		hs := headers{}
    42  		vu.hdrSrc = &hs
    43  
    44  		checkAction(t, ActionAllow, v, vu)
    45  
    46  		hs.obj = makeHeaders("b", "yyy")
    47  		checkAction(t, ActionAllow, v, vu)
    48  
    49  		hs.obj = makeHeaders("a", "xxx")
    50  		checkAction(t, ActionDeny, v, vu)
    51  
    52  		hs.obj = nil
    53  		hs.req = makeHeaders("b", "yyy")
    54  		checkAction(t, ActionAllow, v, vu)
    55  
    56  		hs.req = makeHeaders("b", "abc")
    57  		checkAction(t, ActionDeny, v, vu)
    58  	})
    59  
    60  	t.Run("all filters must match", func(t *testing.T) {
    61  		tb := NewTable()
    62  		r := newRecord(ActionDeny, OperationUnknown, tgt)
    63  		r.AddFilter(HeaderFromObject, MatchStringEqual, "a", "xxx")
    64  		r.AddFilter(HeaderFromRequest, MatchStringEqual, "b", "yyy")
    65  		tb.AddRecord(r)
    66  		tb.AddRecord(newRecord(ActionAllow, OperationUnknown, tgt))
    67  
    68  		v := NewValidator()
    69  		vu := newValidationUnit(RoleOthers, nil, tb)
    70  		hs := headers{}
    71  		vu.hdrSrc = &hs
    72  
    73  		hs.obj = makeHeaders("a", "xxx")
    74  		checkAction(t, ActionAllow, v, vu)
    75  
    76  		hs.req = makeHeaders("b", "yyy")
    77  		checkAction(t, ActionDeny, v, vu)
    78  
    79  		hs.obj = nil
    80  		checkAction(t, ActionAllow, v, vu)
    81  	})
    82  
    83  	t.Run("filters with unknown type are skipped", func(t *testing.T) {
    84  		tb := NewTable()
    85  		r := newRecord(ActionDeny, OperationUnknown, tgt)
    86  		r.AddFilter(HeaderTypeUnknown, MatchStringEqual, "a", "xxx")
    87  		tb.AddRecord(r)
    88  
    89  		r = newRecord(ActionDeny, OperationUnknown, tgt)
    90  		r.AddFilter(0xFF, MatchStringEqual, "b", "yyy")
    91  		tb.AddRecord(r)
    92  
    93  		tb.AddRecord(newRecord(ActionDeny, OperationUnknown, tgt))
    94  
    95  		v := NewValidator()
    96  		vu := newValidationUnit(RoleOthers, nil, tb)
    97  		hs := headers{}
    98  		vu.hdrSrc = &hs
    99  
   100  		checkDefaultAction(t, v, vu)
   101  
   102  		hs.obj = makeHeaders("a", "xxx")
   103  		checkDefaultAction(t, v, vu)
   104  
   105  		hs.obj = nil
   106  		hs.req = makeHeaders("b", "yyy")
   107  		checkDefaultAction(t, v, vu)
   108  	})
   109  
   110  	t.Run("filters with match function are skipped", func(t *testing.T) {
   111  		tb := NewTable()
   112  		r := newRecord(ActionAllow, OperationUnknown, tgt)
   113  		r.AddFilter(HeaderFromObject, 0xFF, "a", "xxx")
   114  		tb.AddRecord(r)
   115  		tb.AddRecord(newRecord(ActionDeny, OperationUnknown, tgt))
   116  
   117  		v := NewValidator()
   118  		vu := newValidationUnit(RoleOthers, nil, tb)
   119  		hs := headers{}
   120  		vu.hdrSrc = &hs
   121  
   122  		checkAction(t, ActionDeny, v, vu)
   123  
   124  		hs.obj = makeHeaders("a", "xxx")
   125  		checkAction(t, ActionDeny, v, vu)
   126  	})
   127  }
   128  
   129  func TestOperationMatch(t *testing.T) {
   130  	tgt := *NewTarget()
   131  	tgt.SetRole(RoleOthers)
   132  
   133  	t.Run("single operation", func(t *testing.T) {
   134  		tb := NewTable()
   135  		tb.AddRecord(newRecord(ActionDeny, OperationPut, tgt))
   136  		tb.AddRecord(newRecord(ActionAllow, OperationGet, tgt))
   137  
   138  		v := NewValidator()
   139  		vu := newValidationUnit(RoleOthers, nil, tb)
   140  
   141  		vu.op = OperationPut
   142  		checkAction(t, ActionDeny, v, vu)
   143  
   144  		vu.op = OperationGet
   145  		checkAction(t, ActionAllow, v, vu)
   146  	})
   147  
   148  	t.Run("unknown operation", func(t *testing.T) {
   149  		tb := NewTable()
   150  		tb.AddRecord(newRecord(ActionDeny, OperationUnknown, tgt))
   151  		tb.AddRecord(newRecord(ActionAllow, OperationGet, tgt))
   152  
   153  		v := NewValidator()
   154  		vu := newValidationUnit(RoleOthers, nil, tb)
   155  
   156  		// TODO discuss if both next tests should result in DENY
   157  		vu.op = OperationPut
   158  		checkDefaultAction(t, v, vu)
   159  
   160  		vu.op = OperationGet
   161  		checkAction(t, ActionAllow, v, vu)
   162  	})
   163  }
   164  
   165  func TestTargetMatches(t *testing.T) {
   166  	pubs := makeKeys(t, 3)
   167  
   168  	tgt1 := NewTarget()
   169  	tgt1.SetBinaryKeys(pubs[0:2])
   170  	tgt1.SetRole(RoleUser)
   171  
   172  	tgt2 := NewTarget()
   173  	tgt2.SetRole(RoleOthers)
   174  
   175  	r := NewRecord()
   176  	r.SetTargets(*tgt1, *tgt2)
   177  
   178  	u := newValidationUnit(RoleUser, pubs[0], nil)
   179  	require.True(t, targetMatches(u, r))
   180  
   181  	u = newValidationUnit(RoleUser, pubs[2], nil)
   182  	require.False(t, targetMatches(u, r))
   183  
   184  	u = newValidationUnit(RoleUnknown, pubs[1], nil)
   185  	require.True(t, targetMatches(u, r))
   186  
   187  	u = newValidationUnit(RoleOthers, pubs[2], nil)
   188  	require.True(t, targetMatches(u, r))
   189  
   190  	u = newValidationUnit(RoleSystem, pubs[2], nil)
   191  	require.False(t, targetMatches(u, r))
   192  }
   193  
   194  func makeKeys(t *testing.T, n int) [][]byte {
   195  	pubs := make([][]byte, n)
   196  	for i := range pubs {
   197  		pubs[i] = make([]byte, 33)
   198  		pubs[i][0] = 0x02
   199  
   200  		_, err := rand.Read(pubs[i][1:])
   201  		require.NoError(t, err)
   202  	}
   203  	return pubs
   204  }
   205  
   206  type (
   207  	hdr struct {
   208  		key, value string
   209  	}
   210  
   211  	headers struct {
   212  		obj []Header
   213  		req []Header
   214  	}
   215  )
   216  
   217  func (h hdr) Key() string   { return h.key }
   218  func (h hdr) Value() string { return h.value }
   219  
   220  func makeHeaders(kv ...string) []Header {
   221  	hs := make([]Header, len(kv)/2)
   222  	for i := 0; i < len(kv); i += 2 {
   223  		hs[i/2] = hdr{kv[i], kv[i+1]}
   224  	}
   225  	return hs
   226  }
   227  
   228  func (h headers) HeadersOfType(ht FilterHeaderType) ([]Header, bool) {
   229  	switch ht {
   230  	case HeaderFromRequest:
   231  		return h.req, true
   232  	case HeaderFromObject:
   233  		return h.obj, true
   234  	default:
   235  		return nil, false
   236  	}
   237  }
   238  
   239  func newRecord(a Action, op Operation, tgt ...Target) *Record {
   240  	r := NewRecord()
   241  	r.SetAction(a)
   242  	r.SetOperation(op)
   243  	r.SetTargets(tgt...)
   244  	return r
   245  }
   246  
   247  func newValidationUnit(role Role, key []byte, table *Table) *ValidationUnit {
   248  	return new(ValidationUnit).
   249  		WithRole(role).
   250  		WithSenderKey(key).
   251  		WithEACLTable(table)
   252  }