usermngmt/user/handle.go

631 lines
13 KiB
Go
Raw Permalink Normal View History

2021-11-10 01:44:22 +00:00
package user
import (
"context"
"errors"
"sync"
"github.com/Selly-Modules/logger"
"github.com/Selly-Modules/mongodb"
2021-11-11 03:20:08 +00:00
"github.com/Selly-Modules/usermngmt/cache"
2021-12-07 07:28:52 +00:00
"github.com/Selly-Modules/usermngmt/config"
2021-11-10 01:44:22 +00:00
"github.com/Selly-Modules/usermngmt/internal"
2021-11-10 04:01:39 +00:00
"github.com/Selly-Modules/usermngmt/model"
2021-11-11 03:20:08 +00:00
"github.com/thoas/go-funk"
2021-11-10 01:44:22 +00:00
"go.mongodb.org/mongo-driver/bson"
2021-11-11 08:16:17 +00:00
"go.mongodb.org/mongo-driver/bson/primitive"
2021-11-10 01:44:22 +00:00
)
// Create ...
2021-11-16 04:11:04 +00:00
func Create(payload model.UserCreateOptions) (result string, err error) {
2021-11-10 01:44:22 +00:00
var (
ctx = context.Background()
)
// Validate payload
2021-11-16 04:11:04 +00:00
if err = payload.Validate(); err != nil {
return
2021-11-10 01:44:22 +00:00
}
// Find roleID exists or not
roleID, isValid := mongodb.NewIDFromString(payload.RoleID)
if !isValid {
2021-12-07 07:28:52 +00:00
err = errors.New(internal.ErrorInvalidRole)
2021-11-16 04:11:04 +00:00
return
2021-11-10 01:44:22 +00:00
}
2021-11-24 06:15:40 +00:00
if !isRoleExisted(ctx, roleID) {
2021-12-07 07:28:52 +00:00
err = errors.New(internal.ErrorNotFoundRole)
2021-11-16 04:11:04 +00:00
return
2021-11-10 01:44:22 +00:00
}
// Find phone number,email exists or not
2021-12-07 07:28:52 +00:00
if config.GetInstance().PhoneNumberIsUnique {
if isPhoneNumberExisted(ctx, payload.Phone) {
err = errors.New(internal.ErrorAlreadyExistedPhoneNumber)
return
}
}
if config.GetInstance().EmailIsUnique {
if isEmailExisted(ctx, payload.Email) {
err = errors.New(internal.ErrorAlreadyExistedEmail)
return
}
2021-11-10 01:44:22 +00:00
}
// New user data from payload
2021-11-16 04:11:04 +00:00
doc := newUser(payload)
2021-11-10 01:44:22 +00:00
// Create user
2021-11-10 04:42:23 +00:00
if err = create(ctx, doc); err != nil {
2021-11-16 04:11:04 +00:00
return
2021-11-10 01:44:22 +00:00
}
2021-11-16 04:11:04 +00:00
result = doc.ID.Hex()
return
2021-11-10 01:44:22 +00:00
}
2021-11-10 02:54:49 +00:00
// newUser ...
2021-11-16 04:11:04 +00:00
func newUser(payload model.UserCreateOptions) model.DBUser {
2021-11-10 02:54:49 +00:00
timeNow := internal.Now()
roleID, _ := mongodb.NewIDFromString(payload.RoleID)
return model.DBUser{
2021-11-17 06:32:29 +00:00
ID: mongodb.NewObjectID(),
Name: payload.Name,
SearchString: internal.GetSearchString(payload.Name, payload.Phone, payload.Email),
Phone: payload.Phone,
Email: payload.Email,
HashedPassword: internal.HashPassword(payload.Password),
RequireToChangePassword: payload.RequireToChangePassword,
Status: payload.Status,
RoleID: roleID,
Other: payload.Other,
2021-11-22 10:35:21 +00:00
Avatar: payload.Avatar,
2021-11-17 06:32:29 +00:00
CreatedAt: timeNow,
UpdatedAt: timeNow,
2021-11-16 04:11:04 +00:00
}
2021-11-10 02:54:49 +00:00
}
2021-11-19 02:59:33 +00:00
// FindUser ...
func FindUser(userID string) (r model.User, err error) {
var (
ctx = context.Background()
)
// Find user exists or not
id, isValid := mongodb.NewIDFromString(userID)
if !isValid {
2021-12-07 07:28:52 +00:00
err = errors.New(internal.ErrorInvalidUser)
2021-11-19 02:59:33 +00:00
return
}
user, _ := findByID(ctx, id)
if user.ID.IsZero() {
2021-12-07 07:28:52 +00:00
err = errors.New(internal.ErrorNotFoundUser)
2021-11-19 02:59:33 +00:00
return
}
r = getResponse(ctx, user)
return
}
2021-12-01 07:34:52 +00:00
// FindUserByEmail ...
func FindUserByEmail(email string) (r model.User, err error) {
var (
ctx = context.Background()
)
// Find user exists or not
if email == "" {
2021-12-07 07:28:52 +00:00
err = errors.New(internal.ErrorInvalidEmail)
2021-12-01 07:34:52 +00:00
return
}
user, _ := findOneByCondition(ctx, bson.M{"email": email})
if user.ID.IsZero() {
2021-12-07 07:28:52 +00:00
err = errors.New(internal.ErrorNotFoundUser)
2021-12-01 07:34:52 +00:00
return
}
r = getResponse(ctx, user)
return
}
// GetHashedPassword ...
func GetHashedPassword(userID string) (result string, err error) {
var (
ctx = context.Background()
)
// Find user exists or not
id, isValid := mongodb.NewIDFromString(userID)
if !isValid {
2021-12-07 07:28:52 +00:00
err = errors.New(internal.ErrorInvalidUser)
2021-12-01 07:34:52 +00:00
return
}
user, _ := findByID(ctx, id)
if user.ID.IsZero() {
2021-12-07 07:28:52 +00:00
err = errors.New(internal.ErrorNotFoundUser)
2021-12-01 07:34:52 +00:00
return
}
result = user.HashedPassword
return
}
2021-11-10 01:44:22 +00:00
// All ...
2021-11-10 04:42:23 +00:00
func All(queryParams model.UserAllQuery) (r model.UserAll) {
2021-11-10 01:44:22 +00:00
var (
ctx = context.Background()
wg sync.WaitGroup
cond = bson.M{}
)
2021-11-10 02:54:49 +00:00
query := model.CommonQuery{
2021-11-10 01:44:22 +00:00
Page: queryParams.Page,
Limit: queryParams.Limit,
Keyword: queryParams.Keyword,
RoleID: queryParams.RoleID,
Status: queryParams.Status,
2021-11-16 07:26:50 +00:00
Sort: queryParams.Sort,
2021-11-18 03:30:12 +00:00
Other: queryParams.Other,
2021-11-10 01:44:22 +00:00
}
// Assign condition
query.SetDefaultLimit()
query.AssignKeyword(cond)
query.AssignRoleID(cond)
query.AssignStatus(cond)
2021-11-20 15:00:00 +00:00
query.AssignDeleted(cond)
2021-11-18 03:30:12 +00:00
query.AssignOther(cond)
2021-11-24 06:15:40 +00:00
cond["deleted"] = false
2021-11-10 01:44:22 +00:00
wg.Add(1)
go func() {
defer wg.Done()
2021-11-10 04:42:23 +00:00
docs := findByCondition(ctx, cond, query.GetFindOptionsUsingPage())
2021-11-10 09:06:33 +00:00
res := make([]model.User, 0)
for _, doc := range docs {
res = append(res, getResponse(ctx, doc))
}
r.List = res
2021-11-10 01:44:22 +00:00
}()
wg.Add(1)
go func() {
defer wg.Done()
2021-11-10 04:42:23 +00:00
r.Total = countByCondition(ctx, cond)
2021-11-10 01:44:22 +00:00
}()
wg.Wait()
2021-11-23 04:48:40 +00:00
r.Limit = query.Limit
2021-11-10 01:44:22 +00:00
return
}
2021-11-19 04:00:18 +00:00
// Count ...
func Count(queryParams model.UserCountQuery) int64 {
var (
ctx = context.Background()
cond = bson.M{}
)
query := model.CommonQuery{
RoleID: queryParams.RoleID,
Other: queryParams.Other,
}
// Assign condition
query.AssignRoleID(cond)
query.AssignOther(cond)
return countByCondition(ctx, cond)
}
2021-11-10 09:06:33 +00:00
func getResponse(ctx context.Context, user model.DBUser) model.User {
roleRaw, _ := roleFindByID(ctx, user.RoleID)
return model.User{
ID: user.ID.Hex(),
Name: user.Name,
Phone: user.Phone,
Email: user.Email,
Status: user.Status,
Role: model.RoleShort{
ID: roleRaw.ID.Hex(),
Name: roleRaw.Name,
2021-11-19 02:59:33 +00:00
Level: roleRaw.Level,
2021-11-10 09:06:33 +00:00
IsAdmin: roleRaw.IsAdmin,
},
2021-11-29 03:01:04 +00:00
RequireToChangePassword: user.RequireToChangePassword,
Avatar: user.Avatar,
Other: user.Other,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
2021-11-10 01:44:22 +00:00
}
}
// UpdateByUserID ...
2021-11-10 04:42:23 +00:00
func UpdateByUserID(userID string, payload model.UserUpdateOptions) error {
2021-11-10 01:44:22 +00:00
var (
ctx = context.Background()
)
// Validate payload
if err := payload.Validate(); err != nil {
return err
}
// Find roleID exists or not
roleID, isValid := mongodb.NewIDFromString(payload.RoleID)
if !isValid {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorInvalidRole)
2021-11-10 01:44:22 +00:00
}
2021-11-24 06:15:40 +00:00
if !isRoleExisted(ctx, roleID) {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorNotFoundRole)
2021-11-10 01:44:22 +00:00
}
2021-11-16 04:11:04 +00:00
// Find user exists or not
id, isValid := mongodb.NewIDFromString(userID)
if !isValid {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorInvalidUser)
2021-11-16 04:11:04 +00:00
}
user, _ := findByID(ctx, id)
if user.ID.IsZero() {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorNotFoundUser)
2021-11-16 04:11:04 +00:00
}
2021-11-10 01:44:22 +00:00
// Find phone number,email exists or not
2021-12-07 07:28:52 +00:00
if config.GetInstance().PhoneNumberIsUnique {
if user.Phone != payload.Phone {
if isPhoneNumberExisted(ctx, payload.Phone) {
return errors.New(internal.ErrorAlreadyExistedPhoneNumber)
}
2021-11-16 04:11:04 +00:00
}
}
2021-12-07 07:28:52 +00:00
if config.GetInstance().EmailIsUnique {
if user.Email != payload.Email {
if isEmailExisted(ctx, payload.Email) {
return errors.New(internal.ErrorAlreadyExistedEmail)
}
2021-11-16 04:11:04 +00:00
}
2021-11-10 01:44:22 +00:00
}
// Setup condition
cond := bson.M{
"_id": id,
}
2021-11-19 04:17:58 +00:00
// Setup Set operator
setOperator := bson.M{
"name": payload.Name,
"searchString": internal.GetSearchString(payload.Name, payload.Phone, payload.Email),
"phone": payload.Phone,
"email": payload.Email,
"roleId": roleID,
"updatedAt": internal.Now(),
}
if len(payload.Other) > 0 {
for key, value := range payload.Other {
setOperator["other."+key] = value
}
2021-11-10 01:44:22 +00:00
}
// Update
2021-11-19 04:17:58 +00:00
if err := updateOneByCondition(ctx, cond, bson.M{
"$set": setOperator,
}); err != nil {
2021-11-10 01:44:22 +00:00
return err
}
return nil
}
// ChangeUserPassword ...
2021-11-10 04:42:23 +00:00
func ChangeUserPassword(userID string, opt model.ChangePasswordOptions) error {
2021-11-10 01:44:22 +00:00
var (
ctx = context.Background()
)
// Validate payload
err := opt.Validate()
if err != nil {
return err
}
// Validate userID
if _, isValid := mongodb.NewIDFromString(userID); !isValid {
logger.Error("usermngmt - ChangePassword: invalid userID data", logger.LogData{
"payload": opt,
2021-11-10 02:54:49 +00:00
"userID": userID,
2021-11-10 01:44:22 +00:00
})
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorInvalidUser)
2021-11-10 01:44:22 +00:00
}
// Find user
id, _ := mongodb.NewIDFromString(userID)
2021-11-10 04:42:23 +00:00
user, _ := findByID(ctx, id)
2021-11-10 01:44:22 +00:00
if user.ID.IsZero() {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorNotFoundUser)
2021-11-10 01:44:22 +00:00
}
// Check old password
if isValid := internal.CheckPasswordHash(opt.OldPassword, user.HashedPassword); !isValid {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorIncorrectPassword)
2021-11-10 01:44:22 +00:00
}
// Update password
2021-11-10 04:42:23 +00:00
if err = updateOneByCondition(ctx, bson.M{"_id": user.ID}, bson.M{
2021-11-10 01:44:22 +00:00
"$set": bson.M{
2021-11-23 10:47:23 +00:00
"hashedPassword": internal.HashPassword(opt.NewPassword),
"requireToChangePassword": false,
"updatedAt": internal.Now(),
2021-11-10 01:44:22 +00:00
},
}); err != nil {
return err
}
return nil
}
2021-12-01 10:51:44 +00:00
// ResetUserPassword ...
func ResetUserPassword(userID string, password string) error {
var (
ctx = context.Background()
)
// Validate Password
if password == "" {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorInvalidPassword)
2021-12-01 10:51:44 +00:00
}
// Validate userID
if _, isValid := mongodb.NewIDFromString(userID); !isValid {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorInvalidUser)
2021-12-01 10:51:44 +00:00
}
// Find user
id, _ := mongodb.NewIDFromString(userID)
user, _ := findByID(ctx, id)
if user.ID.IsZero() {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorNotFoundUser)
2021-12-01 10:51:44 +00:00
}
// Update password
if err := updateOneByCondition(ctx, bson.M{"_id": user.ID}, bson.M{
"$set": bson.M{
"hashedPassword": internal.HashPassword(password),
"requireToChangePassword": false,
"updatedAt": internal.Now(),
},
}); err != nil {
return err
}
return nil
}
2021-11-10 01:44:22 +00:00
// ChangeUserStatus ...
2021-11-10 04:42:23 +00:00
func ChangeUserStatus(userID, newStatus string) error {
2021-11-10 01:44:22 +00:00
var (
ctx = context.Background()
)
// Validate userID
id, isValid := mongodb.NewIDFromString(userID)
if !isValid {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorInvalidUser)
2021-11-10 01:44:22 +00:00
}
2021-11-24 06:15:40 +00:00
if user, _ := findByID(ctx, id); user.ID.IsZero() {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorNotFoundUser)
2021-11-24 06:15:40 +00:00
}
2021-11-10 01:44:22 +00:00
// Update status
2021-11-10 04:42:23 +00:00
if err := updateOneByCondition(ctx, bson.M{"_id": id}, bson.M{
2021-11-10 01:44:22 +00:00
"$set": bson.M{
"status": newStatus,
"updatedAt": internal.Now(),
},
}); err != nil {
return err
}
return nil
}
2021-11-10 07:50:43 +00:00
// ChangeAllUsersStatus ...
func ChangeAllUsersStatus(roleID, status string) error {
var (
ctx = context.Background()
)
// Validate roleID
id, isValid := mongodb.NewIDFromString(roleID)
if !isValid {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorInvalidRole)
2021-11-10 07:50:43 +00:00
}
2021-11-24 06:15:40 +00:00
if !isRoleExisted(ctx, id) {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorNotFoundRole)
2021-11-24 06:15:40 +00:00
}
2021-11-10 07:50:43 +00:00
// Setup condition
cond := bson.M{
"roleId": id,
}
// Setup update data
updateData := bson.M{
"$set": bson.M{
"status": status,
"updatedAt": internal.Now(),
},
}
// Update
if err := updateManyByCondition(ctx, cond, updateData); err != nil {
return err
}
return nil
}
2021-11-10 09:06:33 +00:00
// LoginWithEmailAndPassword ...
func LoginWithEmailAndPassword(email, password string) (result model.User, err error) {
var (
ctx = context.Background()
)
// Validate email, password
2021-12-07 07:28:52 +00:00
if email == "" {
err = errors.New(internal.ErrorInvalidEmail)
return
}
if password == "" {
err = errors.New(internal.ErrorInvalidPassword)
2021-11-10 09:06:33 +00:00
return
}
// Find user
user, _ := findOneByCondition(ctx, bson.M{
2021-11-24 06:15:40 +00:00
"email": email,
"deleted": false,
2021-11-10 09:06:33 +00:00
})
if user.ID.IsZero() {
2021-12-07 07:28:52 +00:00
err = errors.New(internal.ErrorNotFoundUser)
2021-11-10 09:06:33 +00:00
return
}
// Check Password
if !internal.CheckPasswordHash(password, user.HashedPassword) {
2021-12-07 07:28:52 +00:00
err = errors.New(internal.ErrorIncorrectPassword)
2021-11-10 09:06:33 +00:00
return
}
result = getResponse(ctx, user)
return
}
2021-11-10 09:53:31 +00:00
2021-11-10 10:02:16 +00:00
// HasPermission ...
func HasPermission(userID, permission string) (result bool) {
2021-11-10 09:53:31 +00:00
var (
ctx = context.Background()
)
// Validate userID, permission
if userID == "" || permission == "" {
2021-11-11 03:20:08 +00:00
logger.Error("usermngmt - HasPermission: email or password cannot be empty", logger.LogData{
2021-11-10 09:53:31 +00:00
"userID": userID,
"permission": permission,
})
return
}
id, isValid := mongodb.NewIDFromString(userID)
if !isValid {
2021-11-11 03:20:08 +00:00
logger.Error("usermngmt - HasPermission: invalid user id", logger.LogData{
2021-11-10 09:53:31 +00:00
"userID": userID,
"permission": permission,
})
return
}
// Find user
user, _ := findByID(ctx, id)
if user.ID.IsZero() {
2021-11-11 03:20:08 +00:00
logger.Error("usermngmt - HasPermission: user not found", logger.LogData{
2021-11-10 09:53:31 +00:00
"userID": userID,
"permission": permission,
})
return
}
2021-11-11 08:16:17 +00:00
return checkUserHasPermissionFromCache(user.RoleID, permission)
}
func checkUserHasPermissionFromCache(roleID primitive.ObjectID, permission string) bool {
cachedRole := cache.GetCachedRole(roleID.Hex())
2021-11-10 09:53:31 +00:00
2021-11-11 08:16:17 +00:00
// Check permission
if cachedRole.IsAdmin {
return true
}
if _, isValid := funk.FindString(cachedRole.Permissions, func(s string) bool {
return s == permission
2021-11-11 03:20:08 +00:00
}); isValid {
2021-11-11 08:16:17 +00:00
return true
2021-11-10 09:53:31 +00:00
}
2021-11-11 08:16:17 +00:00
return false
2021-11-10 09:53:31 +00:00
}
2021-11-17 06:32:29 +00:00
// UpdateAvatar ...
2021-11-22 10:35:21 +00:00
func UpdateAvatar(userID string, avatar interface{}) error {
2021-11-17 06:32:29 +00:00
var (
ctx = context.Background()
)
2021-11-22 10:35:21 +00:00
if avatar == nil {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorInvalidAvatar)
2021-11-17 06:32:29 +00:00
}
// Find user exists or not
id, isValid := mongodb.NewIDFromString(userID)
if !isValid {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorInvalidUser)
2021-11-17 06:32:29 +00:00
}
user, _ := findByID(ctx, id)
if user.ID.IsZero() {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorNotFoundUser)
2021-11-17 06:32:29 +00:00
}
// Setup condition
cond := bson.M{
"_id": id,
}
// Setup update data
updateData := bson.M{
"$set": bson.M{
"avatar": avatar,
"updatedAt": internal.Now(),
},
}
// Update
if err := updateOneByCondition(ctx, cond, updateData); err != nil {
return err
}
return nil
}
// Delete ...
func Delete(userID string) error {
var (
ctx = context.Background()
)
// Find user exists or not
id, isValid := mongodb.NewIDFromString(userID)
if !isValid {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorInvalidUser)
2021-11-17 06:32:29 +00:00
}
user, _ := findByID(ctx, id)
if user.ID.IsZero() {
2021-12-07 07:28:52 +00:00
return errors.New(internal.ErrorNotFoundUser)
2021-11-17 06:32:29 +00:00
}
// Setup condition
cond := bson.M{
"_id": id,
}
// Setup update data
updateData := bson.M{
"$set": bson.M{
"deleted": true,
"updatedAt": internal.Now(),
},
}
// Update
if err := updateOneByCondition(ctx, cond, updateData); err != nil {
return err
}
return nil
}