feat: (rbac) build in role / logout / login operation log

This commit is contained in:
xushuhui 2022-04-19 18:20:52 +08:00
parent 74ddc44ce7
commit 7e9f39cb88
10 changed files with 155 additions and 28 deletions

View File

@ -3,7 +3,10 @@ package biz
import (
"errors"
"fmt"
"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"
@ -17,12 +20,31 @@ type UserClaims struct {
type User struct {
Username string `json:"username"`
UserId string `json:"user_id"`
Role []string `json:"role"`
Roles []string `json:"roles"`
}
const Secret = "console"
func Login(username string, password string) (m map[string]interface{}, 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)
if err != nil {
return
}
if result.Total == 0 {
err = errors.New("user not found")
return
}
user = result.Result[0].(rbac.User)
if util.MD5digest(password) != user.Password {
err = errors.New("password error")
return
}
return
}
func authenticateAdmin(username string, password string) (user rbac.User, err error) {
u, _ := global.Env().GetConfig("bootstrap.username", "admin")
p, _ := global.Env().GetConfig("bootstrap.password", "admin")
@ -31,11 +53,16 @@ func Login(username string, password string) (m map[string]interface{}, err erro
err = errors.New("invalid username or password")
return
}
user.ID = username
user.Username = username
return user, nil
}
func authorize(user rbac.User) (m map[string]interface{}, err error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, UserClaims{
User: &User{
Username: u,
UserId: "admin",
Role: []string{"admin_user"},
Username: user.Username,
UserId: user.ID,
Roles: []string{"admin"},
},
RegisteredClaims: &jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
@ -44,17 +71,49 @@ func Login(username string, password string) (m map[string]interface{}, err erro
tokenString, err := token.SignedString([]byte(Secret))
if err != nil {
return
}
m = util.MapStr{
"access_token": tokenString,
"username": u,
"userid": "admin",
"username": user.Username,
"userid": user.ID,
}
return
}
func Login(username string, password string) (m map[string]interface{}, err error) {
var user rbac.User
if username == "admin" {
user, err = authenticateAdmin(username, password)
if err != nil {
return nil, err
}
return
} else {
user, err = authenticateUser(username, password)
if err != nil {
return nil, err
}
}
m, err = authorize(user)
if err != nil {
return
}
err = orm.Save(GenerateEvent(event.ActivityMetadata{
Category: "platform",
Group: "rbac",
Name: "login",
Type: "create",
Labels: util.MapStr{
"username": username,
"password": password,
},
User: util.MapStr{
"userid": user.ID,
"username": user.Username,
},
}, nil, nil))
return
}
func ValidateLogin(authorizationHeader string) (clams *UserClaims, err error) {
@ -100,14 +159,14 @@ func ValidatePermission(claims *UserClaims, permissions []string) (err error) {
err = errors.New("user id is empty")
return
}
if reqUser.Role == nil {
if reqUser.Roles == nil {
err = errors.New("api permission is empty")
return
}
// 权限校验
userPermissionMap := make(map[string]struct{})
for _, role := range reqUser.Role {
for _, role := range reqUser.Roles {
if _, ok := RolePermission[role]; ok {
for _, v := range RolePermission[role] {
userPermissionMap[v] = struct{}{}

View File

@ -1,13 +1,64 @@
package enum
import (
"infini.sh/console/model/rbac"
"infini.sh/framework/core/orm"
)
const CreateUser = "create_user"
const UpdateUser = "update_user"
const DeleteUser = "delete_user"
const GetUser = "get_user"
const ListUser = "list_user"
const SearchUser = "search_user"
const CreateRole = "create_role"
const UpdateRole = "update_role"
const DeleteRole = "delete_role"
const GetRole = "get_role"
const ListRole = "list_role"
const SearchRole = "search_role"
const ListPermission = "list_permission"
const CreateRule = "create_rule"
const UpdateRule = "update_rule"
const DeleteRule = "delete_rule"
const GetRule = "get_rule"
const SearchRule = "search_rule"
const CreateInstance = "create_instance"
const UpdateInstance = "update_instance"
const DeleteInstance = "delete_instance"
const GetInstance = "get_instance"
const SearchInstance = "search_instance"
const GetInstanceStatus = "get_instance_status"
const ConnectInstance = "connect_instance"
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 BuildRoles = []rbac.Role{
{
ORMObjectBase: orm.ORMObjectBase{
ID: "admin",
},
Name: "admin",
Description: "管理员",
RoleType: "console",
Permission: rbac.ConsolePermission{
ApiPermission: Admin,
},
BuiltIn: true,
},
{
ORMObjectBase: orm.ORMObjectBase{
ID: "admin_user",
},
Name: "admin_user",
Description: "用户模块管理员",
RoleType: "console",
Permission: rbac.ConsolePermission{
ApiPermission: AdminUser,
},
BuiltIn: true,
},
}

View File

@ -6,12 +6,13 @@ import (
"time"
)
func GenerateEvent(metadata event.ActivityMetadata, fields util.MapStr) *event.Activity {
func GenerateEvent(metadata event.ActivityMetadata, fields util.MapStr, changeLog interface{}) *event.Activity {
return &event.Activity{
ID: util.GetUUID(),
Timestamp: time.Now(),
Metadata: metadata,
Fields: fields,
Changelog: changeLog,
}
}

View File

@ -60,7 +60,7 @@ func CreateRole(localUser *User, req dto.CreateRole) (id string, err error) {
"userid": localUser.UserId,
"username": localUser.Username,
},
}, nil))
}, nil, nil))
if err != nil {
log.Error(err)
@ -99,7 +99,7 @@ func DeleteRole(localUser *User, id string) (err error) {
"type": role.RoleType,
"created": role.Created.Format("2006-01-02 15:04:05"),
"updated": role.Updated.Format("2006-01-02 15:04:05"),
}))
}, nil))
return
}
@ -112,6 +112,7 @@ func UpdateRole(localUser *User, id string, req dto.UpdateRole) (err error) {
err = ErrNotFound
return
}
changeLog, _ := util.DiffTwoObject(role, req)
role.Description = req.Description
role.Permission = req.Permission
role.Updated = time.Now()
@ -134,7 +135,7 @@ func UpdateRole(localUser *User, id string, req dto.UpdateRole) (err error) {
"userid": localUser.UserId,
"username": localUser.Username,
},
}, nil))
}, nil, changeLog))
return
}
func GetRole(id string) (role rbac.Role, err error) {

View File

@ -50,7 +50,7 @@ func DeleteUser(localUser *User, id string) (err error) {
"roles": user.Roles,
"created": user.Created,
"updated": user.Updated,
}))
}, nil))
return
}
func CreateUser(localUser *User, req dto.CreateUser) (id string, err error) {
@ -112,7 +112,7 @@ func CreateUser(localUser *User, req dto.CreateUser) (id string, err error) {
"userid": localUser.UserId,
"username": localUser.Username,
},
}, nil))
}, nil, nil))
return
}
func UpdateUser(localUser *User, id string, req dto.UpdateUser) (err error) {
@ -123,6 +123,7 @@ func UpdateUser(localUser *User, id string, req dto.UpdateUser) (err error) {
err = ErrNotFound
return
}
changeLog, _ := util.DiffTwoObject(user, req)
user.Name = req.Name
user.Email = req.Email
user.Phone = req.Phone
@ -149,7 +150,7 @@ func UpdateUser(localUser *User, id string, req dto.UpdateUser) (err error) {
"userid": localUser.UserId,
"username": localUser.Username,
},
}, nil))
}, nil, changeLog))
return
}
func UpdateUserRole(localUser *User, id string, req dto.UpdateUserRole) (err error) {
@ -160,6 +161,7 @@ func UpdateUserRole(localUser *User, id string, req dto.UpdateUserRole) (err err
err = ErrNotFound
return
}
changeLog, _ := util.DiffTwoObject(user, req)
roles := make([]rbac.UserRole, 0)
for _, v := range req.Roles {
roles = append(roles, rbac.UserRole{
@ -187,7 +189,7 @@ func UpdateUserRole(localUser *User, id string, req dto.UpdateUserRole) (err err
"userid": localUser.UserId,
"username": localUser.Username,
},
}, nil))
}, nil, changeLog))
return
}

View File

@ -32,6 +32,7 @@ func PermissionRequired(h httprouter.Handle, permissions ...string) httprouter.H
w = handleError(w, err)
return
}
r = r.WithContext(biz.NewUserContext(r.Context(), claims))
h(w, r, ps)
}
}

View File

@ -14,8 +14,9 @@ type Role struct {
BuiltIn bool `json:"builtin" elastic_mapping:"builtin:{type:boolean}"` //是否内置
}
type ConsolePermission struct {
ID string `json:"id" elastic_mapping:"id:{type:keyword}"`
Name string `json:"name" elastic_mapping:"name:{type:keyword}"`
ApiPermission []string `json:"api_permission"`
//ID string `json:"id" elastic_mapping:"id:{type:keyword}"`
//Name string `json:"name" elastic_mapping:"name:{type:keyword}"`
}
type ElasticsearchPermission struct {
Cluster []string `json:"cluster" elastic_mapping:"cluster:{type:object}"`

View File

@ -88,6 +88,9 @@ 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) {
h.WriteOKJSON(w, util.MapStr{
"status": "ok",
})
}
func (h Account) Profile(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
reqUser, err := biz.FromUserContext(r.Context())

View File

@ -23,15 +23,14 @@ func registerRouter() {
api.HandleAPIMethod(api.GET, "/role/:id", m.PermissionRequired(r.GetRole, enum.GetRole))
api.HandleAPIMethod(api.DELETE, "/role/:id", m.PermissionRequired(r.DeleteRole, enum.DeleteRole))
api.HandleAPIMethod(api.PUT, "/role/:id", m.PermissionRequired(r.UpdateRole, enum.UpdateRole))
api.HandleAPIMethod(api.GET, "/role/_search", m.PermissionRequired(r.SearchRole, enum.ListRole))
api.HandleAPIMethod(api.GET, "/role/_search", m.PermissionRequired(r.SearchRole, enum.SearchRole))
api.HandleAPIMethod(api.POST, "/user", m.PermissionRequired(r.CreateUser, enum.CreateUser))
api.HandleAPIMethod(api.GET, "/user/:id", m.PermissionRequired(r.GetUser, enum.GetUser))
api.HandleAPIMethod(api.GET, "/user/search", m.PermissionRequired(r.SearchUser, enum.ListUser))
api.HandleAPIMethod(api.DELETE, "/user/:id", m.PermissionRequired(r.DeleteUser, enum.DeleteUser))
api.HandleAPIMethod(api.PUT, "/user/:id", m.PermissionRequired(r.UpdateUser, enum.UpdateUser))
api.HandleAPIMethod(api.PUT, "/user/:id/role", m.PermissionRequired(r.UpdateUserRole, enum.UpdateUser))
api.HandleAPIMethod(api.GET, "/user/_search", m.PermissionRequired(r.SearchUser, enum.ListUser))
api.HandleAPIMethod(api.GET, "/user/_search", m.PermissionRequired(r.SearchUser, enum.SearchUser))
}
@ -58,7 +57,8 @@ func loadJsonConfig() {
}
func loadRolePermission() {
biz.RolePermission = make(map[string][]string)
biz.RolePermission["admin_user"] = []string{enum.GetUser}
biz.RolePermission["admin_user"] = enum.AdminUser
biz.RolePermission["admin"] = enum.Admin
}
func init() {
registerRouter()

View File

@ -3,6 +3,7 @@ package rbac
import (
log "github.com/cihub/seelog"
"infini.sh/console/internal/biz"
"infini.sh/console/internal/biz/enum"
"infini.sh/console/internal/dto"
httprouter "infini.sh/framework/core/api/router"
"net/http"
@ -53,8 +54,15 @@ func (h Rbac) SearchRole(w http.ResponseWriter, r *http.Request, ps httprouter.P
h.Error(w, err)
return
}
roles := make([]interface{}, 0)
for _, role := range enum.BuildRoles {
roles = append(roles, role)
}
for _, v := range res.Result {
roles = append(roles, v)
}
h.WriteOKJSON(w, Response{Hit: res.Result, Total: res.Total})
h.WriteOKJSON(w, Response{Hit: roles, Total: res.Total + int64(len(enum.BuildRoles))})
return
}