From f6d4d9fda58cc7bb2fd1110da4f55c7df7d5aac8 Mon Sep 17 00:00:00 2001 From: xushuhui Date: Wed, 20 Apr 2022 10:22:53 +0800 Subject: [PATCH] feat: (rbac) middleware handle error / password bcrypt hash / user update password --- internal/biz/account.go | 49 ++++++++++++++++++++------- internal/biz/enum/const.go | 2 +- internal/biz/permission.go | 63 +++++++++++++++++++---------------- internal/biz/role.go | 6 ++-- internal/biz/user.go | 8 ++++- internal/dto/user.go | 4 +++ internal/middleware/user.go | 21 ++++++++---- plugin/api/account/account.go | 23 +++++++++++++ 8 files changed, 124 insertions(+), 52 deletions(-) diff --git a/internal/biz/account.go b/internal/biz/account.go index 5dd6f184..253516f3 100644 --- a/internal/biz/account.go +++ b/internal/biz/account.go @@ -3,12 +3,14 @@ package biz import ( "errors" "fmt" + "github.com/golang-jwt/jwt" + "golang.org/x/crypto/bcrypt" + "infini.sh/console/internal/dto" "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" "time" ) @@ -26,11 +28,10 @@ type User struct { const Secret = "console" 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) + err, result := orm.GetBy("username", username, rbac.User{}) if err != nil { + err = ErrNotFound return } if result.Total == 0 { @@ -38,10 +39,12 @@ func authenticateUser(username string, password string) (user rbac.User, err err return } user = result.Result[0].(rbac.User) - if util.MD5digest(password) != user.Password { - err = errors.New("password error") + err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) + if err == bcrypt.ErrMismatchedHashAndPassword { + err = errors.New("password incorrect") return } + return } func authenticateAdmin(username string, password string) (user rbac.User, err error) { @@ -62,7 +65,7 @@ func authorize(user rbac.User) (m map[string]interface{}, err error) { User: &User{ Username: user.Username, UserId: user.ID, - Roles: []string{"admin"}, + Roles: []string{"admin_user"}, }, RegisteredClaims: &jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), @@ -76,7 +79,9 @@ func authorize(user rbac.User) (m map[string]interface{}, err error) { m = util.MapStr{ "access_token": tokenString, "username": user.Username, - "userid": user.ID, + "id": user.ID, + "expire_in": 86400, + "roles": []string{"admin_user"}, } return } @@ -115,7 +120,30 @@ func Login(username string, password string) (m map[string]interface{}, err erro }, nil, nil)) return } - +func UpdatePassword(localUser *User, req dto.UpdatePassword) (err error) { + user := rbac.User{} + user.ID = localUser.UserId + _, err = orm.Get(&user) + if err != nil { + err = ErrNotFound + return + } + err = bcrypt.CompareHashAndPassword([]byte(req.OldPassword), []byte(user.Password)) + if err == bcrypt.ErrMismatchedHashAndPassword { + err = errors.New("old password is not correct") + return + } + hash, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost) + if err != nil { + return + } + user.Password = string(hash) + err = orm.Save(&user) + if err != nil { + return + } + return +} func ValidateLogin(authorizationHeader string) (clams *UserClaims, err error) { if authorizationHeader == "" { @@ -151,10 +179,7 @@ func ValidateLogin(authorizationHeader string) (clams *UserClaims, err error) { func ValidatePermission(claims *UserClaims, permissions []string) (err error) { reqUser := claims.User - if err != nil { - return - } if reqUser.UserId == "" { err = errors.New("user id is empty") return diff --git a/internal/biz/enum/const.go b/internal/biz/enum/const.go index 6c468feb..8c68288b 100644 --- a/internal/biz/enum/const.go +++ b/internal/biz/enum/const.go @@ -35,7 +35,7 @@ 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 AdminRole = []string{CreateRole, UpdateRole, DeleteRole, GetRole, SearchRole, ListPermission} var BuildRoles = []rbac.Role{ { ORMObjectBase: orm.ORMObjectBase{ diff --git a/internal/biz/permission.go b/internal/biz/permission.go index 8ba25b40..48453648 100644 --- a/internal/biz/permission.go +++ b/internal/biz/permission.go @@ -1,6 +1,9 @@ package biz -import "fmt" +import ( + "fmt" + "infini.sh/console/internal/biz/enum" +) var ClusterApis = make([]string, 0) var EsApis = make(map[string][]string) @@ -38,33 +41,37 @@ type ConsolePermisson struct { } func (r ConsoleRole) ListPermission() interface{} { - list := []ConsolePermisson{ - { - Id: "cluster_overview", - Name: "平台概览", - }, - { - Id: "cluster_search", - Name: "平台搜索", - }, - { - Id: "cluster_elasticsearch", - Name: "集群监控", - }, - { - Id: "cluster_elasticsearch_refresh", - Name: "集群监控刷新", - }, - { - Id: "cluster_activities", - Name: "集群动态", - }, - { - Id: "cluster_activities_search", - Name: "集群动态搜索", - }, - } - return list + //list := []ConsolePermisson{ + // { + // Id: "cluster_overview", + // Name: "平台概览", + // }, + // { + // Id: "cluster_search", + // Name: "平台搜索", + // }, + // { + // Id: "cluster_elasticsearch", + // Name: "集群监控", + // }, + // { + // Id: "cluster_elasticsearch_refresh", + // Name: "集群监控刷新", + // }, + // { + // Id: "cluster_activities", + // Name: "集群动态", + // }, + // { + // Id: "cluster_activities_search", + // Name: "集群动态搜索", + // }, + // + //} + m := make(map[string]map[string][]string) + m["api"]["用户管理"] = enum.AdminUser + m["api"]["角色管理"] = enum.AdminRole + return m } func (r ElasticsearchRole) ListPermission() interface{} { list := ElasticsearchPermisson{ diff --git a/internal/biz/role.go b/internal/biz/role.go index f2464500..e723df34 100644 --- a/internal/biz/role.go +++ b/internal/biz/role.go @@ -5,13 +5,11 @@ import ( "infini.sh/console/internal/dto" "infini.sh/console/model/rbac" "infini.sh/framework/core/event" - log "src/github.com/cihub/seelog" - + "infini.sh/framework/core/orm" "infini.sh/framework/core/util" + log "src/github.com/cihub/seelog" "strings" "time" - - "infini.sh/framework/core/orm" ) func CreateRole(localUser *User, req dto.CreateRole) (id string, err error) { diff --git a/internal/biz/user.go b/internal/biz/user.go index 283daa92..5c4c67c0 100644 --- a/internal/biz/user.go +++ b/internal/biz/user.go @@ -2,6 +2,7 @@ package biz import ( "fmt" + "golang.org/x/crypto/bcrypt" "infini.sh/console/internal/dto" "infini.sh/console/model/rbac" "infini.sh/framework/core/event" @@ -73,10 +74,15 @@ func CreateUser(localUser *User, req dto.CreateUser) (id string, err error) { Name: v.Name, }) } + hash, err := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.DefaultCost) + if err != nil { + + return + } user := rbac.User{ Name: req.Name, Username: req.Username, - Password: util.MD5digest(req.Password), + Password: string(hash), Email: req.Email, Phone: req.Phone, Roles: roles, diff --git a/internal/dto/user.go b/internal/dto/user.go index 55a7290f..67a98696 100644 --- a/internal/dto/user.go +++ b/internal/dto/user.go @@ -4,3 +4,7 @@ type Login struct { Username string `json:"username"` Password string `json:"password"` } +type UpdatePassword struct { + OldPassword string `json:"oldPassword"` + NewPassword string `json:"newPassword"` +} diff --git a/internal/middleware/user.go b/internal/middleware/user.go index d822adbc..9b41edf8 100644 --- a/internal/middleware/user.go +++ b/internal/middleware/user.go @@ -12,7 +12,7 @@ func LoginRequired(h httprouter.Handle) httprouter.Handle { claims, err := biz.ValidateLogin(r.Header.Get("Authorization")) if err != nil { - w = handleError(w, err) + w = handleError(w, http.StatusUnauthorized, err) return } r = r.WithContext(biz.NewUserContext(r.Context(), claims)) @@ -24,21 +24,30 @@ func PermissionRequired(h httprouter.Handle, permissions ...string) httprouter.H return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { claims, err := biz.ValidateLogin(r.Header.Get("Authorization")) if err != nil { - w = handleError(w, err) + + w = handleError(w, http.StatusUnauthorized, err) + return } err = biz.ValidatePermission(claims, permissions) if err != nil { - w = handleError(w, err) + w = handleError(w, http.StatusForbidden, err) return } r = r.WithContext(biz.NewUserContext(r.Context(), claims)) h(w, r, ps) } } -func handleError(w http.ResponseWriter, err error) http.ResponseWriter { +func handleError(w http.ResponseWriter, statusCode int, err error) http.ResponseWriter { w.Header().Set("Content-type", util.ContentTypeJson) - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(`{"error":"` + err.Error() + `"}`)) + w.WriteHeader(statusCode) + json := util.ToJson(util.MapStr{ + "error": util.MapStr{ + "status": statusCode, + "reason": err.Error(), + }, + }, true) + w.Write([]byte(json)) + return w } diff --git a/plugin/api/account/account.go b/plugin/api/account/account.go index a281a344..f7de9fe5 100644 --- a/plugin/api/account/account.go +++ b/plugin/api/account/account.go @@ -22,6 +22,7 @@ func init() { api.HandleAPIMethod(api.DELETE, "/account/logout", account.Logout) api.HandleAPIMethod(api.GET, "/account/profile", m.LoginRequired(account.Profile)) + api.HandleAPIMethod(api.PUT, "/account/password", m.LoginRequired(account.UpdatePassword)) } const userInSession = "user_in_session" @@ -101,3 +102,25 @@ func (h Account) Profile(w http.ResponseWriter, r *http.Request, ps httprouter.P h.WriteJSON(w, reqUser, 200) return } +func (h Account) UpdatePassword(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + reqUser, err := biz.FromUserContext(r.Context()) + if err != nil { + h.Error(w, err) + return + } + var req dto.UpdatePassword + err = h.DecodeJSON(r, &req) + if err != nil { + h.Error(w, err) + return + } + err = biz.UpdatePassword(reqUser, req) + if err != nil { + h.Error(w, err) + return + } + h.WriteOKJSON(w, util.MapStr{ + "status": "ok", + }) + +}