Skip to content

Commit 1089d55

Browse files
authored
feat: Contextadapter (#59)
* feat: context adapter * feat: context adapter * feat: context adapter
1 parent 047522b commit 1089d55

File tree

5 files changed

+289
-37
lines changed

5 files changed

+289
-37
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,21 @@ func main() {
101101
}
102102
```
103103

104+
## Context Adapter
105+
106+
`xormadapter` supports adapter with context, the following is a timeout control implemented using context
107+
108+
```go
109+
ca, _ := NewContextAdapter("mysql", "root:@tcp(127.0.0.1:3306)/", "casbin")
110+
// Limited time 300s
111+
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond)
112+
defer cancel()
113+
err := ca.AddPolicyCtx(ctx, "p", "p", []string{"alice", "data1", "read"})
114+
if err != nil {
115+
panic(err)
116+
}
117+
```
118+
104119
## Getting Help
105120

106121
- [Casbin](https://github.com/casbin/casbin)

context_adapter.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2023 The casbin Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package xormadapter
16+
17+
import (
18+
"context"
19+
20+
"github.com/casbin/casbin/v2/model"
21+
)
22+
23+
type ContextAdapter struct {
24+
*Adapter
25+
}
26+
27+
func NewContextAdapter(driverName string, dataSourceName string, dbSpecified ...bool) (*ContextAdapter, error) {
28+
a, err := NewAdapter(driverName, dataSourceName, dbSpecified...)
29+
return &ContextAdapter{
30+
a,
31+
}, err
32+
}
33+
34+
// executeWithContext is a helper function to execute a function with context and return the result or error.
35+
func executeWithContext(ctx context.Context, fn func() error) error {
36+
done := make(chan error)
37+
38+
go func() {
39+
done <- fn()
40+
}()
41+
42+
select {
43+
case <-ctx.Done():
44+
return ctx.Err()
45+
case err := <-done:
46+
return err
47+
}
48+
}
49+
50+
// LoadPolicyCtx loads all policy rules from the storage with context.
51+
func (ca *ContextAdapter) LoadPolicyCtx(ctx context.Context, model model.Model) error {
52+
return executeWithContext(ctx, func() error {
53+
return ca.LoadPolicy(model)
54+
})
55+
}
56+
57+
// SavePolicyCtx saves all policy rules to the storage with context.
58+
func (ca *ContextAdapter) SavePolicyCtx(ctx context.Context, model model.Model) error {
59+
return executeWithContext(ctx, func() error {
60+
return ca.SavePolicy(model)
61+
})
62+
}
63+
64+
// AddPolicyCtx adds a policy rule to the storage with context.
65+
// This is part of the Auto-Save feature.
66+
func (ca *ContextAdapter) AddPolicyCtx(ctx context.Context, sec string, ptype string, rule []string) error {
67+
return executeWithContext(ctx, func() error {
68+
return ca.AddPolicy(sec, ptype, rule)
69+
})
70+
}
71+
72+
// RemovePolicyCtx removes a policy rule from the storage with context.
73+
// This is part of the Auto-Save feature.
74+
func (ca *ContextAdapter) RemovePolicyCtx(ctx context.Context, sec string, ptype string, rule []string) error {
75+
return executeWithContext(ctx, func() error {
76+
return ca.RemovePolicy(sec, ptype, rule)
77+
})
78+
}
79+
80+
// RemoveFilteredPolicyCtx removes policy rules that match the filter from the storage with context.
81+
// This is part of the Auto-Save feature.
82+
func (ca *ContextAdapter) RemoveFilteredPolicyCtx(ctx context.Context, sec string, ptype string, fieldIndex int, fieldValues ...string) error {
83+
return executeWithContext(ctx, func() error {
84+
return ca.RemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues...)
85+
})
86+
}

context_adapter_test.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// Copyright 2023 The casbin Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package xormadapter
16+
17+
import (
18+
"context"
19+
"testing"
20+
"time"
21+
22+
"github.com/agiledragon/gomonkey/v2"
23+
"github.com/casbin/casbin/v2"
24+
"github.com/stretchr/testify/assert"
25+
"xorm.io/xorm"
26+
)
27+
28+
func mockExecuteWithContextTimeOut(ctx context.Context, fn func() error) error {
29+
done := make(chan error)
30+
go func() {
31+
time.Sleep(500 * time.Microsecond)
32+
done <- fn()
33+
}()
34+
35+
select {
36+
case <-ctx.Done():
37+
return ctx.Err()
38+
case err := <-done:
39+
return err
40+
}
41+
}
42+
43+
func clearDBPolicy() (*casbin.Enforcer, *ContextAdapter) {
44+
ca, err := NewContextAdapter("mysql", "root:@tcp(127.0.0.1:3306)/")
45+
if err != nil {
46+
panic(err)
47+
}
48+
49+
e, err := casbin.NewEnforcer("examples/rbac_model.conf", ca)
50+
if err != nil {
51+
panic(err)
52+
}
53+
54+
e.ClearPolicy()
55+
_ = e.SavePolicy()
56+
57+
return e, ca
58+
59+
return e, ca
60+
}
61+
62+
func TestContextAdapter_LoadPolicyCtx(t *testing.T) {
63+
e, ca := clearDBPolicy()
64+
65+
engine, _ := xorm.NewEngine("mysql", "root:@tcp(127.0.0.1:3306)/casbin")
66+
policy := &CasbinRule{
67+
Ptype: "p",
68+
V0: "alice",
69+
V1: "data1",
70+
V2: "read",
71+
}
72+
_, err := engine.Insert(policy)
73+
if err != nil {
74+
panic(err)
75+
}
76+
77+
assert.NoError(t, ca.LoadPolicyCtx(context.Background(), e.GetModel()))
78+
e, _ = casbin.NewEnforcer(e.GetModel(), ca)
79+
testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}})
80+
81+
var p = gomonkey.ApplyFunc(executeWithContext, mockExecuteWithContextTimeOut)
82+
defer p.Reset()
83+
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond)
84+
defer cancel()
85+
86+
assert.EqualError(t, ca.LoadPolicyCtx(ctx, e.GetModel()), "context deadline exceeded")
87+
}
88+
89+
func TestContextAdapter_SavePolicyCtx(t *testing.T) {
90+
e, ca := clearDBPolicy()
91+
92+
e.EnableAutoSave(false)
93+
_, _ = e.AddPolicy("alice", "data1", "read")
94+
assert.NoError(t, ca.SavePolicyCtx(context.Background(), e.GetModel()))
95+
_ = e.LoadPolicy()
96+
testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}})
97+
98+
var p = gomonkey.ApplyFunc(executeWithContext, mockExecuteWithContextTimeOut)
99+
defer p.Reset()
100+
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond)
101+
defer cancel()
102+
assert.EqualError(t, ca.SavePolicyCtx(ctx, e.GetModel()), "context deadline exceeded")
103+
// Sleep, waiting for the completion of the transaction commit
104+
time.Sleep(2 * time.Second)
105+
}
106+
107+
func TestContextAdapter_AddPolicyCtx(t *testing.T) {
108+
e, ca := clearDBPolicy()
109+
110+
assert.NoError(t, ca.AddPolicyCtx(context.Background(), "p", "p", []string{"alice", "data1", "read"}))
111+
_ = e.LoadPolicy()
112+
testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}})
113+
114+
var p = gomonkey.ApplyFunc(executeWithContext, mockExecuteWithContextTimeOut)
115+
defer p.Reset()
116+
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond)
117+
defer cancel()
118+
assert.EqualError(t, ca.AddPolicyCtx(ctx, "p", "p", []string{"alice", "data1", "read"}), "context deadline exceeded")
119+
}
120+
121+
func TestContextAdapter_RemovePolicyCtx(t *testing.T) {
122+
e, ca := clearDBPolicy()
123+
124+
_ = ca.AddPolicy("p", "p", []string{"alice", "data1", "read"})
125+
_ = ca.AddPolicy("p", "p", []string{"alice", "data2", "read"})
126+
assert.NoError(t, ca.RemovePolicyCtx(context.Background(), "p", "p", []string{"alice", "data1", "read"}))
127+
_ = e.LoadPolicy()
128+
testGetPolicy(t, e, [][]string{{"alice", "data2", "read"}})
129+
130+
var p = gomonkey.ApplyFunc(executeWithContext, mockExecuteWithContextTimeOut)
131+
defer p.Reset()
132+
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond)
133+
defer cancel()
134+
assert.EqualError(t, ca.RemovePolicyCtx(ctx, "p", "p", []string{"alice", "data1", "read"}), "context deadline exceeded")
135+
}
136+
137+
func TestContextAdapter_RemoveFilteredPolicyCtx(t *testing.T) {
138+
e, ca := clearDBPolicy()
139+
140+
_ = ca.AddPolicy("p", "p", []string{"alice", "data1", "read"})
141+
_ = ca.AddPolicy("p", "p", []string{"alice", "data1", "write"})
142+
_ = ca.AddPolicy("p", "p", []string{"alice", "data2", "read"})
143+
assert.NoError(t, ca.RemoveFilteredPolicyCtx(context.Background(), "p", "p", 1, "data1"))
144+
_ = e.LoadPolicy()
145+
testGetPolicy(t, e, [][]string{{"alice", "data2", "read"}})
146+
147+
var p = gomonkey.ApplyFunc(executeWithContext, mockExecuteWithContextTimeOut)
148+
defer p.Reset()
149+
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond)
150+
defer cancel()
151+
assert.EqualError(t, ca.RemoveFilteredPolicyCtx(ctx, "p", "p", 1, "data1"), "context deadline exceeded")
152+
}

go.mod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ module github.com/casbin/xorm-adapter/v3
33
go 1.12
44

55
require (
6-
github.com/PuerkitoBio/goquery v1.5.1 // indirect
7-
github.com/casbin/casbin/v2 v2.28.3
6+
github.com/agiledragon/gomonkey/v2 v2.10.1
7+
github.com/casbin/casbin/v2 v2.77.2
88
github.com/go-sql-driver/mysql v1.6.0
99
github.com/lib/pq v1.10.2
10+
github.com/stretchr/testify v1.7.0
1011
xorm.io/xorm v1.3.2
1112
)

0 commit comments

Comments
 (0)