feat: (rbac) middleware handle error / password bcrypt hash / user update password

This commit is contained in:
xushuhui 2022-04-20 10:22:53 +08:00
parent 7e9f39cb88
commit f6d4d9fda5
8 changed files with 124 additions and 52 deletions

View File

@ -3,12 +3,14 @@ package biz
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/golang-jwt/jwt"
"golang.org/x/crypto/bcrypt"
"infini.sh/console/internal/dto"
"infini.sh/console/model/rbac" "infini.sh/console/model/rbac"
"infini.sh/framework/core/event" "infini.sh/framework/core/event"
"infini.sh/framework/core/global" "infini.sh/framework/core/global"
"infini.sh/framework/core/orm" "infini.sh/framework/core/orm"
"infini.sh/framework/core/util" "infini.sh/framework/core/util"
"src/github.com/golang-jwt/jwt"
"strings" "strings"
"time" "time"
) )
@ -26,11 +28,10 @@ type User struct {
const Secret = "console" const Secret = "console"
func authenticateUser(username string, password string) (user rbac.User, 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) err, result := orm.GetBy("username", username, rbac.User{})
if err != nil { if err != nil {
err = ErrNotFound
return return
} }
if result.Total == 0 { if result.Total == 0 {
@ -38,10 +39,12 @@ func authenticateUser(username string, password string) (user rbac.User, err err
return return
} }
user = result.Result[0].(rbac.User) user = result.Result[0].(rbac.User)
if util.MD5digest(password) != user.Password { err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
err = errors.New("password error") if err == bcrypt.ErrMismatchedHashAndPassword {
err = errors.New("password incorrect")
return return
} }
return return
} }
func authenticateAdmin(username string, password string) (user rbac.User, err error) { 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{ User: &User{
Username: user.Username, Username: user.Username,
UserId: user.ID, UserId: user.ID,
Roles: []string{"admin"}, Roles: []string{"admin_user"},
}, },
RegisteredClaims: &jwt.RegisteredClaims{ RegisteredClaims: &jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), 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{ m = util.MapStr{
"access_token": tokenString, "access_token": tokenString,
"username": user.Username, "username": user.Username,
"userid": user.ID, "id": user.ID,
"expire_in": 86400,
"roles": []string{"admin_user"},
} }
return return
} }
@ -115,7 +120,30 @@ func Login(username string, password string) (m map[string]interface{}, err erro
}, nil, nil)) }, nil, nil))
return 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) { func ValidateLogin(authorizationHeader string) (clams *UserClaims, err error) {
if authorizationHeader == "" { if authorizationHeader == "" {
@ -151,10 +179,7 @@ func ValidateLogin(authorizationHeader string) (clams *UserClaims, err error) {
func ValidatePermission(claims *UserClaims, permissions []string) (err error) { func ValidatePermission(claims *UserClaims, permissions []string) (err error) {
reqUser := claims.User reqUser := claims.User
if err != nil {
return
}
if reqUser.UserId == "" { if reqUser.UserId == "" {
err = errors.New("user id is empty") err = errors.New("user id is empty")
return return

View File

@ -35,7 +35,7 @@ const InstanceProxy = "instance_proxy"
var Admin = []string{CreateUser, UpdateUser, DeleteUser, GetUser, SearchUser, CreateRole, UpdateRole, DeleteRole, GetRole, SearchRole, ListPermission} var Admin = []string{CreateUser, UpdateUser, DeleteUser, GetUser, SearchUser, CreateRole, UpdateRole, DeleteRole, GetRole, SearchRole, ListPermission}
var AdminUser = []string{CreateUser, UpdateUser, DeleteUser, GetUser, SearchUser} var AdminUser = []string{CreateUser, UpdateUser, DeleteUser, GetUser, SearchUser}
var AdminRole = []string{CreateRole, UpdateRole, DeleteRole, GetRole, SearchRole, ListPermission}
var BuildRoles = []rbac.Role{ var BuildRoles = []rbac.Role{
{ {
ORMObjectBase: orm.ORMObjectBase{ ORMObjectBase: orm.ORMObjectBase{

View File

@ -1,6 +1,9 @@
package biz package biz
import "fmt" import (
"fmt"
"infini.sh/console/internal/biz/enum"
)
var ClusterApis = make([]string, 0) var ClusterApis = make([]string, 0)
var EsApis = make(map[string][]string) var EsApis = make(map[string][]string)
@ -38,33 +41,37 @@ type ConsolePermisson struct {
} }
func (r ConsoleRole) ListPermission() interface{} { func (r ConsoleRole) ListPermission() interface{} {
list := []ConsolePermisson{ //list := []ConsolePermisson{
{ // {
Id: "cluster_overview", // Id: "cluster_overview",
Name: "平台概览", // Name: "平台概览",
}, // },
{ // {
Id: "cluster_search", // Id: "cluster_search",
Name: "平台搜索", // Name: "平台搜索",
}, // },
{ // {
Id: "cluster_elasticsearch", // Id: "cluster_elasticsearch",
Name: "集群监控", // Name: "集群监控",
}, // },
{ // {
Id: "cluster_elasticsearch_refresh", // Id: "cluster_elasticsearch_refresh",
Name: "集群监控刷新", // Name: "集群监控刷新",
}, // },
{ // {
Id: "cluster_activities", // Id: "cluster_activities",
Name: "集群动态", // Name: "集群动态",
}, // },
{ // {
Id: "cluster_activities_search", // Id: "cluster_activities_search",
Name: "集群动态搜索", // Name: "集群动态搜索",
}, // },
} //
return list //}
m := make(map[string]map[string][]string)
m["api"]["用户管理"] = enum.AdminUser
m["api"]["角色管理"] = enum.AdminRole
return m
} }
func (r ElasticsearchRole) ListPermission() interface{} { func (r ElasticsearchRole) ListPermission() interface{} {
list := ElasticsearchPermisson{ list := ElasticsearchPermisson{

View File

@ -5,13 +5,11 @@ import (
"infini.sh/console/internal/dto" "infini.sh/console/internal/dto"
"infini.sh/console/model/rbac" "infini.sh/console/model/rbac"
"infini.sh/framework/core/event" "infini.sh/framework/core/event"
log "src/github.com/cihub/seelog" "infini.sh/framework/core/orm"
"infini.sh/framework/core/util" "infini.sh/framework/core/util"
log "src/github.com/cihub/seelog"
"strings" "strings"
"time" "time"
"infini.sh/framework/core/orm"
) )
func CreateRole(localUser *User, req dto.CreateRole) (id string, err error) { func CreateRole(localUser *User, req dto.CreateRole) (id string, err error) {

View File

@ -2,6 +2,7 @@ package biz
import ( import (
"fmt" "fmt"
"golang.org/x/crypto/bcrypt"
"infini.sh/console/internal/dto" "infini.sh/console/internal/dto"
"infini.sh/console/model/rbac" "infini.sh/console/model/rbac"
"infini.sh/framework/core/event" "infini.sh/framework/core/event"
@ -73,10 +74,15 @@ func CreateUser(localUser *User, req dto.CreateUser) (id string, err error) {
Name: v.Name, Name: v.Name,
}) })
} }
hash, err := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.DefaultCost)
if err != nil {
return
}
user := rbac.User{ user := rbac.User{
Name: req.Name, Name: req.Name,
Username: req.Username, Username: req.Username,
Password: util.MD5digest(req.Password), Password: string(hash),
Email: req.Email, Email: req.Email,
Phone: req.Phone, Phone: req.Phone,
Roles: roles, Roles: roles,

View File

@ -4,3 +4,7 @@ type Login struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
} }
type UpdatePassword struct {
OldPassword string `json:"oldPassword"`
NewPassword string `json:"newPassword"`
}

View File

@ -12,7 +12,7 @@ func LoginRequired(h httprouter.Handle) httprouter.Handle {
claims, err := biz.ValidateLogin(r.Header.Get("Authorization")) claims, err := biz.ValidateLogin(r.Header.Get("Authorization"))
if err != nil { if err != nil {
w = handleError(w, err) w = handleError(w, http.StatusUnauthorized, err)
return return
} }
r = r.WithContext(biz.NewUserContext(r.Context(), claims)) 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) { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
claims, err := biz.ValidateLogin(r.Header.Get("Authorization")) claims, err := biz.ValidateLogin(r.Header.Get("Authorization"))
if err != nil { if err != nil {
w = handleError(w, err)
w = handleError(w, http.StatusUnauthorized, err)
return return
} }
err = biz.ValidatePermission(claims, permissions) err = biz.ValidatePermission(claims, permissions)
if err != nil { if err != nil {
w = handleError(w, err) w = handleError(w, http.StatusForbidden, err)
return return
} }
r = r.WithContext(biz.NewUserContext(r.Context(), claims)) r = r.WithContext(biz.NewUserContext(r.Context(), claims))
h(w, r, ps) 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.Header().Set("Content-type", util.ContentTypeJson)
w.WriteHeader(http.StatusUnauthorized) w.WriteHeader(statusCode)
w.Write([]byte(`{"error":"` + err.Error() + `"}`)) json := util.ToJson(util.MapStr{
"error": util.MapStr{
"status": statusCode,
"reason": err.Error(),
},
}, true)
w.Write([]byte(json))
return w return w
} }

View File

@ -22,6 +22,7 @@ func init() {
api.HandleAPIMethod(api.DELETE, "/account/logout", account.Logout) api.HandleAPIMethod(api.DELETE, "/account/logout", account.Logout)
api.HandleAPIMethod(api.GET, "/account/profile", m.LoginRequired(account.Profile)) 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" 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) h.WriteJSON(w, reqUser, 200)
return 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",
})
}