forked from TrueCloudLab/policy-engine
[#21] chain: Allow to return first match result
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
parent
1375e8f7fd
commit
8c673ee4f4
3 changed files with 84 additions and 0 deletions
|
@ -478,6 +478,7 @@ func TestProcessDenyFirst(t *testing.T) {
|
||||||
|
|
||||||
identityNativePolicy, err := ConvertToNativeChain(identityPolicy, mockResolver)
|
identityNativePolicy, err := ConvertToNativeChain(identityPolicy, mockResolver)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
identityNativePolicy.MatchType = chain.MatchTypeFirstMatch
|
||||||
|
|
||||||
resourceNativePolicy, err := ConvertToNativeChain(resourcePolicy, mockResolver)
|
resourceNativePolicy, err := ConvertToNativeChain(resourcePolicy, mockResolver)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -12,10 +12,22 @@ import (
|
||||||
// ID is the ID of rule chain.
|
// ID is the ID of rule chain.
|
||||||
type ID string
|
type ID string
|
||||||
|
|
||||||
|
// MatchType is the match type for chain rules.
|
||||||
|
type MatchType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MatchTypeDenyPriority rejects the request if any `Deny` is specified.
|
||||||
|
MatchTypeDenyPriority MatchType = 0
|
||||||
|
// MatchTypeFirstMatch returns the first rule action matched to the request.
|
||||||
|
MatchTypeFirstMatch MatchType = 1
|
||||||
|
)
|
||||||
|
|
||||||
type Chain struct {
|
type Chain struct {
|
||||||
ID ID
|
ID ID
|
||||||
|
|
||||||
Rules []Rule
|
Rules []Rule
|
||||||
|
|
||||||
|
MatchType MatchType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Chain) Bytes() []byte {
|
func (c *Chain) Bytes() []byte {
|
||||||
|
@ -214,6 +226,17 @@ func (r *Rule) matchAll(obj resource.Request) (status Status, matched bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Chain) Match(req resource.Request) (status Status, matched bool) {
|
func (c *Chain) Match(req resource.Request) (status Status, matched bool) {
|
||||||
|
switch c.MatchType {
|
||||||
|
case MatchTypeDenyPriority:
|
||||||
|
return c.denyPriority(req)
|
||||||
|
case MatchTypeFirstMatch:
|
||||||
|
return c.firstMatch(req)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unknown MatchType %d", c.MatchType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chain) firstMatch(req resource.Request) (status Status, matched bool) {
|
||||||
for i := range c.Rules {
|
for i := range c.Rules {
|
||||||
status, matched := c.Rules[i].Match(req)
|
status, matched := c.Rules[i].Match(req)
|
||||||
if matched {
|
if matched {
|
||||||
|
@ -222,3 +245,21 @@ func (c *Chain) Match(req resource.Request) (status Status, matched bool) {
|
||||||
}
|
}
|
||||||
return NoRuleFound, false
|
return NoRuleFound, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Chain) denyPriority(req resource.Request) (status Status, matched bool) {
|
||||||
|
var allowFound bool
|
||||||
|
for i := range c.Rules {
|
||||||
|
status, matched := c.Rules[i].Match(req)
|
||||||
|
if !matched {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if status != Allow {
|
||||||
|
return status, true
|
||||||
|
}
|
||||||
|
allowFound = true
|
||||||
|
}
|
||||||
|
if allowFound {
|
||||||
|
return Allow, true
|
||||||
|
}
|
||||||
|
return NoRuleFound, false
|
||||||
|
}
|
||||||
|
|
|
@ -3,11 +3,14 @@ package chain
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource/testutil"
|
||||||
|
"git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEncodeDecode(t *testing.T) {
|
func TestEncodeDecode(t *testing.T) {
|
||||||
expected := Chain{
|
expected := Chain{
|
||||||
|
MatchType: MatchTypeFirstMatch,
|
||||||
Rules: []Rule{
|
Rules: []Rule{
|
||||||
{
|
{
|
||||||
Status: Allow,
|
Status: Allow,
|
||||||
|
@ -31,3 +34,42 @@ func TestEncodeDecode(t *testing.T) {
|
||||||
require.NoError(t, actual.DecodeBytes(data))
|
require.NoError(t, actual.DecodeBytes(data))
|
||||||
require.Equal(t, expected, actual)
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue