From 972387f17e29c529ad3ff42a8423117825409cd7 Mon Sep 17 00:00:00 2001 From: Tabias Pittman Date: Tue, 5 Mar 2024 05:04:59 -0500 Subject: [PATCH] feat: add context.Context to interfaces (#163) Signed-off-by: Tabias Pittman --- audit_logger.go | 6 +- audit_logger_info.go | 5 +- audit_logger_noop.go | 8 +- audit_logger_test.go | 17 ++-- benchmark_warden_test.go | 7 +- condition.go | 5 +- condition_boolean.go | 4 +- condition_cidr.go | 3 +- condition_cidr_test.go | 3 +- condition_resource_contains.go | 7 +- condition_resource_contains_test.go | 3 +- condition_string_equal.go | 4 +- condition_string_match.go | 3 +- condition_string_match_test.go | 3 +- condition_string_pairs_equal.go | 4 +- condition_string_pairs_equal_test.go | 3 +- condition_subject_equal.go | 4 +- go.mod | 3 +- go.sum | 29 +++++- ladon.go | 34 ++++--- ladon_test.go | 87 +++++++++-------- manager.go | 18 ++-- manager/memory/manager_memory.go | 19 ++-- manager_helper_test.go | 49 ++++++---- manager_mock_test.go | 139 +++++++++++++++------------ warden.go | 4 +- warden_test.go | 19 ++-- 27 files changed, 292 insertions(+), 198 deletions(-) diff --git a/audit_logger.go b/audit_logger.go index 0891058..991aa6e 100644 --- a/audit_logger.go +++ b/audit_logger.go @@ -20,8 +20,10 @@ package ladon +import "context" + // AuditLogger tracks denied and granted authorizations. type AuditLogger interface { - LogRejectedAccessRequest(request *Request, pool Policies, deciders Policies) - LogGrantedAccessRequest(request *Request, pool Policies, deciders Policies) + LogRejectedAccessRequest(ctx context.Context, request *Request, pool Policies, deciders Policies) + LogGrantedAccessRequest(ctx context.Context, request *Request, pool Policies, deciders Policies) } diff --git a/audit_logger_info.go b/audit_logger_info.go index 2472ef4..1360121 100644 --- a/audit_logger_info.go +++ b/audit_logger_info.go @@ -21,6 +21,7 @@ package ladon import ( + "context" "log" "os" "strings" @@ -38,7 +39,7 @@ func (a *AuditLoggerInfo) logger() *log.Logger { return a.Logger } -func (a *AuditLoggerInfo) LogRejectedAccessRequest(r *Request, p Policies, d Policies) { +func (a *AuditLoggerInfo) LogRejectedAccessRequest(ctx context.Context, r *Request, p Policies, d Policies) { if len(d) > 1 { allowed := joinPoliciesNames(d[0 : len(d)-1]) denied := d[len(d)-1].GetID() @@ -51,7 +52,7 @@ func (a *AuditLoggerInfo) LogRejectedAccessRequest(r *Request, p Policies, d Pol } } -func (a *AuditLoggerInfo) LogGrantedAccessRequest(r *Request, p Policies, d Policies) { +func (a *AuditLoggerInfo) LogGrantedAccessRequest(ctx context.Context, r *Request, p Policies, d Policies) { a.logger().Printf("policies %s allow access", joinPoliciesNames(d)) } diff --git a/audit_logger_noop.go b/audit_logger_noop.go index c56878f..89fa3b1 100644 --- a/audit_logger_noop.go +++ b/audit_logger_noop.go @@ -20,10 +20,14 @@ package ladon +import "context" + // AuditLoggerNoOp is the default AuditLogger, that tracks nothing. type AuditLoggerNoOp struct{} -func (*AuditLoggerNoOp) LogRejectedAccessRequest(r *Request, p Policies, d Policies) {} -func (*AuditLoggerNoOp) LogGrantedAccessRequest(r *Request, p Policies, d Policies) {} +func (*AuditLoggerNoOp) LogRejectedAccessRequest(ctx context.Context, r *Request, p Policies, d Policies) { +} +func (*AuditLoggerNoOp) LogGrantedAccessRequest(ctx context.Context, r *Request, p Policies, d Policies) { +} var DefaultAuditLogger = &AuditLoggerNoOp{} diff --git a/audit_logger_test.go b/audit_logger_test.go index 7ebdfef..c3a200d 100644 --- a/audit_logger_test.go +++ b/audit_logger_test.go @@ -22,6 +22,7 @@ package ladon_test import ( "bytes" + "context" "log" "testing" @@ -41,21 +42,23 @@ func TestAuditLogger(t *testing.T) { }, } - warden.Manager.Create(&DefaultPolicy{ + ctx := context.Background() + + warden.Manager.Create(ctx, &DefaultPolicy{ ID: "no-updates", Subjects: []string{"<.*>"}, Actions: []string{"update"}, Resources: []string{"<.*>"}, Effect: DenyAccess, }) - warden.Manager.Create(&DefaultPolicy{ + warden.Manager.Create(ctx, &DefaultPolicy{ ID: "yes-deletes", Subjects: []string{"<.*>"}, Actions: []string{"delete"}, Resources: []string{"<.*>"}, Effect: AllowAccess, }) - warden.Manager.Create(&DefaultPolicy{ + warden.Manager.Create(ctx, &DefaultPolicy{ ID: "no-bob", Subjects: []string{"bob"}, Actions: []string{"delete"}, @@ -64,7 +67,7 @@ func TestAuditLogger(t *testing.T) { }) r := &Request{} - assert.NotNil(t, warden.IsAllowed(r)) + assert.NotNil(t, warden.IsAllowed(ctx, r)) assert.Equal(t, "no policy allowed access\n", output.String()) output.Reset() @@ -72,7 +75,7 @@ func TestAuditLogger(t *testing.T) { r = &Request{ Action: "update", } - assert.NotNil(t, warden.IsAllowed(r)) + assert.NotNil(t, warden.IsAllowed(ctx, r)) assert.Equal(t, "policy no-updates forcefully denied the access\n", output.String()) output.Reset() @@ -81,7 +84,7 @@ func TestAuditLogger(t *testing.T) { Subject: "bob", Action: "delete", } - assert.NotNil(t, warden.IsAllowed(r)) + assert.NotNil(t, warden.IsAllowed(ctx, r)) assert.Equal(t, "policies yes-deletes allow access, but policy no-bob forcefully denied it\n", output.String()) output.Reset() @@ -90,6 +93,6 @@ func TestAuditLogger(t *testing.T) { Subject: "alice", Action: "delete", } - assert.Nil(t, warden.IsAllowed(r)) + assert.Nil(t, warden.IsAllowed(ctx, r)) assert.Equal(t, "policies yes-deletes allow access\n", output.String()) } diff --git a/benchmark_warden_test.go b/benchmark_warden_test.go index 572a917..bce85ef 100644 --- a/benchmark_warden_test.go +++ b/benchmark_warden_test.go @@ -21,6 +21,7 @@ package ladon_test import ( + "context" "fmt" "strconv" "testing" @@ -50,8 +51,10 @@ func benchmarkLadon(i int, b *testing.B, warden *ladon.Ladon) { // sem <- true //} + ctx := context.Background() + for _, pol := range generatePolicies(i) { - if err := warden.Manager.Create(pol); err != nil { + if err := warden.Manager.Create(ctx, pol); err != nil { b.Logf("Got error from warden.Manager.Create: %s", err) } } @@ -59,7 +62,7 @@ func benchmarkLadon(i int, b *testing.B, warden *ladon.Ladon) { b.ResetTimer() var err error for n := 0; n < b.N; n++ { - if err = warden.IsAllowed(&ladon.Request{ + if err = warden.IsAllowed(ctx, &ladon.Request{ Subject: "5", Action: "bar", Resource: "baz", diff --git a/condition.go b/condition.go index ee4bb9e..fc161b1 100644 --- a/condition.go +++ b/condition.go @@ -21,6 +21,7 @@ package ladon import ( + "context" "encoding/json" "github.com/pkg/errors" @@ -32,7 +33,7 @@ type Condition interface { GetName() string // Fulfills returns true if the request is fulfilled by the condition. - Fulfills(interface{}, *Request) bool + Fulfills(context.Context, interface{}, *Request) bool } // Conditions is a collection of conditions. @@ -129,6 +130,6 @@ var ConditionFactories = map[string]func() Condition{ return new(ResourceContainsCondition) }, new(BooleanCondition).GetName(): func() Condition { - return new (BooleanCondition) + return new(BooleanCondition) }, } diff --git a/condition_boolean.go b/condition_boolean.go index eb04c1f..acd0f75 100644 --- a/condition_boolean.go +++ b/condition_boolean.go @@ -1,5 +1,7 @@ package ladon +import "context" + /* BooleanCondition is used to determine if a boolean context matches an expected boolean condition. @@ -18,7 +20,7 @@ func (c *BooleanCondition) GetName() string { // Fulfills determines if the BooleanCondition is fulfilled. // The BooleanCondition is fulfilled if the provided boolean value matches the conditions boolean value. -func (c *BooleanCondition) Fulfills(value interface{}, _ *Request) bool { +func (c *BooleanCondition) Fulfills(ctx context.Context, value interface{}, _ *Request) bool { val, ok := value.(bool) return ok && val == c.BooleanValue diff --git a/condition_cidr.go b/condition_cidr.go index 3a3ce53..85d2e86 100644 --- a/condition_cidr.go +++ b/condition_cidr.go @@ -21,6 +21,7 @@ package ladon import ( + "context" "net" ) @@ -30,7 +31,7 @@ type CIDRCondition struct { } // Fulfills returns true if the the request is fulfilled by the condition. -func (c *CIDRCondition) Fulfills(value interface{}, _ *Request) bool { +func (c *CIDRCondition) Fulfills(ctx context.Context, value interface{}, _ *Request) bool { ips, ok := value.(string) if !ok { return false diff --git a/condition_cidr_test.go b/condition_cidr_test.go index afbb151..c2e5989 100644 --- a/condition_cidr_test.go +++ b/condition_cidr_test.go @@ -21,6 +21,7 @@ package ladon import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -42,6 +43,6 @@ func TestCIDRMatch(t *testing.T) { CIDR: c.cidr, } - assert.Equal(t, c.pass, condition.Fulfills(c.ip, new(Request)), "%s; %s", c.ip, c.cidr) + assert.Equal(t, c.pass, condition.Fulfills(context.Background(), c.ip, new(Request)), "%s; %s", c.ip, c.cidr) } } diff --git a/condition_resource_contains.go b/condition_resource_contains.go index 7ac1040..c3b98b4 100644 --- a/condition_resource_contains.go +++ b/condition_resource_contains.go @@ -20,13 +20,16 @@ package ladon -import "strings" +import ( + "context" + "strings" +) // ResourceContainsCondition is fulfilled if the context matches a substring within the resource name type ResourceContainsCondition struct{} // Fulfills returns true if the request's resouce contains the given value string -func (c *ResourceContainsCondition) Fulfills(value interface{}, r *Request) bool { +func (c *ResourceContainsCondition) Fulfills(ctx context.Context, value interface{}, r *Request) bool { filter, ok := value.(map[string]interface{}) if !ok { diff --git a/condition_resource_contains_test.go b/condition_resource_contains_test.go index 74cca88..50b2369 100644 --- a/condition_resource_contains_test.go +++ b/condition_resource_contains_test.go @@ -33,6 +33,7 @@ package ladon import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -82,7 +83,7 @@ func TestResourceContains(t *testing.T) { } resourceFilter["value"] = c.value - assert.Equal(t, c.pass, condition.Fulfills(resourceFilter, request), "%s", c.matches) + assert.Equal(t, c.pass, condition.Fulfills(context.Background(), resourceFilter, request), "%s", c.matches) assert.Equal(t, condition.GetName(), "ResourceContainsCondition", "should be called ResourceContainsCondition") } } diff --git a/condition_string_equal.go b/condition_string_equal.go index c4ecee2..15686bc 100644 --- a/condition_string_equal.go +++ b/condition_string_equal.go @@ -20,6 +20,8 @@ package ladon +import "context" + // StringEqualCondition is a condition which is fulfilled if the given // string value is the same as specified in StringEqualCondition type StringEqualCondition struct { @@ -28,7 +30,7 @@ type StringEqualCondition struct { // Fulfills returns true if the given value is a string and is the // same as in StringEqualCondition.Equals -func (c *StringEqualCondition) Fulfills(value interface{}, _ *Request) bool { +func (c *StringEqualCondition) Fulfills(ctx context.Context, value interface{}, _ *Request) bool { s, ok := value.(string) return ok && s == c.Equals diff --git a/condition_string_match.go b/condition_string_match.go index bb42b97..61cd4cd 100644 --- a/condition_string_match.go +++ b/condition_string_match.go @@ -21,6 +21,7 @@ package ladon import ( + "context" "regexp" ) @@ -32,7 +33,7 @@ type StringMatchCondition struct { // Fulfills returns true if the given value is a string and matches the regex // pattern in StringMatchCondition.Matches -func (c *StringMatchCondition) Fulfills(value interface{}, _ *Request) bool { +func (c *StringMatchCondition) Fulfills(ctx context.Context, value interface{}, _ *Request) bool { s, ok := value.(string) matches, _ := regexp.MatchString(c.Matches, s) diff --git a/condition_string_match_test.go b/condition_string_match_test.go index 7920c43..ac222d9 100644 --- a/condition_string_match_test.go +++ b/condition_string_match_test.go @@ -21,6 +21,7 @@ package ladon import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -40,6 +41,6 @@ func TestStringMatch(t *testing.T) { Matches: c.matches, } - assert.Equal(t, c.pass, condition.Fulfills(c.value, new(Request)), "%s", c.matches) + assert.Equal(t, c.pass, condition.Fulfills(context.Background(), c.value, new(Request)), "%s", c.matches) } } diff --git a/condition_string_pairs_equal.go b/condition_string_pairs_equal.go index b99ac29..6eee7d2 100644 --- a/condition_string_pairs_equal.go +++ b/condition_string_pairs_equal.go @@ -20,6 +20,8 @@ package ladon +import "context" + // StringPairsEqualCondition is a condition which is fulfilled if the given // array of pairs contains two-element string arrays where both elements // in the string array are equal @@ -27,7 +29,7 @@ type StringPairsEqualCondition struct{} // Fulfills returns true if the given value is an array of string arrays and // each string array has exactly two values which are equal -func (c *StringPairsEqualCondition) Fulfills(value interface{}, _ *Request) bool { +func (c *StringPairsEqualCondition) Fulfills(ctx context.Context, value interface{}, _ *Request) bool { pairs, PairsOk := value.([]interface{}) if !PairsOk { return false diff --git a/condition_string_pairs_equal_test.go b/condition_string_pairs_equal_test.go index a609bd4..4dda59d 100644 --- a/condition_string_pairs_equal_test.go +++ b/condition_string_pairs_equal_test.go @@ -21,6 +21,7 @@ package ladon import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -43,6 +44,6 @@ func TestStringPairsEqualMatch(t *testing.T) { } { condition := &StringPairsEqualCondition{} - assert.Equal(t, c.pass, condition.Fulfills(c.pairs, new(Request)), "%s", c.pairs) + assert.Equal(t, c.pass, condition.Fulfills(context.Background(), c.pairs, new(Request)), "%s", c.pairs) } } diff --git a/condition_subject_equal.go b/condition_subject_equal.go index e58ed18..aa10236 100644 --- a/condition_subject_equal.go +++ b/condition_subject_equal.go @@ -20,11 +20,13 @@ package ladon +import "context" + // EqualsSubjectCondition is a condition which is fulfilled if the request's subject is equal to the given value string type EqualsSubjectCondition struct{} // Fulfills returns true if the request's subject is equal to the given value string -func (c *EqualsSubjectCondition) Fulfills(value interface{}, r *Request) bool { +func (c *EqualsSubjectCondition) Fulfills(ctx context.Context, value interface{}, r *Request) bool { s, ok := value.(string) return ok && s == r.Subject diff --git a/go.mod b/go.mod index c395079..3dcc6d2 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/dlclark/regexp2 v1.2.0 - github.com/golang/mock v1.1.1 + github.com/golang/mock v1.6.0 github.com/hashicorp/golang-lru v0.5.0 github.com/ory/pagination v0.0.1 github.com/pborman/uuid v1.2.0 @@ -16,5 +16,4 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/uuid v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 // indirect ) diff --git a/go.sum b/go.sum index aa561c8..119186f 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= @@ -18,5 +18,26 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 h1:x6rhz8Y9CjbgQkccRGmELH6K+LJj7tOoh3XWeC1yaQM= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/ladon.go b/ladon.go index 1ebdf17..995ee93 100644 --- a/ladon.go +++ b/ladon.go @@ -21,6 +21,8 @@ package ladon import ( + "context" + "github.com/pkg/errors" ) @@ -33,17 +35,17 @@ type Ladon struct { } func (l *Ladon) matcher() matcher { - if l.Matcher == nil { - l.Matcher = DefaultMatcher + if l.Matcher != nil { + return l.Matcher } - return l.Matcher + return DefaultMatcher } func (l *Ladon) auditLogger() AuditLogger { - if l.AuditLogger == nil { - l.AuditLogger = DefaultAuditLogger + if l.AuditLogger != nil { + return l.AuditLogger } - return l.AuditLogger + return DefaultAuditLogger } func (l *Ladon) metric() Metric { @@ -54,8 +56,8 @@ func (l *Ladon) metric() Metric { } // IsAllowed returns nil if subject s has permission p on resource r with context c or an error otherwise. -func (l *Ladon) IsAllowed(r *Request) (err error) { - policies, err := l.Manager.FindRequestCandidates(r) +func (l *Ladon) IsAllowed(ctx context.Context, r *Request) (err error) { + policies, err := l.Manager.FindRequestCandidates(ctx, r) if err != nil { go l.metric().RequestProcessingError(*r, nil, err) return err @@ -64,12 +66,12 @@ func (l *Ladon) IsAllowed(r *Request) (err error) { // Although the manager is responsible of matching the policies, it might decide to just scan for // subjects, it might return all policies, or it might have a different pattern matching than Golang. // Thus, we need to make sure that we actually matched the right policies. - return l.DoPoliciesAllow(r, policies) + return l.DoPoliciesAllow(ctx, r, policies) } // DoPoliciesAllow returns nil if subject s has permission p on resource r with context c for a given policy list or an error otherwise. // The IsAllowed interface should be preferred since it uses the manager directly. This is a lower level interface for when you don't want to use the ladon manager. -func (l *Ladon) DoPoliciesAllow(r *Request, policies []Policy) (err error) { +func (l *Ladon) DoPoliciesAllow(ctx context.Context, r *Request, policies []Policy) (err error) { var allowed = false var deciders = Policies{} @@ -109,7 +111,7 @@ func (l *Ladon) DoPoliciesAllow(r *Request, policies []Policy) (err error) { // Are the policies conditions met? // This is checked first because it usually has a small complexity. - if !l.passesConditions(p, r) { + if !l.passesConditions(ctx, p, r) { // no, continue to next policy continue } @@ -117,7 +119,7 @@ func (l *Ladon) DoPoliciesAllow(r *Request, policies []Policy) (err error) { // Is the policy's effect `deny`? If yes, this overrides all allow policies -> access denied. if !p.AllowAccess() { deciders = append(deciders, p) - l.auditLogger().LogRejectedAccessRequest(r, policies, deciders) + l.auditLogger().LogRejectedAccessRequest(ctx, r, policies, deciders) go l.metric().RequestDeniedBy(*r, p) return errors.WithStack(ErrRequestForcefullyDenied) } @@ -129,19 +131,19 @@ func (l *Ladon) DoPoliciesAllow(r *Request, policies []Policy) (err error) { if !allowed { go l.metric().RequestNoMatch(*r) - l.auditLogger().LogRejectedAccessRequest(r, policies, deciders) + l.auditLogger().LogRejectedAccessRequest(ctx, r, policies, deciders) return errors.WithStack(ErrRequestDenied) } + l.auditLogger().LogGrantedAccessRequest(ctx, r, policies, deciders) l.metric().RequestAllowedBy(*r, deciders) - l.auditLogger().LogGrantedAccessRequest(r, policies, deciders) return nil } -func (l *Ladon) passesConditions(p Policy, r *Request) bool { +func (l *Ladon) passesConditions(ctx context.Context, p Policy, r *Request) bool { for key, condition := range p.GetConditions() { - if pass := condition.Fulfills(r.Context[key], r); !pass { + if pass := condition.Fulfills(ctx, r.Context[key], r); !pass { return false } } diff --git a/ladon_test.go b/ladon_test.go index 18739ab..353e64f 100644 --- a/ladon_test.go +++ b/ladon_test.go @@ -21,88 +21,89 @@ package ladon_test import ( + "context" "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/ory/ladon" - "github.com/ory/ladon/manager/memory" + . "github.com/ory/ladon" + . "github.com/ory/ladon/manager/memory" ) // A bunch of exemplary policies -var pols = []ladon.Policy{ - &ladon.DefaultPolicy{ +var pols = []Policy{ + &DefaultPolicy{ ID: "0", Description: `This policy allows max, peter, zac and ken to create, delete and get the listed resources, but only if the client ip matches and the request states that they are the owner of those resources as well.`, Subjects: []string{"max", "peter", ""}, Resources: []string{"myrn:some.domain.com:resource:123", "myrn:some.domain.com:resource:345", "myrn:something:foo:<.+>"}, Actions: []string{"", "get"}, - Effect: ladon.AllowAccess, - Conditions: ladon.Conditions{ - "owner": &ladon.EqualsSubjectCondition{}, - "clientIP": &ladon.CIDRCondition{ + Effect: AllowAccess, + Conditions: Conditions{ + "owner": &EqualsSubjectCondition{}, + "clientIP": &CIDRCondition{ CIDR: "127.0.0.1/32", }, }, }, - &ladon.DefaultPolicy{ + &DefaultPolicy{ ID: "1", Description: "This policy allows max to update any resource", Subjects: []string{"max"}, Actions: []string{"update"}, Resources: []string{"<.*>"}, - Effect: ladon.AllowAccess, + Effect: AllowAccess, }, - &ladon.DefaultPolicy{ + &DefaultPolicy{ ID: "3", Description: "This policy denies max to broadcast any of the resources", Subjects: []string{"max"}, Actions: []string{"broadcast"}, Resources: []string{"<.*>"}, - Effect: ladon.DenyAccess, + Effect: DenyAccess, }, - &ladon.DefaultPolicy{ + &DefaultPolicy{ ID: "2", Description: "This policy denies max to broadcast any of the resources", Subjects: []string{"max"}, Actions: []string{"random"}, Resources: []string{"<.*>"}, - Effect: ladon.DenyAccess, + Effect: DenyAccess, }, - &ladon.DefaultPolicy{ + &DefaultPolicy{ ID: "4", Description: "This policy allows swen to update any resource except `protected` resources", Subjects: []string{"swen"}, Actions: []string{"update"}, Resources: []string{"myrn:some.domain.com:resource:<(?!protected).*>"}, - Effect: ladon.AllowAccess, + Effect: AllowAccess, }, - &ladon.DefaultPolicy{ + &DefaultPolicy{ ID: "5", Description: "This policy allows richard to update resources which names consists of digits only", Subjects: []string{"richard"}, Actions: []string{"update"}, Resources: []string{"myrn:some.domain.com:resource:<[[:digit:]]+>"}, - Effect: ladon.AllowAccess, + Effect: AllowAccess, }, } // Some test cases var cases = []struct { description string - accessRequest *ladon.Request + accessRequest *Request expectErr bool }{ { description: "should fail because no policy is matching as field clientIP does not satisfy the CIDR condition of policy 1.", - accessRequest: &ladon.Request{ + accessRequest: &Request{ Subject: "peter", Action: "delete", Resource: "myrn:some.domain.com:resource:123", - Context: ladon.Context{ + Context: Context{ "owner": "peter", "clientIP": "0.0.0.0", }, @@ -111,11 +112,11 @@ var cases = []struct { }, { description: "should fail because no policy is matching as the owner of the resource 123 is zac, not peter!", - accessRequest: &ladon.Request{ + accessRequest: &Request{ Subject: "peter", Action: "delete", Resource: "myrn:some.domain.com:resource:123", - Context: ladon.Context{ + Context: Context{ "owner": "zac", "clientIP": "127.0.0.1", }, @@ -124,11 +125,11 @@ var cases = []struct { }, { description: "should pass because policy 1 is matching and has effect allow.", - accessRequest: &ladon.Request{ + accessRequest: &Request{ Subject: "peter", Action: "delete", Resource: "myrn:some.domain.com:resource:123", - Context: ladon.Context{ + Context: Context{ "owner": "peter", "clientIP": "127.0.0.1", }, @@ -137,7 +138,7 @@ var cases = []struct { }, { description: "should pass because max is allowed to update all resources.", - accessRequest: &ladon.Request{ + accessRequest: &Request{ Subject: "max", Action: "update", Resource: "myrn:some.domain.com:resource:123", @@ -146,7 +147,7 @@ var cases = []struct { }, { description: "should pass because max is allowed to update all resource, even if none is given.", - accessRequest: &ladon.Request{ + accessRequest: &Request{ Subject: "max", Action: "update", Resource: "", @@ -155,7 +156,7 @@ var cases = []struct { }, { description: "should fail because max is not allowed to broadcast any resource.", - accessRequest: &ladon.Request{ + accessRequest: &Request{ Subject: "max", Action: "broadcast", Resource: "myrn:some.domain.com:resource:123", @@ -164,7 +165,7 @@ var cases = []struct { }, { description: "should fail because max is not allowed to broadcast any resource, even empty ones!", - accessRequest: &ladon.Request{ + accessRequest: &Request{ Subject: "max", Action: "broadcast", }, @@ -172,7 +173,7 @@ var cases = []struct { }, { description: "should pass because swen is allowed to update all resources except `protected` resources.", - accessRequest: &ladon.Request{ + accessRequest: &Request{ Subject: "swen", Action: "update", Resource: "myrn:some.domain.com:resource:123", @@ -181,7 +182,7 @@ var cases = []struct { }, { description: "should fail because swen is not allowed to update `protected` resource", - accessRequest: &ladon.Request{ + accessRequest: &Request{ Subject: "swen", Action: "update", Resource: "myrn:some.domain.com:resource:protected123", @@ -190,7 +191,7 @@ var cases = []struct { }, { description: "should fail because richard is not allowed to update a resource with alphanumeric name", - accessRequest: &ladon.Request{ + accessRequest: &Request{ Subject: "richard", Action: "update", Resource: "myrn:some.domain.com:resource:protected123", @@ -199,7 +200,7 @@ var cases = []struct { }, { description: "should pass because richard is allowed to update a resources with a name containing digits only", - accessRequest: &ladon.Request{ + accessRequest: &Request{ Subject: "richard", Action: "update", Resource: "myrn:some.domain.com:resource:25222", @@ -209,18 +210,21 @@ var cases = []struct { } func TestLadon(t *testing.T) { + + ctx := context.Background() + // Instantiate ladon with the default in-memory store. - warden := &ladon.Ladon{Manager: memory.NewMemoryManager()} + warden := &Ladon{Manager: NewMemoryManager()} // Add the policies defined above to the memory manager. for _, pol := range pols { - require.Nil(t, warden.Manager.Create(pol)) + require.Nil(t, warden.Manager.Create(ctx, pol)) } for i := 0; i < len(pols); i++ { - polices, err := warden.Manager.GetAll(int64(1), int64(i)) + polices, err := warden.Manager.GetAll(ctx, int64(1), int64(i)) require.NoError(t, err) - p, err := warden.Manager.Get(fmt.Sprintf("%d", i)) + p, err := warden.Manager.Get(ctx, fmt.Sprintf("%d", i)) if err == nil { AssertPolicyEqual(t, p, polices[0]) } @@ -230,7 +234,7 @@ func TestLadon(t *testing.T) { t.Run(fmt.Sprintf("case=%d-%s", k, c.description), func(t *testing.T) { // This is where we ask the warden if the access requests should be granted - err := warden.IsAllowed(c.accessRequest) + err := warden.IsAllowed(ctx, c.accessRequest) assert.Equal(t, c.expectErr, err != nil) }) @@ -239,7 +243,10 @@ func TestLadon(t *testing.T) { } func TestLadonEmpty(t *testing.T) { + + ctx := context.Background() + // If no policy was given, the warden must return an error! - warden := &ladon.Ladon{Manager: memory.NewMemoryManager()} - assert.NotNil(t, warden.IsAllowed(&ladon.Request{})) + warden := &Ladon{Manager: NewMemoryManager()} + assert.NotNil(t, warden.IsAllowed(ctx, &Request{})) } diff --git a/manager.go b/manager.go index d775ffe..93d5936 100644 --- a/manager.go +++ b/manager.go @@ -20,36 +20,38 @@ package ladon +import "context" + // Manager is responsible for managing and persisting policies. type Manager interface { // Create persists the policy. - Create(policy Policy) error + Create(ctx context.Context, policy Policy) error // Update updates an existing policy. - Update(policy Policy) error + Update(ctx context.Context, policy Policy) error // Get retrieves a policy. - Get(id string) (Policy, error) + Get(ctx context.Context, id string) (Policy, error) // Delete removes a policy. - Delete(id string) error + Delete(ctx context.Context, id string) error // GetAll retrieves all policies. - GetAll(limit, offset int64) (Policies, error) + GetAll(ctx context.Context, limit, offset int64) (Policies, error) // FindRequestCandidates returns candidates that could match the request object. It either returns // a set that exactly matches the request, or a superset of it. If an error occurs, it returns nil and // the error. - FindRequestCandidates(r *Request) (Policies, error) + FindRequestCandidates(ctx context.Context, r *Request) (Policies, error) // FindPoliciesForSubject returns policies that could match the subject. It either returns // a set of policies that applies to the subject, or a superset of it. // If an error occurs, it returns nil and the error. - FindPoliciesForSubject(subject string) (Policies, error) + FindPoliciesForSubject(ctx context.Context, subject string) (Policies, error) // FindPoliciesForResource returns policies that could match the resource. It either returns // a set of policies that apply to the resource, or a superset of it. // If an error occurs, it returns nil and the error. - FindPoliciesForResource(resource string) (Policies, error) + FindPoliciesForResource(ctx context.Context, resource string) (Policies, error) } diff --git a/manager/memory/manager_memory.go b/manager/memory/manager_memory.go index 799c84b..3c85f6a 100644 --- a/manager/memory/manager_memory.go +++ b/manager/memory/manager_memory.go @@ -21,13 +21,14 @@ package memory import ( + "context" + "sort" "sync" "github.com/pkg/errors" . "github.com/ory/ladon" "github.com/ory/pagination" - "sort" ) // MemoryManager is an in-memory (non-persistent) implementation of Manager. @@ -44,7 +45,7 @@ func NewMemoryManager() *MemoryManager { } // Update updates an existing policy. -func (m *MemoryManager) Update(policy Policy) error { +func (m *MemoryManager) Update(ctx context.Context, policy Policy) error { m.Lock() defer m.Unlock() m.Policies[policy.GetID()] = policy @@ -52,7 +53,7 @@ func (m *MemoryManager) Update(policy Policy) error { } // GetAll returns all policies. -func (m *MemoryManager) GetAll(limit, offset int64) (Policies, error) { +func (m *MemoryManager) GetAll(ctx context.Context, limit, offset int64) (Policies, error) { keys := make([]string, len(m.Policies)) i := 0 m.RLock() @@ -74,7 +75,7 @@ func (m *MemoryManager) GetAll(limit, offset int64) (Policies, error) { } // Create a new pollicy to MemoryManager. -func (m *MemoryManager) Create(policy Policy) error { +func (m *MemoryManager) Create(ctx context.Context, policy Policy) error { m.Lock() defer m.Unlock() @@ -87,7 +88,7 @@ func (m *MemoryManager) Create(policy Policy) error { } // Get retrieves a policy. -func (m *MemoryManager) Get(id string) (Policy, error) { +func (m *MemoryManager) Get(ctx context.Context, id string) (Policy, error) { m.RLock() defer m.RUnlock() p, ok := m.Policies[id] @@ -99,7 +100,7 @@ func (m *MemoryManager) Get(id string) (Policy, error) { } // Delete removes a policy. -func (m *MemoryManager) Delete(id string) error { +func (m *MemoryManager) Delete(ctx context.Context, id string) error { m.Lock() defer m.Unlock() delete(m.Policies, id) @@ -121,20 +122,20 @@ func (m *MemoryManager) findAllPolicies() (Policies, error) { // FindRequestCandidates returns candidates that could match the request object. It either returns // a set that exactly matches the request, or a superset of it. If an error occurs, it returns nil and // the error. -func (m *MemoryManager) FindRequestCandidates(r *Request) (Policies, error) { +func (m *MemoryManager) FindRequestCandidates(ctx context.Context, r *Request) (Policies, error) { return m.findAllPolicies() } // FindPoliciesForSubject returns policies that could match the subject. It either returns // a set of policies that applies to the subject, or a superset of it. // If an error occurs, it returns nil and the error. -func (m *MemoryManager) FindPoliciesForSubject(subject string) (Policies, error) { +func (m *MemoryManager) FindPoliciesForSubject(ctx context.Context, subject string) (Policies, error) { return m.findAllPolicies() } // FindPoliciesForResource returns policies that could match the resource. It either returns // a set of policies that apply to the resource, or a superset of it. // If an error occurs, it returns nil and the error. -func (m *MemoryManager) FindPoliciesForResource(resource string) (Policies, error) { +func (m *MemoryManager) FindPoliciesForResource(ctx context.Context, resource string) (Policies, error) { return m.findAllPolicies() } diff --git a/manager_helper_test.go b/manager_helper_test.go index eb267d8..860a021 100644 --- a/manager_helper_test.go +++ b/manager_helper_test.go @@ -21,6 +21,7 @@ package ladon_test import ( + "context" "fmt" "reflect" "testing" @@ -257,14 +258,16 @@ var testPolicies = []*ladon.DefaultPolicy{ } func HelperTestFindPoliciesForSubject(k string, s ladon.Manager) func(t *testing.T) { + ctx := context.Background() + return func(t *testing.T) { for _, c := range testPolicies { t.Run(fmt.Sprintf("create=%s", k), func(t *testing.T) { - require.NoError(t, s.Create(c)) + require.NoError(t, s.Create(ctx, c)) }) } - res, err := s.FindRequestCandidates(&ladon.Request{ + res, err := s.FindRequestCandidates(ctx, &ladon.Request{ Subject: "sqlmatch", Resource: "article", Action: "create", @@ -280,7 +283,7 @@ func HelperTestFindPoliciesForSubject(k string, s ladon.Manager) func(t *testing AssertPolicyEqual(t, testPolicies[1], res[0]) } - res, err = s.FindRequestCandidates(&ladon.Request{ + res, err = s.FindRequestCandidates(ctx, &ladon.Request{ Subject: "sqlamatch", Resource: "article", Action: "create", @@ -293,14 +296,16 @@ func HelperTestFindPoliciesForSubject(k string, s ladon.Manager) func(t *testing } func HelperTestFindPoliciesForResource(k string, s ladon.Manager) func(t *testing.T) { + ctx := context.Background() + return func(t *testing.T) { for _, c := range testPolicies { t.Run(fmt.Sprintf("create=%s", k), func(t *testing.T) { - require.NoError(t, s.Create(c)) + require.NoError(t, s.Create(ctx, c)) }) } - res, err := s.FindPoliciesForResource("sqlmatch_resource") + res, err := s.FindPoliciesForResource(ctx, "sqlmatch_resource") require.NoError(t, err) require.Len(t, res, 2) @@ -312,7 +317,7 @@ func HelperTestFindPoliciesForResource(k string, s ladon.Manager) func(t *testin AssertPolicyEqual(t, testPolicies[len(testPolicies)-1], res[0]) } - res, err = s.FindPoliciesForResource("sqlamatch_resource") + res, err = s.FindPoliciesForResource(ctx, "sqlamatch_resource") require.NoError(t, err) require.Len(t, res, 1) @@ -370,26 +375,30 @@ func testEq(a, b []string) error { } func HelperTestGetErrors(s ladon.Manager) func(t *testing.T) { + ctx := context.Background() + return func(t *testing.T) { - _, err := s.Get(uuid.New()) + _, err := s.Get(ctx, uuid.New()) assert.Error(t, err) - _, err = s.Get("asdf") + _, err = s.Get(ctx, "asdf") assert.Error(t, err) } } func HelperTestCreateGetDelete(s ladon.Manager) func(t *testing.T) { + ctx := context.Background() + return func(t *testing.T) { for i, c := range TestManagerPolicies { t.Run(fmt.Sprintf("case=%d/id=%s/type=create", i, c.GetID()), func(t *testing.T) { - _, err := s.Get(c.GetID()) + _, err := s.Get(ctx, c.GetID()) require.Error(t, err) - require.NoError(t, s.Create(c)) + require.NoError(t, s.Create(ctx, c)) }) t.Run(fmt.Sprintf("case=%d/id=%s/type=query", i, c.GetID()), func(t *testing.T) { - get, err := s.Get(c.GetID()) + get, err := s.Get(ctx, c.GetID()) require.NoError(t, err) AssertPolicyEqual(t, c, get) @@ -397,16 +406,16 @@ func HelperTestCreateGetDelete(s ladon.Manager) func(t *testing.T) { t.Run(fmt.Sprintf("case=%d/id=%s/type=update", i, c.GetID()), func(t *testing.T) { c.Description = c.Description + "_updated" - require.NoError(t, s.Update(c)) + require.NoError(t, s.Update(ctx, c)) - get, err := s.Get(c.GetID()) + get, err := s.Get(ctx, c.GetID()) require.NoError(t, err) AssertPolicyEqual(t, c, get) }) t.Run(fmt.Sprintf("case=%d/id=%s/type=query", i, c.GetID()), func(t *testing.T) { - get, err := s.Get(c.GetID()) + get, err := s.Get(ctx, c.GetID()) require.NoError(t, err) AssertPolicyEqual(t, c, get) @@ -416,19 +425,19 @@ func HelperTestCreateGetDelete(s ladon.Manager) func(t *testing.T) { t.Run("type=query-all", func(t *testing.T) { count := int64(len(TestManagerPolicies)) - pols, err := s.GetAll(100, 0) + pols, err := s.GetAll(ctx, 100, 0) require.NoError(t, err) assert.Len(t, pols, len(TestManagerPolicies)) - pols4, err := s.GetAll(1, 0) + pols4, err := s.GetAll(ctx, 1, 0) require.NoError(t, err) assert.Len(t, pols4, 1) - pols2, err := s.GetAll(100, count-1) + pols2, err := s.GetAll(ctx, 100, count-1) require.NoError(t, err) assert.Len(t, pols2, 1) - pols3, err := s.GetAll(100, count) + pols3, err := s.GetAll(ctx, 100, count) require.NoError(t, err) assert.Len(t, pols3, 0) @@ -459,9 +468,9 @@ func HelperTestCreateGetDelete(s ladon.Manager) func(t *testing.T) { for i, c := range TestManagerPolicies { t.Run(fmt.Sprintf("case=%d/id=%s/type=delete", i, c.GetID()), func(t *testing.T) { - assert.NoError(t, s.Delete(c.ID)) + assert.NoError(t, s.Delete(ctx, c.ID)) - _, err := s.Get(c.GetID()) + _, err := s.Get(ctx, c.GetID()) assert.Error(t, err) }) } diff --git a/manager_mock_test.go b/manager_mock_test.go index 969d83c..e5bcd08 100644 --- a/manager_mock_test.go +++ b/manager_mock_test.go @@ -1,136 +1,153 @@ -/* - * Copyright © 2016-2018 Aeneas Rekkas - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @author Aeneas Rekkas - * @copyright 2015-2018 Aeneas Rekkas - * @license Apache-2.0 - */ - -// Automatically generated by MockGen. DO NOT EDIT! +// Code generated by MockGen. DO NOT EDIT. // Source: github.com/ory/ladon (interfaces: Manager) +// Package ladon_test is a generated GoMock package. package ladon_test import ( - gomock "github.com/golang/mock/gomock" + context "context" + reflect "reflect" + gomock "github.com/golang/mock/gomock" ladon "github.com/ory/ladon" ) -// Mock of Manager interface +// MockManager is a mock of Manager interface. type MockManager struct { ctrl *gomock.Controller - recorder *_MockManagerRecorder + recorder *MockManagerMockRecorder } -// Recorder for MockManager (not exported) -type _MockManagerRecorder struct { +// MockManagerMockRecorder is the mock recorder for MockManager. +type MockManagerMockRecorder struct { mock *MockManager } +// NewMockManager creates a new mock instance. func NewMockManager(ctrl *gomock.Controller) *MockManager { mock := &MockManager{ctrl: ctrl} - mock.recorder = &_MockManagerRecorder{mock} + mock.recorder = &MockManagerMockRecorder{mock} return mock } -func (_m *MockManager) EXPECT() *_MockManagerRecorder { - return _m.recorder +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockManager) EXPECT() *MockManagerMockRecorder { + return m.recorder } -func (_m *MockManager) Create(_param0 ladon.Policy) error { - ret := _m.ctrl.Call(_m, "Create", _param0) +// Create mocks base method. +func (m *MockManager) Create(arg0 context.Context, arg1 ladon.Policy) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockManagerRecorder) Create(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "Create", arg0) +// Create indicates an expected call of Create. +func (mr *MockManagerMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockManager)(nil).Create), arg0, arg1) } -func (_m *MockManager) Delete(_param0 string) error { - ret := _m.ctrl.Call(_m, "Delete", _param0) +// Delete mocks base method. +func (m *MockManager) Delete(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockManagerRecorder) Delete(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "Delete", arg0) +// Delete indicates an expected call of Delete. +func (mr *MockManagerMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockManager)(nil).Delete), arg0, arg1) } -func (_m *MockManager) FindRequestCandidates(_param0 *ladon.Request) (ladon.Policies, error) { - ret := _m.ctrl.Call(_m, "FindRequestCandidates", _param0) +// FindPoliciesForResource mocks base method. +func (m *MockManager) FindPoliciesForResource(arg0 context.Context, arg1 string) (ladon.Policies, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindPoliciesForResource", arg0, arg1) ret0, _ := ret[0].(ladon.Policies) ret1, _ := ret[1].(error) return ret0, ret1 } -func (_mr *_MockManagerRecorder) FindRequestCandidates(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "FindRequestCandidates", arg0) +// FindPoliciesForResource indicates an expected call of FindPoliciesForResource. +func (mr *MockManagerMockRecorder) FindPoliciesForResource(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindPoliciesForResource", reflect.TypeOf((*MockManager)(nil).FindPoliciesForResource), arg0, arg1) } -func (_m *MockManager) FindPoliciesForSubject(_param0 string) (ladon.Policies, error) { - ret := _m.ctrl.Call(_m, "FindPoliciesForSubject", _param0) +// FindPoliciesForSubject mocks base method. +func (m *MockManager) FindPoliciesForSubject(arg0 context.Context, arg1 string) (ladon.Policies, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindPoliciesForSubject", arg0, arg1) ret0, _ := ret[0].(ladon.Policies) ret1, _ := ret[1].(error) return ret0, ret1 } -func (_mr *_MockManagerRecorder) FindPoliciesForSubject(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "FindPoliciesForSubject", arg0) +// FindPoliciesForSubject indicates an expected call of FindPoliciesForSubject. +func (mr *MockManagerMockRecorder) FindPoliciesForSubject(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindPoliciesForSubject", reflect.TypeOf((*MockManager)(nil).FindPoliciesForSubject), arg0, arg1) } -func (_m *MockManager) FindPoliciesForResource(_param0 string) (ladon.Policies, error) { - ret := _m.ctrl.Call(_m, "FindPoliciesForResource", _param0) +// FindRequestCandidates mocks base method. +func (m *MockManager) FindRequestCandidates(arg0 context.Context, arg1 *ladon.Request) (ladon.Policies, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindRequestCandidates", arg0, arg1) ret0, _ := ret[0].(ladon.Policies) ret1, _ := ret[1].(error) return ret0, ret1 } -func (_mr *_MockManagerRecorder) FindPoliciesForResource(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "FindPoliciesForResource", arg0) +// FindRequestCandidates indicates an expected call of FindRequestCandidates. +func (mr *MockManagerMockRecorder) FindRequestCandidates(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindRequestCandidates", reflect.TypeOf((*MockManager)(nil).FindRequestCandidates), arg0, arg1) } -func (_m *MockManager) Get(_param0 string) (ladon.Policy, error) { - ret := _m.ctrl.Call(_m, "Get", _param0) +// Get mocks base method. +func (m *MockManager) Get(arg0 context.Context, arg1 string) (ladon.Policy, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0, arg1) ret0, _ := ret[0].(ladon.Policy) ret1, _ := ret[1].(error) return ret0, ret1 } -func (_mr *_MockManagerRecorder) Get(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "Get", arg0) +// Get indicates an expected call of Get. +func (mr *MockManagerMockRecorder) Get(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockManager)(nil).Get), arg0, arg1) } -func (_m *MockManager) GetAll(_param0 int64, _param1 int64) (ladon.Policies, error) { - ret := _m.ctrl.Call(_m, "GetAll", _param0, _param1) +// GetAll mocks base method. +func (m *MockManager) GetAll(arg0 context.Context, arg1, arg2 int64) (ladon.Policies, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAll", arg0, arg1, arg2) ret0, _ := ret[0].(ladon.Policies) ret1, _ := ret[1].(error) return ret0, ret1 } -func (_mr *_MockManagerRecorder) GetAll(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "GetAll", arg0, arg1) +// GetAll indicates an expected call of GetAll. +func (mr *MockManagerMockRecorder) GetAll(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAll", reflect.TypeOf((*MockManager)(nil).GetAll), arg0, arg1, arg2) } -func (_m *MockManager) Update(_param0 ladon.Policy) error { - ret := _m.ctrl.Call(_m, "Update", _param0) +// Update mocks base method. +func (m *MockManager) Update(arg0 context.Context, arg1 ladon.Policy) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockManagerRecorder) Update(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "Update", arg0) +// Update indicates an expected call of Update. +func (mr *MockManagerMockRecorder) Update(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockManager)(nil).Update), arg0, arg1) } diff --git a/warden.go b/warden.go index fd46b82..2d45a52 100644 --- a/warden.go +++ b/warden.go @@ -20,6 +20,8 @@ package ladon +import "context" + // Request is the warden's request object. type Request struct { // Resource is the resource that access is requested to. @@ -41,5 +43,5 @@ type Warden interface { // if err := guard.IsAllowed(&Request{Resource: "article/1234", Action: "update", Subject: "peter"}); err != nil { // return errors.New("Not allowed") // } - IsAllowed(r *Request) error + IsAllowed(ctx context.Context, r *Request) error } diff --git a/warden_test.go b/warden_test.go index d9abb17..24a90e9 100644 --- a/warden_test.go +++ b/warden_test.go @@ -21,6 +21,7 @@ package ladon_test import ( + "context" "fmt" "testing" @@ -40,6 +41,8 @@ func TestWardenIsGranted(t *testing.T) { Manager: m, } + ctx := context.Background() + for k, c := range []struct { r *Request description string @@ -50,7 +53,7 @@ func TestWardenIsGranted(t *testing.T) { description: "should fail because no policies are found for peter", r: &Request{Subject: "peter"}, setup: func() { - m.EXPECT().FindRequestCandidates(gomock.Eq(&Request{Subject: "peter"})).Return(Policies{}, nil) + m.EXPECT().FindRequestCandidates(ctx, gomock.Eq(&Request{Subject: "peter"})).Return(Policies{}, nil) }, expectErr: true, }, @@ -58,7 +61,7 @@ func TestWardenIsGranted(t *testing.T) { description: "should fail because lookup failure when accessing policies for peter", r: &Request{Subject: "peter"}, setup: func() { - m.EXPECT().FindRequestCandidates(gomock.Eq(&Request{Subject: "peter"})).Return(Policies{}, errors.New("asdf")) + m.EXPECT().FindRequestCandidates(ctx, gomock.Eq(&Request{Subject: "peter"})).Return(Policies{}, errors.New("asdf")) }, expectErr: true, }, @@ -70,7 +73,7 @@ func TestWardenIsGranted(t *testing.T) { Action: "view", }, setup: func() { - m.EXPECT().FindRequestCandidates(gomock.Eq(&Request{ + m.EXPECT().FindRequestCandidates(ctx, gomock.Eq(&Request{ Subject: "peter", Resource: "articles:1234", Action: "view", @@ -93,7 +96,7 @@ func TestWardenIsGranted(t *testing.T) { Action: "view", }, setup: func() { - m.EXPECT().FindRequestCandidates(gomock.Eq(&Request{ + m.EXPECT().FindRequestCandidates(ctx, gomock.Eq(&Request{ Subject: "ken", Resource: "articles:1234", Action: "view", @@ -116,7 +119,7 @@ func TestWardenIsGranted(t *testing.T) { Action: "view", }, setup: func() { - m.EXPECT().FindRequestCandidates(gomock.Eq(&Request{ + m.EXPECT().FindRequestCandidates(ctx, gomock.Eq(&Request{ Subject: "ken", Resource: "printers:321", Action: "view", @@ -139,7 +142,7 @@ func TestWardenIsGranted(t *testing.T) { Action: "view", }, setup: func() { - m.EXPECT().FindRequestCandidates(gomock.Eq(&Request{ + m.EXPECT().FindRequestCandidates(ctx, gomock.Eq(&Request{ Subject: "ken", Resource: "articles:321", Action: "view", @@ -162,7 +165,7 @@ func TestWardenIsGranted(t *testing.T) { Action: "foo", }, setup: func() { - m.EXPECT().FindRequestCandidates(gomock.Eq(&Request{ + m.EXPECT().FindRequestCandidates(ctx, gomock.Eq(&Request{ Subject: "ken", Resource: "articles:321", Action: "foo", @@ -180,7 +183,7 @@ func TestWardenIsGranted(t *testing.T) { } { t.Run(fmt.Sprintf("case=%d/description=%s", k, c.description), func(t *testing.T) { c.setup() - err := w.IsAllowed(c.r) + err := w.IsAllowed(ctx, c.r) if c.expectErr { assert.NotNil(t, err) } else {