refac: reamaining auth surface rewrite, T0

This commit is contained in:
fsylvain committed 2026-09-24 05:11:49 +02:00
1 parent 6d510d2805
commit aa865af2e9
50 files changed
+3723 -149

No files matched your search

@@ -21,29 +21,31 @@ func DomainToNullableString(value string) *string {
func UserToDomain(m models.User) user.User {
return user.User{
ID: m.ID,
Username: m.Username,
Password: m.Password,
PasswordResetCode: nullableStringToDomain(m.PasswordResetCode),
Email: m.Email,
IsVerified: m.IsVerified,
VerificationCode: nullableStringToDomain(m.VerificationCode),
VerificationExpiration: m.VerificationExpiration,
CreatedAt: m.CreatedAt,
UpdatedAt: m.UpdatedAt,
ID: m.ID,
Username: m.Username,
Password: m.Password,
PasswordResetCode: nullableStringToDomain(m.PasswordResetCode),
Email: m.Email,
IsVerified: m.IsVerified,
VerificationCode: nullableStringToDomain(m.VerificationCode),
VerificationExpiration: m.VerificationExpiration,
PasswordResetExpiration: m.PasswordResetExpiration,
CreatedAt: m.CreatedAt,
UpdatedAt: m.UpdatedAt,
}
}
func UserToModel(a user.User) models.User {
return models.User{
Username: a.Username,
Password: a.Password,
PasswordResetCode: DomainToNullableString(a.PasswordResetCode),
Email: a.Email,
IsVerified: a.IsVerified,
VerificationCode: DomainToNullableString(a.VerificationCode),
VerificationExpiration: a.VerificationExpiration,
CreatedAt: a.CreatedAt,
UpdatedAt: a.UpdatedAt,
Username: a.Username,
Password: a.Password,
PasswordResetCode: DomainToNullableString(a.PasswordResetCode),
Email: a.Email,
IsVerified: a.IsVerified,
VerificationCode: DomainToNullableString(a.VerificationCode),
VerificationExpiration: a.VerificationExpiration,
PasswordResetExpiration: a.PasswordResetExpiration,
CreatedAt: a.CreatedAt,
UpdatedAt: a.UpdatedAt,
}
}
@@ -5,14 +5,15 @@ import (
)
type User struct {
ID uint32 `gorm:"primaryKey;autoIncrement"`
Username string `gorm:"unique;not null"`
Password string `gorm:"not null"`
PasswordResetCode *string `gorm:"unique"`
Email string `gorm:"unique;not null"`
IsVerified bool `gorm:"default=false;not null"`
VerificationCode *string `gorm:"unique"`
VerificationExpiration time.Time
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
ID uint32 `gorm:"primaryKey;autoIncrement"`
Username string `gorm:"unique;not null"`
Password string `gorm:"not null"`
PasswordResetCode *string `gorm:"unique"`
Email string `gorm:"unique;not null"`
IsVerified bool `gorm:"default=false;not null"`
VerificationCode *string `gorm:"unique"`
VerificationExpiration time.Time
PasswordResetExpiration time.Time
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}
@@ -4,6 +4,7 @@ import (
domainUser "RenewCMS/internal/domain/user"
"RenewCMS/internal/infrastructure/persistence/mappers"
entity "RenewCMS/internal/infrastructure/persistence/models"
"time"
"gorm.io/gorm"
)
@@ -115,7 +116,7 @@ func (u *UserRepository) UpdatePassword(userId uint32, password string) (domainU
return mappers.UserToDomain(localUser), nil
}
func (u *UserRepository) UpdatePasswordResetCode(userId uint32, code string) (domainUser.User, error) {
func (u *UserRepository) UpdatePasswordResetCode(userId uint32, code string, expiration time.Time) (domainUser.User, error) {
var localUser entity.User
err := u.db.Model(&entity.User{}).First(&localUser, userId).Error
if err != nil {
@@ -123,6 +124,42 @@ func (u *UserRepository) UpdatePasswordResetCode(userId uint32, code string) (do
}
localUser.PasswordResetCode = mappers.DomainToNullableString(code)
localUser.PasswordResetExpiration = expiration
err = u.db.Save(&localUser).Error
if err != nil {
return domainUser.User{}, err
}
return mappers.UserToDomain(localUser), nil
}
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
if err != nil {
return domainUser.User{}, err
}
return mappers.UserToDomain(localUser), 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
if err != nil {
return domainUser.User{}, mapNotFoundErr(err, domainUser.ErrUserNotFound)
}
localUser.Password = hashedPassword
localUser.PasswordResetCode = mappers.DomainToNullableString("")
localUser.PasswordResetExpiration = time.Time{}
err = u.db.Save(&localUser).Error
if err != nil {
return domainUser.User{}, err
@@ -0,0 +1,26 @@
package useCases
import (
"RenewCMS/internal/domain/user"
)
type CancelRegistrationUseCase struct {
userRepository user.Repository
}
func NewCancelRegistrationUseCase(userRepository user.Repository) *CancelRegistrationUseCase {
return &CancelRegistrationUseCase{userRepository}
}
func (c *CancelRegistrationUseCase) CancelRegistration(userId uint32) error {
dbUser, err := c.userRepository.Get(userId)
if err != nil {
return err
}
if dbUser.IsVerified {
return user.ErrAlreadyVerified
}
return c.userRepository.Delete(userId)
}
@@ -0,0 +1,53 @@
package useCases
import (
"RenewCMS/internal/domain/hash"
"RenewCMS/internal/domain/mail"
"RenewCMS/internal/domain/user"
"time"
"github.com/google/uuid"
)
type RequestPasswordResetUseCase struct {
userRepository user.Repository
hasher hash.Hasher
mailRepository mail.Repository
publicURL string
}
type MailPasswordResetData struct {
Host string
VerificationCode string
Email string
}
func NewRequestPasswordResetUseCase(userRepository user.Repository, hasher hash.Hasher, mailRepository mail.Repository, publicURL string) *RequestPasswordResetUseCase {
return &RequestPasswordResetUseCase{userRepository, hasher, mailRepository, publicURL}
}
func (g *RequestPasswordResetUseCase) RequestPasswordReset(email string) error {
foundUser, err := g.userRepository.GetByEmail(email)
if err != nil {
return nil
}
rawCode := uuid.NewString()
hashedCode, err := g.hasher.Hash(rawCode)
if err != nil {
return nil
}
if _, err := g.userRepository.UpdatePasswordResetCode(foundUser.ID, hashedCode, time.Now().Add(time.Hour)); err != nil {
return nil
}
_ = g.mailRepository.Send(foundUser.Email, "passwordReset", MailPasswordResetData{
Host: g.publicURL,
VerificationCode: rawCode,
Email: foundUser.Email,
})
return nil
}
@@ -0,0 +1,39 @@
package useCases
import (
"RenewCMS/internal/domain/hash"
"RenewCMS/internal/domain/user"
"time"
)
type ResetPasswordUseCase struct {
userRepository user.Repository
hasher hash.Hasher
}
func NewResetPasswordUseCase(userRepository user.Repository, hasher hash.Hasher) *ResetPasswordUseCase {
return &ResetPasswordUseCase{userRepository, hasher}
}
func (r *ResetPasswordUseCase) ResetPassword(email string, code string, newPassword string) error {
dbUser, err := r.userRepository.GetByEmail(email)
if err != nil {
return user.ErrInvalidPasswordResetCode
}
if dbUser.PasswordResetCode == "" || r.hasher.Compare(dbUser.PasswordResetCode, code) != nil {
return user.ErrInvalidPasswordResetCode
}
if time.Now().After(dbUser.PasswordResetExpiration) {
return user.ErrPasswordResetCodeExpired
}
hashedPassword, err := r.hasher.Hash(newPassword)
if err != nil {
return err
}
_, err = r.userRepository.ResetPassword(dbUser.ID, hashedPassword)
return err
}
@@ -3,6 +3,7 @@ package useCases
import (
"RenewCMS/internal/domain/hash"
"RenewCMS/internal/domain/user"
"time"
)
type UpdateUserUseCase struct {
@@ -18,12 +19,15 @@ func (g *UpdateUserUseCase) UpdateVerificationStatus(userId uint32, isVerified b
return g.userRepository.UpdateVerificationStatus(userId, isVerified)
}
func (g *UpdateUserUseCase) UpdatePasswordResetCode(userId uint32, code string) (user.User, error) {
func (g *UpdateUserUseCase) UpdatePasswordResetCode(userId uint32, code string, expiration time.Time) (user.User, error) {
if code == "" {
return g.userRepository.UpdatePasswordResetCode(userId, "", expiration)
}
hashedCode, err := g.hasher.Hash(code)
if err != nil {
return user.User{}, err
}
return g.userRepository.UpdatePasswordResetCode(userId, hashedCode)
return g.userRepository.UpdatePasswordResetCode(userId, hashedCode, expiration)
}
func (g *UpdateUserUseCase) UpdatePassword(userId uint32, password string) (user.User, error) {
@@ -0,0 +1,33 @@
package useCases
import (
"RenewCMS/internal/domain/hash"
"RenewCMS/internal/domain/user"
"time"
)
type VerifyUserUseCase struct {
userRepository user.Repository
hasher hash.Hasher
}
func NewVerifyUserUseCase(userRepository user.Repository, hasher hash.Hasher) *VerifyUserUseCase {
return &VerifyUserUseCase{userRepository, hasher}
}
func (v *VerifyUserUseCase) Verify(userId uint32, code string) (user.User, error) {
dbUser, err := v.userRepository.Get(userId)
if err != nil {
return user.User{}, err
}
if dbUser.VerificationCode == "" || v.hasher.Compare(dbUser.VerificationCode, code) != nil {
return user.User{}, user.ErrInvalidVerificationCode
}
if time.Now().After(dbUser.VerificationExpiration) {
return user.User{}, user.ErrVerificationCodeExpired
}
return v.userRepository.ClearVerificationCode(userId)
}