mirror of
https://github.com/crawlab-team/crawlab.git
synced 2026-01-22 17:31:03 +01:00
feat: updated permission management
This commit is contained in:
@@ -27,14 +27,16 @@ func GetUserById(c *gin.Context) {
|
||||
}
|
||||
|
||||
// get role
|
||||
if !user.RoleId.IsZero() {
|
||||
role, err := service.NewModelService[models.Role]().GetById(user.RoleId)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
if utils.IsPro() {
|
||||
if !user.RoleId.IsZero() {
|
||||
role, err := service.NewModelService[models.Role]().GetById(user.RoleId)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
user.Role = role.Name
|
||||
user.IsAdmin = role.Admin
|
||||
}
|
||||
user.Role = role.Name
|
||||
user.IsAdmin = role.Admin
|
||||
}
|
||||
|
||||
HandleSuccessWithData(c, user)
|
||||
@@ -62,31 +64,33 @@ func GetUserList(c *gin.Context) {
|
||||
}
|
||||
|
||||
// get roles
|
||||
var roleIds []primitive.ObjectID
|
||||
for _, user := range users {
|
||||
if !user.RoleId.IsZero() {
|
||||
roleIds = append(roleIds, user.RoleId)
|
||||
}
|
||||
}
|
||||
if len(roleIds) > 0 {
|
||||
roles, err := service.NewModelService[models.Role]().GetMany(bson.M{
|
||||
"_id": bson.M{"$in": roleIds},
|
||||
}, nil)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
rolesMap := make(map[primitive.ObjectID]models.Role)
|
||||
for _, role := range roles {
|
||||
rolesMap[role.Id] = role
|
||||
}
|
||||
for i, user := range users {
|
||||
if user.RoleId.IsZero() {
|
||||
continue
|
||||
if utils.IsPro() {
|
||||
var roleIds []primitive.ObjectID
|
||||
for _, user := range users {
|
||||
if !user.RoleId.IsZero() {
|
||||
roleIds = append(roleIds, user.RoleId)
|
||||
}
|
||||
if role, ok := rolesMap[user.RoleId]; ok {
|
||||
users[i].Role = role.Name
|
||||
users[i].IsAdmin = role.Admin
|
||||
}
|
||||
if len(roleIds) > 0 {
|
||||
roles, err := service.NewModelService[models.Role]().GetMany(bson.M{
|
||||
"_id": bson.M{"$in": roleIds},
|
||||
}, nil)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
rolesMap := make(map[primitive.ObjectID]models.Role)
|
||||
for _, role := range roles {
|
||||
rolesMap[role.Id] = role
|
||||
}
|
||||
for i, user := range users {
|
||||
if user.RoleId.IsZero() {
|
||||
continue
|
||||
}
|
||||
if role, ok := rolesMap[user.RoleId]; ok {
|
||||
users[i].Role = role.Name
|
||||
users[i].IsAdmin = role.Admin
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,7 +148,6 @@ func PostUser(c *gin.Context) {
|
||||
}
|
||||
|
||||
HandleSuccessWithData(c, result)
|
||||
|
||||
}
|
||||
|
||||
func PostUserChangePassword(c *gin.Context) {
|
||||
@@ -163,20 +166,24 @@ func PostUserChangePassword(c *gin.Context) {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
if len(payload.Password) < 5 {
|
||||
HandleErrorBadRequest(c, errors.New("password must be at least 5 characters"))
|
||||
return
|
||||
}
|
||||
|
||||
// get user
|
||||
u := GetUserFromContext(c)
|
||||
modelSvc := service.NewModelService[models.User]()
|
||||
|
||||
// update password
|
||||
user, err := modelSvc.GetById(id)
|
||||
userDb, err := modelSvc.GetById(id)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
user.SetUpdated(u.Id)
|
||||
user.Password = utils.EncryptMd5(payload.Password)
|
||||
if err := modelSvc.ReplaceById(user.Id, *user); err != nil {
|
||||
userDb.SetUpdated(u.Id)
|
||||
userDb.Password = utils.EncryptMd5(payload.Password)
|
||||
if err := modelSvc.ReplaceById(userDb.Id, *userDb); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
@@ -192,6 +199,20 @@ func GetUserMe(c *gin.Context) {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if utils.IsPro() {
|
||||
if !_u.RoleId.IsZero() {
|
||||
r, err := service.NewModelService[models.Role]().GetById(_u.RoleId)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
_u.Role = r.Name
|
||||
_u.IsAdmin = r.Admin
|
||||
_u.Routes = r.Routes
|
||||
}
|
||||
}
|
||||
|
||||
HandleSuccessWithData(c, _u)
|
||||
}
|
||||
|
||||
@@ -206,6 +227,7 @@ func PutUserById(c *gin.Context) {
|
||||
// get user
|
||||
u := GetUserFromContext(c)
|
||||
|
||||
// model service
|
||||
modelSvc := service.NewModelService[models.User]()
|
||||
|
||||
// update user
|
||||
@@ -214,7 +236,17 @@ func PutUserById(c *gin.Context) {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// if root admin, disallow changing username and role
|
||||
if userDb.RootAdmin {
|
||||
user.Username = userDb.Username
|
||||
user.RoleId = userDb.RoleId
|
||||
}
|
||||
|
||||
// disallow changing password
|
||||
user.Password = userDb.Password
|
||||
|
||||
// update user
|
||||
user.SetUpdated(u.Id)
|
||||
if user.Id.IsZero() {
|
||||
user.Id = u.Id
|
||||
|
||||
@@ -62,8 +62,9 @@ func InitIndexes() {
|
||||
|
||||
// users
|
||||
RecreateIndexes(mongo.GetMongoCol(service.GetCollectionNameByInstance(models.User{})), []mongo2.IndexModel{
|
||||
{Keys: bson.M{"username": 1}},
|
||||
{Keys: bson.M{"username": 1}, Options: (&options.IndexOptions{}).SetUnique(true)},
|
||||
{Keys: bson.M{"role": 1}},
|
||||
{Keys: bson.M{"role_id": 1}},
|
||||
{Keys: bson.M{"email": 1}},
|
||||
})
|
||||
|
||||
@@ -76,9 +77,4 @@ func InitIndexes() {
|
||||
RecreateIndexes(mongo.GetMongoCol(service.GetCollectionNameByInstance(models.Token{})), []mongo2.IndexModel{
|
||||
{Keys: bson.M{"name": 1}},
|
||||
})
|
||||
|
||||
// data collections
|
||||
RecreateIndexes(mongo.GetMongoCol(service.GetCollectionNameByInstance(models.DataCollection{})), []mongo2.IndexModel{
|
||||
{Keys: bson.M{"name": 1}},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@ type User struct {
|
||||
Password string `json:"-" bson:"password"`
|
||||
Role string `json:"role" bson:"role"`
|
||||
RoleId primitive.ObjectID `json:"role_id" bson:"role_id"`
|
||||
FirstName string `json:"first_name" bson:"first_name"`
|
||||
LastName string `json:"last_name" bson:"last_name"`
|
||||
Email string `json:"email" bson:"email"`
|
||||
IsAdmin bool `json:"admin" bson:"-"`
|
||||
RootAdmin bool `json:"root_admin,omitempty" bson:"root_admin"`
|
||||
IsAdmin bool `json:"admin,omitempty" bson:"-"`
|
||||
Routes []string `json:"routes,omitempty" bson:"-"`
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
errors2 "errors"
|
||||
"github.com/apex/log"
|
||||
"github.com/crawlab-team/crawlab/core/constants"
|
||||
"github.com/crawlab-team/crawlab/core/errors"
|
||||
"github.com/crawlab-team/crawlab/core/models/models"
|
||||
"github.com/crawlab-team/crawlab/core/models/service"
|
||||
"github.com/crawlab-team/crawlab/core/utils"
|
||||
mongo2 "github.com/crawlab-team/crawlab/db/mongo"
|
||||
"github.com/crawlab-team/crawlab/trace"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
@@ -20,41 +20,74 @@ import (
|
||||
type Service struct {
|
||||
jwtSecret string
|
||||
jwtSigningMethod jwt.SigningMethod
|
||||
modelSvc *service.ModelService[models.User]
|
||||
}
|
||||
|
||||
func (svc *Service) Init() (err error) {
|
||||
_, err = svc.modelSvc.GetOne(bson.M{"username": constants.DefaultAdminUsername}, nil)
|
||||
if err == nil {
|
||||
return nil
|
||||
if utils.IsPro() {
|
||||
return svc.initPro()
|
||||
}
|
||||
if err.Error() != mongo.ErrNoDocuments.Error() {
|
||||
return err
|
||||
}
|
||||
return svc.Create(
|
||||
constants.DefaultAdminUsername,
|
||||
constants.DefaultAdminPassword,
|
||||
constants.RoleAdmin,
|
||||
"",
|
||||
primitive.NilObjectID,
|
||||
)
|
||||
return svc.init()
|
||||
}
|
||||
|
||||
func (svc *Service) SetJwtSecret(secret string) {
|
||||
svc.jwtSecret = secret
|
||||
func (svc *Service) init() (err error) {
|
||||
_, err = service.NewModelService[models.User]().GetOne(bson.M{"username": constants.DefaultAdminUsername}, nil)
|
||||
if err != nil {
|
||||
if !errors2.Is(err, mongo.ErrNoDocuments) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// exists
|
||||
return
|
||||
}
|
||||
|
||||
// add user
|
||||
u := models.User{
|
||||
Username: constants.DefaultAdminUsername,
|
||||
Password: utils.EncryptMd5(constants.DefaultAdminPassword),
|
||||
Role: constants.RoleAdmin,
|
||||
RootAdmin: true,
|
||||
}
|
||||
u.SetCreatedAt(time.Now())
|
||||
u.SetUpdatedAt(time.Now())
|
||||
_, err = service.NewModelService[models.User]().InsertOne(u)
|
||||
return err
|
||||
}
|
||||
|
||||
func (svc *Service) SetJwtSigningMethod(method jwt.SigningMethod) {
|
||||
svc.jwtSigningMethod = method
|
||||
func (svc *Service) initPro() (err error) {
|
||||
_, err = service.NewModelService[models.User]().GetOne(bson.M{
|
||||
"$or": []bson.M{
|
||||
{"username": constants.DefaultAdminUsername},
|
||||
{"root_admin": true},
|
||||
},
|
||||
}, nil)
|
||||
if err != nil {
|
||||
if !errors2.Is(err, mongo.ErrNoDocuments) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// exists
|
||||
return
|
||||
}
|
||||
|
||||
// add user
|
||||
u := models.User{
|
||||
Username: constants.DefaultAdminUsername,
|
||||
Password: utils.EncryptMd5(constants.DefaultAdminPassword),
|
||||
RootAdmin: true,
|
||||
}
|
||||
u.SetCreatedAt(time.Now())
|
||||
u.SetUpdatedAt(time.Now())
|
||||
_, err = service.NewModelService[models.User]().InsertOne(u)
|
||||
return err
|
||||
}
|
||||
|
||||
func (svc *Service) Create(username, password, role, email string, by primitive.ObjectID) (err error) {
|
||||
// validate options
|
||||
if username == "" || password == "" {
|
||||
return trace.TraceError(errors.ErrorUserMissingRequiredFields)
|
||||
return errors.ErrorUserMissingRequiredFields
|
||||
}
|
||||
if len(password) < 5 {
|
||||
return trace.TraceError(errors.ErrorUserInvalidPassword)
|
||||
return errors.ErrorUserInvalidPassword
|
||||
}
|
||||
|
||||
// normalize options
|
||||
@@ -63,29 +96,48 @@ func (svc *Service) Create(username, password, role, email string, by primitive.
|
||||
}
|
||||
|
||||
// check if user exists
|
||||
if u, err := svc.modelSvc.GetOne(bson.M{"username": username}, nil); err == nil && u != nil && !u.Id.IsZero() {
|
||||
return trace.TraceError(errors.ErrorUserAlreadyExists)
|
||||
if u, err := service.NewModelService[models.User]().GetOne(bson.M{"username": username}, nil); err == nil && u != nil && !u.Id.IsZero() {
|
||||
return errors.ErrorUserAlreadyExists
|
||||
}
|
||||
|
||||
// transaction
|
||||
return mongo2.RunTransaction(func(ctx mongo.SessionContext) error {
|
||||
// add user
|
||||
u := models.User{
|
||||
Username: username,
|
||||
Role: role,
|
||||
Password: utils.EncryptMd5(password),
|
||||
Email: email,
|
||||
}
|
||||
u.SetCreated(by)
|
||||
u.SetUpdated(by)
|
||||
_, err = svc.modelSvc.InsertOne(u)
|
||||
// add user
|
||||
u := models.User{
|
||||
Username: username,
|
||||
Role: role,
|
||||
Password: utils.EncryptMd5(password),
|
||||
Email: email,
|
||||
}
|
||||
u.SetCreated(by)
|
||||
u.SetUpdated(by)
|
||||
_, err = service.NewModelService[models.User]().InsertOne(u)
|
||||
|
||||
return err
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (svc *Service) CreateUser(u *models.User, by primitive.ObjectID) (err error) {
|
||||
// validate options
|
||||
if u.Username == "" || u.Password == "" {
|
||||
return errors.ErrorUserMissingRequiredFields
|
||||
}
|
||||
if len(u.Password) < 5 {
|
||||
return errors.ErrorUserInvalidPassword
|
||||
}
|
||||
|
||||
// check if user exists
|
||||
if u, err := service.NewModelService[models.User]().GetOne(bson.M{"username": u.Username}, nil); err == nil && u != nil && !u.Id.IsZero() {
|
||||
return errors.ErrorUserAlreadyExists
|
||||
}
|
||||
|
||||
// add user
|
||||
u.SetCreated(by)
|
||||
u.SetUpdated(by)
|
||||
_, err = service.NewModelService[models.User]().InsertOne(*u)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (svc *Service) Login(username, password string) (token string, u *models.User, err error) {
|
||||
u, err = svc.modelSvc.GetOne(bson.M{"username": username}, nil)
|
||||
u, err = service.NewModelService[models.User]().GetOne(bson.M{"username": username}, nil)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
@@ -104,13 +156,13 @@ func (svc *Service) CheckToken(tokenStr string) (u *models.User, err error) {
|
||||
}
|
||||
|
||||
func (svc *Service) ChangePassword(id primitive.ObjectID, password string, by primitive.ObjectID) (err error) {
|
||||
u, err := svc.modelSvc.GetById(id)
|
||||
u, err := service.NewModelService[models.User]().GetById(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.Password = utils.EncryptMd5(password)
|
||||
u.SetCreatedBy(by)
|
||||
return svc.modelSvc.ReplaceById(id, *u)
|
||||
return service.NewModelService[models.User]().ReplaceById(id, *u)
|
||||
}
|
||||
|
||||
func (svc *Service) MakeToken(user *models.User) (tokenStr string, err error) {
|
||||
@@ -148,7 +200,7 @@ func (svc *Service) checkToken(tokenStr string) (user *models.User, err error) {
|
||||
return user, err
|
||||
}
|
||||
username := claim["username"].(string)
|
||||
user, err = svc.modelSvc.GetById(id)
|
||||
user, err = service.NewModelService[models.User]().GetById(id)
|
||||
if err != nil {
|
||||
err = errors.ErrorUserNotExists
|
||||
return
|
||||
@@ -171,7 +223,6 @@ func (svc *Service) getSecretFunc() jwt.Keyfunc {
|
||||
func newUserService() (svc *Service, err error) {
|
||||
// service
|
||||
svc = &Service{
|
||||
modelSvc: service.NewModelService[models.User](),
|
||||
jwtSecret: "crawlab",
|
||||
jwtSigningMethod: jwt.SigningMethodHS256,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user