feat: (rbac) permission map

This commit is contained in:
xushuhui 2022-04-28 10:50:57 +08:00
parent 2acae3582a
commit c472adbf32
7 changed files with 243 additions and 110 deletions

View File

@ -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
}

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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() {

View File

@ -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