Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
127
cloud/maplefile-backend/internal/service/auth/verify_email.go
Normal file
127
cloud/maplefile-backend/internal/service/auth/verify_email.go
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/service/auth/verify_email.go
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
uc_user "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/usecase/user"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/auditlog"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/validation"
|
||||
)
|
||||
|
||||
type VerifyEmailRequestDTO struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
type VerifyEmailResponseDTO struct {
|
||||
Message string `json:"message"`
|
||||
Success bool `json:"success"`
|
||||
UserRole int8 `json:"user_role"`
|
||||
}
|
||||
|
||||
type VerifyEmailService interface {
|
||||
Execute(ctx context.Context, req *VerifyEmailRequestDTO) (*VerifyEmailResponseDTO, error)
|
||||
}
|
||||
|
||||
type verifyEmailServiceImpl struct {
|
||||
logger *zap.Logger
|
||||
auditLogger auditlog.AuditLogger
|
||||
userGetByVerificationCodeUC uc_user.UserGetByVerificationCodeUseCase
|
||||
userUpdateUC uc_user.UserUpdateUseCase
|
||||
}
|
||||
|
||||
func NewVerifyEmailService(
|
||||
logger *zap.Logger,
|
||||
auditLogger auditlog.AuditLogger,
|
||||
userGetByVerificationCodeUC uc_user.UserGetByVerificationCodeUseCase,
|
||||
userUpdateUC uc_user.UserUpdateUseCase,
|
||||
) VerifyEmailService {
|
||||
return &verifyEmailServiceImpl{
|
||||
logger: logger.Named("VerifyEmailService"),
|
||||
auditLogger: auditLogger,
|
||||
userGetByVerificationCodeUC: userGetByVerificationCodeUC,
|
||||
userUpdateUC: userUpdateUC,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *verifyEmailServiceImpl) Execute(ctx context.Context, req *VerifyEmailRequestDTO) (*VerifyEmailResponseDTO, error) {
|
||||
// Validate request
|
||||
if err := s.validateVerifyEmailRequest(req); err != nil {
|
||||
return nil, err // Returns RFC 9457 ProblemDetail
|
||||
}
|
||||
|
||||
// Get user by verification code
|
||||
user, err := s.userGetByVerificationCodeUC.Execute(ctx, req.Code)
|
||||
if err != nil || user == nil {
|
||||
s.logger.Warn("Invalid verification code attempted")
|
||||
return nil, httperror.NewNotFoundError("Verification code not found or has already been used")
|
||||
}
|
||||
|
||||
// Check if code has expired
|
||||
if time.Now().After(user.SecurityData.CodeExpiry) {
|
||||
s.logger.Warn("Verification code expired",
|
||||
zap.String("user_id", user.ID.String()),
|
||||
zap.Time("expiry", user.SecurityData.CodeExpiry))
|
||||
return nil, httperror.NewBadRequestError("Verification code has expired. Please request a new verification email.")
|
||||
}
|
||||
|
||||
// Update user to mark as verified
|
||||
user.SecurityData.WasEmailVerified = true
|
||||
user.SecurityData.Code = ""
|
||||
user.SecurityData.CodeExpiry = time.Time{}
|
||||
user.ModifiedAt = time.Now()
|
||||
|
||||
if err := s.userUpdateUC.Execute(ctx, user); err != nil {
|
||||
s.logger.Error("Failed to update user", zap.Error(err))
|
||||
return nil, httperror.NewInternalServerError(fmt.Sprintf("Failed to verify email: %v", err))
|
||||
}
|
||||
|
||||
s.logger.Info("Email verified successfully", zap.String("user_id", user.ID.String()))
|
||||
|
||||
// Audit log email verification
|
||||
s.auditLogger.LogAuth(ctx, auditlog.EventTypeEmailVerified, auditlog.OutcomeSuccess,
|
||||
validation.MaskEmail(user.Email), "", map[string]string{
|
||||
"user_id": user.ID.String(),
|
||||
})
|
||||
|
||||
return &VerifyEmailResponseDTO{
|
||||
Message: "Email verified successfully. You can now log in.",
|
||||
Success: true,
|
||||
UserRole: user.Role,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// validateVerifyEmailRequest validates the verify email request.
|
||||
// Returns RFC 9457 ProblemDetail error with field-specific errors.
|
||||
func (s *verifyEmailServiceImpl) validateVerifyEmailRequest(req *VerifyEmailRequestDTO) error {
|
||||
errors := make(map[string]string)
|
||||
|
||||
// Validate verification code
|
||||
code := strings.TrimSpace(req.Code)
|
||||
if code == "" {
|
||||
errors["code"] = "Verification code is required"
|
||||
} else if len(code) != 8 {
|
||||
errors["code"] = "Verification code must be 8 digits"
|
||||
} else {
|
||||
// Validate that code is numeric
|
||||
for _, c := range code {
|
||||
if c < '0' || c > '9' {
|
||||
errors["code"] = "Verification code must contain only numbers"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there are validation errors, return RFC 9457 error
|
||||
if len(errors) > 0 {
|
||||
return httperror.NewValidationError(errors)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue