Échec de la validation : l'adresse e-mail principale est déjà utilisée lors de la tentative de gérer les variantes Gmail avec des points ou d'ajouter des e-mails secondaires

Version de Discourse : 2026.5.0-latest.1

Contexte

Lorsqu’un utilisateur externe envoie un e-mail au gestionnaire de courrier entrant en utilisant une variante « point » de Gmail (par exemple, user.name@gmail.com) alors que son compte de forum enregistré est la version principale sans point (username@gmail.com), le gestionnaire de courrier entrant plante avec une exception non gérée : ActiveRecord::RecordInvalid (Validation failed: Primary email has already been taken).

De plus, toute tentative de résoudre ce problème en ajoutant la variante avec points comme e-mail secondaire au profil de l’utilisateur — que ce soit via l’interface utilisateur ou la couche modèle de la console Rails en utilisant UserEmail.create! — échoue avec la même erreur de boucle de validation. La seule solution de contournement consiste à exécuter une injection SQL brute dans la base de données, en contournant ActiveRecord.

Étapes pour reproduire le problème

  1. Créez un compte utilisateur sur Discourse avec l’e-mail principal username@gmail.com.

  2. Demandez à cet utilisateur d’envoyer un e-mail entrant à une adresse de catégorie/réponse depuis user.name@gmail.com.

  3. Observez le rejet dans les journaux de courrier entrant en raison de ActiveRecord::RecordInvalid.

  4. Essayez d’ajouter user.name@gmail.com comme e-mail secondaire au compte username@gmail.com via la console Rails :

    UserEmail.create!(user_id: target_id, email: 'user.name@gmail.com', primary: false)
    
    
  5. Observez le plantage de la validation du modèle.

Comportement attendu

Discourse gère la normalisation des adresses Gmail de manière fluide. Il devrait soit :

  1. Reconnaître la variante Gmail avec points entrante comme appartenant au compte principal de manière transparente lors de la phase de traitement du courrier entrant.

  2. À tout le moins, permettre à un administrateur d’ajouter la variante avec points comme e-mail secondaire au compte principal sans déclencher un blocage de l’application du type « E-mail principal déjà pris », puisqu’il appartient au même utilisateur et est explicitement défini sur primary: false.

Comportement réel

La couche application reste bloquée dans une boucle logique :

  • Elle considère user.name@gmail.com comme une chaîne « nouvelle », elle tente donc d’agir dessus (créer un utilisateur en attente ou ajouter un e-mail secondaire).

  • Lors de la phase de validation, le modèle UserEmail exécute sa logique de normalisation Gmail, supprime les points, constate que username@gmail.com est déjà l’index d’e-mail principal pour cet user_id, et bloque son propre exécution en supposant à tort qu’un conflit de doublon de registre se produit.

Solution de contournement utilisée pour débloquer la situation

La seule façon de résoudre ce problème était de se connecter en SSH au conteneur et d’exécuter du SQL brut pour contourner complètement les validations d’ActiveRecord :

sql = "INSERT INTO user_emails (user_id, email, \"primary\", created_at, updated_at) VALUES (X, 'user.name@gmail.com', false, NOW(), NOW())"
ActiveRecord::Base.connection.execute(sql)

Une fois forcé via du SQL brut, le suivi du courrier entrant fonctionne parfaitement. Le code de validation devrait être mis à jour pour tenir compte de ce cas limite.

Merci !

2 « J'aime »