veza/veza-backend-api/internal/core/auth/service.go

834 lines
31 KiB
Go
Raw Normal View History

2025-12-03 19:29:37 +00:00
package auth
import (
"context"
"errors"
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
"fmt"
"os"
2025-12-03 19:29:37 +00:00
"strings"
"time"
"veza-backend-api/internal/models"
2025-12-16 18:34:08 +00:00
"veza-backend-api/internal/monitoring"
2025-12-03 19:29:37 +00:00
"veza-backend-api/internal/services" // Added import for services
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
"veza-backend-api/internal/workers"
2025-12-03 19:29:37 +00:00
2025-12-16 18:34:08 +00:00
"github.com/google/uuid"
2025-12-03 19:29:37 +00:00
"go.uber.org/zap"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
"veza-backend-api/internal/validators" // Import the validators package
)
type AuthService struct {
db *gorm.DB
logger *zap.Logger
JWTService *services.JWTService // Changed to pointer
2025-12-03 19:29:37 +00:00
emailVerificationService *services.EmailVerificationService // Changed to pointer
refreshTokenService *services.RefreshTokenService // Changed to pointer
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
passwordResetService *services.PasswordResetService // Added for password reset
2025-12-03 19:29:37 +00:00
emailValidator *validators.EmailValidator
passwordValidator *validators.PasswordValidator
passwordService *services.PasswordService // Changed to pointer
emailService *services.EmailService // Changed to pointer
jobWorker *workers.JobWorker // Job worker pour envoi d'emails asynchrones
accountLockoutService *services.AccountLockoutService // BE-SEC-007: Account lockout service
2025-12-03 19:29:37 +00:00
}
func NewAuthService(
db *gorm.DB,
emailValidator *validators.EmailValidator,
passwordValidator *validators.PasswordValidator,
passwordService *services.PasswordService, // Changed to pointer
jwtService *services.JWTService, // Changed to pointer
refreshTokenService *services.RefreshTokenService, // Changed to pointer
emailVerificationService *services.EmailVerificationService, // Changed to pointer
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
passwordResetService *services.PasswordResetService, // Added for password reset
2025-12-03 19:29:37 +00:00
emailService *services.EmailService, // Changed to pointer
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
jobWorker *workers.JobWorker, // Job worker pour emails asynchrones
2025-12-03 19:29:37 +00:00
logger *zap.Logger,
) *AuthService {
return &AuthService{
db: db,
logger: logger,
JWTService: jwtService,
emailVerificationService: emailVerificationService,
refreshTokenService: refreshTokenService,
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
passwordResetService: passwordResetService,
2025-12-03 19:29:37 +00:00
emailValidator: emailValidator,
passwordValidator: passwordValidator,
passwordService: passwordService,
emailService: emailService,
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
jobWorker: jobWorker,
accountLockoutService: nil, // Will be set via SetAccountLockoutService
2025-12-03 19:29:37 +00:00
}
}
// SetAccountLockoutService définit le service de verrouillage de compte
// BE-SEC-007: Implement account lockout after failed login attempts
func (s *AuthService) SetAccountLockoutService(lockoutService *services.AccountLockoutService) {
s.accountLockoutService = lockoutService
}
2025-12-03 19:29:37 +00:00
// GetUserByUsername récupère un utilisateur par son nom d'utilisateur
func (s *AuthService) GetUserByUsername(ctx context.Context, username string) (*models.User, error) {
var user models.User
if err := s.db.WithContext(ctx).Where("username = ?", username).First(&user).Error; err != nil {
return nil, err
}
return &user, nil
}
// Refresh est un alias pour RefreshToken
func (s *AuthService) Refresh(ctx context.Context, refreshToken string) (*models.TokenPair, error) {
return s.RefreshToken(ctx, refreshToken)
}
func (s *AuthService) Register(ctx context.Context, email, username, password string) (*models.User, *models.TokenPair, error) {
fmt.Println(">>> REGISTER: START")
fmt.Printf(">>> REGISTER: email=%s, username=%s\n", email, username)
s.logger.Info("=== REGISTER START ===", zap.String("email", email), zap.String("username", username))
defer func() {
if r := recover(); r != nil {
fmt.Printf(">>> REGISTER: PANIC recovered: %v\n", r)
s.logger.Error("PANIC in Register", zap.Any("panic", r))
panic(r) // Re-panic pour que le middleware Recovery le capture
}
}()
2025-12-03 19:29:37 +00:00
// Valider l'email
fmt.Println(">>> REGISTER: Checking email...")
if err := s.emailValidator.Validate(email); err != nil {
fmt.Printf(">>> REGISTER: Email check ERROR: %v\n", err)
s.logger.Warn("Registration failed: invalid email", zap.String("email", email), zap.Error(err))
return nil, nil, errors.New("invalid email: " + err.Error())
}
fmt.Println(">>> REGISTER: Email OK")
2025-12-03 19:29:37 +00:00
// Vérifier si le username existe déjà
fmt.Println(">>> REGISTER: Checking username uniqueness...")
var usernameCount int64
if err := s.db.WithContext(ctx).Model(&models.User{}).Where("LOWER(username) = LOWER(?)", username).Count(&usernameCount).Error; err != nil {
fmt.Printf(">>> REGISTER: Username check ERROR: %v\n", err)
s.logger.Error("Failed to check username uniqueness", zap.String("username", username), zap.Error(err))
return nil, nil, fmt.Errorf("failed to check username: %w", err)
}
if usernameCount > 0 {
fmt.Printf(">>> REGISTER: Username already exists: %s\n", username)
s.logger.Warn("Registration failed: username already exists", zap.String("username", username))
return nil, nil, services.ErrUserAlreadyExists
}
fmt.Println(">>> REGISTER: Username OK")
2025-12-03 19:29:37 +00:00
// Valider le mot de passe
fmt.Println(">>> REGISTER: Checking password...")
2025-12-03 19:29:37 +00:00
passwordStrength, err := s.passwordValidator.Validate(password)
if err != nil {
fmt.Printf(">>> REGISTER: Password validation ERROR: %v\n", err)
s.logger.Warn("Registration failed: weak password", zap.String("email", email), zap.Error(err))
return nil, nil, errors.New("weak password: " + err.Error())
}
if !passwordStrength.Valid {
fmt.Printf(">>> REGISTER: Password validation FAILED: %v\n", passwordStrength.Details)
s.logger.Warn("Registration failed: weak password", zap.String("email", email), zap.Any("details", passwordStrength.Details))
err = errors.New("weak password: " + strings.Join(passwordStrength.Details, ", "))
return nil, nil, err
}
fmt.Println(">>> REGISTER: Password OK")
2025-12-03 19:29:37 +00:00
// Hacher le mot de passe
fmt.Println(">>> REGISTER: Hashing password...")
2025-12-03 19:29:37 +00:00
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
fmt.Printf(">>> REGISTER: Hash ERROR: %v\n", err)
s.logger.Error("Failed to hash password", zap.Error(err))
return nil, nil, err
}
fmt.Println(">>> REGISTER: Password hashed OK")
2025-12-03 19:29:37 +00:00
// Générer un slug unique à partir du username
// Le slug doit être unique, donc on vérifie et on ajoute un suffixe si nécessaire
fmt.Println(">>> REGISTER: Generating slug...")
baseSlug := strings.ToLower(username)
slug := baseSlug
counter := 1
for {
var count int64
err := s.db.WithContext(ctx).Model(&models.User{}).Where("slug = ?", slug).Count(&count).Error
if err != nil {
fmt.Printf(">>> REGISTER: Slug check ERROR: %v\n", err)
return nil, nil, err
}
if count == 0 {
break
}
slug = fmt.Sprintf("%s%d", baseSlug, counter)
counter++
if counter > 1000 {
// Fallback: utiliser un timestamp si trop de collisions
slug = fmt.Sprintf("user_%d", time.Now().Unix())
break
}
}
fmt.Printf(">>> REGISTER: Slug generated: %s\n", slug)
2025-12-03 19:29:37 +00:00
// Créer l'utilisateur dans la base de données
// IMPORTANT: Initialiser explicitement tous les champs NOT NULL pour éviter les erreurs de contrainte
fmt.Println(">>> REGISTER: Creating user object...")
now := time.Now()
2025-12-03 19:29:37 +00:00
user := &models.User{
ID: uuid.New(), // Générer un nouvel UUID
Email: email,
Username: username,
Slug: slug,
2025-12-03 19:29:37 +00:00
PasswordHash: string(hashedPassword),
Role: "user", // Valeur par défaut (doit correspondre à l'ENUM PostgreSQL)
IsActive: true, // Valeur par défaut
IsVerified: true, // MVP: Auto-verify email pour permettre login immédiat
IsBanned: false, // Valeur par défaut (required NOT NULL field)
TokenVersion: 0, // Valeur par défaut (required NOT NULL field)
LoginCount: 0, // Valeur par défaut (required NOT NULL field)
CreatedAt: now, // Explicitement défini pour éviter les problèmes GORM
UpdatedAt: now, // Explicitement défini pour éviter les problèmes GORM
2025-12-03 19:29:37 +00:00
}
fmt.Printf(">>> REGISTER: User object created: id=%s, email=%s, username=%s, role=%s, is_banned=%v, login_count=%d\n",
user.ID.String(), user.Email, user.Username, user.Role, user.IsBanned, user.LoginCount)
// Log les valeurs avant insertion pour diagnostic
s.logger.Info("Creating user with values",
zap.String("email", email),
zap.String("username", username),
zap.Bool("is_banned", user.IsBanned),
zap.Int("login_count", user.LoginCount),
zap.Int("token_version", user.TokenVersion),
zap.Bool("is_active", user.IsActive),
zap.Bool("is_verified", user.IsVerified),
zap.String("role", user.Role),
zap.String("slug", user.Slug),
zap.String("user_id", user.ID.String()),
)
// Utiliser GORM Create - GORM gère automatiquement les placeholders PostgreSQL
// Tous les champs NOT NULL sont maintenant explicitement initialisés
// IMPORTANT: Utiliser Omit pour exclure les relations qui pourraient causer des problèmes
fmt.Println("========== REGISTER: Inserting in DB ==========")
fmt.Printf(">>> User ID: %s\n", user.ID.String())
fmt.Printf(">>> Email: %s\n", user.Email)
fmt.Printf(">>> Username: %s\n", user.Username)
fmt.Printf(">>> Role: %s\n", user.Role)
fmt.Printf(">>> IsActive: %v\n", user.IsActive)
fmt.Printf(">>> IsVerified: %v\n", user.IsVerified)
fmt.Printf(">>> IsBanned: %v\n", user.IsBanned)
fmt.Printf(">>> TokenVersion: %d\n", user.TokenVersion)
fmt.Printf(">>> LoginCount: %d\n", user.LoginCount)
result := s.db.WithContext(ctx).Omit("Roles", "TrackLikes").Create(user)
if result.Error != nil {
fmt.Printf("========== REGISTER: DB INSERT ERROR ==========\n")
fmt.Printf(">>> ERROR: %v\n", result.Error)
fmt.Printf(">>> ERROR TYPE: %T\n", result.Error)
fmt.Printf(">>> ROWS AFFECTED: %d\n", result.RowsAffected)
fmt.Printf("===============================================\n")
// Log l'erreur complète pour diagnostic
err := result.Error
s.logger.Error("Failed to create user in database - full error details",
zap.Error(err),
zap.String("email", email),
zap.String("username", username),
zap.String("user_id", user.ID.String()),
zap.Any("user_fields", map[string]interface{}{
"is_banned": user.IsBanned,
"login_count": user.LoginCount,
"token_version": user.TokenVersion,
"is_active": user.IsActive,
"is_verified": user.IsVerified,
"role": user.Role,
"slug": user.Slug,
}),
)
2025-12-03 19:29:37 +00:00
// PostgreSQL error code 23505 is unique_violation
// We check for specific constraint names if possible, or fallback to generic "duplicate"
errMsg := err.Error()
if strings.Contains(errMsg, "users_email_key") || strings.Contains(errMsg, "idx_users_email") {
2025-12-03 19:29:37 +00:00
s.logger.Warn("Registration failed: email already exists", zap.String("email", email))
return nil, nil, services.ErrUserAlreadyExists
2025-12-03 19:29:37 +00:00
}
if strings.Contains(errMsg, "users_username_key") || strings.Contains(errMsg, "idx_users_username") {
s.logger.Warn("Registration failed: username already exists", zap.String("username", username))
// We can return the same error or a more specific one if needed
return nil, nil, errors.New("username already exists")
}
if strings.Contains(errMsg, "users_slug_key") || strings.Contains(errMsg, "idx_users_slug") {
s.logger.Warn("Registration failed: slug collision", zap.String("slug", user.Slug))
// In a real robust system, we would retry with a suffix here
// For now, fail explicitly so the user knows
return nil, nil, errors.New("username unavailable (slug collision)")
}
2025-12-13 02:34:34 +00:00
// Fallback for generic unique constraint
if strings.Contains(errMsg, "unique constraint") || strings.Contains(errMsg, "duplicate key") {
s.logger.Warn("Registration failed: unique constraint violation", zap.Error(err))
return nil, nil, services.ErrUserAlreadyExists
}
2025-12-13 02:34:34 +00:00
// Pour toutes les autres erreurs, retourner l'erreur originale avec contexte
return nil, nil, fmt.Errorf("database error: %w", err)
2025-12-03 19:29:37 +00:00
}
fmt.Printf("========== REGISTER: DB INSERT SUCCESS ==========\n")
fmt.Printf(">>> Rows affected: %d\n", result.RowsAffected)
fmt.Printf(">>> User ID: %s\n", user.ID.String())
fmt.Println("===============================================")
2025-12-03 19:29:37 +00:00
// Générer le token de vérification d'email (non-bloquant)
// Si la génération échoue, on continue quand même avec l'inscription
// L'utilisateur pourra demander un nouveau token plus tard
if s.emailVerificationService != nil {
token, err := s.emailVerificationService.GenerateToken()
if err != nil {
s.logger.Warn("Failed to generate email verification token (non-blocking)", zap.Error(err))
} else {
// Stocker le token
if err := s.emailVerificationService.StoreToken(user.ID, user.Email, token); err != nil {
s.logger.Warn("Failed to store email verification token (non-blocking)", zap.Error(err))
} else {
// Envoyer l'email de vérification (simulation pour l'instant)
s.logger.Info("Sending verification email",
zap.String("email", user.Email),
zap.String("token", token),
zap.String("user_id", user.ID.String()))
}
}
} else {
s.logger.Warn("Email verification service not available - skipping token generation")
2025-12-03 19:29:37 +00:00
}
s.logger.Info("User registered successfully", zap.String("user_id", user.ID.String()))
fmt.Println("========== REGISTER: Generating tokens ==========")
2025-12-16 18:34:08 +00:00
// MVP: Générer les tokens JWT pour permettre l'authentification immédiate
if s.JWTService == nil {
fmt.Println(">>> ERROR: JWTService is nil!")
s.logger.Error("JWTService is nil - cannot generate tokens")
return nil, nil, fmt.Errorf("JWT service not available")
}
fmt.Println(">>> JWTService OK")
accessToken, err := s.JWTService.GenerateAccessToken(user)
if err != nil {
fmt.Printf(">>> ERROR generating access token: %v\n", err)
s.logger.Error("Failed to generate access token after registration", zap.Error(err), zap.String("user_id", user.ID.String()))
return nil, nil, fmt.Errorf("failed to generate access token: %w", err)
}
fmt.Printf(">>> Access token generated: %s...\n", accessToken[:50])
refreshToken, err := s.JWTService.GenerateRefreshToken(user)
if err != nil {
fmt.Printf(">>> ERROR generating refresh token: %v\n", err)
s.logger.Error("Failed to generate refresh token after registration", zap.Error(err), zap.String("user_id", user.ID.String()))
return nil, nil, fmt.Errorf("failed to generate refresh token: %w", err)
}
fmt.Printf(">>> Refresh token generated: %s...\n", refreshToken[:50])
// Stocker le refresh token en base
fmt.Println(">>> Storing refresh token...")
refreshTokenTTL := s.JWTService.Config.RefreshTokenTTL
if s.refreshTokenService != nil {
if err := s.refreshTokenService.Store(user.ID, refreshToken, refreshTokenTTL); err != nil {
fmt.Printf(">>> ERROR storing refresh token: %v\n", err)
s.logger.Error("Failed to store refresh token after registration", zap.Error(err), zap.String("user_id", user.ID.String()))
return nil, nil, fmt.Errorf("failed to store refresh token: %w", err)
}
fmt.Println(">>> Refresh token stored OK")
} else {
fmt.Println(">>> WARNING: Refresh token service not available - skipping token storage")
s.logger.Warn("Refresh token service not available - skipping token storage")
}
2025-12-16 18:34:08 +00:00
// MOD-P2-003: Enregistrer la métrique business
monitoring.RecordUserRegistered()
tokenPair := &models.TokenPair{
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresIn: int(s.JWTService.Config.AccessTokenTTL.Seconds()),
}
fmt.Println("========== REGISTER: SUCCESS ==========")
fmt.Printf(">>> User ID: %s\n", user.ID.String())
fmt.Printf(">>> Access Token: %s...\n", accessToken[:50])
fmt.Println("======================================")
return user, tokenPair, nil
2025-12-03 19:29:37 +00:00
}
func (s *AuthService) Login(ctx context.Context, email, password string, rememberMe bool) (*models.User, *models.TokenPair, error) {
s.logger.Info("Attempting login", zap.String("email", email))
// BE-SEC-007: Check if account is locked
if s.accountLockoutService != nil {
locked, lockedUntil, err := s.accountLockoutService.IsAccountLocked(ctx, email)
if err != nil {
s.logger.Warn("Failed to check account lockout status",
zap.String("email", email),
zap.Error(err))
// Continue with login attempt if check fails (fail-open)
} else if locked {
if lockedUntil != nil {
remaining := time.Until(*lockedUntil)
s.logger.Warn("Login blocked: account is locked",
zap.String("email", email),
zap.Time("locked_until", *lockedUntil),
zap.Duration("remaining", remaining))
return nil, nil, fmt.Errorf("account is locked. Please try again after %v", remaining.Round(time.Minute))
}
return nil, nil, errors.New("account is locked due to too many failed login attempts")
}
}
2025-12-03 19:29:37 +00:00
var user models.User
if err := s.db.WithContext(ctx).Where("email = ?", email).First(&user).Error; err != nil {
if err == gorm.ErrRecordNotFound {
s.logger.Warn("Login failed: user not found", zap.String("email", email))
// BE-SEC-007: Record failed attempt even if user not found (to prevent email enumeration)
if s.accountLockoutService != nil {
if err := s.accountLockoutService.RecordFailedAttempt(ctx, email); err != nil {
s.logger.Warn("Failed to record failed login attempt",
zap.String("email", email),
zap.Error(err))
}
}
2025-12-03 19:29:37 +00:00
return nil, nil, errors.New("invalid credentials")
}
s.logger.Error("Database error during login", zap.Error(err), zap.String("email", email))
2025-12-03 19:29:37 +00:00
return nil, nil, err
}
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)); err != nil {
s.logger.Warn("Login failed: invalid password", zap.String("email", email))
// BE-SEC-007: Record failed attempt
if s.accountLockoutService != nil {
if err := s.accountLockoutService.RecordFailedAttempt(ctx, email); err != nil {
s.logger.Warn("Failed to record failed login attempt",
zap.String("email", email),
zap.Error(err))
}
}
2025-12-03 19:29:37 +00:00
return nil, nil, errors.New("invalid credentials")
}
if !user.IsVerified {
s.logger.Warn("Login failed: email not verified", zap.String("email", email))
// BE-SEC-007: Record failed attempt (email not verified is also a failed attempt)
if s.accountLockoutService != nil {
if err := s.accountLockoutService.RecordFailedAttempt(ctx, email); err != nil {
s.logger.Warn("Failed to record failed login attempt",
zap.String("email", email),
zap.Error(err))
}
}
2025-12-03 19:29:37 +00:00
return nil, nil, errors.New("email not verified")
}
// BE-SEC-007: Record successful login (reset failed attempts counter)
if s.accountLockoutService != nil {
if err := s.accountLockoutService.RecordSuccessfulLogin(ctx, email); err != nil {
s.logger.Warn("Failed to record successful login",
zap.String("email", email),
zap.Error(err))
// Non-critique, on continue
}
}
2025-12-03 19:29:37 +00:00
// Générer les tokens JWT
accessToken, err := s.JWTService.GenerateAccessToken(&user)
if err != nil {
s.logger.Error("Failed to generate access token", zap.Error(err), zap.String("user_id", user.ID.String()))
return nil, nil, fmt.Errorf("failed to generate access token: %w", err)
2025-12-03 19:29:37 +00:00
}
refreshTokenTTL := s.JWTService.Config.RefreshTokenTTL
if rememberMe {
refreshTokenTTL = s.JWTService.Config.RememberMeRefreshTokenTTL // Assurez-vous que ce champ existe dans models.JWTConfig
}
refreshToken, err := s.JWTService.GenerateRefreshToken(&user)
if err != nil {
s.logger.Error("Failed to generate refresh token", zap.Error(err), zap.String("user_id", user.ID.String()))
return nil, nil, fmt.Errorf("failed to generate refresh token: %w", err)
2025-12-03 19:29:37 +00:00
}
// Stocker le refresh token en base
if err := s.refreshTokenService.Store(user.ID, refreshToken, refreshTokenTTL); err != nil {
s.logger.Error("Failed to store refresh token", zap.Error(err), zap.String("user_id", user.ID.String()))
return nil, nil, fmt.Errorf("failed to store refresh token: %w", err)
2025-12-03 19:29:37 +00:00
}
s.logger.Info("User logged in successfully", zap.String("user_id", user.ID.String()))
return &user, &models.TokenPair{
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresIn: int(s.JWTService.Config.AccessTokenTTL.Seconds()),
}, nil
}
func (s *AuthService) RefreshToken(ctx context.Context, refreshToken string) (*models.TokenPair, error) {
claims, err := s.JWTService.ValidateToken(refreshToken)
if err != nil {
s.logger.Warn("Invalid refresh token format", zap.Error(err))
return nil, errors.New("invalid refresh token")
}
if !claims.IsRefresh {
s.logger.Warn("Token is not a refresh token")
return nil, errors.New("invalid token type")
}
if err := s.refreshTokenService.Validate(claims.UserID, refreshToken); err != nil {
s.logger.Warn("Refresh token invalid or revoked", zap.Error(err))
return nil, errors.New("invalid or revoked refresh token")
}
var user models.User
if err := s.db.WithContext(ctx).First(&user, claims.UserID).Error; err != nil {
s.logger.Error("User not found for refresh token", zap.Error(err))
return nil, errors.New("user not found")
}
newAccessToken, err := s.JWTService.GenerateAccessToken(&user)
if err != nil {
s.logger.Error("Failed to generate new access token", zap.Error(err))
return nil, err
}
newRefreshToken, err := s.JWTService.GenerateRefreshToken(&user)
if err != nil {
s.logger.Error("Failed to generate new refresh token", zap.Error(err))
return nil, err
}
if err := s.refreshTokenService.Rotate(user.ID, refreshToken, newRefreshToken, s.JWTService.Config.RefreshTokenTTL); err != nil {
s.logger.Error("Failed to rotate refresh token", zap.Error(err))
return nil, err
}
return &models.TokenPair{
AccessToken: newAccessToken,
RefreshToken: newRefreshToken,
ExpiresIn: int(s.JWTService.Config.AccessTokenTTL.Seconds()),
}, nil
}
func (s *AuthService) VerifyEmail(ctx context.Context, token string) error {
userID, err := s.emailVerificationService.VerifyToken(token)
if err != nil {
s.logger.Warn("Email verification failed", zap.Error(err))
return err
}
if err := s.db.WithContext(ctx).Model(&models.User{}).Where("id = ?", userID).Update("is_verified", true).Error; err != nil {
s.logger.Error("Failed to update user verification status", zap.Error(err))
return err
}
if err := s.emailVerificationService.InvalidateOldTokens(userID); err != nil {
s.logger.Warn("Failed to invalidate old verification tokens", zap.Error(err))
}
s.logger.Info("Email verified successfully", zap.String("user_id", userID.String()))
return nil
}
func (s *AuthService) ResendVerificationEmail(ctx context.Context, email string) error {
var user models.User
if err := s.db.WithContext(ctx).Where("email = ?", email).First(&user).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil
}
return err
}
if user.IsVerified {
return errors.New("email already verified")
}
if err := s.emailVerificationService.InvalidateOldTokens(user.ID); err != nil {
s.logger.Error("Failed to invalidate old tokens", zap.Error(err))
}
token, err := s.emailVerificationService.GenerateToken()
if err != nil {
return err
}
if err := s.emailVerificationService.StoreToken(user.ID, user.Email, token); err != nil {
2025-12-03 19:29:37 +00:00
return err
}
s.logger.Info("Resending verification email",
zap.String("email", user.Email),
zap.String("token", token),
zap.String("user_id", user.ID.String()))
return nil
}
func (s *AuthService) Logout(ctx context.Context, userID uuid.UUID, refreshToken string) error {
// Valider le refresh token
claims, err := s.JWTService.ValidateToken(refreshToken)
if err != nil {
s.logger.Warn("Invalid refresh token during logout", zap.Error(err), zap.String("user_id", userID.String()))
return nil // Ne pas retourner d'erreur pour ne pas bloquer le logout côté UI
}
if claims.UserID != userID {
s.logger.Warn("User ID mismatch for logout request", zap.String("requested_user_id", userID.String()), zap.String("token_user_id", claims.UserID.String()))
return errors.New("user ID mismatch")
}
if err := s.refreshTokenService.Revoke(claims.UserID, refreshToken); err != nil {
s.logger.Error("Failed to revoke refresh token during logout", zap.Error(err), zap.String("user_id", userID.String()))
return err
}
s.logger.Info("User logged out successfully", zap.String("user_id", userID.String()))
return nil
}
func (s *AuthService) InvalidateAllUserSessions(ctx context.Context, userID uuid.UUID, sessionService interface {
RevokeAllUserSessions(ctx context.Context, userID uuid.UUID) (int64, error)
}) error {
if err := s.refreshTokenService.RevokeAll(userID); err != nil {
s.logger.Error("Failed to revoke all refresh tokens", zap.Error(err))
return err
}
if sessionService != nil {
count, err := sessionService.RevokeAllUserSessions(ctx, userID)
if err != nil {
s.logger.Error("Failed to revoke user sessions", zap.Error(err))
} else {
s.logger.Info("Revoked user sessions", zap.Int64("count", count), zap.String("user_id", userID.String()))
}
}
s.logger.Info("All user sessions invalidated", zap.String("user_id", userID.String()))
return nil
}
// MIGRATION UUID: userID migré vers uuid.UUID
func (s *AuthService) AdminVerifyUser(ctx context.Context, userID uuid.UUID) error {
result := s.db.WithContext(ctx).Model(&models.User{}).Where("id = ?", userID).Update("is_verified", true)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return errors.New("user not found")
}
_ = s.emailVerificationService.InvalidateOldTokens(userID)
s.logger.Info("User verified by admin", zap.String("user_id", userID.String()))
return nil
}
// MIGRATION UUID: userID migré vers uuid.UUID
func (s *AuthService) AdminBlockUser(ctx context.Context, userID uuid.UUID) error {
if err := s.refreshTokenService.RevokeAll(userID); err != nil {
return err
}
s.logger.Info("User blocked by admin", zap.String("user_id", userID.String()))
return nil
}
func (s *AuthService) RequestPasswordReset(ctx context.Context, email string) error {
var user models.User
if err := s.db.WithContext(ctx).Where("email = ?", email).First(&user).Error; err != nil {
if err == gorm.ErrRecordNotFound {
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
// Return nil to prevent email enumeration - always return success
2025-12-03 19:29:37 +00:00
return nil
}
return err
}
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
// Invalidate old tokens for this user
if err := s.passwordResetService.InvalidateOldTokens(user.ID); err != nil {
s.logger.Warn("Failed to invalidate old password reset tokens",
zap.String("user_id", user.ID.String()),
zap.Error(err),
)
// Continue anyway, not critical
}
// Generate new reset token
token, err := s.passwordResetService.GenerateToken()
2025-12-03 19:29:37 +00:00
if err != nil {
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
s.logger.Error("Failed to generate password reset token",
zap.String("user_id", user.ID.String()),
zap.Error(err),
)
return fmt.Errorf("failed to generate reset token: %w", err)
}
// Store token in database
if err := s.passwordResetService.StoreToken(user.ID, token); err != nil {
s.logger.Error("Failed to store password reset token",
zap.String("user_id", user.ID.String()),
zap.Error(err),
)
return fmt.Errorf("failed to store reset token: %w", err)
}
// Send password reset email via job worker (asynchrone)
if s.jobWorker != nil {
// Construire l'URL de reset
baseURL := os.Getenv("FRONTEND_URL")
if baseURL == "" {
baseURL = "http://localhost:5173"
}
resetURL := fmt.Sprintf("%s/reset-password?token=%s", baseURL, token)
// Préparer les données du template
templateData := map[string]interface{}{
"Username": user.Username,
"ResetURL": resetURL,
}
// Enqueue le job d'email avec template
s.jobWorker.EnqueueEmailJobWithTemplate(
user.Email,
"Reset your Veza password",
"password_reset",
templateData,
)
s.logger.Info("Password reset email job enqueued",
zap.String("user_id", user.ID.String()),
zap.String("email", user.Email),
)
} else {
// Fallback sur l'ancien système si job worker non disponible
s.logger.Warn("Job worker not available, using direct email service")
if err := s.emailService.SendPasswordResetEmail(user.ID, user.Email, token); err != nil {
s.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-03 19:29:37 +00:00
}
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
s.logger.Info("Password reset requested successfully",
zap.String("email", email),
zap.String("user_id", user.ID.String()),
zap.String("token_preview", token[:min(len(token), 8)]+"..."),
)
2025-12-03 19:29:37 +00:00
return nil
}
func (s *AuthService) ResetPassword(ctx context.Context, token, newPassword string) error {
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
// Verify the reset token
userID, err := s.passwordResetService.VerifyToken(token)
2025-12-03 19:29:37 +00:00
if err != nil {
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
s.logger.Warn("Password reset token verification failed",
zap.String("token_preview", token[:min(len(token), 8)]+"..."),
zap.Error(err),
)
return fmt.Errorf("invalid or expired token: %w", err)
}
// Validate password strength
if err := s.passwordService.ValidatePassword(newPassword); err != nil {
s.logger.Warn("Password validation failed during reset",
zap.String("user_id", userID.String()),
zap.Error(err),
)
return fmt.Errorf("invalid password: %w", err)
}
// Update password using PasswordService
if err := s.passwordService.UpdatePassword(userID, newPassword); err != nil {
s.logger.Error("Failed to update password during reset",
zap.String("user_id", userID.String()),
zap.Error(err),
)
return fmt.Errorf("failed to update password: %w", err)
}
// Mark token as used
if err := s.passwordResetService.MarkTokenAsUsed(token); err != nil {
// Log but don't fail - password is already updated
s.logger.Warn("Failed to mark password reset token as used",
zap.String("user_id", userID.String()),
zap.String("token_preview", token[:min(len(token), 8)]+"..."),
zap.Error(err),
)
}
// Invalidate all user sessions (revoke refresh tokens)
if err := s.refreshTokenService.RevokeAll(userID); err != nil {
s.logger.Warn("Failed to revoke refresh tokens after password reset",
zap.String("user_id", userID.String()),
zap.Error(err),
)
// Don't fail - password is already updated
2025-12-03 19:29:37 +00:00
}
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
s.logger.Info("Password reset completed successfully",
zap.String("user_id", userID.String()),
)
2025-12-03 19:29:37 +00:00
return nil
}
// MIGRATION UUID: userID migré vers uuid.UUID
func (s *AuthService) ChangePassword(ctx context.Context, userID uuid.UUID, currentPassword, newPassword string) error {
var user models.User
if err := s.db.WithContext(ctx).First(&user, userID).Error; err != nil {
return err
}
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(currentPassword)); err != nil {
return errors.New("invalid current password")
}
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
if err != nil {
return err
}
if err := s.db.WithContext(ctx).Model(&user).Update("password_hash", string(hashedPassword)).Error; err != nil {
return err
}
if err := s.refreshTokenService.RevokeAll(userID); err != nil {
s.logger.Warn("Failed to revoke refresh tokens after password change", zap.Error(err))
}
s.logger.Info("Password changed successfully", zap.String("user_id", userID.String()))
return nil
}
func (s *AuthService) ValidateAccessToken(tokenString string) (*models.CustomClaims, error) {
return s.JWTService.ValidateToken(tokenString)
}
func (s *AuthService) UpdateLastLogin(ctx context.Context, userID uuid.UUID) error {
return s.db.WithContext(ctx).Model(&models.User{}).
Where("id = ?", userID).
Update("last_login_at", time.Now()).Error
}
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
// min returns the minimum of two integers (helper function)
func min(a, b int) int {
if a < b {
return a
}
return b
}