2025-12-03 19:29:37 +00:00
|
|
|
package handlers
|
|
|
|
|
|
|
|
|
|
import (
|
2025-12-29 18:23:23 +00:00
|
|
|
"context"
|
2025-12-03 19:29:37 +00:00
|
|
|
"net/http"
|
|
|
|
|
|
|
|
|
|
"veza-backend-api/internal/core/auth" // Added import for authcore
|
|
|
|
|
"veza-backend-api/internal/services"
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
2025-12-24 11:27:39 +00:00
|
|
|
"github.com/google/uuid"
|
2025-12-03 19:29:37 +00:00
|
|
|
"go.uber.org/zap"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// RequestPasswordResetRequest represents a request to reset password
|
|
|
|
|
// T0193: Request structure for password reset endpoint
|
2025-12-16 18:34:08 +00:00
|
|
|
// MOD-P1-001: Ajout tags validate pour validation systématique
|
2025-12-03 19:29:37 +00:00
|
|
|
type RequestPasswordResetRequest struct {
|
2025-12-16 18:34:08 +00:00
|
|
|
Email string `json:"email" binding:"required,email" validate:"required,email"`
|
2025-12-03 19:29:37 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-29 18:23:23 +00:00
|
|
|
// PasswordResetServiceInterface defines methods needed for password reset handler
|
|
|
|
|
type PasswordResetServiceInterface interface {
|
|
|
|
|
GenerateToken() (string, error)
|
|
|
|
|
StoreToken(userID uuid.UUID, token string) error
|
|
|
|
|
VerifyToken(token string) (uuid.UUID, error)
|
|
|
|
|
MarkTokenAsUsed(token string) error
|
|
|
|
|
InvalidateOldTokens(userID uuid.UUID) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PasswordServiceInterface defines methods needed for password operations
|
|
|
|
|
type PasswordServiceInterface interface {
|
|
|
|
|
GetUserByEmail(email string) (*services.UserInfo, error)
|
|
|
|
|
ValidatePassword(password string) error
|
|
|
|
|
UpdatePassword(userID uuid.UUID, password string) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// EmailServiceInterface defines methods needed for email operations
|
|
|
|
|
type EmailServiceInterface interface {
|
|
|
|
|
SendPasswordResetEmail(userID uuid.UUID, email, token string) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AuditServiceInterface defines methods needed for audit operations
|
|
|
|
|
type AuditServiceInterface interface {
|
|
|
|
|
LogPasswordResetRequest(ctx context.Context, userID *uuid.UUID, email, ip, userAgent string) error
|
|
|
|
|
LogPasswordReset(ctx context.Context, userID uuid.UUID, success bool, ip, userAgent string) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SessionServiceInterface defines methods needed for session operations
|
|
|
|
|
type SessionServiceInterface interface {
|
|
|
|
|
RevokeAllUserSessions(ctx context.Context, userID uuid.UUID) (int64, error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AuthServiceInterface defines methods needed for auth operations
|
|
|
|
|
type AuthServiceInterface interface {
|
|
|
|
|
InvalidateAllUserSessions(ctx context.Context, userID uuid.UUID, sessionService SessionServiceInterface) error
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-03 19:29:37 +00:00
|
|
|
// RequestPasswordReset handles password reset request
|
|
|
|
|
// T0193: Creates endpoint POST /api/v1/auth/password/reset-request
|
2025-12-24 11:27:39 +00:00
|
|
|
// BE-SEC-013: Added audit logging for password reset requests
|
2025-12-03 19:29:37 +00:00
|
|
|
func RequestPasswordReset(
|
|
|
|
|
passwordResetService *services.PasswordResetService,
|
|
|
|
|
passwordService *services.PasswordService,
|
|
|
|
|
emailService *services.EmailService,
|
2025-12-24 11:27:39 +00:00
|
|
|
auditService *services.AuditService,
|
2025-12-03 19:29:37 +00:00
|
|
|
logger *zap.Logger,
|
2025-12-29 18:23:23 +00:00
|
|
|
) gin.HandlerFunc {
|
|
|
|
|
return RequestPasswordResetWithInterfaces(
|
|
|
|
|
passwordResetService,
|
|
|
|
|
passwordService,
|
|
|
|
|
emailService,
|
|
|
|
|
auditService,
|
|
|
|
|
logger,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RequestPasswordResetWithInterfaces handles password reset request with interfaces (for testing)
|
|
|
|
|
func RequestPasswordResetWithInterfaces(
|
|
|
|
|
passwordResetService PasswordResetServiceInterface,
|
|
|
|
|
passwordService PasswordServiceInterface,
|
|
|
|
|
emailService EmailServiceInterface,
|
|
|
|
|
auditService AuditServiceInterface,
|
|
|
|
|
logger *zap.Logger,
|
2025-12-03 19:29:37 +00:00
|
|
|
) gin.HandlerFunc {
|
|
|
|
|
return func(c *gin.Context) {
|
P0: stabilisation backend/chat/stream + nouvelle base migrations v1
Backend Go:
- Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN.
- Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError).
- Sécurisation de config.go, CORS, statuts de santé et monitoring.
- Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles).
- Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés.
- Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*.
Chat server (Rust):
- Refonte du pipeline JWT + sécurité, audit et rate limiting avancé.
- Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing).
- Nettoyage des panics, gestion d’erreurs robuste, logs structurés.
- Migrations chat alignées sur le schéma UUID et nouvelles features.
Stream server (Rust):
- Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core.
- Transactions P0 pour les jobs et segments, garanties d’atomicité.
- Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION).
Documentation & audits:
- TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services.
- Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3).
- Scripts de reset et de cleanup pour la lab DB et la V1.
Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
2025-12-06 10:14:38 +00:00
|
|
|
commonHandler := NewCommonHandler(logger)
|
2025-12-03 19:29:37 +00:00
|
|
|
var req RequestPasswordResetRequest
|
P0: stabilisation backend/chat/stream + nouvelle base migrations v1
Backend Go:
- Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN.
- Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError).
- Sécurisation de config.go, CORS, statuts de santé et monitoring.
- Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles).
- Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés.
- Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*.
Chat server (Rust):
- Refonte du pipeline JWT + sécurité, audit et rate limiting avancé.
- Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing).
- Nettoyage des panics, gestion d’erreurs robuste, logs structurés.
- Migrations chat alignées sur le schéma UUID et nouvelles features.
Stream server (Rust):
- Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core.
- Transactions P0 pour les jobs et segments, garanties d’atomicité.
- Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION).
Documentation & audits:
- TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services.
- Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3).
- Scripts de reset et de cleanup pour la lab DB et la V1.
Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
2025-12-06 10:14:38 +00:00
|
|
|
if appErr := commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
|
|
|
|
|
RespondWithAppError(c, appErr)
|
2025-12-03 19:29:37 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find user by email
|
|
|
|
|
user, err := passwordService.GetUserByEmail(req.Email)
|
|
|
|
|
if err != nil {
|
|
|
|
|
// Always return success for security (prevent email enumeration)
|
2025-12-06 16:21:59 +00:00
|
|
|
RespondSuccess(c, http.StatusOK, gin.H{"message": "If the email exists, a reset link has been sent"})
|
2025-12-03 19:29:37 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Invalidate old tokens
|
|
|
|
|
if err := passwordResetService.InvalidateOldTokens(user.ID); err != nil {
|
|
|
|
|
logger.Error("Failed to invalidate old tokens",
|
|
|
|
|
zap.String("user_id", user.ID.String()),
|
|
|
|
|
zap.Error(err),
|
|
|
|
|
)
|
|
|
|
|
// Continue anyway, not critical
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate token
|
|
|
|
|
token, err := passwordResetService.GenerateToken()
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("Failed to generate password reset token",
|
|
|
|
|
zap.String("user_id", user.ID.String()),
|
|
|
|
|
zap.Error(err),
|
|
|
|
|
)
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate token"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store token
|
|
|
|
|
if err := passwordResetService.StoreToken(user.ID, token); err != nil {
|
|
|
|
|
logger.Error("Failed to store password reset token",
|
|
|
|
|
zap.String("user_id", user.ID.String()),
|
|
|
|
|
zap.Error(err),
|
|
|
|
|
)
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to store token"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Send email
|
|
|
|
|
if err := emailService.SendPasswordResetEmail(user.ID, user.Email, token); err != nil {
|
|
|
|
|
// Log but don't fail - user should still get success message
|
|
|
|
|
logger.Error("Failed to send password reset email",
|
|
|
|
|
zap.String("user_id", user.ID.String()),
|
|
|
|
|
zap.String("email", user.Email),
|
|
|
|
|
zap.Error(err),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 11:27:39 +00:00
|
|
|
// BE-SEC-013: Log password reset request
|
|
|
|
|
if auditService != nil {
|
|
|
|
|
userID := user.ID
|
|
|
|
|
if err := auditService.LogPasswordResetRequest(c.Request.Context(), &userID, user.Email, c.ClientIP(), c.GetHeader("User-Agent")); err != nil {
|
|
|
|
|
logger.Warn("Failed to log password reset request",
|
|
|
|
|
zap.String("user_id", user.ID.String()),
|
|
|
|
|
zap.Error(err),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-03 19:29:37 +00:00
|
|
|
// Always return generic success message for security
|
2025-12-06 16:21:59 +00:00
|
|
|
RespondSuccess(c, http.StatusOK, gin.H{"message": "If the email exists, a reset link has been sent"})
|
2025-12-03 19:29:37 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ResetPasswordRequest represents a request to complete password reset
|
|
|
|
|
// T0194: Request structure for password reset completion
|
2025-12-16 18:34:08 +00:00
|
|
|
// MOD-P1-001: Ajout tags validate pour validation systématique
|
2025-12-03 19:29:37 +00:00
|
|
|
type ResetPasswordRequest struct {
|
2025-12-16 18:34:08 +00:00
|
|
|
Token string `json:"token" binding:"required" validate:"required"`
|
chore: consolidate CI, E2E, backend and frontend updates
- CI: workflows updates (cd, ci), remove playwright.yml
- E2E: global-setup, auth/playlists/profile specs
- Remove playwright-report and test-results artifacts from tracking
- Backend: auth, handlers, services, workers, migrations
- Frontend: components, features, vite config
- Add e2e-results.json to gitignore
- Docs: REMEDIATION_PROGRESS, audit archive
- Rust: chat-server, stream-server updates
2026-02-17 15:43:21 +00:00
|
|
|
NewPassword string `json:"new_password" binding:"required,min=12" validate:"required,min=12"`
|
2025-12-03 19:29:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ResetPassword handles password reset completion
|
|
|
|
|
// T0194: Creates endpoint POST /api/v1/auth/password/reset
|
|
|
|
|
// T0200: Uses AuthService.InvalidateAllUserSessions to invalidate sessions and update token_version
|
2025-12-24 11:27:39 +00:00
|
|
|
// BE-SEC-013: Added audit logging for password reset completion
|
2025-12-03 19:29:37 +00:00
|
|
|
func ResetPassword(
|
|
|
|
|
passwordResetService *services.PasswordResetService,
|
|
|
|
|
passwordService *services.PasswordService,
|
|
|
|
|
authService *auth.AuthService, // Changed to *auth.AuthService
|
|
|
|
|
sessionService *services.SessionService,
|
2025-12-24 11:27:39 +00:00
|
|
|
auditService *services.AuditService,
|
2025-12-03 19:29:37 +00:00
|
|
|
logger *zap.Logger,
|
2025-12-29 18:23:23 +00:00
|
|
|
) gin.HandlerFunc {
|
|
|
|
|
// Convert concrete types to interfaces
|
|
|
|
|
var authServiceInterface AuthServiceInterface
|
|
|
|
|
if authService != nil {
|
|
|
|
|
authServiceInterface = &authServiceAdapter{authService: authService}
|
|
|
|
|
}
|
|
|
|
|
var sessionServiceInterface SessionServiceInterface
|
|
|
|
|
if sessionService != nil {
|
|
|
|
|
sessionServiceInterface = &sessionServiceAdapter{sessionService: sessionService}
|
|
|
|
|
}
|
|
|
|
|
return ResetPasswordWithInterfaces(
|
|
|
|
|
passwordResetService,
|
|
|
|
|
passwordService,
|
|
|
|
|
authServiceInterface,
|
|
|
|
|
sessionServiceInterface,
|
|
|
|
|
auditService,
|
|
|
|
|
logger,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// authServiceAdapter adapts *auth.AuthService to AuthServiceInterface
|
|
|
|
|
type authServiceAdapter struct {
|
|
|
|
|
authService *auth.AuthService
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *authServiceAdapter) InvalidateAllUserSessions(ctx context.Context, userID uuid.UUID, sessionService SessionServiceInterface) error {
|
|
|
|
|
return a.authService.InvalidateAllUserSessions(ctx, userID, sessionService)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// sessionServiceAdapter adapts *services.SessionService to SessionServiceInterface
|
|
|
|
|
type sessionServiceAdapter struct {
|
|
|
|
|
sessionService *services.SessionService
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sessionServiceAdapter) RevokeAllUserSessions(ctx context.Context, userID uuid.UUID) (int64, error) {
|
|
|
|
|
return s.sessionService.RevokeAllUserSessions(ctx, userID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ResetPasswordWithInterfaces handles password reset completion with interfaces (for testing)
|
|
|
|
|
func ResetPasswordWithInterfaces(
|
|
|
|
|
passwordResetService PasswordResetServiceInterface,
|
|
|
|
|
passwordService PasswordServiceInterface,
|
|
|
|
|
authService AuthServiceInterface,
|
|
|
|
|
sessionService SessionServiceInterface,
|
|
|
|
|
auditService AuditServiceInterface,
|
|
|
|
|
logger *zap.Logger,
|
2025-12-03 19:29:37 +00:00
|
|
|
) gin.HandlerFunc {
|
|
|
|
|
return func(c *gin.Context) {
|
P0: stabilisation backend/chat/stream + nouvelle base migrations v1
Backend Go:
- Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN.
- Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError).
- Sécurisation de config.go, CORS, statuts de santé et monitoring.
- Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles).
- Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés.
- Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*.
Chat server (Rust):
- Refonte du pipeline JWT + sécurité, audit et rate limiting avancé.
- Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing).
- Nettoyage des panics, gestion d’erreurs robuste, logs structurés.
- Migrations chat alignées sur le schéma UUID et nouvelles features.
Stream server (Rust):
- Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core.
- Transactions P0 pour les jobs et segments, garanties d’atomicité.
- Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION).
Documentation & audits:
- TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services.
- Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3).
- Scripts de reset et de cleanup pour la lab DB et la V1.
Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
2025-12-06 10:14:38 +00:00
|
|
|
commonHandler := NewCommonHandler(logger)
|
2025-12-03 19:29:37 +00:00
|
|
|
var req ResetPasswordRequest
|
P0: stabilisation backend/chat/stream + nouvelle base migrations v1
Backend Go:
- Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN.
- Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError).
- Sécurisation de config.go, CORS, statuts de santé et monitoring.
- Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles).
- Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés.
- Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*.
Chat server (Rust):
- Refonte du pipeline JWT + sécurité, audit et rate limiting avancé.
- Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing).
- Nettoyage des panics, gestion d’erreurs robuste, logs structurés.
- Migrations chat alignées sur le schéma UUID et nouvelles features.
Stream server (Rust):
- Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core.
- Transactions P0 pour les jobs et segments, garanties d’atomicité.
- Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION).
Documentation & audits:
- TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services.
- Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3).
- Scripts de reset et de cleanup pour la lab DB et la V1.
Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
2025-12-06 10:14:38 +00:00
|
|
|
if appErr := commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
|
|
|
|
|
RespondWithAppError(c, appErr)
|
2025-12-03 19:29:37 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Verify token
|
|
|
|
|
userID, err := passwordResetService.VerifyToken(req.Token)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Warn("Password reset token verification failed",
|
|
|
|
|
zap.String("token", req.Token[:min(len(req.Token), 8)]+"..."),
|
|
|
|
|
zap.Error(err),
|
|
|
|
|
)
|
2025-12-24 11:27:39 +00:00
|
|
|
// BE-SEC-013: Log failed password reset attempt
|
|
|
|
|
if auditService != nil {
|
|
|
|
|
if err := auditService.LogPasswordReset(c.Request.Context(), uuid.Nil, false, c.ClientIP(), c.GetHeader("User-Agent")); err != nil {
|
|
|
|
|
logger.Warn("Failed to log password reset failure", zap.Error(err))
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-03 19:29:37 +00:00
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid or expired token"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate password strength
|
|
|
|
|
if err := passwordService.ValidatePassword(req.NewPassword); err != nil {
|
|
|
|
|
logger.Warn("Password validation failed",
|
|
|
|
|
zap.String("user_id", userID.String()),
|
|
|
|
|
zap.Error(err),
|
|
|
|
|
)
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update password
|
|
|
|
|
if err := passwordService.UpdatePassword(userID, req.NewPassword); err != nil {
|
|
|
|
|
logger.Error("Failed to update password",
|
|
|
|
|
zap.String("user_id", userID.String()),
|
|
|
|
|
zap.Error(err),
|
|
|
|
|
)
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update password"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mark token as used
|
|
|
|
|
if err := passwordResetService.MarkTokenAsUsed(req.Token); err != nil {
|
|
|
|
|
// Log but don't fail - password is already updated
|
|
|
|
|
logger.Warn("Failed to mark token as used",
|
|
|
|
|
zap.String("user_id", userID.String()),
|
|
|
|
|
zap.String("token", req.Token[:min(len(req.Token), 8)]+"..."),
|
|
|
|
|
zap.Error(err),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// T0200: Invalidate all user sessions via AuthService
|
|
|
|
|
// This updates token_version and revokes all sessions
|
2025-12-29 18:23:23 +00:00
|
|
|
if authService != nil && sessionService != nil {
|
2025-12-03 19:29:37 +00:00
|
|
|
err := authService.InvalidateAllUserSessions(c.Request.Context(), userID, sessionService)
|
|
|
|
|
if err != nil {
|
|
|
|
|
// Log but don't fail - password is already updated
|
|
|
|
|
logger.Warn("Failed to invalidate user sessions",
|
|
|
|
|
zap.String("user_id", userID.String()),
|
|
|
|
|
zap.Error(err),
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
logger.Info("User sessions invalidated after password reset",
|
|
|
|
|
zap.String("user_id", userID.String()),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.Info("Password reset completed successfully",
|
|
|
|
|
zap.String("user_id", userID.String()),
|
|
|
|
|
)
|
|
|
|
|
|
2025-12-24 11:27:39 +00:00
|
|
|
// BE-SEC-013: Log successful password reset
|
|
|
|
|
if auditService != nil {
|
|
|
|
|
if err := auditService.LogPasswordReset(c.Request.Context(), userID, true, c.ClientIP(), c.GetHeader("User-Agent")); err != nil {
|
|
|
|
|
logger.Warn("Failed to log password reset success",
|
|
|
|
|
zap.String("user_id", userID.String()),
|
|
|
|
|
zap.Error(err),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-06 16:21:59 +00:00
|
|
|
RespondSuccess(c, http.StatusOK, gin.H{"message": "Password reset successfully"})
|
2025-12-03 19:29:37 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// min returns the minimum of two integers (helper function)
|
|
|
|
|
func min(a, b int) int {
|
|
|
|
|
if a < b {
|
|
|
|
|
return a
|
|
|
|
|
}
|
|
|
|
|
return b
|
|
|
|
|
}
|