Repository: casbin/casbin
Pull request #1195 (2023-02-14 11:21:48)

feat: fix locks in SyncedEnforcer Self* management API
fix: https://github.com/casbin/casbin/issues/1193

casbin-bot:  @tangyang9464 @JalinWang please review
hsluoyz: @PokIsemaine fix:

![image](https://user-images.githubusercontent.com/3787410/218726200-b2a82a4d-6550-4e46-b69c-f2ce51caedae.png)


Patch:
diff --git a/enforcer_synced.go b/enforcer_synced.go
index d087a43..3a9001a 100644
--- a/enforcer_synced.go
+++ b/enforcer_synced.go
@@ -596,3 +596,45 @@ func (e *SyncedEnforcer) AddFunction(name string, function govaluate.ExpressionF
 	defer e.m.Unlock()
 	e.Enforcer.AddFunction(name, function)
 }
+
+func (e *SyncedEnforcer) SelfAddPolicy(sec string, ptype string, rule []string) (bool, error) {
+	e.m.Lock()
+	defer e.m.Unlock()
+	return e.Enforcer.SelfAddPolicy(sec, ptype, rule)
+}
+
+func (e *SyncedEnforcer) SelfAddPolicies(sec string, ptype string, rules [][]string) (bool, error) {
+	e.m.Lock()
+	defer e.m.Unlock()
+	return e.Enforcer.SelfAddPolicies(sec, ptype, rules)
+}
+
+func (e *SyncedEnforcer) SelfRemovePolicy(sec string, ptype string, rule []string) (bool, error) {
+	e.m.Lock()
+	defer e.m.Unlock()
+	return e.Enforcer.SelfRemovePolicy(sec, ptype, rule)
+}
+
+func (e *SyncedEnforcer) SelfRemovePolicies(sec string, ptype string, rules [][]string) (bool, error) {
+	e.m.Lock()
+	defer e.m.Unlock()
+	return e.Enforcer.SelfRemovePolicies(sec, ptype, rules)
+}
+
+func (e *SyncedEnforcer) SelfRemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) (bool, error) {
+	e.m.Lock()
+	defer e.m.Unlock()
+	return e.Enforcer.SelfRemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues...)
+}
+
+func (e *SyncedEnforcer) SelfUpdatePolicy(sec string, ptype string, oldRule, newRule []string) (bool, error) {
+	e.m.Lock()
+	defer e.m.Unlock()
+	return e.Enforcer.SelfUpdatePolicy(sec, ptype, oldRule, newRule)
+}
+
+func (e *SyncedEnforcer) SelfUpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) (bool, error) {
+	e.m.Lock()
+	defer e.m.Unlock()
+	return e.Enforcer.SelfUpdatePolicies(sec, ptype, oldRules, newRules)
+}
diff --git a/util/util.go b/util/util.go
index dad7d7d..88f4345 100644
--- a/util/util.go
+++ b/util/util.go
@@ -70,6 +70,44 @@ func Array2DEquals(a [][]string, b [][]string) bool {
 	return true
 }
 
+// SortArray2D  Sorts the two-dimensional string array
+func SortArray2D(arr [][]string) {
+	if len(arr) != 0 {
+		sort.Slice(arr, func(i, j int) bool {
+			elementLen := len(arr[0])
+			for k := 0; k < elementLen; k++ {
+				if arr[i][k] < arr[j][k] {
+					return true
+				} else if arr[i][k] > arr[j][k] {
+					return false
+				}
+			}
+			return true
+		})
+	}
+}
+
+// SortedArray2DEquals determines whether two 2-dimensional string arrays are identical.
+func SortedArray2DEquals(a [][]string, b [][]string) bool {
+	if len(a) != len(b) {
+		return false
+	}
+	copyA := make([][]string, len(a))
+	copy(copyA, a)
+	copyB := make([][]string, len(b))
+	copy(copyB, b)
+
+	SortArray2D(copyA)
+	SortArray2D(copyB)
+
+	for i, v := range copyA {
+		if !ArrayEquals(v, copyB[i]) {
+			return false
+		}
+	}
+	return true
+}
+
 // ArrayRemoveDuplicates removes any duplicated elements in a string array.
 func ArrayRemoveDuplicates(s *[]string) {
 	found := make(map[string]bool)


Test patch:
diff --git a/enforcer_synced_test.go b/enforcer_synced_test.go
index 9576632..b597ff2 100644
--- a/enforcer_synced_test.go
+++ b/enforcer_synced_test.go
@@ -15,6 +15,7 @@
 package casbin
 
 import (
+	"github.com/casbin/casbin/v2/util"
 	"testing"
 	"time"
 )
@@ -66,3 +67,295 @@ func TestStopAutoLoadPolicy(t *testing.T) {
 		t.Error("auto load is still running")
 	}
 }
+
+func testSyncedEnforcerGetPolicy(t *testing.T, e *SyncedEnforcer, res [][]string) {
+	t.Helper()
+	myRes := e.GetPolicy()
+
+	if !util.SortedArray2DEquals(res, myRes) {
+		t.Error("Policy: ", myRes, ", supposed to be ", res)
+	} else {
+		t.Log("Policy: ", myRes)
+	}
+}
+
+func TestSyncedEnforcerSelfAddPolicy(t *testing.T) {
+	for i := 0; i < 10; i++ {
+		e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv")
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user1", "data1", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user2", "data2", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user3", "data3", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user4", "data4", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user5", "data5", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user6", "data6", "read"}) }()
+		time.Sleep(100 * time.Millisecond)
+
+		testSyncedEnforcerGetPolicy(t, e, [][]string{
+			{"alice", "data1", "read"},
+			{"bob", "data2", "write"},
+			{"user1", "data1", "read"},
+			{"user2", "data2", "read"},
+			{"user3", "data3", "read"},
+			{"user4", "data4", "read"},
+			{"user5", "data5", "read"},
+			{"user6", "data6", "read"},
+		})
+	}
+}
+
+func TestSyncedEnforcerSelfAddPolicies(t *testing.T) {
+	for i := 0; i < 10; i++ {
+		e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv")
+		go func() {
+			_, _ = e.SelfAddPolicies("p", "p", [][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}})
+		}()
+		go func() {
+			_, _ = e.SelfAddPolicies("p", "p", [][]string{{"user3", "data3", "read"}, {"user4", "data4", "read"}})
+		}()
+		go func() {
+			_, _ = e.SelfAddPolicies("p", "p", [][]string{{"user5", "data5", "read"}, {"user6", "data6", "read"}})
+		}()
+
+		time.Sleep(100 * time.Millisecond)
+
+		testSyncedEnforcerGetPolicy(t, e, [][]string{
+			{"alice", "data1", "read"},
+			{"bob", "data2", "write"},
+			{"user1", "data1", "read"},
+			{"user2", "data2", "read"},
+			{"user3", "data3", "read"},
+			{"user4", "data4", "read"},
+			{"user5", "data5", "read"},
+			{"user6", "data6", "read"},
+		})
+	}
+}
+
+func TestSyncedEnforcerSelfRemovePolicy(t *testing.T) {
+	for i := 0; i < 10; i++ {
+		e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv")
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user1", "data1", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user2", "data2", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user3", "data3", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user4", "data4", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user5", "data5", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user6", "data6", "read"}) }()
+
+		time.Sleep(100 * time.Millisecond)
+
+		testSyncedEnforcerGetPolicy(t, e, [][]string{
+			{"alice", "data1", "read"},
+			{"bob", "data2", "write"},
+			{"user1", "data1", "read"},
+			{"user2", "data2", "read"},
+			{"user3", "data3", "read"},
+			{"user4", "data4", "read"},
+			{"user5", "data5", "read"},
+			{"user6", "data6", "read"},
+		})
+
+		go func() { _, _ = e.SelfRemovePolicy("p", "p", []string{"user1", "data1", "read"}) }()
+		go func() { _, _ = e.SelfRemovePolicy("p", "p", []string{"user2", "data2", "read"}) }()
+		go func() { _, _ = e.SelfRemovePolicy("p", "p", []string{"user3", "data3", "read"}) }()
+		go func() { _, _ = e.SelfRemovePolicy("p", "p", []string{"user4", "data4", "read"}) }()
+		go func() { _, _ = e.SelfRemovePolicy("p", "p", []string{"user5", "data5", "read"}) }()
+		go func() { _, _ = e.SelfRemovePolicy("p", "p", []string{"user6", "data6", "read"}) }()
+
+		time.Sleep(100 * time.Millisecond)
+		testSyncedEnforcerGetPolicy(t, e, [][]string{
+			{"alice", "data1", "read"},
+			{"bob", "data2", "write"},
+		})
+	}
+}
+
+func TestSyncedEnforcerSelfRemovePolicies(t *testing.T) {
+	for i := 0; i < 10; i++ {
+		e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv")
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user1", "data1", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user2", "data2", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user3", "data3", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user4", "data4", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user5", "data5", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user6", "data6", "read"}) }()
+
+		time.Sleep(100 * time.Millisecond)
+
+		testSyncedEnforcerGetPolicy(t, e, [][]string{
+			{"alice", "data1", "read"},
+			{"bob", "data2", "write"},
+			{"user1", "data1", "read"},
+			{"user2", "data2", "read"},
+			{"user3", "data3", "read"},
+			{"user4", "data4", "read"},
+			{"user5", "data5", "read"},
+			{"user6", "data6", "read"},
+		})
+
+		go func() {
+			_, _ = e.SelfRemovePolicies("p", "p", [][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}})
+		}()
+		go func() {
+			_, _ = e.SelfRemovePolicies("p", "p", [][]string{{"user3", "data3", "read"}, {"user4", "data4", "read"}})
+		}()
+		go func() {
+			_, _ = e.SelfRemovePolicies("p", "p", [][]string{{"user5", "data5", "read"}, {"user6", "data6", "read"}})
+		}()
+
+		time.Sleep(100 * time.Millisecond)
+		testSyncedEnforcerGetPolicy(t, e, [][]string{
+			{"alice", "data1", "read"},
+			{"bob", "data2", "write"},
+		})
+	}
+}
+
+func TestSyncedEnforcerSelfRemoveFilteredPolicy(t *testing.T) {
+	for i := 0; i < 10; i++ {
+		e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv")
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user1", "data1", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user2", "data2", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user3", "data3", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user4", "data4", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user5", "data5", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user6", "data6", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user7", "data7", "write"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user8", "data8", "write"}) }()
+		time.Sleep(100 * time.Millisecond)
+
+		testSyncedEnforcerGetPolicy(t, e, [][]string{
+			{"alice", "data1", "read"},
+			{"bob", "data2", "write"},
+			{"user1", "data1", "read"},
+			{"user2", "data2", "read"},
+			{"user3", "data3", "read"},
+			{"user4", "data4", "read"},
+			{"user5", "data5", "read"},
+			{"user6", "data6", "read"},
+			{"user7", "data7", "write"},
+			{"user8", "data8", "write"},
+		})
+
+		go func() { _, _ = e.SelfRemoveFilteredPolicy("p", "p", 0, "user1") }()
+		go func() { _, _ = e.SelfRemoveFilteredPolicy("p", "p", 0, "user2") }()
+		go func() { _, _ = e.SelfRemoveFilteredPolicy("p", "p", 1, "data3") }()
+		go func() { _, _ = e.SelfRemoveFilteredPolicy("p", "p", 1, "data4") }()
+		go func() { _, _ = e.SelfRemoveFilteredPolicy("p", "p", 0, "user5") }()
+		go func() { _, _ = e.SelfRemoveFilteredPolicy("p", "p", 0, "user6") }()
+		go func() { _, _ = e.SelfRemoveFilteredPolicy("p", "p", 2, "write") }()
+
+		time.Sleep(100 * time.Millisecond)
+		testSyncedEnforcerGetPolicy(t, e, [][]string{
+			{"alice", "data1", "read"},
+		})
+	}
+}
+
+func TestSyncedEnforcerSelfUpdatePolicy(t *testing.T) {
+	for i := 0; i < 10; i++ {
+		e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv")
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user1", "data1", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user2", "data2", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user3", "data3", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user4", "data4", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user5", "data5", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user6", "data6", "read"}) }()
+		time.Sleep(100 * time.Millisecond)
+
+		testSyncedEnforcerGetPolicy(t, e, [][]string{
+			{"alice", "data1", "read"},
+			{"bob", "data2", "write"},
+			{"user1", "data1", "read"},
+			{"user2", "data2", "read"},
+			{"user3", "data3", "read"},
+			{"user4", "data4", "read"},
+			{"user5", "data5", "read"},
+			{"user6", "data6", "read"},
+		})
+
+		go func() {
+			_, _ = e.SelfUpdatePolicy("p", "p", []string{"user1", "data1", "read"}, []string{"user1", "data1", "write"})
+		}()
+		go func() {
+			_, _ = e.SelfUpdatePolicy("p", "p", []string{"user2", "data2", "read"}, []string{"user2", "data2", "write"})
+		}()
+		go func() {
+			_, _ = e.SelfUpdatePolicy("p", "p", []string{"user3", "data3", "read"}, []string{"user3", "data3", "write"})
+		}()
+		go func() {
+			_, _ = e.SelfUpdatePolicy("p", "p", []string{"user4", "data4", "read"}, []string{"user4", "data4", "write"})
+		}()
+		go func() {
+			_, _ = e.SelfUpdatePolicy("p", "p", []string{"user5", "data5", "read"}, []string{"user5", "data5", "write"})
+		}()
+		go func() {
+			_, _ = e.SelfUpdatePolicy("p", "p", []string{"user6", "data6", "read"}, []string{"user6", "data6", "write"})
+		}()
+
+		time.Sleep(100 * time.Millisecond)
+		testSyncedEnforcerGetPolicy(t, e, [][]string{
+			{"alice", "data1", "read"},
+			{"bob", "data2", "write"},
+			{"user1", "data1", "write"},
+			{"user2", "data2", "write"},
+			{"user3", "data3", "write"},
+			{"user4", "data4", "write"},
+			{"user5", "data5", "write"},
+			{"user6", "data6", "write"},
+		})
+	}
+}
+
+func TestSyncedEnforcerSelfUpdatePolicies(t *testing.T) {
+	for i := 0; i < 10; i++ {
+		e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv")
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user1", "data1", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user2", "data2", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user3", "data3", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user4", "data4", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user5", "data5", "read"}) }()
+		go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user6", "data6", "read"}) }()
+		time.Sleep(100 * time.Millisecond)
+
+		testSyncedEnforcerGetPolicy(t, e, [][]string{
+			{"alice", "data1", "read"},
+			{"bob", "data2", "write"},
+			{"user1", "data1", "read"},
+			{"user2", "data2", "read"},
+			{"user3", "data3", "read"},
+			{"user4", "data4", "read"},
+			{"user5", "data5", "read"},
+			{"user6", "data6", "read"},
+		})
+
+		go func() {
+			_, _ = e.SelfUpdatePolicies("p", "p",
+				[][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}},
+				[][]string{{"user1", "data1", "write"}, {"user2", "data2", "write"}})
+		}()
+
+		go func() {
+			_, _ = e.SelfUpdatePolicies("p", "p",
+				[][]string{{"user3", "data3", "read"}, {"user4", "data4", "read"}},
+				[][]string{{"user3", "data3", "write"}, {"user4", "data4", "write"}})
+		}()
+
+		go func() {
+			_, _ = e.SelfUpdatePolicies("p", "p",
+				[][]string{{"user5", "data5", "read"}, {"user6", "data6", "read"}},
+				[][]string{{"user5", "data5", "write"}, {"user6", "data6", "write"}})
+		}()
+
+		time.Sleep(100 * time.Millisecond)
+		testSyncedEnforcerGetPolicy(t, e, [][]string{
+			{"alice", "data1", "read"},
+			{"bob", "data2", "write"},
+			{"user1", "data1", "write"},
+			{"user2", "data2", "write"},
+			{"user3", "data3", "write"},
+			{"user4", "data4", "write"},
+			{"user5", "data5", "write"},
+			{"user6", "data6", "write"},
+		})
+	}
+}