From 5eb00597f173d982625ced823a1c86217ff9e1a3 Mon Sep 17 00:00:00 2001 From: yxh Date: Tue, 24 Dec 2019 23:38:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B4=E5=90=88casbin=E6=B7=BB=E5=8A=A0adapt?= =?UTF-8?q?er=E5=A4=84=E7=90=86=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitattributes | 3 + .gitignore | 21 +++ README.MD | 3 + app/api/hello/hello.go | 10 ++ app/api/user/user.go | 81 +++++++++ app/model/.gitkeep | 0 app/service/.gitkeep | 0 app/service/user/user.go | 91 ++++++++++ boot/.gitkeep | 0 boot/boot.go | 8 + config/.gitkeep | 0 docfile/.gitkeep | 0 go.mod | 9 + i18n/.gitkeep | 0 library/response/response.go | 29 ++++ library/utils/casbin_adapter.go | 279 +++++++++++++++++++++++++++++++ main.go | 11 ++ main2.go | 24 +++ public/html/.gitkeep | 0 public/plugin/.gitkeep | 0 public/resource/css/.gitkeep | 0 public/resource/image/.gitkeep | 0 public/resource/js/.gitkeep | 0 router/.gitkeep | 0 router/router.go | 13 ++ template/.gitkeep | 0 test/casbin_conf/model.conf | 15 ++ test/casbin_conf/policy.csv | 2 + test/casbin_conf/rbac_model.conf | 14 ++ test/casbin_conf/rbac_policy.csv | 5 + test/demo_test.go | 170 +++++++++++++++++++ 31 files changed, 788 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 README.MD create mode 100644 app/api/hello/hello.go create mode 100644 app/api/user/user.go create mode 100644 app/model/.gitkeep create mode 100644 app/service/.gitkeep create mode 100644 app/service/user/user.go create mode 100644 boot/.gitkeep create mode 100644 boot/boot.go create mode 100644 config/.gitkeep create mode 100644 docfile/.gitkeep create mode 100644 go.mod create mode 100644 i18n/.gitkeep create mode 100644 library/response/response.go create mode 100644 library/utils/casbin_adapter.go create mode 100644 main.go create mode 100644 main2.go create mode 100644 public/html/.gitkeep create mode 100644 public/plugin/.gitkeep create mode 100644 public/resource/css/.gitkeep create mode 100644 public/resource/image/.gitkeep create mode 100644 public/resource/js/.gitkeep create mode 100644 router/.gitkeep create mode 100644 router/router.go create mode 100644 template/.gitkeep create mode 100644 test/casbin_conf/model.conf create mode 100644 test/casbin_conf/policy.csv create mode 100644 test/casbin_conf/rbac_model.conf create mode 100644 test/casbin_conf/rbac_policy.csv create mode 100644 test/demo_test.go diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8d6f5cf --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +*.js linguist-language=GO +*.css linguist-language=GO +*.html linguist-language=GO \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c1db1c7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +.buildpath +.hgignore.swp +.project +.orig +.swp +.idea/ +.settings/ +.vscode/ +vender/ +log/ +composer.lock +gitpush.sh +pkg/ +bin/ +cbuild +*/.DS_Store +config/config.toml +main +.vscode +go.sum +*.exe \ No newline at end of file diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..320d20c --- /dev/null +++ b/README.MD @@ -0,0 +1,3 @@ +# GoFrame Project + +https://goframe.org diff --git a/app/api/hello/hello.go b/app/api/hello/hello.go new file mode 100644 index 0000000..bde4596 --- /dev/null +++ b/app/api/hello/hello.go @@ -0,0 +1,10 @@ +package hello + +import ( + "github.com/gogf/gf/net/ghttp" +) + +// Hello World +func Handler(r *ghttp.Request) { + r.Response.Writeln("Hello World!") +} diff --git a/app/api/user/user.go b/app/api/user/user.go new file mode 100644 index 0000000..a046dcf --- /dev/null +++ b/app/api/user/user.go @@ -0,0 +1,81 @@ +package user + +import ( + "gfast/app/service/user" + "gfast/library/response" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/util/gvalid" +) + +//用户API管理对象 +type Controller struct {} + +//用户注册接口 +func (c Controller) SignUp(r *ghttp.Request) { + if err:=user.SignUp(r.GetPostMapStrStr());err!=nil{ + response.Json(r, 1, err.Error()) + }else{ + response.Json(r, 0, "ok") + } +} + + +// 用户登录接口 +func (c *Controller) SignIn(r *ghttp.Request) { + data := r.GetPostMapStrStr() + rules := map[string]string{ + "passport": "required", + "password": "required", + } + msgs := map[string]interface{}{ + "passport": "账号不能为空", + "password": "密码不能为空", + } + if e := gvalid.CheckMap(data, rules, msgs); e != nil { + response.Json(r, 1, e.String()) + } + if err := user.SignIn(data["passport"], data["password"], r.Session); err != nil { + response.Json(r, 1, err.Error()) + } else { + response.Json(r, 0, "ok") + } +} + +// 判断用户是否已经登录 +func (c *Controller) IsSignedIn(r *ghttp.Request) { + if user.IsSignedIn(r.Session) { + response.Json(r, 0, "ok") + } else { + response.Json(r, 1, "") + } +} + +// 用户注销/退出接口 +func (c *Controller) SignOut(r *ghttp.Request) { + user.SignOut(r.Session) + response.Json(r, 0, "ok") +} + +// 检测用户账号接口(唯一性校验) +func (c *Controller) CheckPassport(r *ghttp.Request) { + passport := r.GetString("passport") + if e := gvalid.Check(passport, "required", "请输入账号"); e != nil { + response.Json(r, 1, e.String()) + } + if user.CheckPassport(passport) { + response.Json(r, 0, "ok") + } + response.Json(r, 1, "账号已经存在") +} + +// 检测用户昵称接口(唯一性校验) +func (c *Controller) CheckNickName(r *ghttp.Request) { + nickname := r.Get("nickname") + if e := gvalid.Check(nickname, "required", "请输入昵称"); e != nil { + response.Json(r, 1, e.String()) + } + if user.CheckNickName(r.GetString("nickname")) { + response.Json(r, 0, "ok") + } + response.Json(r, 1, "昵称已经存在") +} diff --git a/app/model/.gitkeep b/app/model/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/service/.gitkeep b/app/service/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/service/user/user.go b/app/service/user/user.go new file mode 100644 index 0000000..3127f89 --- /dev/null +++ b/app/service/user/user.go @@ -0,0 +1,91 @@ +package user + +import ( + "database/sql" + "errors" + "fmt" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/util/gvalid" +) + +const USER_SESSION_MARK = "user_info" + +var ( + //表对象 + table = g.DB().Table("user").Safe() +) + +//用户注册 +func SignUp(data g.MapStrStr)error{ + //数据校验 + rules:=[]string{ + "passport @required|length:6,16#账号不能为空|账号长度应在:min到:max之间", + "password2@required|length:6,16#请输入确认密码|密码长度应当在:min到:max之间", + "password @required|length:6,16|same:password2#密码不能为空|密码长度应当在:min到:max之间|两次密码输入不相等", + } + if err:=gvalid.CheckMap(data,rules);err!=nil{ + return errors.New(err.String()) + } + if _, ok := data["nickname"]; !ok { + data["nickname"] = data["passport"] + } + // 唯一性数据检查 + if !CheckPassport(data["passport"]) { + return errors.New(fmt.Sprintf("账号 %s 已经存在", data["passport"])) + } + if !CheckNickName(data["nickname"]) { + return errors.New(fmt.Sprintf("昵称 %s 已经存在", data["nickname"])) + } + // 记录账号创建/注册时间 + if _, ok := data["create_time"]; !ok { + data["create_time"] = gtime.Now().String() + } + if _, err := table.Filter().Data(data).Save(); err != nil { + return err + } + return nil +} + +// 判断用户是否已经登录 +func IsSignedIn(session *ghttp.Session) bool { + return session.Contains(USER_SESSION_MARK) +} + +// 用户登录,成功返回用户信息,否则返回nil; passport应当会md5值字符串 +func SignIn(passport, password string, session *ghttp.Session) error { + record, err := table.Where("passport=? and password=?", passport, password).One() + if err != nil && err != sql.ErrNoRows { + return err + } + if record == nil { + return errors.New("账号或密码错误") + } + session.Set(USER_SESSION_MARK, record) + return nil +} + +// 用户注销 +func SignOut(session *ghttp.Session) { + session.Remove(USER_SESSION_MARK) +} + + +// 检查账号是否符合规范(目前仅检查唯一性),存在返回false,否则true +func CheckPassport(passport string) bool { + if i, err := table.Where("passport", passport).Count(); err != nil && err != sql.ErrNoRows { + return false + } else { + return i == 0 + } +} + +// 检查昵称是否符合规范(目前仅检查唯一性),存在返回false,否则true +func CheckNickName(nickname string) bool { + if i, err := table.Where("nickname", nickname).Count(); err != nil && err != sql.ErrNoRows { + return false + } else { + return i == 0 + } +} \ No newline at end of file diff --git a/boot/.gitkeep b/boot/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/boot/boot.go b/boot/boot.go new file mode 100644 index 0000000..1f87801 --- /dev/null +++ b/boot/boot.go @@ -0,0 +1,8 @@ +package boot + +import "github.com/gogf/gf/frame/g" + +func init() { + g.Server().SetPort(8200) +} + diff --git a/config/.gitkeep b/config/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docfile/.gitkeep b/docfile/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..85084a1 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module gfast + +require ( + github.com/casbin/casbin/v2 v2.1.2 + github.com/gogf/gf v1.10.0 + google.golang.org/appengine v1.6.5 // indirect +) + +go 1.13 diff --git a/i18n/.gitkeep b/i18n/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/library/response/response.go b/library/response/response.go new file mode 100644 index 0000000..830d984 --- /dev/null +++ b/library/response/response.go @@ -0,0 +1,29 @@ +package response + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" +) + +// 标准返回结果数据结构封装。 +// 返回固定数据结构的JSON: +// err: 错误码(0:成功, 1:失败, >1:错误码); +// msg: 请求结果信息; +// data: 请求结果,根据不同接口返回结果的数据结构不同; +func Json(r *ghttp.Request, err int, msg string, data ...interface{}) { + responseData := interface{}(nil) + if len(data) > 0 { + responseData = data[0] + } + r.Response.WriteJson(g.Map{ + "err": err, + "msg": msg, + "data": responseData, + }) +} + +// 返回JSON数据并退出当前HTTP执行函数。 +func JsonExit(r *ghttp.Request, err int, msg string, data ...interface{}) { + Json(r, err, msg, data...) + r.Exit() +} diff --git a/library/utils/casbin_adapter.go b/library/utils/casbin_adapter.go new file mode 100644 index 0000000..4178f6f --- /dev/null +++ b/library/utils/casbin_adapter.go @@ -0,0 +1,279 @@ +package utils + +import ( + "fmt" + "github.com/casbin/casbin/v2/model" + "github.com/casbin/casbin/v2/persist" + "github.com/gogf/gf/database/gdb" + "runtime" +) + +type CasbinRule struct { + PType string `json:"ptype"` + V0 string `json:"v0"` + V1 string `json:"v1"` + V2 string `json:"v2"` + V3 string `json:"v3"` + V4 string `json:"v4"` + V5 string `json:"v5"` +} + +// Adapter represents the gdb adapter for policy storage. +type Adapter struct { + DriverName string + DataSourceName string + TableName string + Db gdb.DB +} + +// finalizer is the destructor for Adapter. +func finalizer(a *Adapter) { + // 注意不用的时候不需要使用Close方法关闭数据库连接(并且gdb也没有提供Close方法), + // 数据库引擎底层采用了链接池设计,当链接不再使用时会自动关闭 + a.Db = nil +} + +// NewAdapter is the constructor for Adapter. +func NewAdapter(driverName string, dataSourceName string) (*Adapter, error) { + a := &Adapter{} + a.DriverName = driverName + a.DataSourceName = dataSourceName + a.TableName = "casbin_rule" + + // Open the DB, create it if not existed. + err := a.open() + if err != nil { + return nil, err + } + + // Call the destructor when the object is released. + runtime.SetFinalizer(a, finalizer) + + return a, nil +} + +// NewAdapterFromOptions is the constructor for Adapter with existed connection +func NewAdapterFromOptions(adapter *Adapter) (*Adapter, error) { + + if adapter.TableName == "" { + adapter.TableName = "casbin_rule" + } + if adapter.Db == nil { + err := adapter.open() + if err != nil { + return nil, err + } + + runtime.SetFinalizer(adapter, finalizer) + } + return adapter, nil +} + +func (a *Adapter) open() error { + var err error + var db gdb.DB + + gdb.SetConfig(gdb.Config{ + "casbin": gdb.ConfigGroup{ + gdb.ConfigNode{ + Type: a.DriverName, + LinkInfo: a.DataSourceName, + Role: "master", + Weight: 100, + }, + }, + }) + db, err = gdb.New("casbin") + + if err != nil { + return err + } + + a.Db = db + + return a.createTable() +} + +func (a *Adapter) close() error { + // 注意不用的时候不需要使用Close方法关闭数据库连接(并且gdb也没有提供Close方法), + // 数据库引擎底层采用了链接池设计,当链接不再使用时会自动关闭 + a.Db = nil + return nil +} + +func (a *Adapter) createTable() error { + _, err := a.Db.Exec(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (ptype VARCHAR(10), v0 VARCHAR(256), v1 VARCHAR(256), v2 VARCHAR(256), v3 VARCHAR(256), v4 VARCHAR(256), v5 VARCHAR(256))", a.TableName)) + return err +} + +func (a *Adapter) dropTable() error { + _, err := a.Db.Exec(fmt.Sprintf("DROP TABLE %s", a.TableName)) + return err +} + +func loadPolicyLine(line CasbinRule, model model.Model) { + lineText := line.PType + if line.V0 != "" { + lineText += ", " + line.V0 + } + if line.V1 != "" { + lineText += ", " + line.V1 + } + if line.V2 != "" { + lineText += ", " + line.V2 + } + if line.V3 != "" { + lineText += ", " + line.V3 + } + if line.V4 != "" { + lineText += ", " + line.V4 + } + if line.V5 != "" { + lineText += ", " + line.V5 + } + + persist.LoadPolicyLine(lineText, model) +} + +// LoadPolicy loads policy from database. +func (a *Adapter) LoadPolicy(model model.Model) error { + var lines []CasbinRule + + if err := a.Db.Table(a.TableName).Scan(&lines); err != nil { + return err + } + + for _, line := range lines { + loadPolicyLine(line, model) + } + + return nil +} + +func savePolicyLine(ptype string, rule []string) CasbinRule { + line := CasbinRule{} + + line.PType = ptype + if len(rule) > 0 { + line.V0 = rule[0] + } + if len(rule) > 1 { + line.V1 = rule[1] + } + if len(rule) > 2 { + line.V2 = rule[2] + } + if len(rule) > 3 { + line.V3 = rule[3] + } + if len(rule) > 4 { + line.V4 = rule[4] + } + if len(rule) > 5 { + line.V5 = rule[5] + } + + return line +} + +// SavePolicy saves policy to database. +func (a *Adapter) SavePolicy(model model.Model) error { + err := a.dropTable() + if err != nil { + return err + } + err = a.createTable() + if err != nil { + return err + } + + for ptype, ast := range model["p"] { + for _, rule := range ast.Policy { + line := savePolicyLine(ptype, rule) + _, err := a.Db.Table(a.TableName).Data(&line).Insert() + if err != nil { + return err + } + } + } + + for ptype, ast := range model["g"] { + for _, rule := range ast.Policy { + line := savePolicyLine(ptype, rule) + _, err := a.Db.Table(a.TableName).Data(&line).Insert() + if err != nil { + return err + } + } + } + + return nil +} + +// AddPolicy adds a policy rule to the storage. +func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error { + line := savePolicyLine(ptype, rule) + _, err := a.Db.Table(a.TableName).Data(&line).Insert() + return err +} + +// RemovePolicy removes a policy rule from the storage. +func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error { + line := savePolicyLine(ptype, rule) + err := rawDelete(a, line) + return err +} + +// RemoveFilteredPolicy removes policy rules that match the filter from the storage. +func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error { + line := CasbinRule{} + + line.PType = ptype + if fieldIndex <= 0 && 0 < fieldIndex+len(fieldValues) { + line.V0 = fieldValues[0-fieldIndex] + } + if fieldIndex <= 1 && 1 < fieldIndex+len(fieldValues) { + line.V1 = fieldValues[1-fieldIndex] + } + if fieldIndex <= 2 && 2 < fieldIndex+len(fieldValues) { + line.V2 = fieldValues[2-fieldIndex] + } + if fieldIndex <= 3 && 3 < fieldIndex+len(fieldValues) { + line.V3 = fieldValues[3-fieldIndex] + } + if fieldIndex <= 4 && 4 < fieldIndex+len(fieldValues) { + line.V4 = fieldValues[4-fieldIndex] + } + if fieldIndex <= 5 && 5 < fieldIndex+len(fieldValues) { + line.V5 = fieldValues[5-fieldIndex] + } + err := rawDelete(a, line) + return err +} + +func rawDelete(a *Adapter, line CasbinRule) error { + db := a.Db.Table(a.TableName) + + db.Where("ptype = ?", line.PType) + if line.V0 != "" { + db.Where("v0 = ?", line.V0) + } + if line.V1 != "" { + db.Where("v1 = ?", line.V1) + } + if line.V2 != "" { + db.Where("v2 = ?", line.V2) + } + if line.V3 != "" { + db.Where("v3 = ?", line.V3) + } + if line.V4 != "" { + db.Where("v4 = ?", line.V4) + } + if line.V5 != "" { + db.Where("v5 = ?", line.V5) + } + + _, err := db.Delete() + return err +} \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..7514891 --- /dev/null +++ b/main.go @@ -0,0 +1,11 @@ +package main + +import( + _ "gfast/boot" + _ "gfast/router" + "github.com/gogf/gf/frame/g" +) + +func main() { + g.Server().Run() +} diff --git a/main2.go b/main2.go new file mode 100644 index 0000000..ca9e39e --- /dev/null +++ b/main2.go @@ -0,0 +1,24 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/os/glog" +) + +func main() { + // 基本事件回调使用 + p := "/:name/info/{uid}" + s := g.Server() + s.BindHookHandlerByMap(p, map[string]ghttp.HandlerFunc{ + ghttp.HOOK_BEFORE_SERVE : func(r *ghttp.Request){ glog.Println(ghttp.HOOK_BEFORE_SERVE) }, + ghttp.HOOK_AFTER_SERVE : func(r *ghttp.Request){ glog.Println(ghttp.HOOK_AFTER_SERVE) }, + ghttp.HOOK_BEFORE_OUTPUT : func(r *ghttp.Request){ glog.Println(ghttp.HOOK_BEFORE_OUTPUT) }, + ghttp.HOOK_AFTER_OUTPUT : func(r *ghttp.Request){ glog.Println(ghttp.HOOK_AFTER_OUTPUT) }, + }) + s.BindHandler(p, func(r *ghttp.Request) { + r.Response.Write("用户:", r.Get("name"), ", uid:", r.Get("uid")) + }) + s.SetPort(8199) + s.Run() +} \ No newline at end of file diff --git a/public/html/.gitkeep b/public/html/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/public/plugin/.gitkeep b/public/plugin/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/public/resource/css/.gitkeep b/public/resource/css/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/public/resource/image/.gitkeep b/public/resource/image/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/public/resource/js/.gitkeep b/public/resource/js/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/router/.gitkeep b/router/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/router/router.go b/router/router.go new file mode 100644 index 0000000..f5c0f02 --- /dev/null +++ b/router/router.go @@ -0,0 +1,13 @@ +package router + +import ( + "gfast/app/api/hello" + "gfast/app/api/user" + "github.com/gogf/gf/frame/g" +) + +// 统一路由注册. +func init() { + g.Server().BindHandler("/", hello.Handler) + g.Server().BindObject("/user", new(user.Controller)) +} diff --git a/template/.gitkeep b/template/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/casbin_conf/model.conf b/test/casbin_conf/model.conf new file mode 100644 index 0000000..3ed985c --- /dev/null +++ b/test/casbin_conf/model.conf @@ -0,0 +1,15 @@ +# Request definition +[request_definition] +r = sub, obj, act + +# Policy definition +[policy_definition] +p = sub, obj, act + +# Policy effect +[policy_effect] +e = some(where (p.eft == allow)) + +# Matchers +[matchers] +m = r.sub == p.sub && r.obj == p.obj && r.act == p.act \ No newline at end of file diff --git a/test/casbin_conf/policy.csv b/test/casbin_conf/policy.csv new file mode 100644 index 0000000..57aaa97 --- /dev/null +++ b/test/casbin_conf/policy.csv @@ -0,0 +1,2 @@ +p, alice, data1, read +p, bob, data2, write \ No newline at end of file diff --git a/test/casbin_conf/rbac_model.conf b/test/casbin_conf/rbac_model.conf new file mode 100644 index 0000000..71159e3 --- /dev/null +++ b/test/casbin_conf/rbac_model.conf @@ -0,0 +1,14 @@ +[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 \ No newline at end of file diff --git a/test/casbin_conf/rbac_policy.csv b/test/casbin_conf/rbac_policy.csv new file mode 100644 index 0000000..f93d6df --- /dev/null +++ b/test/casbin_conf/rbac_policy.csv @@ -0,0 +1,5 @@ +p, alice, data1, read +p, bob, data2, write +p, data2_admin, data2, read +p, data2_admin, data2, write +g, alice, data2_admin \ No newline at end of file diff --git a/test/demo_test.go b/test/demo_test.go new file mode 100644 index 0000000..146f031 --- /dev/null +++ b/test/demo_test.go @@ -0,0 +1,170 @@ +package test + +import ( + "fmt" + "gfast/library/utils" + "github.com/casbin/casbin/v2" + "github.com/casbin/casbin/v2/util" + "github.com/gogf/gf/os/glog" + "testing" +) + +func TestDemo(t *testing.T){ + //t.Run("demo1" ,Demo1) + t.Run("Adapters_test", Adapters) +} + +func Demo1(t *testing.T){ + e,err:=casbin.NewEnforcer("casbin_conf/model.conf","casbin_conf/policy.csv") + if err!=nil{ + panic(err) + } + sub := "alice" // the user that wants to access a resource. 对象 + obj := "data1" // the resource that is going to be accessed. 资源 + act := "write" // the operation that the user performs on the resource. 操作 + ok, err := e.Enforce(sub, obj, act) + + if err != nil { + fmt.Println("验证失败",err) + } + + if ok == true { + fmt.Println("权限通过") + } else { + fmt.Println("没有权限") + } +} + +func Adapters(t *testing.T) { + a := initAdapter(t, "mysql", "root:123456@tcp(127.0.0.1:3306)/test2") + testAutoSave(t, a) + testSaveLoad(t, a) + + a = initAdapterFormOptions(t, &utils.Adapter{ + DriverName: "mysql", + DataSourceName: "root:123456@tcp(127.0.0.1:3306)/test2", + }) + testAutoSave(t, a) + testSaveLoad(t, a) +} + +func initAdapterFormOptions(t *testing.T, adapter *utils.Adapter) *utils.Adapter { + // Create an adapter + a, _ := utils.NewAdapterFromOptions(adapter) + // Initialize some policy in DB. + initPolicy(t, a) + // Now the DB has policy, so we can provide a normal use case. + // Note: you don't need to look at the above code + // if you already have a working DB with policy inside. + + return a +} + +func initPolicy(t *testing.T, a *utils.Adapter) { + // Because the DB is empty at first, + // so we need to load the policy from the file adapter (.CSV) first. + e, err := casbin.NewEnforcer("casbin_conf/rbac_model.conf", "casbin_conf/rbac_policy.csv") + if err != nil { + panic(err) + } + + // This is a trick to save the current policy to the DB. + // We can't call e.SavePolicy() because the adapter in the enforcer is still the file adapter. + // The current policy means the policy in the Casbin enforcer (aka in memory). + err = a.SavePolicy(e.GetModel()) + if err != nil { + panic(err) + } + + // Clear the current policy. + e.ClearPolicy() + testGetPolicy(t, e, [][]string{}) + + // Load the policy from DB. + err = a.LoadPolicy(e.GetModel()) + if err != nil { + panic(err) + } + testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}) +} + +func testGetPolicy(t *testing.T, e *casbin.Enforcer, res [][]string) { + myRes := e.GetPolicy() + glog.Info("Policy: ", myRes) + + if !util.Array2DEquals(res, myRes) { + t.Error("Policy: ", myRes, ", supposed to be ", res) + } +} + +func initAdapter(t *testing.T, driverName string, dataSourceName string) *utils.Adapter { + // Create an adapter + a, err := utils.NewAdapter(driverName, dataSourceName) + if err != nil { + panic(err) + } + + // Initialize some policy in DB. + initPolicy(t, a) + // Now the DB has policy, so we can provide a normal use case. + // Note: you don't need to look at the above code + // if you already have a working DB with policy inside. + + return a +} + +func testAutoSave(t *testing.T, a *utils.Adapter) { + + // NewEnforcer() will load the policy automatically. + e, err := casbin.NewEnforcer("casbin_conf/rbac_model.conf", a) + if err!=nil{ + panic(err) + } + // 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") + // Reload the policy from the storage to see the effect. + e.LoadPolicy() + // This is still the original policy. + 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) + + // Because AutoSave is enabled, the policy change not only affects the policy in Casbin enforcer, + // but also affects the policy in the storage. + e.AddPolicy("alice", "data1", "write") + // Reload the policy from the storage to see the effect. + e.LoadPolicy() + // 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"}}) + + // Remove the added rule. + e.RemovePolicy("alice", "data1", "write") + e.LoadPolicy() + 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") + e.LoadPolicy() + testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}}) +} + +func testSaveLoad(t *testing.T, a *utils.Adapter) { + // Initialize some policy in DB. + initPolicy(t, a) + // Note: you don't need to look at the above code + // if you already have a working DB with policy inside. + + // 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. + + e, _ := casbin.NewEnforcer("casbin_conf/rbac_model.conf", a) + testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}) +} \ No newline at end of file