diff --git a/internal/biz/account.go b/internal/biz/account.go index b3dfd653..47d62fc8 100644 --- a/internal/biz/account.go +++ b/internal/biz/account.go @@ -2,7 +2,6 @@ package biz import ( "errors" - "fmt" "github.com/golang-jwt/jwt" "github.com/mitchellh/mapstructure" "golang.org/x/crypto/bcrypt" @@ -13,7 +12,6 @@ import ( "infini.sh/framework/core/orm" "infini.sh/framework/core/util" - "strings" "time" ) @@ -156,7 +154,7 @@ func UpdatePassword(localUser *User, req dto.UpdatePassword) (err error) { user.ID = localUser.UserId _, err = orm.Get(&user) if err != nil { - err = ErrNotFound + return } err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.OldPassword)) @@ -173,93 +171,50 @@ func UpdatePassword(localUser *User, req dto.UpdatePassword) (err error) { if err != nil { return } + err = orm.Save(GenerateEvent(event.ActivityMetadata{ + Category: "platform", + Group: "rbac", + Name: "user", + Type: "update", + Labels: util.MapStr{ + "old_password": req.OldPassword, + "new_password": req.NewPassword, + }, + User: util.MapStr{ + "userid": user.ID, + "username": user.Username, + }, + }, nil, nil)) return } -func ValidateLogin(authorizationHeader string) (clams *UserClaims, err error) { - - if authorizationHeader == "" { - err = errors.New("authorization header is empty") - return - } - fields := strings.Fields(authorizationHeader) - if fields[0] != "Bearer" || len(fields) != 2 { - err = errors.New("authorization header is invalid") - return - } - tokenString := fields[1] - - token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) - } - return []byte(Secret), nil - }) +func UpdateProfile(localUser *User, req dto.UpdateProfile) (err error) { + user := rbac.User{} + user.ID = localUser.UserId + _, err = orm.Get(&user) if err != nil { return } - clams, ok := token.Claims.(*UserClaims) - - if clams.UserId == "" { - err = errors.New("user id is empty") + user.Name = req.Name + user.Email = req.Email + user.Phone = req.Phone + err = orm.Save(&user) + if err != nil { return } - fmt.Println("user token", clams.UserId, TokenMap[clams.UserId]) - tokenVal, ok := TokenMap[clams.UserId] - if !ok { - err = errors.New("token is invalid") - return - } - if tokenVal.ExpireIn < time.Now().Unix() { - err = errors.New("token is expire in") - delete(TokenMap, clams.UserId) - return - } - if ok && token.Valid { - return clams, nil - } + err = orm.Save(GenerateEvent(event.ActivityMetadata{ + Category: "platform", + Group: "rbac", + Name: "user", + Type: "update", + Labels: util.MapStr{ + "name": req.Name, + "email": req.Email, + "phone": req.Phone, + }, + User: util.MapStr{ + "userid": user.ID, + "username": user.Username, + }, + }, nil, nil)) return - -} -func ValidatePermission(claims *UserClaims, permissions []string) (err error) { - - user := claims.User - - if user.UserId == "" { - err = errors.New("user id is empty") - return - } - if user.Roles == nil { - err = errors.New("api permission is empty") - return - } - - // 权限校验 - userPermissionMap := make(map[string]struct{}) - for _, role := range user.Roles { - if _, ok := RoleMap[role]; ok { - for _, v := range RoleMap[role].Platform { - - userPermissionMap[v] = struct{}{} - //all include read - if strings.Contains(v, "all") { - key := v[:len(v)-3] + "read" - userPermissionMap[key] = struct{}{} - } - } - } - } - - var count int - for _, v := range permissions { - if _, ok := userPermissionMap[v]; ok { - count++ - continue - } - } - if count == len(permissions) { - return nil - } - err = errors.New("permission denied") - return - } diff --git a/internal/biz/enum/const.go b/internal/biz/enum/const.go index 218f4bfc..09345fdd 100644 --- a/internal/biz/enum/const.go +++ b/internal/biz/enum/const.go @@ -4,20 +4,60 @@ import ( "time" ) -const UserRead = "system.user:read" -const UserAll = "system.user:all" +var PermissionMap = make(map[string][]string) -const RoleRead = "system.role:read" -const RoleAll = "system.role:all" +const ( + UserRead = "system.user:read" + UserAll = "system.user:all" + RoleRead = "system.role:read" + RoleAll = "system.role:all" + RuleRead = "alerting.rule:read" + RuleAll = "alerting.rule:all" + InstanceRead = "gateway.instance:read" + InstanceAll = "gateway.instance:all" + FlowRead = "gateway.flow:read" + FlowAll = "gateway.flow:all" + IndexAll = "data.index:read" + IndexRead = "data.index:all" + ViewsAll = "data.views:read" + ViewsRead = "data.views:all" + DiscoverAll = "data.discover:read" + DiscoverRead = "data.discover:all" + ClusterAll = "system.cluster:all" + ClusterRead = "system.cluster:read" -const RuleRead = "alerting.rule:read" -const RuleAll = "alerting.rule:all" + CommandAll = "system.command:all" + CommandRead = "system.command:read" -const InstanceRead = "gateway.instance:read" -const InstanceAll = "gateway.instance:all" + EntryAll = "gateway.entry:all" + EntryRead = "gateway.entry:read" + RouterRead = "gateway.router:read" + RouterAll = "gateway.router:all" +) + +var UserReadPermission = []string{"user:read"} +var UserAllPermission = []string{"user:read", "user:write"} + +var RoleReadPermission = []string{"role:read"} +var RoleAllPermission = []string{"role:read", "role:write"} + +var RuleReadPermission = []string{"rule:read"} +var RuleAllPermission = []string{"rule:read", "rule:write"} + +var InstanceReadPermission = []string{"instance:read"} +var InstanceAllPermission = []string{"instance:all"} + +var EntryReadPermission = []string{"entry:read"} +var EntryAllPermission = []string{"entry:all"} + +var RouterReadPermission = []string{"router:read"} +var RouterAllPermission = []string{"router:all"} var AdminPrivilege = []string{ - UserRead, UserAll, RoleRead, RoleAll, + UserAll, RoleAll, RuleAll, EntryAll, + InstanceAll, ClusterAll, CommandAll, RouterAll, + FlowRead, FlowAll, IndexAll, ViewsAll, + DiscoverAll, } var BuildRoles = make(map[string]map[string]interface{}, 0) @@ -33,5 +73,19 @@ func init() { "description": "is admin", "created": time.Now(), } + PermissionMap = map[string][]string{ + UserRead: UserReadPermission, + UserAll: UserAllPermission, + RoleRead: RoleReadPermission, + RoleAll: RoleAllPermission, + RuleRead: RuleReadPermission, + RuleAll: RuleAllPermission, + InstanceRead: InstanceReadPermission, + InstanceAll: InstanceAllPermission, + EntryRead: EntryReadPermission, + EntryAll: EntryAllPermission, + RouterRead: RouterReadPermission, + RouterAll: RouterAllPermission, + } } diff --git a/internal/biz/validate.go b/internal/biz/validate.go index b35ffad9..ebd1bf3b 100644 --- a/internal/biz/validate.go +++ b/internal/biz/validate.go @@ -2,9 +2,13 @@ package biz import ( "errors" + "fmt" + "infini.sh/console/internal/biz/enum" httprouter "infini.sh/framework/core/api/router" "infini.sh/framework/core/util" + "src/github.com/golang-jwt/jwt" "strings" + "time" ) type EsRequest struct { @@ -159,3 +163,98 @@ func FilterIndex(roles []string, index []string) []string { } return realIndex } +func ValidateLogin(authorizationHeader string) (clams *UserClaims, err error) { + + if authorizationHeader == "" { + err = errors.New("authorization header is empty") + return + } + fields := strings.Fields(authorizationHeader) + if fields[0] != "Bearer" || len(fields) != 2 { + err = errors.New("authorization header is invalid") + return + } + tokenString := fields[1] + + token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + return []byte(Secret), nil + }) + if err != nil { + return + } + clams, ok := token.Claims.(*UserClaims) + + if clams.UserId == "" { + err = errors.New("user id is empty") + return + } + fmt.Println("user token", clams.UserId, TokenMap[clams.UserId]) + tokenVal, ok := TokenMap[clams.UserId] + if !ok { + err = errors.New("token is invalid") + return + } + if tokenVal.ExpireIn < time.Now().Unix() { + err = errors.New("token is expire in") + delete(TokenMap, clams.UserId) + return + } + if ok && token.Valid { + return clams, nil + } + return + +} +func ValidatePermission(claims *UserClaims, permissions []string) (err error) { + + user := claims.User + + if user.UserId == "" { + err = errors.New("user id is empty") + return + } + if user.Roles == nil { + err = errors.New("api permission is empty") + return + } + + // 权限校验 + userPermissions := make([]string, 0) + for _, role := range user.Roles { + if _, ok := RoleMap[role]; ok { + for _, v := range RoleMap[role].Platform { + userPermissions = append(userPermissions, v) + + //all include read + if strings.Contains(v, "all") { + key := v[:len(v)-3] + "read" + userPermissions = append(userPermissions, key) + } + } + } + } + userPermissionMap := make(map[string]struct{}) + for _, val := range userPermissions { + for _, v := range enum.PermissionMap[val] { + userPermissionMap[v] = struct{}{} + } + + } + + var count int + for _, v := range permissions { + if _, ok := userPermissionMap[v]; ok { + count++ + continue + } + } + if count == len(permissions) { + return nil + } + err = errors.New("permission denied") + return + +} diff --git a/internal/dto/user.go b/internal/dto/user.go index a9b4dde7..68d7ed83 100644 --- a/internal/dto/user.go +++ b/internal/dto/user.go @@ -8,3 +8,8 @@ type UpdatePassword struct { OldPassword string `json:"old_password"` NewPassword string `json:"new_password"` } +type UpdateProfile struct { + Name string `json:"name"` + Phone string `json:"phone"` + Email string `json:"email"` +} diff --git a/plugin/api/account/account.go b/plugin/api/account/account.go index 06048cf7..638ddcc9 100644 --- a/plugin/api/account/account.go +++ b/plugin/api/account/account.go @@ -92,7 +92,13 @@ 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) { + reqUser, err := biz.FromUserContext(r.Context()) + if err != nil { + h.ErrorInternalServer(w, err.Error()) + return + } + delete(biz.TokenMap, reqUser.UserId) h.WriteOKJSON(w, util.MapStr{ "status": "ok", }) @@ -149,8 +155,22 @@ func (h Account) UpdatePassword(w http.ResponseWriter, r *http.Request, ps httpr h.ErrorInternalServer(w, err.Error()) return } - h.WriteOKJSON(w, util.MapStr{ - "status": "ok", - }) - + h.WriteOKJSON(w, core.UpdateResponse(reqUser.UserId)) + return +} +func (h Account) UpdateProfile(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + reqUser, err := biz.FromUserContext(r.Context()) + if err != nil { + h.ErrorInternalServer(w, err.Error()) + return + } + var req dto.UpdateProfile + err = h.DecodeJSON(r, &req) + err = biz.UpdateProfile(reqUser, req) + if err != nil { + h.ErrorInternalServer(w, err.Error()) + return + } + h.WriteOKJSON(w, core.UpdateResponse(reqUser.UserId)) + return } diff --git a/plugin/api/rbac/api.go b/plugin/api/rbac/api.go index 2578f9e5..591dcb3a 100644 --- a/plugin/api/rbac/api.go +++ b/plugin/api/rbac/api.go @@ -22,19 +22,19 @@ func init() { r := Rbac{} api.HandleAPIMethod(api.GET, "/permission/:type", r.ListPermission) - api.HandleAPIMethod(api.POST, "/role/:type", m.PermissionRequired(r.CreateRole, enum.RoleAll)) - api.HandleAPIMethod(api.GET, "/role/:id", m.PermissionRequired(r.GetRole, enum.RoleRead)) - api.HandleAPIMethod(api.DELETE, "/role/:id", m.PermissionRequired(r.DeleteRole, enum.RoleAll)) - api.HandleAPIMethod(api.PUT, "/role/:id", m.PermissionRequired(r.UpdateRole, enum.RoleAll)) - api.HandleAPIMethod(api.GET, "/role/_search", m.PermissionRequired(r.SearchRole, enum.RoleRead)) + api.HandleAPIMethod(api.POST, "/role/:type", m.PermissionRequired(r.CreateRole, enum.RoleAllPermission...)) + api.HandleAPIMethod(api.GET, "/role/:id", m.PermissionRequired(r.GetRole, enum.RoleReadPermission...)) + api.HandleAPIMethod(api.DELETE, "/role/:id", m.PermissionRequired(r.DeleteRole, enum.RoleAllPermission...)) + api.HandleAPIMethod(api.PUT, "/role/:id", m.PermissionRequired(r.UpdateRole, enum.RoleAllPermission...)) + api.HandleAPIMethod(api.GET, "/role/_search", m.PermissionRequired(r.SearchRole, enum.RoleReadPermission...)) - api.HandleAPIMethod(api.POST, "/user", m.PermissionRequired(r.CreateUser, enum.UserAll)) - api.HandleAPIMethod(api.GET, "/user/:id", m.PermissionRequired(r.GetUser, enum.UserRead)) - api.HandleAPIMethod(api.DELETE, "/user/:id", m.PermissionRequired(r.DeleteUser, enum.UserAll)) - api.HandleAPIMethod(api.PUT, "/user/:id", m.PermissionRequired(r.UpdateUser, enum.UserAll)) - api.HandleAPIMethod(api.PUT, "/user/:id/role", m.PermissionRequired(r.UpdateUserRole, enum.UserAll)) - api.HandleAPIMethod(api.GET, "/user/_search", m.PermissionRequired(r.SearchUser, enum.UserRead)) - api.HandleAPIMethod(api.PUT, "/user/:id/password", m.PermissionRequired(r.UpdateUserPassword, enum.UserAll)) + api.HandleAPIMethod(api.POST, "/user", m.PermissionRequired(r.CreateUser, enum.UserAllPermission...)) + api.HandleAPIMethod(api.GET, "/user/:id", m.PermissionRequired(r.GetUser, enum.UserReadPermission...)) + api.HandleAPIMethod(api.DELETE, "/user/:id", m.PermissionRequired(r.DeleteUser, enum.UserAllPermission...)) + api.HandleAPIMethod(api.PUT, "/user/:id", m.PermissionRequired(r.UpdateUser, enum.UserAllPermission...)) + api.HandleAPIMethod(api.PUT, "/user/:id/role", m.PermissionRequired(r.UpdateUserRole, enum.UserAllPermission...)) + api.HandleAPIMethod(api.GET, "/user/_search", m.PermissionRequired(r.SearchUser, enum.UserReadPermission...)) + api.HandleAPIMethod(api.PUT, "/user/:id/password", m.PermissionRequired(r.UpdateUserPassword, enum.UserAllPermission...)) } func loadJsonConfig() { diff --git a/plugin/api/rbac/user.go b/plugin/api/rbac/user.go index 9423b729..7a00f798 100644 --- a/plugin/api/rbac/user.go +++ b/plugin/api/rbac/user.go @@ -185,7 +185,7 @@ func (h Rbac) UpdateUserPassword(w http.ResponseWriter, r *http.Request, ps http h.ErrorInternalServer(w, err.Error()) return } - + delete(biz.TokenMap, localUser.UserId) _ = h.WriteOKJSON(w, core.UpdateResponse(id)) return