feat: Users API, ownership transfer and removal of the legacy role string
This commit is contained in:
1 parent
094df63a7f
commit
80f7037d3a
124 files changed
+4644
-824
No files matched your search
@@ -1,12 +1,14 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"RenewCMS/internal/domain/role"
|
||||
domainUser "RenewCMS/internal/domain/user"
|
||||
"RenewCMS/internal/infrastructure/persistence/mappers"
|
||||
entity "RenewCMS/internal/infrastructure/persistence/models"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type UserRepository struct {
|
||||
@@ -34,7 +36,7 @@ func (u *UserRepository) Create(user domainUser.User) (domainUser.User, error) {
|
||||
PasswordResetCode: mappers.DomainToNullableString(user.PasswordResetCode),
|
||||
Email: user.Email,
|
||||
IsVerified: user.IsVerified,
|
||||
Role: string(user.Role),
|
||||
RoleID: mappers.DomainToNullableID(user.RoleID),
|
||||
VerificationCode: mappers.DomainToNullableString(user.VerificationCode),
|
||||
VerificationExpiration: user.VerificationExpiration,
|
||||
}
|
||||
@@ -145,14 +147,17 @@ func (u *UserRepository) UpdatePasswordResetCode(userId uint32, code string, exp
|
||||
|
||||
func (u *UserRepository) ClearVerificationCode(userId uint32) (domainUser.User, error) {
|
||||
var localUser entity.User
|
||||
err := u.db.Model(&entity.User{}).First(&localUser, userId).Error
|
||||
if err != nil {
|
||||
return domainUser.User{}, mapNotFoundErr(err, domainUser.ErrUserNotFound)
|
||||
}
|
||||
|
||||
localUser.IsVerified = true
|
||||
localUser.VerificationCode = mappers.DomainToNullableString("")
|
||||
err = u.db.Save(&localUser).Error
|
||||
err := u.db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Model(&entity.User{}).First(&localUser, userId).Error; err != nil {
|
||||
return mapNotFoundErr(err, domainUser.ErrUserNotFound)
|
||||
}
|
||||
if err := refuseSecondVerifiedOwner(tx, localUser); err != nil {
|
||||
return err
|
||||
}
|
||||
localUser.IsVerified = true
|
||||
localUser.VerificationCode = mappers.DomainToNullableString("")
|
||||
return tx.Save(&localUser).Error
|
||||
})
|
||||
if err != nil {
|
||||
return domainUser.User{}, err
|
||||
}
|
||||
@@ -160,6 +165,26 @@ func (u *UserRepository) ClearVerificationCode(userId uint32) (domainUser.User,
|
||||
return mappers.UserToDomain(localUser), nil
|
||||
}
|
||||
|
||||
func refuseSecondVerifiedOwner(tx *gorm.DB, candidate entity.User) error {
|
||||
if candidate.RoleID == nil {
|
||||
return nil
|
||||
}
|
||||
var roles []entity.Role
|
||||
err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ? AND built_in = ? AND name = ?", *candidate.RoleID, true, role.OwnerName).Find(&roles).Error
|
||||
if err != nil || len(roles) == 0 {
|
||||
return err
|
||||
}
|
||||
var others int64
|
||||
err = tx.Model(&entity.User{}).Where("role_id = ? AND is_verified = ? AND id <> ?", *candidate.RoleID, true, candidate.ID).Count(&others).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if others > 0 {
|
||||
return domainUser.ErrInvalidVerificationCode
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UserRepository) ResetPassword(userId uint32, hashedPassword string) (domainUser.User, error) {
|
||||
var localUser entity.User
|
||||
err := u.db.Model(&entity.User{}).First(&localUser, userId).Error
|
||||
@@ -195,4 +220,68 @@ func (u *UserRepository) UpdateDisplayName(userId uint32, displayName string) (d
|
||||
return mappers.UserToDomain(localUser), nil
|
||||
}
|
||||
|
||||
func (u *UserRepository) UpdateRole(id uint32, from uint32, to uint32) (domainUser.User, error) {
|
||||
err := u.db.Transaction(func(tx *gorm.DB) error {
|
||||
query := tx.Model(&entity.User{}).Where("id = ? AND EXISTS (SELECT 1 FROM roles WHERE id = ?)", id, to)
|
||||
if from == 0 {
|
||||
query = query.Where("(role_id IS NULL OR role_id = 0)")
|
||||
} else {
|
||||
query = query.Where("role_id = ?", from)
|
||||
}
|
||||
result := query.Update("role_id", to)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
if result.RowsAffected > 0 {
|
||||
return nil
|
||||
}
|
||||
return u.explainRoleRefusal(tx, id, to)
|
||||
})
|
||||
if err != nil {
|
||||
return domainUser.User{}, err
|
||||
}
|
||||
return u.Get(id)
|
||||
}
|
||||
|
||||
func (u *UserRepository) explainRoleRefusal(tx *gorm.DB, id uint32, to uint32) error {
|
||||
var users int64
|
||||
if err := tx.Model(&entity.User{}).Where("id = ?", id).Count(&users).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if users == 0 {
|
||||
return domainUser.ErrUserNotFound
|
||||
}
|
||||
var roles int64
|
||||
if err := tx.Model(&entity.Role{}).Where("id = ?", to).Count(&roles).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if roles == 0 {
|
||||
return role.ErrRoleNotFound
|
||||
}
|
||||
return domainUser.ErrRoleChanged
|
||||
}
|
||||
|
||||
func (u *UserRepository) TransferOwnership(from uint32, to uint32, ownerRoleID uint32, formerRoleID uint32) error {
|
||||
if from == to {
|
||||
return domainUser.ErrInvalidTransfer
|
||||
}
|
||||
return u.db.Transaction(func(tx *gorm.DB) error {
|
||||
demoted := tx.Model(&entity.User{}).Where("id = ? AND role_id = ?", from, ownerRoleID).Update("role_id", formerRoleID)
|
||||
if demoted.Error != nil {
|
||||
return demoted.Error
|
||||
}
|
||||
if demoted.RowsAffected == 0 {
|
||||
return domainUser.ErrRoleChanged
|
||||
}
|
||||
promoted := tx.Model(&entity.User{}).Where("id = ? AND is_verified = ?", to, true).Update("role_id", ownerRoleID)
|
||||
if promoted.Error != nil {
|
||||
return promoted.Error
|
||||
}
|
||||
if promoted.RowsAffected == 0 {
|
||||
return domainUser.ErrInvalidTransfer
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ domainUser.Repository = &UserRepository{}
|
||||
Reference in new issue
Block a user