forked from TrueCloudLab/policy-engine
[#11] Support inverted action and resource
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
5eee1a7334
commit
8d291039d8
3 changed files with 107 additions and 31 deletions
63
chain.go
63
chain.go
|
@ -37,15 +37,25 @@ func (c *Chain) DecodeBytes(b []byte) error {
|
|||
type Rule struct {
|
||||
Status Status
|
||||
// Actions the operation is applied to.
|
||||
Action []string
|
||||
Actions Actions
|
||||
// List of the resources the operation is applied to.
|
||||
Resource []string
|
||||
Resources Resources
|
||||
// True iff individual conditions must be combined with the logical OR.
|
||||
// By default AND is used, so _each_ condition must pass.
|
||||
Any bool
|
||||
Condition []Condition
|
||||
}
|
||||
|
||||
type Actions struct {
|
||||
Inverted bool
|
||||
Names []string
|
||||
}
|
||||
|
||||
type Resources struct {
|
||||
Inverted bool
|
||||
Names []string
|
||||
}
|
||||
|
||||
type Condition struct {
|
||||
Op ConditionType
|
||||
Object ObjectType
|
||||
|
@ -88,6 +98,45 @@ const (
|
|||
CondNumericGreaterThanEquals
|
||||
)
|
||||
|
||||
func (c ConditionType) String() string {
|
||||
switch c {
|
||||
case CondStringEquals:
|
||||
return "StringEquals"
|
||||
case CondStringNotEquals:
|
||||
return "StringNotEquals"
|
||||
case CondStringEqualsIgnoreCase:
|
||||
return "StringEqualsIgnoreCase"
|
||||
case CondStringNotEqualsIgnoreCase:
|
||||
return "StringNotEqualsIgnoreCase"
|
||||
case CondStringLike:
|
||||
return "StringLike"
|
||||
case CondStringNotLike:
|
||||
return "StringNotLike"
|
||||
case CondStringLessThan:
|
||||
return "StringLessThan"
|
||||
case CondStringLessThanEquals:
|
||||
return "StringLessThanEquals"
|
||||
case CondStringGreaterThan:
|
||||
return "StringGreaterThan"
|
||||
case CondStringGreaterThanEquals:
|
||||
return "StringGreaterThanEquals"
|
||||
case CondNumericEquals:
|
||||
return "NumericEquals"
|
||||
case CondNumericNotEquals:
|
||||
return "NumericNotEquals"
|
||||
case CondNumericLessThan:
|
||||
return "NumericLessThan"
|
||||
case CondNumericLessThanEquals:
|
||||
return "NumericLessThanEquals"
|
||||
case CondNumericGreaterThan:
|
||||
return "NumericGreaterThan"
|
||||
case CondNumericGreaterThanEquals:
|
||||
return "NumericGreaterThanEquals"
|
||||
default:
|
||||
return "unknown condition type"
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Condition) Match(req Request) bool {
|
||||
var val string
|
||||
switch c.Object {
|
||||
|
@ -126,9 +175,9 @@ func (c *Condition) Match(req Request) bool {
|
|||
}
|
||||
|
||||
func (r *Rule) Match(req Request) (status Status, matched bool) {
|
||||
found := len(r.Resource) == 0
|
||||
for i := range r.Resource {
|
||||
if globMatch(req.Resource().Name(), r.Resource[i]) {
|
||||
found := len(r.Resources.Names) == 0
|
||||
for i := range r.Resources.Names {
|
||||
if globMatch(req.Resource().Name(), r.Resources.Names[i]) != r.Resources.Inverted {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
@ -136,8 +185,8 @@ func (r *Rule) Match(req Request) (status Status, matched bool) {
|
|||
if !found {
|
||||
return NoRuleFound, false
|
||||
}
|
||||
for i := range r.Action {
|
||||
if globMatch(req.Operation(), r.Action[i]) {
|
||||
for i := range r.Actions.Names {
|
||||
if globMatch(req.Operation(), r.Actions.Names[i]) != r.Actions.Inverted {
|
||||
return r.matchCondition(req)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ func TestEncodeDecode(t *testing.T) {
|
|||
Rules: []Rule{
|
||||
{
|
||||
Status: Allow,
|
||||
Action: []string{
|
||||
Actions: Actions{Names: []string{
|
||||
"native::PutObject",
|
||||
},
|
||||
Resource: []string{"*"},
|
||||
}},
|
||||
Resources: Resources{Names: []string{"*"}},
|
||||
Condition: []Condition{
|
||||
{
|
||||
Op: CondStringEquals,
|
||||
|
|
|
@ -11,6 +11,7 @@ func TestInmemory(t *testing.T) {
|
|||
object = "native::object::abc/xyz"
|
||||
container = "native::object::abc/*"
|
||||
namespace = "Tenant1"
|
||||
namespace2 = "Tenant2"
|
||||
actor1 = "owner1"
|
||||
actor2 = "owner2"
|
||||
)
|
||||
|
@ -33,13 +34,13 @@ func TestInmemory(t *testing.T) {
|
|||
Rules: []Rule{
|
||||
{ // Restrict to remove ANY object from the namespace.
|
||||
Status: AccessDenied,
|
||||
Action: []string{"native::object::delete"},
|
||||
Resource: []string{"native::object::*"},
|
||||
Actions: Actions{Names: []string{"native::object::delete"}},
|
||||
Resources: Resources{Names: []string{"native::object::*"}},
|
||||
},
|
||||
{ // Allow to put object only from the trusted subnet AND trusted actor, deny otherwise.
|
||||
Status: AccessDenied,
|
||||
Action: []string{"native::object::put"},
|
||||
Resource: []string{"native::object::*"},
|
||||
Actions: Actions{Names: []string{"native::object::put"}},
|
||||
Resources: Resources{Names: []string{"native::object::*"}},
|
||||
Any: true,
|
||||
Condition: []Condition{
|
||||
{
|
||||
|
@ -59,12 +60,22 @@ func TestInmemory(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
s.AddNameSpaceChain(Ingress, namespace2, &Chain{
|
||||
Rules: []Rule{
|
||||
{ // Deny all expect "native::object::get" for all objects expect "native::object::abc/xyz".
|
||||
Status: AccessDenied,
|
||||
Actions: Actions{Inverted: true, Names: []string{"native::object::get"}},
|
||||
Resources: Resources{Inverted: true, Names: []string{object}},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
s.AddResourceChain(Ingress, container, &Chain{
|
||||
Rules: []Rule{
|
||||
{ // Allow to actor2 to get objects from the specific container only if they have `Department=HR` attribute.
|
||||
Status: Allow,
|
||||
Action: []string{"native::object::get"},
|
||||
Resource: []string{"native::object::abc/*"},
|
||||
Actions: Actions{Names: []string{"native::object::get"}},
|
||||
Resources: Resources{Names: []string{"native::object::abc/*"}},
|
||||
Condition: []Condition{
|
||||
{
|
||||
Op: CondStringEquals,
|
||||
|
@ -140,8 +151,8 @@ func TestInmemory(t *testing.T) {
|
|||
s.AddOverride(Ingress, &Chain{
|
||||
Rules: []Rule{{
|
||||
Status: QuotaLimitReached,
|
||||
Action: []string{"native::object::put"},
|
||||
Resource: []string{"native::object::cba/*"},
|
||||
Actions: Actions{Names: []string{"native::object::put"}},
|
||||
Resources: Resources{Names: []string{"native::object::cba/*"}},
|
||||
}},
|
||||
})
|
||||
|
||||
|
@ -153,8 +164,8 @@ func TestInmemory(t *testing.T) {
|
|||
s.AddOverride(Ingress, &Chain{
|
||||
Rules: []Rule{{
|
||||
Status: QuotaLimitReached,
|
||||
Action: []string{"native::object::put"},
|
||||
Resource: []string{"native::object::abc/*"},
|
||||
Actions: Actions{Names: []string{"native::object::put"}},
|
||||
Resources: Resources{Names: []string{"native::object::abc/*"}},
|
||||
}},
|
||||
})
|
||||
|
||||
|
@ -163,4 +174,20 @@ func TestInmemory(t *testing.T) {
|
|||
require.True(t, ok)
|
||||
})
|
||||
})
|
||||
t.Run("inverted rules", func(t *testing.T) {
|
||||
req := newRequest("native::object::put", newResource(object, nil), nil)
|
||||
status, ok = s.IsAllowed(Ingress, namespace2, req)
|
||||
require.Equal(t, NoRuleFound, status)
|
||||
require.False(t, ok)
|
||||
|
||||
req = newRequest("native::object::put", newResource("native::object::cba/def", nil), nil)
|
||||
status, ok = s.IsAllowed(Ingress, namespace2, req)
|
||||
require.Equal(t, AccessDenied, status)
|
||||
require.True(t, ok)
|
||||
|
||||
req = newRequest("native::object::get", newResource("native::object::cba/def", nil), nil)
|
||||
status, ok = s.IsAllowed(Ingress, namespace2, req)
|
||||
require.Equal(t, NoRuleFound, status)
|
||||
require.False(t, ok)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue