Discourse 버전: 2026.5.0-latest.1
배경
외부 사용자가 Gmail의 “점(dot)” 변형 주소(예: user.name@gmail.com)를 사용하여 수신 메일 핸들러에 이메일을 보냈으나, 해당 사용자의 등록된 포럼 계점이 점 없는 기본 버전(username@gmail.com)인 경우, 수신 메일 핸들러가 처리되지 않은 예외 ActiveRecord::RecordInvalid (Validation failed: Primary email has already been taken)와 함께 충돌합니다.
또한, 사용자 프로필에 점 있는 변형 주소를 부(secondary) 이메일로 추가하여 이를 해결하려는 시도(UI를 통해 또는 UserEmail.create!를 사용한 Rails 콘솔 모델 계층을 통해)는 동일한 검증 루프 오류로 실패합니다. 유일한 우회 방법은 ActiveRecord를 우회하여 데이터베이스에 원시 SQL을 주입하는 것입니다.
재현 단계
-
Discourse에서 기본 이메일이
username@gmail.com인 사용자 계정을 생성합니다. -
해당 사용자에게
user.name@gmail.com에서 카테고리/답글 주소로 수신 이메일을 보내게 합니다. -
ActiveRecord::RecordInvalid로 인해 수신 메일 로그에서 거부되는 것을 관찰합니다. -
Rails 콘솔을 통해
username@gmail.com계정에user.name@gmail.com을 부 이메일로 추가해 봅니다:UserEmail.create!(user_id: target_id, email: 'user.name@gmail.com', primary: false) -
모델 검증 충돌을 관찰합니다.
예상 동작
Discourse는 Gmail 정규화를 깔끔하게 처리해야 합니다. 다음 중 하나가 되어야 합니다:
-
수신 메일 처리 단계에서 수신된 점 있는 Gmail 변형 주소를 기본 계정에 속하는 것으로 매끄럽게 인식해야 합니다.
-
최소한, 해당 주소가 동일한 사용자에게 속해 있고
primary: false로 명시적으로 설정되어 있으므로, 관리자가 “Primary email taken” 애플리케이션 블록을 트리거하지 않고 점 있는 변형 주소를 메인 계정의 부 이메일로 추가할 수 있어야 합니다.
실제 동작
애플리케이션 계층이 논리 루프에 갇힙니다:
-
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로 강제 삽입한 후, 수신 메일 추적은 완벽하게 작동합니다. 이 엣지 케이스를 고려하도록 검증 코드가 업데이트되어야 합니다.
감사합니다!