Repository: casbin/casbin
Pull request #1201 (2023-02-25 12:08:32)

feat: merge string-adapter into casbin main library
Hi @hsluoyz  @qiangmzsx 

  This PR is to merge [string-adapter](https://github.com/qiangmzsx/string-adapter) into casbin main  library.

  All codes are copy from string-adapter and all unit test cases have passed. thank you. @qiangmzsx.

 


casbin-bot:  @tangyang9464 @JalinWang please review
CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/casbin/casbin?pullRequest=1201) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/casbin/casbin?pullRequest=1201) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/casbin/casbin?pullRequest=1201) it.</sub>
hsluoyz: @rts-gordon fix CI:

![image](https://user-images.githubusercontent.com/3787410/221360567-7fa161ed-271d-4ce2-9fa4-84edeaa4c032.png)

rts-gordon: > @rts-gordon fix CI:
> 
> ![image](https://user-images.githubusercontent.com/3787410/221360567-7fa161ed-271d-4ce2-9fa4-84edeaa4c032.png)

@hsluoyz 
  There is only "Semantic PR" Failed. I have installed "Semantic PR" for my "rts-gordon/casbin" repository and updated the title of PR, but it is still failed. Can you please give me some advice?
PokIsemaine: @rts-gordon It checks all commits, so you should probably check for new commits, such as add comments and remove comments
rts-gordon: > @rts-gordon It checks all commits, so you should probably check for new commits, such as add comments and remove comments

Hi @PokIsemaine
 Yes, I have added/removed some comments and commit twice after the first commit, but it is still failed. 

 Can you be more specific?

 Thank you
rts-gordon: > It's not just copying code here. Please refactor all code to conform to Casbin's code format

Refactor is done. But "Semantic PR" still failed. What can I do?

Patch:
diff --git a/persist/string-adapter/adapter.go b/persist/string-adapter/adapter.go
new file mode 100644
index 0000000..f2a187e
--- /dev/null
+++ b/persist/string-adapter/adapter.go
@@ -0,0 +1,92 @@
+// Copyright 2017 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 stringadapter
+
+import (
+	"bytes"
+	"errors"
+	"strings"
+
+	"github.com/casbin/casbin/v2/model"
+	"github.com/casbin/casbin/v2/persist"
+	"github.com/casbin/casbin/v2/util"
+)
+
+// Adapter is the string adapter for Casbin.
+// It can load policy from string or save policy to string.
+type Adapter struct {
+	Line string
+}
+
+// NewAdapter is the constructor for Adapter.
+func NewAdapter(line string) *Adapter {
+	return &Adapter{
+		Line: line,
+	}
+}
+
+// LoadPolicy loads all policy rules from the storage.
+func (a *Adapter) LoadPolicy(model model.Model) error {
+	if a.Line == "" {
+		return errors.New("invalid line, line cannot be empty")
+	}
+	strs := strings.Split(a.Line, "\n")
+	for _, str := range strs {
+		if str == "" {
+			continue
+		}
+		_ = persist.LoadPolicyLine(str, model)
+	}
+
+	return nil
+}
+
+// SavePolicy saves all policy rules to the storage.
+func (a *Adapter) SavePolicy(model model.Model) error {
+	var tmp bytes.Buffer
+	for ptype, ast := range model["p"] {
+		for _, rule := range ast.Policy {
+			tmp.WriteString(ptype + ", ")
+			tmp.WriteString(util.ArrayToString(rule))
+			tmp.WriteString("\n")
+		}
+	}
+
+	for ptype, ast := range model["g"] {
+		for _, rule := range ast.Policy {
+			tmp.WriteString(ptype + ", ")
+			tmp.WriteString(util.ArrayToString(rule))
+			tmp.WriteString("\n")
+		}
+	}
+	a.Line = strings.TrimRight(tmp.String(), "\n")
+	return nil
+}
+
+// AddPolicy adds a policy rule to the storage.
+func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error {
+	return errors.New("not implemented")
+}
+
+// RemovePolicy removes a policy rule from the storage.
+func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error {
+	a.Line = ""
+	return nil
+}
+
+// RemoveFilteredPolicy removes policy rules that match the filter from the storage.
+func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error {
+	return errors.New("not implemented")
+}


Test patch:
diff --git a/persist/string-adapter/adapter_test.go b/persist/string-adapter/adapter_test.go
new file mode 100644
index 0000000..cd26627
--- /dev/null
+++ b/persist/string-adapter/adapter_test.go
@@ -0,0 +1,101 @@
+// Copyright 2017 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 stringadapter
+
+import (
+	"testing"
+
+	"github.com/casbin/casbin/v2"
+	"github.com/casbin/casbin/v2/model"
+)
+
+func Test_KeyMatchRbac(t *testing.T) {
+	conf := `
+[request_definition]
+r = sub, obj, act
+
+[policy_definition]
+p = sub, obj, act
+
+[role_definition]
+g = _ , _
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = g(r.sub, p.sub)  && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)
+`
+	line := `
+p, alice, /alice_data/*, (GET)|(POST)
+p, alice, /alice_data/resource1, POST
+p, data_group_admin, /admin/*, POST
+p, data_group_admin, /bob_data/*, POST
+g, alice, data_group_admin
+`
+	a := NewAdapter(line)
+	m := model.NewModel()
+	err := m.LoadModelFromText(conf)
+	if err != nil {
+		t.Errorf("load model from text failed: %v", err.Error())
+		return
+	}
+	e, _ := casbin.NewEnforcer(m, a)
+	sub := "alice"
+	obj := "/alice_data/login"
+	act := "POST"
+	if res, _ := e.Enforce(sub, obj, act); !res {
+		t.Error("unexpected enforce result")
+	}
+}
+
+func Test_StringRbac(t *testing.T) {
+	conf := `
+[request_definition]
+r = sub, obj, act
+
+[policy_definition]
+p = sub, obj, act
+
+[role_definition]
+g = _ , _
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
+`
+	line := `
+p, alice, data1, read
+p, data_group_admin, data3, read
+p, data_group_admin, data3, write
+g, alice, data_group_admin
+`
+	a := NewAdapter(line)
+	m := model.NewModel()
+	err := m.LoadModelFromText(conf)
+	if err != nil {
+		t.Errorf("load model from text failed: %v", err.Error())
+		return
+	}
+	e, _ := casbin.NewEnforcer(m, a)
+	sub := "alice" // the user that wants to access a resource.
+	obj := "data1" // the resource that is going to be accessed.
+	act := "read"  // the operation that the user performs on the resource.
+	if res, _ := e.Enforce(sub, obj, act); !res {
+		t.Error("unexpected enforce result")
+	}
+}