From 438879ac4f9918283e158edf2204355ebcf499b8 Mon Sep 17 00:00:00 2001 From: gazebo Date: Tue, 6 Aug 2019 11:26:08 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=BC=80=E5=A7=8B=E6=B7=BB=E5=8A=A0casbin?= =?UTF-8?q?=E5=81=9A=E8=A7=92=E8=89=B2=EF=BC=8C=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- business/authz/authz.go | 10 ++ business/authz/authz_utils.go | 29 ++++ business/authz/casbinauth/adapter.go | 185 ++++++++++++++++++++++ business/authz/casbinauth/casbinauth.go | 44 +++++ business/authz/casbinauth/rbac_model.conf | 14 ++ business/jxstore/cms/authz.go | 94 +++++++++++ business/jxstore/cms/authz_test.go | 29 ++++ globals/api2/api2.go | 14 ++ globals/beegodb/beegodb.go | 3 + globals/testinit/testinit.go | 2 + main.go | 3 + 11 files changed, 427 insertions(+) create mode 100644 business/authz/authz.go create mode 100644 business/authz/authz_utils.go create mode 100644 business/authz/casbinauth/adapter.go create mode 100644 business/authz/casbinauth/casbinauth.go create mode 100644 business/authz/casbinauth/rbac_model.conf create mode 100644 business/jxstore/cms/authz.go create mode 100644 business/jxstore/cms/authz_test.go create mode 100644 globals/api2/api2.go diff --git a/business/authz/authz.go b/business/authz/authz.go new file mode 100644 index 000000000..67ecb00a0 --- /dev/null +++ b/business/authz/authz.go @@ -0,0 +1,10 @@ +package authz + +type IAuthz interface { + AddStoreRole4User(userID string, storeID int) (err error) + DelStoreRole4User(userID string, storeID int) (err error) + AddRole4User(userID, roleName string) (err error) + DelRole4User(userID, roleName string) (err error) + GetUserRoleList(userID string) (roleList []string, err error) + GetRoleUserList(roleName string) (userIDList []string, err error) +} diff --git a/business/authz/authz_utils.go b/business/authz/authz_utils.go new file mode 100644 index 000000000..dcc47c1d6 --- /dev/null +++ b/business/authz/authz_utils.go @@ -0,0 +1,29 @@ +package authz + +import ( + "strings" + + "git.rosy.net.cn/baseapi/utils" +) + +const ( + RoleNameSep = "/" + RolePrefix = "role" + StoreRolePrefix = "store" +) + +func GenStoreRoleName(storeID int) (roleName string) { + return strings.Join([]string{ + RolePrefix, + StoreRolePrefix, + utils.Int2Str(storeID), + }, RoleNameSep) +} + +func GetStoreIDFromRole(roleName string) (storeID int) { + list := strings.Split(roleName, RoleNameSep) + if len(list) == 3 { + storeID = int(utils.Str2Int64WithDefault(list[2], 0)) + } + return storeID +} diff --git a/business/authz/casbinauth/adapter.go b/business/authz/casbinauth/adapter.go new file mode 100644 index 000000000..7598e8c08 --- /dev/null +++ b/business/authz/casbinauth/adapter.go @@ -0,0 +1,185 @@ +package casbinauth + +import ( + "git.rosy.net.cn/jx-callback/globals" + "github.com/astaxie/beego/orm" + "github.com/casbin/casbin/model" + "github.com/casbin/casbin/persist" +) + +type CasbinRule struct { + ID int `orm:"column(id)" json:"id"` + PType string + V0 string + V1 string + V2 string + V3 string + V4 string + V5 string +} + +type Adapter struct { +} + +func RegisterModel() { + orm.RegisterModel(new(CasbinRule)) +} + +// finalizer is the destructor for Adapter. +func finalizer(a *Adapter) { +} + +func NewAdapter() *Adapter { + return &Adapter{} +} + +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) +} + +func (a *Adapter) LoadPolicy(model model.Model) error { + var lines []CasbinRule + o := orm.NewOrm() + _, err := o.QueryTable("casbin_rule").Limit(-1).All(&lines) + if 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 +} + +func (a *Adapter) clearAll(o orm.Ormer) (err error) { + _, err = o.Raw(` + DELETE t1 + FROM casbin_rule t1 + `).Exec() + return err +} + +// SavePolicy saves policy to database. +func (a *Adapter) SavePolicy(model model.Model) error { + globals.SugarLogger.Debugf("SavePolicy") + o := orm.NewOrm() + + a.clearAll(o) + var lines []CasbinRule + + for ptype, ast := range model["p"] { + for _, rule := range ast.Policy { + line := savePolicyLine(ptype, rule) + lines = append(lines, line) + } + } + + for ptype, ast := range model["g"] { + for _, rule := range ast.Policy { + line := savePolicyLine(ptype, rule) + lines = append(lines, line) + } + } + + _, err := o.InsertMulti(len(lines), lines) + return err +} + +// AddPolicy adds a policy rule to the storage. +func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error { + o := orm.NewOrm() + line := savePolicyLine(ptype, rule) + _, err := o.Insert(&line) + return err +} + +// RemovePolicy removes a policy rule from the storage. +func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error { + o := orm.NewOrm() + line := savePolicyLine(ptype, rule) + _, err := o.Delete(&line, "p_type", "v0", "v1", "v2", "v3", "v4", "v5") + 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 + filter := []string{} + filter = append(filter, "p_type") + if fieldIndex <= 0 && 0 < fieldIndex+len(fieldValues) { + line.V0 = fieldValues[0-fieldIndex] + filter = append(filter, "v0") + } + if fieldIndex <= 1 && 1 < fieldIndex+len(fieldValues) { + line.V1 = fieldValues[1-fieldIndex] + filter = append(filter, "v1") + } + if fieldIndex <= 2 && 2 < fieldIndex+len(fieldValues) { + line.V2 = fieldValues[2-fieldIndex] + filter = append(filter, "v2") + } + if fieldIndex <= 3 && 3 < fieldIndex+len(fieldValues) { + line.V3 = fieldValues[3-fieldIndex] + filter = append(filter, "v3") + } + if fieldIndex <= 4 && 4 < fieldIndex+len(fieldValues) { + line.V4 = fieldValues[4-fieldIndex] + filter = append(filter, "v4") + } + if fieldIndex <= 5 && 5 < fieldIndex+len(fieldValues) { + line.V5 = fieldValues[5-fieldIndex] + filter = append(filter, "v5") + } + + o := orm.NewOrm() + _, err := o.Delete(&line, filter...) + return err +} diff --git a/business/authz/casbinauth/casbinauth.go b/business/authz/casbinauth/casbinauth.go new file mode 100644 index 000000000..394d9fd34 --- /dev/null +++ b/business/authz/casbinauth/casbinauth.go @@ -0,0 +1,44 @@ +package casbinauth + +import ( + "git.rosy.net.cn/jx-callback/business/authz" + "github.com/casbin/casbin" +) + +type CasbinAuthz struct { + enforcer *casbin.SyncedEnforcer +} + +func New(modelFile string) (authObj authz.IAuthz, err error) { + obj := &CasbinAuthz{} + obj.enforcer, err = casbin.NewSyncedEnforcer(modelFile, NewAdapter()) + return obj, err +} + +func (c *CasbinAuthz) AddStoreRole4User(userID string, storeID int) (err error) { + return c.AddRole4User(userID, authz.GenStoreRoleName(storeID)) +} + +func (c *CasbinAuthz) DelStoreRole4User(userID string, storeID int) (err error) { + return c.DelRole4User(userID, authz.GenStoreRoleName(storeID)) +} + +func (c *CasbinAuthz) AddRole4User(userID, roleName string) (err error) { + _, err = c.enforcer.AddRoleForUser(userID, roleName) + return err +} + +func (c *CasbinAuthz) DelRole4User(userID, roleName string) (err error) { + _, err = c.enforcer.DeleteRoleForUser(userID, roleName) + return err +} + +func (c *CasbinAuthz) GetUserRoleList(userID string) (roleList []string, err error) { + roleList, err = c.enforcer.GetRolesForUser(userID) + return roleList, err +} + +func (c *CasbinAuthz) GetRoleUserList(roleName string) (userIDList []string, err error) { + userIDList, err = c.enforcer.GetUsersForRole(roleName) + return userIDList, err +} diff --git a/business/authz/casbinauth/rbac_model.conf b/business/authz/casbinauth/rbac_model.conf new file mode 100644 index 000000000..71159e387 --- /dev/null +++ b/business/authz/casbinauth/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/business/jxstore/cms/authz.go b/business/jxstore/cms/authz.go new file mode 100644 index 000000000..7ced42b36 --- /dev/null +++ b/business/jxstore/cms/authz.go @@ -0,0 +1,94 @@ +package cms + +import ( + "git.rosy.net.cn/jx-callback/business/auth2" + "git.rosy.net.cn/jx-callback/business/auth2/authprovider/weixin" + "git.rosy.net.cn/jx-callback/business/model" + "git.rosy.net.cn/jx-callback/business/model/dao" + "git.rosy.net.cn/jx-callback/business/model/legacymodel" + "git.rosy.net.cn/jx-callback/globals/api2" +) + +func TransferLegacyWeixins() (err error) { + // DELETE t1 + // FROM auth_bind t1 + // WHERE t1.created_at > '2019-08-05'; + + // DELETE t1 + // FROM user t1 + // WHERE t1.created_at > '2019-08-05'; + + // DELETE t1 + // FROM casbin_rule t1; + + sql := ` + SELECT * + FROM weixins + ORDER BY parentid` + var weixinList []*legacymodel.WeiXins + db := dao.GetDB() + err = dao.GetRows(db, &weixinList, sql) + if err != nil { + return err + } + parentMap := make(map[int]*legacymodel.WeiXins) + for _, v := range weixinList { + if v.ParentID == -1 { + parentMap[v.ID] = v + } else { + if parentMap[v.ParentID] != nil { + v.JxStoreID = parentMap[v.ParentID].JxStoreID + } + } + if v.Tel != "" { + user := &model.User{ + UserID2: v.Tel, + Name: v.NickName, + Mobile: v.Tel, + Type: model.UserTypeStoreBoss, + } + if user.Name == "" { + user.Name = user.Mobile + } + userList, err2 := dao.GetUsers(db, 0, "", "", v.Tel, "") + if err = err2; err != nil { + return err + } + // globals.SugarLogger.Debug(utils.Format4Output(user, false)) + if len(userList) == 0 { + err = CreateUser(user) + } else { + user = userList[0] + } + if err != nil { + return err + } + if v.OpenID != "" { + auth2.AddAuthBind(user, &auth2.AuthInfo{ + AuthBindInfo: &auth2.AuthBindEx{ + AuthBind: model.AuthBind{ + Type: weixin.AuthTypeMP, + AuthID: v.OpenID, + AuthID2: v.OpenIDUnion, + }, + }, + }) + } + if v.OpenIDMini != "" { + auth2.AddAuthBind(user, &auth2.AuthInfo{ + AuthBindInfo: &auth2.AuthBindEx{ + AuthBind: model.AuthBind{ + Type: weixin.AuthTypeMini, + AuthID: v.OpenIDMini, + AuthID2: v.OpenIDUnion, + }, + }, + }) + } + if v.JxStoreID > 0 { + api2.RoleMan.AddStoreRole4User(user.GetID(), v.JxStoreID) + } + } + } + return err +} diff --git a/business/jxstore/cms/authz_test.go b/business/jxstore/cms/authz_test.go new file mode 100644 index 000000000..c0f7271e1 --- /dev/null +++ b/business/jxstore/cms/authz_test.go @@ -0,0 +1,29 @@ +package cms + +import ( + "testing" + + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/globals/api2" + + _ "git.rosy.net.cn/jx-callback/business/partner/purchase/ebai" + _ "git.rosy.net.cn/jx-callback/business/partner/purchase/elm" + _ "git.rosy.net.cn/jx-callback/business/partner/purchase/jd" + _ "git.rosy.net.cn/jx-callback/business/partner/purchase/mtwm" + _ "git.rosy.net.cn/jx-callback/business/partner/purchase/weimob/wsc" +) + +func TestTransferLegacyWeixins(t *testing.T) { + err := TransferLegacyWeixins() + if err != nil { + t.Fatal(err) + } +} + +func TestCasbin(t *testing.T) { + userList, err := api2.RoleMan.GetRoleUserList("role/store/100324") + t.Log(utils.Format4Output(userList, false)) + if err != nil { + t.Fatal(err) + } +} diff --git a/globals/api2/api2.go b/globals/api2/api2.go new file mode 100644 index 000000000..a781b3e4e --- /dev/null +++ b/globals/api2/api2.go @@ -0,0 +1,14 @@ +package api2 + +import ( + "git.rosy.net.cn/jx-callback/business/authz" + "git.rosy.net.cn/jx-callback/business/authz/casbinauth" +) + +var ( + RoleMan authz.IAuthz +) + +func Init() { + RoleMan, _ = casbinauth.New("business/authz/casbinauth/rbac_model.conf") +} diff --git a/globals/beegodb/beegodb.go b/globals/beegodb/beegodb.go index 857c4932f..361a4f573 100644 --- a/globals/beegodb/beegodb.go +++ b/globals/beegodb/beegodb.go @@ -1,6 +1,7 @@ package beegodb import ( + "git.rosy.net.cn/jx-callback/business/authz/casbinauth" "git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model/legacymodel" "git.rosy.net.cn/jx-callback/globals" @@ -51,6 +52,8 @@ func Init() { orm.RegisterModel(&model.Act{}, &model.ActOrderRule{}, &model.ActStoreSku{}) orm.RegisterModel(&model.ActMap{}, &model.ActStoreSkuMap{}) orm.RegisterModel(&model.NewConfig{}) + + casbinauth.RegisterModel() // create table orm.RunSyncdb("default", false, true) } diff --git a/globals/testinit/testinit.go b/globals/testinit/testinit.go index 9aa3e0d45..301fe1a89 100644 --- a/globals/testinit/testinit.go +++ b/globals/testinit/testinit.go @@ -2,10 +2,12 @@ package testinit import ( "git.rosy.net.cn/jx-callback/globals/api" + "git.rosy.net.cn/jx-callback/globals/api2" "git.rosy.net.cn/jx-callback/globals/testinit1" ) func Init() { testinit1.Init() api.Init() + api2.Init() } diff --git a/main.go b/main.go index c5c8d3e42..5bf1ad462 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ import ( "git.rosy.net.cn/jx-callback/business/partner/purchase/ebai" "git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals/api" + "git.rosy.net.cn/jx-callback/globals/api2" "git.rosy.net.cn/jx-callback/globals/beegodb" _ "git.rosy.net.cn/jx-callback/routers" @@ -46,6 +47,8 @@ var ( func Init() { beegodb.Init() + api2.Init() + defsch.Init() buildTime, err := time.ParseInLocation(time.RFC3339, BuildDate, time.UTC) if err == nil {