Skip to content

Commit

Permalink
feat: implement ContextAdapter interface (#222)
Browse files Browse the repository at this point in the history
* feat: context adapter

* fix: fix db connection

* fix: fix db connection

* fix: fix db connection

* fix: fix db connection

* Update context_adapter.go

* Update context_adapter_test.go

* Update context_adapter_test.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
  • Loading branch information
PokIsemaine and hsluoyz authored Sep 2, 2023
1 parent bb83b9a commit 6f78166
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 3 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,20 @@ func TestGetAllowedRecordsForUser(t *testing.T) {
}
```

## Context Adapter

`gormadapter` supports adapter with context, the following is a timeout control implemented using context

```go
ca, _ := NewContextAdapter("mysql", "root:@tcp(127.0.0.1:3306)/", "casbin")
// Limited time 300s
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond)
defer cancel()
err := ca.AddPolicyCtx(ctx, "p", "p", []string{"alice", "data1", "read"})
if err != nil {
panic(err)
}
```

## Getting Help

Expand Down
85 changes: 85 additions & 0 deletions context_adapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2023 The casbin Authors. All Rights Reserved.
//
// 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.

package gormadapter

import (
"context"

"github.com/casbin/casbin/v2/model"
)

type ContextAdapter struct {
*Adapter
}

func NewContextAdapter(driverName string, dataSourceName string, params ...interface{}) (*ContextAdapter, error) {
a, err := NewAdapter(driverName, dataSourceName, params...)
return &ContextAdapter{
a,
}, err
}

// executeWithContext is a helper function to execute a function with context and return the result or error.
func executeWithContext(ctx context.Context, fn func() error) error {
done := make(chan error)
go func() {
done <- fn()
}()

select {
case <-ctx.Done():
return ctx.Err()
case err := <-done:
return err
}
}

// LoadPolicyCtx loads all policy rules from the storage with context.
func (ca *ContextAdapter) LoadPolicyCtx(ctx context.Context, model model.Model) error {
return executeWithContext(ctx, func() error {
return ca.LoadPolicy(model)
})
}

// SavePolicyCtx saves all policy rules to the storage with context.
func (ca *ContextAdapter) SavePolicyCtx(ctx context.Context, model model.Model) error {
return executeWithContext(ctx, func() error {
return ca.SavePolicy(model)
})
}

// AddPolicyCtx adds a policy rule to the storage with context.
// This is part of the Auto-Save feature.
func (ca *ContextAdapter) AddPolicyCtx(ctx context.Context, sec string, ptype string, rule []string) error {
return executeWithContext(ctx, func() error {
return ca.AddPolicy(sec, ptype, rule)
})
}

// RemovePolicyCtx removes a policy rule from the storage with context.
// This is part of the Auto-Save feature.
func (ca *ContextAdapter) RemovePolicyCtx(ctx context.Context, sec string, ptype string, rule []string) error {
return executeWithContext(ctx, func() error {
return ca.RemovePolicy(sec, ptype, rule)
})
}

// RemoveFilteredPolicyCtx removes policy rules that match the filter from the storage with context.
// This is part of the Auto-Save feature.
func (ca *ContextAdapter) RemoveFilteredPolicyCtx(ctx context.Context, sec string, ptype string, fieldIndex int, fieldValues ...string) error {
return executeWithContext(ctx, func() error {
return ca.RemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues...)
})
}
141 changes: 141 additions & 0 deletions context_adapter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright 2023 The casbin Authors. All Rights Reserved.
//
// 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.

package gormadapter

import (
"context"
"testing"
"time"

"github.com/agiledragon/gomonkey/v2"
"github.com/casbin/casbin/v2"
"github.com/stretchr/testify/assert"
)

func mockExecuteWithContextTimeOut(ctx context.Context, fn func() error) error {
done := make(chan error)
go func() {
time.Sleep(500 * time.Microsecond)
done <- fn()
}()

select {
case <-ctx.Done():
return ctx.Err()
case err := <-done:
return err
}
}

func clearDBPolicy() (*casbin.Enforcer, *ContextAdapter) {
ca, err := NewContextAdapter("mysql", "root:@tcp(127.0.0.1:3306)/", "casbin")
if err != nil {
panic(err)
}
e, err := casbin.NewEnforcer("examples/rbac_model.conf", ca)
if err != nil {
panic(err)
}
e.ClearPolicy()
_ = e.SavePolicy()

return e, ca
}

func TestContextAdapter_LoadPolicyCtx(t *testing.T) {
e, ca := clearDBPolicy()

db, _ := openDBConnection("mysql", "root:@tcp(127.0.0.1:3306)/casbin")
policy := &CasbinRule{
Ptype: "p",
V0: "alice",
V1: "data1",
V2: "read",
}
db.Create(policy)

assert.NoError(t, ca.LoadPolicyCtx(context.Background(), e.GetModel()))
e, _ = casbin.NewEnforcer(e.GetModel(), ca)
testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}})

var p = gomonkey.ApplyFunc(executeWithContext, mockExecuteWithContextTimeOut)
defer p.Reset()
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond)
defer cancel()
assert.EqualError(t, ca.LoadPolicyCtx(ctx, e.GetModel()), "context deadline exceeded")
}

func TestContextAdapter_SavePolicyCtx(t *testing.T) {
e, ca := clearDBPolicy()

e.EnableAutoSave(false)
_, _ = e.AddPolicy("alice", "data1", "read")
assert.NoError(t, ca.SavePolicyCtx(context.Background(), e.GetModel()))
_ = e.LoadPolicy()
testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}})

var p = gomonkey.ApplyFunc(executeWithContext, mockExecuteWithContextTimeOut)
defer p.Reset()
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond)
defer cancel()
assert.EqualError(t, ca.SavePolicyCtx(ctx, e.GetModel()), "context deadline exceeded")
}

func TestContextAdapter_AddPolicyCtx(t *testing.T) {
e, ca := clearDBPolicy()

assert.NoError(t, ca.AddPolicyCtx(context.Background(), "p", "p", []string{"alice", "data1", "read"}))
_ = e.LoadPolicy()
testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}})

var p = gomonkey.ApplyFunc(executeWithContext, mockExecuteWithContextTimeOut)
defer p.Reset()
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond)
defer cancel()
assert.EqualError(t, ca.AddPolicyCtx(ctx, "p", "p", []string{"alice", "data1", "read"}), "context deadline exceeded")
}

func TestContextAdapter_RemovePolicyCtx(t *testing.T) {
e, ca := clearDBPolicy()

_ = ca.AddPolicy("p", "p", []string{"alice", "data1", "read"})
_ = ca.AddPolicy("p", "p", []string{"alice", "data2", "read"})
assert.NoError(t, ca.RemovePolicyCtx(context.Background(), "p", "p", []string{"alice", "data1", "read"}))
_ = e.LoadPolicy()
testGetPolicy(t, e, [][]string{{"alice", "data2", "read"}})

var p = gomonkey.ApplyFunc(executeWithContext, mockExecuteWithContextTimeOut)
defer p.Reset()
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond)
defer cancel()
assert.EqualError(t, ca.RemovePolicyCtx(ctx, "p", "p", []string{"alice", "data1", "read"}), "context deadline exceeded")
}

func TestContextAdapter_RemoveFilteredPolicyCtx(t *testing.T) {
e, ca := clearDBPolicy()

_ = ca.AddPolicy("p", "p", []string{"alice", "data1", "read"})
_ = ca.AddPolicy("p", "p", []string{"alice", "data1", "write"})
_ = ca.AddPolicy("p", "p", []string{"alice", "data2", "read"})
assert.NoError(t, ca.RemoveFilteredPolicyCtx(context.Background(), "p", "p", 1, "data1"))
_ = e.LoadPolicy()
testGetPolicy(t, e, [][]string{{"alice", "data2", "read"}})

var p = gomonkey.ApplyFunc(executeWithContext, mockExecuteWithContextTimeOut)
defer p.Reset()
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond)
defer cancel()
assert.EqualError(t, ca.RemoveFilteredPolicyCtx(ctx, "p", "p", 1, "data1"), "context deadline exceeded")
}
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ module github.com/casbin/gorm-adapter/v3
go 1.20

require (
github.com/casbin/casbin/v2 v2.69.1
github.com/agiledragon/gomonkey/v2 v2.2.0
github.com/casbin/casbin/v2 v2.77.1
github.com/glebarez/sqlite v1.7.0
github.com/go-sql-driver/mysql v1.6.0
github.com/lib/pq v1.10.2
Expand Down Expand Up @@ -37,6 +38,9 @@ require (
github.com/microsoft/go-mssqldb v0.17.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578 // indirect
github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/text v0.3.8 // indirect
Expand Down
17 changes: 15 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/casbin/casbin/v2 v2.69.1 h1:R3e7uveIRN5Pdqvq0GXEhXmn7HyfoEVjp21/mgEXbdI=
github.com/casbin/casbin/v2 v2.69.1/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/agiledragon/gomonkey/v2 v2.2.0 h1:QJWqpdEhGV/JJy70sZ/LDnhbSlMrqHAWHcNOjz1kyuI=
github.com/agiledragon/gomonkey/v2 v2.2.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
github.com/casbin/casbin/v2 v2.77.1 h1:+H46VamJCTlmCPcb0N99Zaj4tSorfuvBh3v5lyGopeU=
github.com/casbin/casbin/v2 v2.77.1/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
Expand Down Expand Up @@ -47,6 +49,7 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
Expand Down Expand Up @@ -99,6 +102,7 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
Expand Down Expand Up @@ -144,6 +148,8 @@ github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXY
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
Expand All @@ -156,6 +162,12 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
Expand Down Expand Up @@ -227,6 +239,7 @@ golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
Expand Down

0 comments on commit 6f78166

Please sign in to comment.