Discourse バージョン: 2026.5.0-latest.1
背景
外部ユーザーが Gmail の「ドット」バリエーション(例:user.name@gmail.com)を使用して受信メールハンドラにメールを送信した場合、登録されているフォーラムアカウントがドットなしのプライマリバージョン(username@gmail.com)であるとき、受信メールハンドラは未処理の例外 ActiveRecord::RecordInvalid (Validation failed: Primary email has already been taken) でクラッシュします。
さらに、UI または Rails コンソールのモデル層で UserEmail.create! を使用して、ドット付きバリエーションをユーザープロファイルのセカンダリメールアドレスとして追加しようとすると、同じ検証ループエラーが発生します。回避策は、ActiveRecord をバイパスしてデータベースに生 SQL を直接実行することだけです。
再現手順
-
username@gmail.comをプライマリメールアドレスとして Discourse にユーザーアカウントを作成します。 -
そのユーザーが
user.name@gmail.comからカテゴリ/返信アドレス宛に受信メールを送信します。 -
ActiveRecord::RecordInvalidにより、受信メールログで拒否されることを確認します。 -
Rails コンソールを介して
user.name@gmail.comをusername@gmail.comアカウントのセカンダリメールアドレスとして追加しようとします。UserEmail.create!(user_id: target_id, email: 'user.name@gmail.com', primary: false) -
モデル検証によるクラッシュを確認します。
期待される動作
Discourse は Gmail の正規化を適切に処理する必要があります。具体的には以下のいずれかであるべきです。
-
受信メール処理の段階で、ドット付きの Gmail バリエーションをプライマリアカウントに属するものとしてシームレスに認識する。
-
少なくとも、管理者がドット付きバリエーションをメインアカウントのセカンダリメールアドレスとして追加できるようにし、「プライマリメールアドレスが既に存在します」というアプリケーションブロックを発生させない。これは同一ユーザーに属し、明示的に
primary: falseに設定されているためです。
実際の動作
アプリケーション層が論理ループに陥ります。
-
user.name@gmail.comを「新しい」文字列として認識するため、それに対して処理を実行しようとする(ステージングユーザーの作成またはセカンダリメールアドレスの追加)。 -
検証フェーズにおいて、
UserEmailモデルが Gmail 正規化ロジックを実行し、ドットを除去すると、username@gmail.comがそのuser_idのプライマリメールアドレスインデックスとして既に存在していることを検知し、重複レコード競合が発生しているという誤った前提の下で自身の実行をブロックする。
使用した回避策
この問題を解決した唯一の方法は、SSH でコンテナにアクセスし、ActiveRecord の検証を完全にバイパスする生 SQL を実行することでした。
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)
生 SQL で強制的に追加すると、受信メールの追跡は正常に機能します。この検証コードは、このエッジケースに対応するように更新されるべきです。
ありがとうございます。