package useCases import ( "RenewCMS/internal/domain/audit" "RenewCMS/internal/domain/hash" "RenewCMS/internal/domain/user" "context" "time" ) type ConfirmEmailChangeUseCase struct { userRepository user.Repository hasher hash.Hasher now func() time.Time recorder audit.Recorder } func NewConfirmEmailChangeUseCase(userRepository user.Repository, hasher hash.Hasher, now func() time.Time, recorder audit.Recorder) *ConfirmEmailChangeUseCase { return &ConfirmEmailChangeUseCase{userRepository, hasher, now, recorder} } func (u *ConfirmEmailChangeUseCase) ConfirmEmailChange(ctx context.Context, userId uint32, code string) (string, error) { account, err := u.userRepository.Get(userId) if err != nil { return "", err } event := audit.NewEvent(audit.ActionUserConfirmEmail, audit.CategoryUsers) event.Target = userTarget(account) event.Security = true if !u.accepts(account, code) { recordFailure(ctx, u.recorder, event, user.ErrInvalidEmailChangeCode) return "", user.ErrInvalidEmailChangeCode } updated, err := u.userRepository.ConfirmEmailChange(userId, account.PendingEmail, account.EmailChangeCode, u.now()) if err != nil { recordFailure(ctx, u.recorder, event, err) return "", err } u.recorder.Record(ctx, event) return updated.Email, nil } func (u *ConfirmEmailChangeUseCase) accepts(account user.User, code string) bool { if account.PendingEmail == "" || account.EmailChangeCode == "" { return false } if u.now().After(account.EmailChangeExpiration) { return false } return u.hasher.Compare(account.EmailChangeCode, code) == nil }