feat: (rbac) init load rolePermission / validate user permission / operation log

This commit is contained in:
xushuhui 2022-04-19 12:03:56 +08:00
parent 65dd1f2209
commit 74ddc44ce7
9 changed files with 226 additions and 84 deletions

View File

@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"infini.sh/framework/core/global" "infini.sh/framework/core/global"
"infini.sh/framework/core/util" "infini.sh/framework/core/util"
"net/http"
"src/github.com/golang-jwt/jwt" "src/github.com/golang-jwt/jwt"
"strings" "strings"
"time" "time"
@ -16,9 +15,9 @@ type UserClaims struct {
*User *User
} }
type User struct { type User struct {
Username string `json:"username"` Username string `json:"username"`
UserId string `json:"user_id"` UserId string `json:"user_id"`
ApiPermission map[string]struct{} `json:"api_permission"` Role []string `json:"role"`
} }
const Secret = "console" const Secret = "console"
@ -36,9 +35,7 @@ func Login(username string, password string) (m map[string]interface{}, err erro
User: &User{ User: &User{
Username: u, Username: u,
UserId: "admin", UserId: "admin",
ApiPermission: map[string]struct{}{ Role: []string{"admin_user"},
"account.profile": struct{}{},
},
}, },
RegisteredClaims: &jwt.RegisteredClaims{ RegisteredClaims: &jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
@ -92,8 +89,9 @@ func ValidateLogin(authorizationHeader string) (clams *UserClaims, err error) {
return return
} }
func ValidatePermission(r *http.Request, permissions string) (err error) { func ValidatePermission(claims *UserClaims, permissions []string) (err error) {
reqUser, err := FromUserContext(r.Context())
reqUser := claims.User
if err != nil { if err != nil {
return return
@ -102,16 +100,31 @@ func ValidatePermission(r *http.Request, permissions string) (err error) {
err = errors.New("user id is empty") err = errors.New("user id is empty")
return return
} }
if reqUser.ApiPermission == nil { if reqUser.Role == nil {
err = errors.New("api permission is empty") err = errors.New("api permission is empty")
return return
} }
if _, ok := reqUser.ApiPermission[permissions]; !ok { // 权限校验
err = errors.New("permission denied") userPermissionMap := make(map[string]struct{})
return for _, role := range reqUser.Role {
if _, ok := RolePermission[role]; ok {
for _, v := range RolePermission[role] {
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 return
} }

View File

@ -0,0 +1,13 @@
package enum
const CreateUser = "create_user"
const UpdateUser = "update_user"
const DeleteUser = "delete_user"
const GetUser = "get_user"
const ListUser = "list_user"
const CreateRole = "create_role"
const UpdateRole = "update_role"
const DeleteRole = "delete_role"
const GetRole = "get_role"
const ListRole = "list_role"
const ListPermission = "list_permission"

View File

@ -4,6 +4,7 @@ import "fmt"
var ClusterApis = make([]string, 0) var ClusterApis = make([]string, 0)
var EsApis = make(map[string][]string) var EsApis = make(map[string][]string)
var RolePermission = make(map[string][]string)
type RoleType = string type RoleType = string
@ -15,10 +16,8 @@ const (
type IRole interface { type IRole interface {
ListPermission() interface{} ListPermission() interface{}
} }
type ConsoleRole struct { type ConsoleRole struct{}
} type ElasticsearchRole struct{}
type ElasticsearchRole struct {
}
func NewRole(typ string) (r IRole, err error) { func NewRole(typ string) (r IRole, err error) {
switch typ { switch typ {
@ -68,31 +67,9 @@ func (r ConsoleRole) ListPermission() interface{} {
return list return list
} }
func (r ElasticsearchRole) ListPermission() interface{} { func (r ElasticsearchRole) ListPermission() interface{} {
list := []ConsolePermisson{ list := ElasticsearchPermisson{
{ ClusterPrivileges: ClusterApis,
Id: "cluster_overview", IndexPrivileges: EsApis["indices"],
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 return list
} }
@ -101,12 +78,3 @@ type ElasticsearchPermisson struct {
IndexPrivileges []string `json:"index_privileges"` IndexPrivileges []string `json:"index_privileges"`
ClusterPrivileges []string `json:"cluster_privileges"` ClusterPrivileges []string `json:"cluster_privileges"`
} }
func ListElasticsearchPermisson() (permisson ElasticsearchPermisson, err error) {
permisson = ElasticsearchPermisson{
ClusterPrivileges: ClusterApis,
IndexPrivileges: EsApis["indices"],
}
return
}

View File

@ -91,11 +91,16 @@ func DeleteRole(localUser *User, id string) (err error) {
"userid": localUser.UserId, "userid": localUser.UserId,
"username": localUser.Username, "username": localUser.Username,
}, },
}, nil)) }, util.MapStr{
"id": id,
"name": role.Name,
"description": role.Description,
"permission": role.Permission,
"type": role.RoleType,
"created": role.Created.Format("2006-01-02 15:04:05"),
"updated": role.Updated.Format("2006-01-02 15:04:05"),
}))
if err != nil {
log.Error(err)
}
return return
} }
@ -111,6 +116,25 @@ func UpdateRole(localUser *User, id string, req dto.UpdateRole) (err error) {
role.Permission = req.Permission role.Permission = req.Permission
role.Updated = time.Now() role.Updated = time.Now()
err = orm.Save(role) err = orm.Save(role)
if err != nil {
return
}
err = orm.Save(GenerateEvent(event.ActivityMetadata{
Category: "platform",
Group: "rbac",
Name: "role",
Type: "update",
Labels: util.MapStr{
"id": id,
"description": role.Description,
"permission": role.Permission,
"updated": role.Updated,
},
User: util.MapStr{
"userid": localUser.UserId,
"username": localUser.Username,
},
}, nil))
return return
} }
func GetRole(id string) (role rbac.Role, err error) { func GetRole(id string) (role rbac.Role, err error) {

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"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/orm" "infini.sh/framework/core/orm"
"infini.sh/framework/core/util" "infini.sh/framework/core/util"
@ -13,7 +14,7 @@ import (
var ErrNotFound = fmt.Errorf("not found") var ErrNotFound = fmt.Errorf("not found")
func DeleteUser(id string) (err error) { func DeleteUser(localUser *User, id string) (err error) {
user := rbac.User{} user := rbac.User{}
user.ID = id user.ID = id
@ -22,10 +23,37 @@ func DeleteUser(id string) (err error) {
err = ErrNotFound err = ErrNotFound
return return
} }
return orm.Delete(user) err = orm.Delete(user)
if err != nil {
return
}
err = orm.Save(GenerateEvent(event.ActivityMetadata{
Category: "platform",
Group: "rbac",
Name: "user",
Type: "delete",
Labels: util.MapStr{
"id": id,
},
User: util.MapStr{
"userid": localUser.UserId,
"username": localUser.Username,
},
}, util.MapStr{
"id": id,
"username": user.Username,
"email": user.Email,
"phone": user.Phone,
"password": user.Password,
"name": user.Name,
"tags": user.Tags,
"roles": user.Roles,
"created": user.Created,
"updated": user.Updated,
}))
return
} }
func CreateUser(req dto.CreateUser) (id string, err error) { func CreateUser(localUser *User, req dto.CreateUser) (id string, err error) {
q := orm.Query{Size: 1000} q := orm.Query{Size: 1000}
q.Conds = orm.And(orm.Eq("username", req.Username)) q.Conds = orm.And(orm.Eq("username", req.Username))
@ -62,9 +90,32 @@ func CreateUser(req dto.CreateUser) (id string, err error) {
return return
} }
return user.ID, nil id = user.ID
err = orm.Save(GenerateEvent(event.ActivityMetadata{
Category: "platform",
Group: "rbac",
Name: "user",
Type: "create",
Labels: util.MapStr{
"id": id,
"username": user.Username,
"email": user.Email,
"phone": user.Phone,
"password": user.Password,
"name": user.Name,
"tags": user.Tags,
"roles": user.Roles,
"created": user.Created,
"updated": user.Updated,
},
User: util.MapStr{
"userid": localUser.UserId,
"username": localUser.Username,
},
}, nil))
return
} }
func UpdateUser(id string, req dto.UpdateUser) (err error) { func UpdateUser(localUser *User, id string, req dto.UpdateUser) (err error) {
user := rbac.User{} user := rbac.User{}
user.ID = id user.ID = id
_, err = orm.Get(&user) _, err = orm.Get(&user)
@ -78,9 +129,30 @@ func UpdateUser(id string, req dto.UpdateUser) (err error) {
user.Tags = req.Tags user.Tags = req.Tags
user.Updated = time.Now() user.Updated = time.Now()
err = orm.Save(user) err = orm.Save(user)
if err != nil {
return
}
err = orm.Save(GenerateEvent(event.ActivityMetadata{
Category: "platform",
Group: "rbac",
Name: "user",
Type: "update",
Labels: util.MapStr{
"id": id,
"email": user.Email,
"phone": user.Phone,
"name": user.Name,
"tags": user.Tags,
"updated": user.Updated,
},
User: util.MapStr{
"userid": localUser.UserId,
"username": localUser.Username,
},
}, nil))
return return
} }
func UpdateUserRole(id string, req dto.UpdateUserRole) (err error) { func UpdateUserRole(localUser *User, id string, req dto.UpdateUserRole) (err error) {
user := rbac.User{} user := rbac.User{}
user.ID = id user.ID = id
_, err = orm.Get(&user) _, err = orm.Get(&user)
@ -98,6 +170,24 @@ func UpdateUserRole(id string, req dto.UpdateUserRole) (err error) {
user.Roles = roles user.Roles = roles
user.Updated = time.Now() user.Updated = time.Now()
err = orm.Save(user) err = orm.Save(user)
if err != nil {
return
}
err = orm.Save(GenerateEvent(event.ActivityMetadata{
Category: "platform",
Group: "rbac",
Name: "user",
Type: "update",
Labels: util.MapStr{
"id": id,
"roles": user.Roles,
"updated": user.Updated,
},
User: util.MapStr{
"userid": localUser.UserId,
"username": localUser.Username,
},
}, nil))
return return
} }

View File

@ -20,9 +20,14 @@ func LoginRequired(h httprouter.Handle) httprouter.Handle {
} }
} }
func PermissionRequired(h httprouter.Handle, permissions string) httprouter.Handle { func PermissionRequired(h httprouter.Handle, permissions ...string) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
err := biz.ValidatePermission(r, permissions) claims, err := biz.ValidateLogin(r.Header.Get("Authorization"))
if err != nil {
w = handleError(w, err)
return
}
err = biz.ValidatePermission(claims, permissions)
if err != nil { if err != nil {
w = handleError(w, err) w = handleError(w, err)
return return

View File

@ -20,7 +20,7 @@ func init() {
//api.HandleAPIMethod(api.GET, "/account/current_user", account.CurrentUser) //api.HandleAPIMethod(api.GET, "/account/current_user", account.CurrentUser)
api.HandleAPIMethod(api.DELETE, "/account/logout", m.LoginRequired(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))
} }

View File

@ -3,6 +3,7 @@ package rbac
import ( import (
"encoding/json" "encoding/json"
"infini.sh/console/internal/biz" "infini.sh/console/internal/biz"
"infini.sh/console/internal/biz/enum"
m "infini.sh/console/internal/middleware" m "infini.sh/console/internal/middleware"
"infini.sh/framework/core/api" "infini.sh/framework/core/api"
@ -17,24 +18,23 @@ type Rbac struct {
func registerRouter() { func registerRouter() {
r := Rbac{} r := Rbac{}
api.HandleAPIMethod(api.GET, "/permission/:type", m.LoginRequired(m.PermissionRequired(r.ListPermission, "list.permission"))) api.HandleAPIMethod(api.GET, "/permission/:type", m.PermissionRequired(r.ListPermission, enum.ListPermission))
api.HandleAPIMethod(api.POST, "/role/:type", m.LoginRequired(m.PermissionRequired(r.CreateRole, "create.role"))) api.HandleAPIMethod(api.POST, "/role/:type", m.PermissionRequired(r.CreateRole, enum.CreateRole))
api.HandleAPIMethod(api.GET, "/role/:id", m.LoginRequired(m.PermissionRequired(r.GetRole, "get.role"))) api.HandleAPIMethod(api.GET, "/role/:id", m.PermissionRequired(r.GetRole, enum.GetRole))
api.HandleAPIMethod(api.DELETE, "/role/:id", m.LoginRequired(m.PermissionRequired(r.DeleteRole, "delete.role"))) api.HandleAPIMethod(api.DELETE, "/role/:id", m.PermissionRequired(r.DeleteRole, enum.DeleteRole))
api.HandleAPIMethod(api.PUT, "/role/:id", m.LoginRequired(m.PermissionRequired(r.UpdateRole, "update.role"))) api.HandleAPIMethod(api.PUT, "/role/:id", m.PermissionRequired(r.UpdateRole, enum.UpdateRole))
api.HandleAPIMethod(api.GET, "/role/_search", m.LoginRequired(m.PermissionRequired(r.SearchRole, "search.role"))) api.HandleAPIMethod(api.GET, "/role/_search", m.PermissionRequired(r.SearchRole, enum.ListRole))
api.HandleAPIMethod(api.POST, "/user", m.LoginRequired(m.PermissionRequired(r.CreateUser, "create.user"))) api.HandleAPIMethod(api.POST, "/user", m.PermissionRequired(r.CreateUser, enum.CreateUser))
api.HandleAPIMethod(api.GET, "/user/:id", m.LoginRequired(m.PermissionRequired(r.GetUser, "get.user"))) api.HandleAPIMethod(api.GET, "/user/:id", m.PermissionRequired(r.GetUser, enum.GetUser))
api.HandleAPIMethod(api.GET, "/user/search", m.LoginRequired(m.PermissionRequired(r.SearchUser, "search.user"))) api.HandleAPIMethod(api.GET, "/user/search", m.PermissionRequired(r.SearchUser, enum.ListUser))
api.HandleAPIMethod(api.DELETE, "/user/:id", m.LoginRequired(m.PermissionRequired(r.DeleteUser, "delete.user"))) api.HandleAPIMethod(api.DELETE, "/user/:id", m.PermissionRequired(r.DeleteUser, enum.DeleteUser))
api.HandleAPIMethod(api.PUT, "/user/:id", m.LoginRequired(m.PermissionRequired(r.UpdateUser, "update.user"))) api.HandleAPIMethod(api.PUT, "/user/:id", m.PermissionRequired(r.UpdateUser, enum.UpdateUser))
api.HandleAPIMethod(api.PUT, "/user/:id/role", m.LoginRequired(m.PermissionRequired(r.UpdateUserRole, "update.user.role"))) api.HandleAPIMethod(api.PUT, "/user/:id/role", m.PermissionRequired(r.UpdateUserRole, enum.UpdateUser))
api.HandleAPIMethod(api.GET, "/user/_search", m.LoginRequired(m.PermissionRequired(r.SearchUser, "search.user"))) api.HandleAPIMethod(api.GET, "/user/_search", m.PermissionRequired(r.SearchUser, enum.ListUser))
} }
//TODO 权限一级配置全局变量,
func loadJsonConfig() { func loadJsonConfig() {
pwd, _ := os.Getwd() pwd, _ := os.Getwd()
@ -56,9 +56,14 @@ func loadJsonConfig() {
biz.ClusterApis = list biz.ClusterApis = list
} }
func loadRolePermission() {
biz.RolePermission = make(map[string][]string)
biz.RolePermission["admin_user"] = []string{enum.GetUser}
}
func init() { func init() {
registerRouter() registerRouter()
loadJsonConfig() loadJsonConfig()
loadRolePermission()
} }
type Response struct { type Response struct {

View File

@ -27,7 +27,13 @@ func (h Rbac) CreateUser(w http.ResponseWriter, r *http.Request, ps httprouter.P
h.Error(w, err) h.Error(w, err)
return return
} }
id, err := biz.CreateUser(req) localUser, err := biz.FromUserContext(r.Context())
if err != nil {
log.Error(err.Error())
h.Error(w, err)
return
}
id, err := biz.CreateUser(localUser, req)
if err != nil { if err != nil {
_ = log.Error(err.Error()) _ = log.Error(err.Error())
h.Error(w, err) h.Error(w, err)
@ -64,7 +70,13 @@ func (h Rbac) UpdateUser(w http.ResponseWriter, r *http.Request, ps httprouter.P
h.Error(w, err) h.Error(w, err)
return return
} }
err = biz.UpdateUser(id, req) localUser, err := biz.FromUserContext(r.Context())
if err != nil {
log.Error(err.Error())
h.Error(w, err)
return
}
err = biz.UpdateUser(localUser, id, req)
if err != nil { if err != nil {
_ = log.Error(err.Error()) _ = log.Error(err.Error())
@ -84,7 +96,13 @@ func (h Rbac) UpdateUserRole(w http.ResponseWriter, r *http.Request, ps httprout
h.Error(w, err) h.Error(w, err)
return return
} }
err = biz.UpdateUserRole(id, req) localUser, err := biz.FromUserContext(r.Context())
if err != nil {
log.Error(err.Error())
h.Error(w, err)
return
}
err = biz.UpdateUserRole(localUser, id, req)
if err != nil { if err != nil {
_ = log.Error(err.Error()) _ = log.Error(err.Error())
@ -97,7 +115,13 @@ func (h Rbac) UpdateUserRole(w http.ResponseWriter, r *http.Request, ps httprout
func (h Rbac) DeleteUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { func (h Rbac) DeleteUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
id := ps.MustGetParameter("id") id := ps.MustGetParameter("id")
err := biz.DeleteUser(id) localUser, err := biz.FromUserContext(r.Context())
if err != nil {
log.Error(err.Error())
h.Error(w, err)
return
}
err = biz.DeleteUser(localUser, id)
if errors.Is(err, elastic.ErrNotFound) { if errors.Is(err, elastic.ErrNotFound) {
h.WriteJSON(w, NotFoundResponse(id), http.StatusNotFound) h.WriteJSON(w, NotFoundResponse(id), http.StatusNotFound)
return return