From 7e9f39cb88d24acbc7ba58384c6cc484d57d8ff1 Mon Sep 17 00:00:00 2001 From: xushuhui Date: Tue, 19 Apr 2022 18:20:52 +0800 Subject: [PATCH] feat: (rbac) build in role / logout / login operation log --- internal/biz/account.go | 81 ++++++++++++++++++++++++++++++----- internal/biz/enum/const.go | 55 +++++++++++++++++++++++- internal/biz/event.go | 3 +- internal/biz/role.go | 7 +-- internal/biz/user.go | 10 +++-- internal/middleware/user.go | 1 + model/rbac/role.go | 5 ++- plugin/api/account/account.go | 3 ++ plugin/api/rbac/init.go | 8 ++-- plugin/api/rbac/role.go | 10 ++++- 10 files changed, 155 insertions(+), 28 deletions(-) diff --git a/internal/biz/account.go b/internal/biz/account.go index a891b15e..5dd6f184 100644 --- a/internal/biz/account.go +++ b/internal/biz/account.go @@ -3,7 +3,10 @@ package biz import ( "errors" "fmt" + "infini.sh/console/model/rbac" + "infini.sh/framework/core/event" "infini.sh/framework/core/global" + "infini.sh/framework/core/orm" "infini.sh/framework/core/util" "src/github.com/golang-jwt/jwt" "strings" @@ -17,12 +20,31 @@ type UserClaims struct { type User struct { Username string `json:"username"` UserId string `json:"user_id"` - Role []string `json:"role"` + Roles []string `json:"roles"` } const Secret = "console" -func Login(username string, password string) (m map[string]interface{}, err error) { +func authenticateUser(username string, password string) (user rbac.User, err error) { + q := orm.Query{Size: 1000} + q.Conds = orm.And(orm.Eq("username", username)) + + err, result := orm.Search(rbac.User{}, &q) + if err != nil { + return + } + if result.Total == 0 { + err = errors.New("user not found") + return + } + user = result.Result[0].(rbac.User) + if util.MD5digest(password) != user.Password { + err = errors.New("password error") + return + } + return +} +func authenticateAdmin(username string, password string) (user rbac.User, err error) { u, _ := global.Env().GetConfig("bootstrap.username", "admin") p, _ := global.Env().GetConfig("bootstrap.password", "admin") @@ -31,11 +53,16 @@ func Login(username string, password string) (m map[string]interface{}, err erro err = errors.New("invalid username or password") return } + user.ID = username + user.Username = username + return user, nil +} +func authorize(user rbac.User) (m map[string]interface{}, err error) { token := jwt.NewWithClaims(jwt.SigningMethodHS256, UserClaims{ User: &User{ - Username: u, - UserId: "admin", - Role: []string{"admin_user"}, + Username: user.Username, + UserId: user.ID, + Roles: []string{"admin"}, }, RegisteredClaims: &jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), @@ -44,17 +71,49 @@ func Login(username string, password string) (m map[string]interface{}, err erro tokenString, err := token.SignedString([]byte(Secret)) if err != nil { - return } m = util.MapStr{ "access_token": tokenString, - "username": u, - "userid": "admin", + "username": user.Username, + "userid": user.ID, + } + return +} +func Login(username string, password string) (m map[string]interface{}, err error) { + var user rbac.User + if username == "admin" { + user, err = authenticateAdmin(username, password) + if err != nil { + return nil, err + } + + } else { + user, err = authenticateUser(username, password) + if err != nil { + return nil, err + } } + m, err = authorize(user) + if err != nil { + return + } + err = orm.Save(GenerateEvent(event.ActivityMetadata{ + Category: "platform", + Group: "rbac", + Name: "login", + Type: "create", + Labels: util.MapStr{ + "username": username, + "password": password, + }, + User: util.MapStr{ + "userid": user.ID, + "username": user.Username, + }, + }, nil, nil)) return - } func ValidateLogin(authorizationHeader string) (clams *UserClaims, err error) { @@ -100,14 +159,14 @@ func ValidatePermission(claims *UserClaims, permissions []string) (err error) { err = errors.New("user id is empty") return } - if reqUser.Role == nil { + if reqUser.Roles == nil { err = errors.New("api permission is empty") return } // 权限校验 userPermissionMap := make(map[string]struct{}) - for _, role := range reqUser.Role { + for _, role := range reqUser.Roles { if _, ok := RolePermission[role]; ok { for _, v := range RolePermission[role] { userPermissionMap[v] = struct{}{} diff --git a/internal/biz/enum/const.go b/internal/biz/enum/const.go index f64f892d..6c468feb 100644 --- a/internal/biz/enum/const.go +++ b/internal/biz/enum/const.go @@ -1,13 +1,64 @@ package enum +import ( + "infini.sh/console/model/rbac" + "infini.sh/framework/core/orm" +) + const CreateUser = "create_user" const UpdateUser = "update_user" const DeleteUser = "delete_user" const GetUser = "get_user" -const ListUser = "list_user" +const SearchUser = "search_user" + const CreateRole = "create_role" const UpdateRole = "update_role" const DeleteRole = "delete_role" const GetRole = "get_role" -const ListRole = "list_role" +const SearchRole = "search_role" const ListPermission = "list_permission" + +const CreateRule = "create_rule" +const UpdateRule = "update_rule" +const DeleteRule = "delete_rule" +const GetRule = "get_rule" +const SearchRule = "search_rule" + +const CreateInstance = "create_instance" +const UpdateInstance = "update_instance" +const DeleteInstance = "delete_instance" +const GetInstance = "get_instance" +const SearchInstance = "search_instance" +const GetInstanceStatus = "get_instance_status" +const ConnectInstance = "connect_instance" +const InstanceProxy = "instance_proxy" + +var Admin = []string{CreateUser, UpdateUser, DeleteUser, GetUser, SearchUser, CreateRole, UpdateRole, DeleteRole, GetRole, SearchRole, ListPermission} +var AdminUser = []string{CreateUser, UpdateUser, DeleteUser, GetUser, SearchUser} + +var BuildRoles = []rbac.Role{ + { + ORMObjectBase: orm.ORMObjectBase{ + ID: "admin", + }, + Name: "admin", + Description: "管理员", + RoleType: "console", + Permission: rbac.ConsolePermission{ + ApiPermission: Admin, + }, + BuiltIn: true, + }, + { + ORMObjectBase: orm.ORMObjectBase{ + ID: "admin_user", + }, + Name: "admin_user", + Description: "用户模块管理员", + RoleType: "console", + Permission: rbac.ConsolePermission{ + ApiPermission: AdminUser, + }, + BuiltIn: true, + }, +} diff --git a/internal/biz/event.go b/internal/biz/event.go index 8619e491..224b82a5 100644 --- a/internal/biz/event.go +++ b/internal/biz/event.go @@ -6,12 +6,13 @@ import ( "time" ) -func GenerateEvent(metadata event.ActivityMetadata, fields util.MapStr) *event.Activity { +func GenerateEvent(metadata event.ActivityMetadata, fields util.MapStr, changeLog interface{}) *event.Activity { return &event.Activity{ ID: util.GetUUID(), Timestamp: time.Now(), Metadata: metadata, Fields: fields, + Changelog: changeLog, } } diff --git a/internal/biz/role.go b/internal/biz/role.go index 75ce6f66..f2464500 100644 --- a/internal/biz/role.go +++ b/internal/biz/role.go @@ -60,7 +60,7 @@ func CreateRole(localUser *User, req dto.CreateRole) (id string, err error) { "userid": localUser.UserId, "username": localUser.Username, }, - }, nil)) + }, nil, nil)) if err != nil { log.Error(err) @@ -99,7 +99,7 @@ func DeleteRole(localUser *User, id string) (err error) { "type": role.RoleType, "created": role.Created.Format("2006-01-02 15:04:05"), "updated": role.Updated.Format("2006-01-02 15:04:05"), - })) + }, nil)) return } @@ -112,6 +112,7 @@ func UpdateRole(localUser *User, id string, req dto.UpdateRole) (err error) { err = ErrNotFound return } + changeLog, _ := util.DiffTwoObject(role, req) role.Description = req.Description role.Permission = req.Permission role.Updated = time.Now() @@ -134,7 +135,7 @@ func UpdateRole(localUser *User, id string, req dto.UpdateRole) (err error) { "userid": localUser.UserId, "username": localUser.Username, }, - }, nil)) + }, nil, changeLog)) return } func GetRole(id string) (role rbac.Role, err error) { diff --git a/internal/biz/user.go b/internal/biz/user.go index 271761a0..283daa92 100644 --- a/internal/biz/user.go +++ b/internal/biz/user.go @@ -50,7 +50,7 @@ func DeleteUser(localUser *User, id string) (err error) { "roles": user.Roles, "created": user.Created, "updated": user.Updated, - })) + }, nil)) return } func CreateUser(localUser *User, req dto.CreateUser) (id string, err error) { @@ -112,7 +112,7 @@ func CreateUser(localUser *User, req dto.CreateUser) (id string, err error) { "userid": localUser.UserId, "username": localUser.Username, }, - }, nil)) + }, nil, nil)) return } func UpdateUser(localUser *User, id string, req dto.UpdateUser) (err error) { @@ -123,6 +123,7 @@ func UpdateUser(localUser *User, id string, req dto.UpdateUser) (err error) { err = ErrNotFound return } + changeLog, _ := util.DiffTwoObject(user, req) user.Name = req.Name user.Email = req.Email user.Phone = req.Phone @@ -149,7 +150,7 @@ func UpdateUser(localUser *User, id string, req dto.UpdateUser) (err error) { "userid": localUser.UserId, "username": localUser.Username, }, - }, nil)) + }, nil, changeLog)) return } func UpdateUserRole(localUser *User, id string, req dto.UpdateUserRole) (err error) { @@ -160,6 +161,7 @@ func UpdateUserRole(localUser *User, id string, req dto.UpdateUserRole) (err err err = ErrNotFound return } + changeLog, _ := util.DiffTwoObject(user, req) roles := make([]rbac.UserRole, 0) for _, v := range req.Roles { roles = append(roles, rbac.UserRole{ @@ -187,7 +189,7 @@ func UpdateUserRole(localUser *User, id string, req dto.UpdateUserRole) (err err "userid": localUser.UserId, "username": localUser.Username, }, - }, nil)) + }, nil, changeLog)) return } diff --git a/internal/middleware/user.go b/internal/middleware/user.go index e3466cd6..d822adbc 100644 --- a/internal/middleware/user.go +++ b/internal/middleware/user.go @@ -32,6 +32,7 @@ func PermissionRequired(h httprouter.Handle, permissions ...string) httprouter.H w = handleError(w, err) return } + r = r.WithContext(biz.NewUserContext(r.Context(), claims)) h(w, r, ps) } } diff --git a/model/rbac/role.go b/model/rbac/role.go index a174a590..07331e6c 100644 --- a/model/rbac/role.go +++ b/model/rbac/role.go @@ -14,8 +14,9 @@ type Role struct { BuiltIn bool `json:"builtin" elastic_mapping:"builtin:{type:boolean}"` //是否内置 } type ConsolePermission struct { - ID string `json:"id" elastic_mapping:"id:{type:keyword}"` - Name string `json:"name" elastic_mapping:"name:{type:keyword}"` + ApiPermission []string `json:"api_permission"` + //ID string `json:"id" elastic_mapping:"id:{type:keyword}"` + //Name string `json:"name" elastic_mapping:"name:{type:keyword}"` } type ElasticsearchPermission struct { Cluster []string `json:"cluster" elastic_mapping:"cluster:{type:object}"` diff --git a/plugin/api/account/account.go b/plugin/api/account/account.go index e0d6db45..a281a344 100644 --- a/plugin/api/account/account.go +++ b/plugin/api/account/account.go @@ -88,6 +88,9 @@ func (h Account) CurrentUser(w http.ResponseWriter, req *http.Request, ps httpro } func (h Account) Logout(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + h.WriteOKJSON(w, util.MapStr{ + "status": "ok", + }) } func (h Account) Profile(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { reqUser, err := biz.FromUserContext(r.Context()) diff --git a/plugin/api/rbac/init.go b/plugin/api/rbac/init.go index b988105c..bbb2d2e1 100644 --- a/plugin/api/rbac/init.go +++ b/plugin/api/rbac/init.go @@ -23,15 +23,14 @@ func registerRouter() { api.HandleAPIMethod(api.GET, "/role/:id", m.PermissionRequired(r.GetRole, enum.GetRole)) api.HandleAPIMethod(api.DELETE, "/role/:id", m.PermissionRequired(r.DeleteRole, enum.DeleteRole)) api.HandleAPIMethod(api.PUT, "/role/:id", m.PermissionRequired(r.UpdateRole, enum.UpdateRole)) - api.HandleAPIMethod(api.GET, "/role/_search", m.PermissionRequired(r.SearchRole, enum.ListRole)) + api.HandleAPIMethod(api.GET, "/role/_search", m.PermissionRequired(r.SearchRole, enum.SearchRole)) api.HandleAPIMethod(api.POST, "/user", m.PermissionRequired(r.CreateUser, enum.CreateUser)) api.HandleAPIMethod(api.GET, "/user/:id", m.PermissionRequired(r.GetUser, enum.GetUser)) - api.HandleAPIMethod(api.GET, "/user/search", m.PermissionRequired(r.SearchUser, enum.ListUser)) api.HandleAPIMethod(api.DELETE, "/user/:id", m.PermissionRequired(r.DeleteUser, enum.DeleteUser)) api.HandleAPIMethod(api.PUT, "/user/:id", m.PermissionRequired(r.UpdateUser, enum.UpdateUser)) api.HandleAPIMethod(api.PUT, "/user/:id/role", m.PermissionRequired(r.UpdateUserRole, enum.UpdateUser)) - api.HandleAPIMethod(api.GET, "/user/_search", m.PermissionRequired(r.SearchUser, enum.ListUser)) + api.HandleAPIMethod(api.GET, "/user/_search", m.PermissionRequired(r.SearchUser, enum.SearchUser)) } @@ -58,7 +57,8 @@ func loadJsonConfig() { } func loadRolePermission() { biz.RolePermission = make(map[string][]string) - biz.RolePermission["admin_user"] = []string{enum.GetUser} + biz.RolePermission["admin_user"] = enum.AdminUser + biz.RolePermission["admin"] = enum.Admin } func init() { registerRouter() diff --git a/plugin/api/rbac/role.go b/plugin/api/rbac/role.go index 32a687f5..cda8dd91 100644 --- a/plugin/api/rbac/role.go +++ b/plugin/api/rbac/role.go @@ -3,6 +3,7 @@ package rbac import ( log "github.com/cihub/seelog" "infini.sh/console/internal/biz" + "infini.sh/console/internal/biz/enum" "infini.sh/console/internal/dto" httprouter "infini.sh/framework/core/api/router" "net/http" @@ -53,8 +54,15 @@ func (h Rbac) SearchRole(w http.ResponseWriter, r *http.Request, ps httprouter.P h.Error(w, err) return } + roles := make([]interface{}, 0) + for _, role := range enum.BuildRoles { + roles = append(roles, role) + } + for _, v := range res.Result { + roles = append(roles, v) + } - h.WriteOKJSON(w, Response{Hit: res.Result, Total: res.Total}) + h.WriteOKJSON(w, Response{Hit: roles, Total: res.Total + int64(len(enum.BuildRoles))}) return }