From f05d2fc257c54e94b4e802999f7632d6e1b3d624 Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Mon, 1 Jun 2020 23:12:46 +0200 Subject: [PATCH] Add actionlog policies --- pkg/actionlog/canned_policies.go | 12 ++++ pkg/actionlog/policy.go | 118 +++++++++++++++++++++++++++++++ pkg/actionlog/policy_test.go | 112 +++++++++++++++++++++++++++++ 3 files changed, 242 insertions(+) create mode 100644 pkg/actionlog/canned_policies.go create mode 100644 pkg/actionlog/policy.go create mode 100644 pkg/actionlog/policy_test.go diff --git a/pkg/actionlog/canned_policies.go b/pkg/actionlog/canned_policies.go new file mode 100644 index 000000000..a57e464df --- /dev/null +++ b/pkg/actionlog/canned_policies.go @@ -0,0 +1,12 @@ +package actionlog + +func MakeDebugPolicy() policyMatcher { + return NewPolicyAll() +} + +func MakeProductionPolicy() policyMatcher { + return NewPolicyAll( + // Ignore debug actions + NewPolicyNegate(NewPolicyMatchSeverity(Debug)), + ) +} diff --git a/pkg/actionlog/policy.go b/pkg/actionlog/policy.go new file mode 100644 index 000000000..67250f344 --- /dev/null +++ b/pkg/actionlog/policy.go @@ -0,0 +1,118 @@ +package actionlog + +import "github.com/cortezaproject/corteza-server/pkg/slice" + +type ( + policyMatcher interface { + Match(*Action) bool + } + + logPolicyAny struct { + mm []policyMatcher + } + + logPolicyAll struct { + mm []policyMatcher + } + + logPolicyNegate struct { + m policyMatcher + } + + logPolicyMatchRequestOrigin struct { + origin map[string]bool + } + + logPolicyMatchResource struct { + resources map[string]bool + } + + logPolicyMatchAction struct { + actions map[string]bool + } + + logPolicyMatchSeverity struct { + severities map[Severity]bool + } +) + +// NewPolicyAny returns policy where at least one of the subpolicies should match +func NewPolicyAny(mm ...policyMatcher) policyMatcher { + return &logPolicyAny{mm: mm} +} + +func (p logPolicyAny) Match(a *Action) bool { + for _, m := range p.mm { + if m.Match(a) { + return true + } + } + + return false +} + +// NewPolicyAll returns policy where all subpolicies should match +func NewPolicyAll(mm ...policyMatcher) policyMatcher { + return &logPolicyAll{mm: mm} +} + +func (p logPolicyAll) Match(a *Action) bool { + for _, m := range p.mm { + if !m.Match(a) { + return false + } + } + + return true +} + +// NewPolicyNegate negates passed policy +func NewPolicyNegate(m policyMatcher) policyMatcher { + return &logPolicyNegate{m: m} +} + +func (p logPolicyNegate) Match(a *Action) bool { + return !p.m.Match(a) +} + +// NewPolicyMatchResource matches resources +func NewPolicyMatchResource(rr ...string) policyMatcher { + return &logPolicyMatchResource{resources: slice.ToStringBoolMap(rr)} +} + +func (p logPolicyMatchResource) Match(a *Action) bool { + return p.resources[a.Resource] +} + +// NewPolicyMatchAction matches action +func NewPolicyMatchAction(aa ...string) policyMatcher { + return &logPolicyMatchAction{actions: slice.ToStringBoolMap(aa)} +} + +func (p logPolicyMatchAction) Match(a *Action) bool { + return p.actions[a.Action] +} + +// NewPolicyMatchSeverity matches severity +func NewPolicyMatchSeverity(ss ...Severity) policyMatcher { + var p = &logPolicyMatchSeverity{severities: make(map[Severity]bool)} + + for _, s := range ss { + p.severities[s] = true + } + + return p +} + +func (p logPolicyMatchSeverity) Match(a *Action) bool { + return p.severities[a.Severity] +} + +// NewPolicyMatchRequestOrigin matches resources +func NewPolicyMatchRequestOrigin(rr ...string) policyMatcher { + return &logPolicyMatchRequestOrigin{origin: slice.ToStringBoolMap(rr)} +} + +func (p logPolicyMatchRequestOrigin) Match(a *Action) bool { + return p.origin[a.RequestOrigin] +} diff --git a/pkg/actionlog/policy_test.go b/pkg/actionlog/policy_test.go new file mode 100644 index 000000000..3db81226d --- /dev/null +++ b/pkg/actionlog/policy_test.go @@ -0,0 +1,112 @@ +package actionlog + +import ( + "testing" +) + +func TestPolicyMatchers(t *testing.T) { + tests := []struct { + name string + actn *Action + mtch policyMatcher + want bool + }{ + { + "simple any", + &Action{}, + NewPolicyAny(), + false, + }, + { + "simple all", + &Action{}, + NewPolicyAll(), + true, + }, + { + "should match resource", + &Action{Resource: "foo"}, + NewPolicyMatchResource("foo"), + true, + }, + { + "should not match resource", + &Action{Resource: "bar"}, + NewPolicyMatchResource("foo"), + false, + }, + { + "should match one of resource", + &Action{Resource: "baz"}, + NewPolicyMatchResource("foo", "bar", "baz"), + true, + }, + { + "should match action", + &Action{Action: "foo"}, + NewPolicyMatchAction("foo"), + true, + }, + { + "should not match action", + &Action{Action: "bar"}, + NewPolicyMatchAction("foo"), + false, + }, + { + "should match one of action", + &Action{Action: "baz"}, + NewPolicyMatchAction("foo", "bar", "baz"), + true, + }, + { + "should match severity", + &Action{Severity: Emergency}, + NewPolicyMatchSeverity(Emergency), + true, + }, + { + "should not match severity", + &Action{Severity: Debug}, + NewPolicyMatchSeverity(Alert), + false, + }, + { + "should match one of severity", + &Action{Severity: Warning}, + NewPolicyMatchSeverity(Emergency, Debug, Warning), + true, + }, + { + "complex match", + &Action{Severity: Warning, Resource: "foo", Action: "do"}, + NewPolicyAll( + NewPolicyMatchResource("foo"), + NewPolicyMatchAction("do"), + NewPolicyMatchSeverity(Warning), + ), + true, + }, + { + "complex miss", + &Action{Severity: Warning, Resource: "bar", Action: "do"}, + NewPolicyAll( + NewPolicyMatchResource("foo"), + NewPolicyMatchAction("do"), + NewPolicyMatchSeverity(Warning), + ), + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.want != tt.mtch.Match(tt.actn) { + if tt.want { + t.Errorf("expecting to match") + } else { + t.Errorf("expecting not to match ") + } + } + }) + } +}