Skip to content

Commit

Permalink
feat: add batch operations
Browse files Browse the repository at this point in the history
Signed-off-by: yyellowsun <[email protected]>
  • Loading branch information
yyellowsun committed Dec 16, 2020
1 parent 5c1ea3f commit 36726a1
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 21 deletions.
42 changes: 40 additions & 2 deletions adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func finalizer(a *adapter) {

// NewAdapter is the constructor for Adapter. If database name is not provided
// in the Mongo URL, 'casbin' will be used as database name.
func NewAdapter(url string, timeout ...interface{}) (persist.Adapter, error) {
func NewAdapter(url string, timeout ...interface{}) (persist.BatchAdapter, error) {
if !strings.HasPrefix(url, "mongodb+srv://") && !strings.HasPrefix(url, "mongodb://") {
url = fmt.Sprint("mongodb://" + url)
}
Expand All @@ -83,7 +83,7 @@ func NewAdapter(url string, timeout ...interface{}) (persist.Adapter, error) {

// NewAdapterWithClientOption is an alternative constructor for Adapter
// that does the same as NewAdapter, but uses mongo.ClientOption instead of a Mongo URL
func NewAdapterWithClientOption(clientOption *options.ClientOptions, databaseName string, timeout ...interface{}) (persist.Adapter, error) {
func NewAdapterWithClientOption(clientOption *options.ClientOptions, databaseName string, timeout ...interface{}) (persist.BatchAdapter, error) {
a := &adapter{
clientOption: clientOption,
}
Expand Down Expand Up @@ -309,6 +309,44 @@ func (a *adapter) AddPolicy(sec string, ptype string, rule []string) error {
return nil
}

// AddPolicies adds policy rules to the storage.
func (a *adapter) AddPolicies(sec string, ptype string, rules [][]string) error {
var lines []CasbinRule
for _,rule := range rules{
line := savePolicyLine(ptype, rule)
lines = append(lines, line)
}

for _,line := range lines{
ctx, cancel := context.WithTimeout(context.TODO(), a.timeout)
defer cancel()
if _, err := a.collection.InsertOne(ctx, line); err != nil {
return err
}
}

return nil
}

// RemovePolicies removes policy rules from the storage.
func (a *adapter) RemovePolicies(sec string, ptype string, rules [][]string) error {
var lines []CasbinRule
for _,rule := range rules{
line := savePolicyLine(ptype, rule)
lines = append(lines, line)
}

for _,line := range lines{
ctx, cancel := context.WithTimeout(context.TODO(), a.timeout)
defer cancel()
if _, err := a.collection.DeleteOne(ctx, line); err != nil {
return err
}
}

return nil
}

// RemovePolicy removes a policy rule from the storage.
func (a *adapter) RemovePolicy(sec string, ptype string, rule []string) error {
line := savePolicyLine(ptype, rule)
Expand Down
164 changes: 147 additions & 17 deletions adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,13 @@ func initPolicy(t *testing.T) {
if err != nil {
panic(err)
}
testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}})
testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"bob", "data2", "write"},
{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"},
},
)
}

func TestAdapter(t *testing.T) {
Expand All @@ -93,12 +99,16 @@ func TestAdapter(t *testing.T) {
if err != nil {
panic(err)
}
testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}})

testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"bob", "data2", "write"},
{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"},
},
)
// AutoSave is enabled by default.
// Now we disable it.
e.EnableAutoSave(false)

// Because AutoSave is disabled, the policy change only affects the policy in Casbin enforcer,
// it doesn't affect the policy in the storage.
e.AddPolicy("alice", "data1", "write")
Expand All @@ -107,7 +117,13 @@ func TestAdapter(t *testing.T) {
t.Errorf("Expected LoadPolicy() to be successful; got %v", err)
}
// This is still the original policy.
testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}})
testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"bob", "data2", "write"},
{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"},
},
)

// Now we enable the AutoSave.
e.EnableAutoSave(true)
Expand All @@ -120,7 +136,14 @@ func TestAdapter(t *testing.T) {
t.Errorf("Expected LoadPolicy() to be successful; got %v", err)
}
// The policy has a new rule: {"alice", "data1", "write"}.
testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}, {"alice", "data1", "write"}})
testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"bob", "data2", "write"},
{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"},
{"alice", "data1", "write"},
},
)

// Remove the added rule.
e.RemovePolicy("alice", "data1", "write")
Expand All @@ -130,15 +153,25 @@ func TestAdapter(t *testing.T) {
if err := e.LoadPolicy(); err != nil {
t.Errorf("Expected LoadPolicy() to be successful; got %v", err)
}
testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}})
testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"bob", "data2", "write"},
{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"},
},
)

// Remove "data2_admin" related policy rules via a filter.
// Two rules: {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"} are deleted.
e.RemoveFilteredPolicy(0, "data2_admin")
if err := e.LoadPolicy(); err != nil {
t.Errorf("Expected LoadPolicy() to be successful; got %v", err)
}
testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}})
testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"bob", "data2", "write"},
},
)

e.RemoveFilteredPolicy(1, "data1")
if err := e.LoadPolicy(); err != nil {
Expand All @@ -152,12 +185,81 @@ func TestAdapter(t *testing.T) {
}
testGetPolicy(t, e, [][]string{})
}
func TestDeleteFilteredAdapter(t *testing.T) {

func TestAddPolicies(t *testing.T) {
initPolicy(t)

a, err := NewAdapter(getDbURL())
if err != nil {
panic(err)
}

e, err := casbin.NewEnforcer("examples/rbac_model.conf", a)
if err != nil {
panic(err)
}

testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"bob", "data2", "write"},
{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"},
},
)
a.AddPolicies("p","p",[][]string{
{"bob", "data2", "read"},
{"alice", "data2", "write"},
{"alice", "data2", "read"},
{"bob", "data1", "write"},
{"bob", "data1", "read"},
},
)

if err := e.LoadPolicy(); err != nil {
t.Errorf("Expected LoadPolicy() to be successful; got %v", err)
}

testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"bob", "data2", "write"},
{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"},
{"bob", "data2", "read"},
{"alice", "data2", "write"},
{"alice", "data2", "read"},
{"bob", "data1", "write"},
{"bob", "data1", "read"},
},
)

// Remove the added rule.
if err := a.RemovePolicies("p", "p", [][]string{
{"bob", "data2", "read"},
{"alice", "data2", "write"},
{"alice", "data2", "read"},
{"bob", "data1", "write"},
{"bob", "data1", "read"},
}); err != nil {
t.Errorf("Expected RemovePolicies() to be successful; got %v", err)
}
if err := e.LoadPolicy(); err != nil {
t.Errorf("Expected LoadPolicy() to be successful; got %v", err)
}
testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"bob", "data2", "write"},
{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"},
},
)
}

func TestDeleteFilteredAdapter(t *testing.T) {
a, err := NewFilteredAdapter(getDbURL())
if err != nil {
panic(err)
}

e, err := casbin.NewEnforcer("examples/rbac_tenant_service.conf", a)
if err != nil {
panic(err)
Expand All @@ -171,27 +273,47 @@ func TestDeleteFilteredAdapter(t *testing.T) {
t.Errorf("Expected LoadPolicy() to be successful; got %v", err)
}
// The policy has a new rule: {"alice", "data1", "write"}.
testGetPolicy(t, e, [][]string{{"domain1", "alice", "data3", "read", "accept", "service1"},
{"domain1", "alice", "data3", "write", "accept", "service2"}})
testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"bob", "data2", "write"},
{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"},
{"domain1", "alice", "data3", "read", "accept", "service1"},
{"domain1", "alice", "data3", "write", "accept", "service2"},
},
)
// test RemoveFiltered Policy with "" fileds
e.RemoveFilteredPolicy(0, "domain1", "", "", "read")
if err := e.LoadPolicy(); err != nil {
t.Errorf("Expected LoadPolicy() to be successful; got %v", err)
}
testGetPolicy(t, e, [][]string{{"domain1", "alice", "data3", "write", "accept", "service2"}})
testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"bob", "data2", "write"},
{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"},
{"domain1", "alice", "data3", "write", "accept", "service2"},
},
)

e.RemoveFilteredPolicy(0, "domain1", "", "", "", "", "service2")
if err := e.LoadPolicy(); err != nil {
t.Errorf("Expected LoadPolicy() to be successful; got %v", err)
}
testGetPolicy(t, e, [][]string{})
testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"bob", "data2", "write"},
{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"},
},
)
}

func TestFilteredAdapter(t *testing.T) {
// Now the DB has policy, so we can provide a normal use case.
// Create an adapter and an enforcer.
// NewEnforcer() will load the policy automatically.
a, err := NewAdapter(getDbURL())
a, err := NewFilteredAdapter(getDbURL())
if err != nil {
panic(err)
}
Expand All @@ -200,7 +322,7 @@ func TestFilteredAdapter(t *testing.T) {
if err != nil {
panic(err)
}

// Load filtered policies from the database.
e.AddPolicy("alice", "data1", "write")
e.AddPolicy("bob", "data2", "write")
Expand All @@ -218,7 +340,11 @@ func TestFilteredAdapter(t *testing.T) {
t.Errorf("Expected LoadFilteredPolicy() to be successful; got %v", err)
}
// Only alice's policy should have been loaded,
testGetPolicy(t, e, [][]string{{"alice", "data1", "write"}})
testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"alice", "data1", "write"},
},
)

// Test safe handling of SavePolicy when using filtered policies.
if err := e.SavePolicy(); err == nil {
Expand All @@ -235,7 +361,11 @@ func TestFilteredAdapter(t *testing.T) {
if err := e.LoadPolicy(); err != nil {
t.Errorf("Expected LoadPolicy() to be successful; got %v", err)
}
testGetPolicy(t, e, [][]string{})
testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"data2_admin", "data2", "read"},
},
)
}

func TestNewAdapterWithInvalidURL(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ module github.com/casbin/mongodb-adapter/v3
go 1.13

require (
github.com/casbin/casbin/v2 v2.12.0
go.mongodb.org/mongo-driver v1.4.1
github.com/casbin/casbin/v2 v2.19.4
go.mongodb.org/mongo-driver v1.4.4
)
Loading

0 comments on commit 36726a1

Please sign in to comment.