From f52e4fb91b967530c61fba6df94aadfb6614c672 Mon Sep 17 00:00:00 2001 From: Marc Saleiko Date: Wed, 17 Apr 2024 16:58:06 +0200 Subject: [PATCH] Support X-Original-To headers for email ingestion Incoming email can contain the project key or reply key in the X-Original-To header. Changelog: added --- doc/administration/incoming_email.md | 4 ++- lib/gitlab/email/receiver.rb | 6 ++++ spec/lib/gitlab/email/receiver_spec.rb | 33 ++++++++++++++----- .../email/service_desk_receiver_spec.rb | 21 ++++++++++-- 4 files changed, 53 insertions(+), 11 deletions(-) diff --git a/doc/administration/incoming_email.md b/doc/administration/incoming_email.md index 06d902e638becc..80eb4acc0c3084 100644 --- a/doc/administration/incoming_email.md +++ b/doc/administration/incoming_email.md @@ -74,6 +74,7 @@ this method only supports replies, and not the other features of [incoming email > - Accepting `Received` headers [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81489) in GitLab 14.9. > - Accepting `Cc` headers [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348572) in GitLab 16.5. +> - Accepting `X-Original-To` headers [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/149874) in GitLab 17.0. Email is processed correctly when a configured email address is present in one of the following headers (sorted in the order they are checked): @@ -82,6 +83,7 @@ Email is processed correctly when a configured email address is present in one o - `Delivered-To` - `Envelope-To` or `X-Envelope-To` - `Received` +- `X-Original-To` - `Cc` The `References` header is also accepted, however it is used specifically to relate email responses to existing discussion threads. It is not used for creating issues by email. @@ -92,7 +94,7 @@ also checks accepted headers. Usually, the "To" field contains the email address of the primary receiver. However, it might not include the configured GitLab email address if: -- The address is in the "BCC" field. +- The address is in the `BCC` field. - The email was forwarded. The `Received` header can contain multiple email addresses. These are checked in the order that they appear. diff --git a/lib/gitlab/email/receiver.rb b/lib/gitlab/email/receiver.rb index e36b07da801e70..61bdb7c09a5637 100644 --- a/lib/gitlab/email/receiver.rb +++ b/lib/gitlab/email/receiver.rb @@ -50,6 +50,7 @@ def mail_metadata delivered_to: delivered_to.map(&:value), envelope_to: envelope_to.map(&:value), x_envelope_to: x_envelope_to.map(&:value), + x_original_to: x_original_to.map(&:value), cc_address: cc, # reduced down to what looks like an email in the received headers received_recipients: recipients_from_received_headers, @@ -112,6 +113,7 @@ def key_from_additional_headers find_first_key_from(envelope_to) || find_first_key_from(x_envelope_to) || find_first_key_from(recipients_from_received_headers) || + find_first_key_from(x_original_to) || find_first_key_from(cc) end @@ -163,6 +165,10 @@ def received Array(mail[:received]) end + def x_original_to + Array(mail[:x_original_to]) + end + def recipients_from_received_headers strong_memoize :emails_from_received_headers do received.filter_map { |header| header.value[RECEIVED_HEADER_REGEX, 1] } diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb index aff5928c3da896..7dc25044434857 100644 --- a/spec/lib/gitlab/email/receiver_spec.rb +++ b/spec/lib/gitlab/email/receiver_spec.rb @@ -33,7 +33,7 @@ metadata = receiver.mail_metadata - expect(metadata.keys).to match_array(%i[mail_uid from_address to_address mail_key references delivered_to envelope_to x_envelope_to meta received_recipients cc_address]) + expect(metadata.keys).to match_array(%i[mail_uid from_address to_address mail_key references delivered_to envelope_to x_envelope_to meta received_recipients cc_address x_original_to]) expect(metadata[:meta]).to include(client_id: client_id, project: project.full_path) expect(metadata[meta_key]).to eq(meta_value) end @@ -57,6 +57,9 @@ end context 'when the email contains a valid email address in a header' do + let(:incoming_email) { "incoming+gitlabhq/gitlabhq+auth_token@appmail.example.com" } + let(:meta_value) { [incoming_email] } + before do stub_incoming_email_setting(enabled: true, address: "incoming+%{key}@appmail.example.com") end @@ -64,7 +67,7 @@ context 'when in a Delivered-To header' do let(:email_raw) { fixture_file('emails/forwarded_new_issue.eml') } let(:meta_key) { :delivered_to } - let(:meta_value) { ["incoming+gitlabhq/gitlabhq+auth_token@appmail.example.com", "support@example.com"] } + let(:meta_value) { [incoming_email, "support@example.com"] } it_behaves_like 'successful receive' end @@ -72,7 +75,6 @@ context 'when in an Envelope-To header' do let(:email_raw) { fixture_file('emails/envelope_to_header.eml') } let(:meta_key) { :envelope_to } - let(:meta_value) { ["incoming+gitlabhq/gitlabhq+auth_token@appmail.example.com"] } it_behaves_like 'successful receive' end @@ -80,7 +82,6 @@ context 'when in an X-Envelope-To header' do let(:email_raw) { fixture_file('emails/x_envelope_to_header.eml') } let(:meta_key) { :x_envelope_to } - let(:meta_value) { ["incoming+gitlabhq/gitlabhq+auth_token@appmail.example.com"] } it_behaves_like 'successful receive' end @@ -88,7 +89,7 @@ context 'when enclosed with angle brackets in an Envelope-To header' do let(:email_raw) { fixture_file('emails/envelope_to_header_with_angle_brackets.eml') } let(:meta_key) { :envelope_to } - let(:meta_value) { [""] } + let(:meta_value) { ["<#{incoming_email}>"] } it_behaves_like 'successful receive' end @@ -106,7 +107,7 @@ context 'when all other headers are missing' do let(:email_raw) { fixture_file('emails/missing_delivered_to_header.eml') } let(:meta_key) { :received_recipients } - let(:meta_value) { ['incoming+gitlabhq/gitlabhq+auth_token@appmail.example.com', 'incoming+gitlabhq/gitlabhq@example.com'] } + let(:meta_value) { [incoming_email, 'incoming+gitlabhq/gitlabhq@example.com'] } describe 'it uses receive headers to find the key' do it_behaves_like 'successful receive' @@ -118,7 +119,7 @@ <<~EMAIL From: jake@example.com To: to@example.com - Cc: incoming+gitlabhq/gitlabhq+auth_token@appmail.example.com + Cc: #{incoming_email} Subject: Issue titile Issue description @@ -126,7 +127,23 @@ end let(:meta_key) { :cc_address } - let(:meta_value) { ["incoming+gitlabhq/gitlabhq+auth_token@appmail.example.com"] } + + it_behaves_like 'successful receive' + end + + context 'when in a X-Original-To header' do + let(:email_raw) do + <<~EMAIL + From: jake@example.com + To: to@example.com + X-Original-To: #{incoming_email} + Subject: Issue titile + + Issue description + EMAIL + end + + let(:meta_key) { :x_original_to } it_behaves_like 'successful receive' end diff --git a/spec/lib/gitlab/email/service_desk_receiver_spec.rb b/spec/lib/gitlab/email/service_desk_receiver_spec.rb index 4b67020471a036..18243ef2a0e515 100644 --- a/spec/lib/gitlab/email/service_desk_receiver_spec.rb +++ b/spec/lib/gitlab/email/service_desk_receiver_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::Email::ServiceDeskReceiver do +RSpec.describe Gitlab::Email::ServiceDeskReceiver, feature_category: :service_desk do let(:email) { fixture_file('emails/service_desk_custom_address.eml') } let(:receiver) { described_class.new(email) } @@ -31,6 +31,8 @@ end context 'when the email contains a valid email address in a header' do + let(:service_desk_email) { "support+project_slug-project_key@example.com" } + context 'when in a Delivered-To header' do let(:email) { fixture_file('emails/service_desk_custom_address_reply.eml') } @@ -49,12 +51,27 @@ it_behaves_like 'received successfully' end + context 'when in a X-Original-To header' do + let(:email) do + <<~EMAIL + From: from@example.com + To: to@example.com + X-Original-To: #{service_desk_email} + Subject: Issue titile + + Issue description + EMAIL + end + + it_behaves_like 'received successfully' + end + context 'when in a Cc header' do let(:email) do <<~EMAIL From: from@example.com To: to@example.com - Cc: support+project_slug-project_key@example.com + Cc: #{service_desk_email} Subject: Issue titile Issue description -- GitLab