FEATURE: Limit maximum recipients for group emails (#17971)
New maximum_recipients_per_new_group_email site setting can be used to prevent spam group emails with many recipients.
This commit is contained in:
parent
e49167b311
commit
b082f459c9
|
@ -2043,6 +2043,7 @@ en:
|
||||||
max_emails_per_day_per_user: "Maximum number of emails to send users per day. 0 to disable the limit"
|
max_emails_per_day_per_user: "Maximum number of emails to send users per day. 0 to disable the limit"
|
||||||
enable_staged_users: "Automatically create staged users when processing incoming emails."
|
enable_staged_users: "Automatically create staged users when processing incoming emails."
|
||||||
maximum_staged_users_per_email: "Maximum number of staged users created when processing an incoming email."
|
maximum_staged_users_per_email: "Maximum number of staged users created when processing an incoming email."
|
||||||
|
maximum_recipients_per_new_group_email: "Block incoming emails with too many recipients."
|
||||||
auto_generated_allowlist: "List of email addresses that won't be checked for auto-generated content. Example: foo@bar.com|discourse@bar.com"
|
auto_generated_allowlist: "List of email addresses that won't be checked for auto-generated content. Example: foo@bar.com|discourse@bar.com"
|
||||||
block_auto_generated_emails: "Block incoming emails identified as being auto generated."
|
block_auto_generated_emails: "Block incoming emails identified as being auto generated."
|
||||||
ignore_by_title: "Ignore incoming emails based on their title."
|
ignore_by_title: "Ignore incoming emails based on their title."
|
||||||
|
@ -3359,6 +3360,16 @@ en:
|
||||||
|
|
||||||
If you believe this is an error, [contact a staff member](%{base_url}/about).
|
If you believe this is an error, [contact a staff member](%{base_url}/about).
|
||||||
|
|
||||||
|
email_reject_too_many_recipients:
|
||||||
|
title: "Email Reject Too Many Recipients"
|
||||||
|
subject_template: "[%{email_prefix}] Email issue -- Too Many Recipients"
|
||||||
|
text_body_template: |
|
||||||
|
We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work.
|
||||||
|
|
||||||
|
You attempted to email more than %{max_recipients_count} people and our system automatically tagged your email as spam.
|
||||||
|
|
||||||
|
If you believe this is an error, [contact a staff member](%{base_url}/about).
|
||||||
|
|
||||||
email_error_notification:
|
email_error_notification:
|
||||||
title: "Email Error Notification"
|
title: "Email Error Notification"
|
||||||
subject_template: "[%{email_prefix}] Email issue -- POP authentication error"
|
subject_template: "[%{email_prefix}] Email issue -- POP authentication error"
|
||||||
|
|
|
@ -1202,6 +1202,9 @@ email:
|
||||||
max_emails_per_day_per_user: 100
|
max_emails_per_day_per_user: 100
|
||||||
enable_staged_users: true
|
enable_staged_users: true
|
||||||
maximum_staged_users_per_email: 10
|
maximum_staged_users_per_email: 10
|
||||||
|
maximum_recipients_per_new_group_email:
|
||||||
|
default: 10
|
||||||
|
min: 1
|
||||||
auto_generated_allowlist:
|
auto_generated_allowlist:
|
||||||
default: ""
|
default: ""
|
||||||
type: list
|
type: list
|
||||||
|
|
|
@ -67,6 +67,7 @@ module Email
|
||||||
when Email::Receiver::OldDestinationError then :email_reject_old_destination
|
when Email::Receiver::OldDestinationError then :email_reject_old_destination
|
||||||
when Email::Receiver::ReplyNotAllowedError then :email_reject_reply_not_allowed
|
when Email::Receiver::ReplyNotAllowedError then :email_reject_reply_not_allowed
|
||||||
when Email::Receiver::ReplyToDigestError then :email_reject_reply_to_digest
|
when Email::Receiver::ReplyToDigestError then :email_reject_reply_to_digest
|
||||||
|
when Email::Receiver::TooManyRecipientsError then :email_reject_too_many_recipients
|
||||||
else :email_reject_unrecognized_error
|
else :email_reject_unrecognized_error
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -96,6 +97,11 @@ module Email
|
||||||
template_args[:number_of_days] = SiteSetting.disallow_reply_by_email_after_days
|
template_args[:number_of_days] = SiteSetting.disallow_reply_by_email_after_days
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if message_template == :email_reject_too_many_recipients
|
||||||
|
template_args[:recipients_count] = e.recipients_count
|
||||||
|
template_args[:max_recipients_count] = SiteSetting.maximum_recipients_per_new_group_email
|
||||||
|
end
|
||||||
|
|
||||||
if message_template
|
if message_template
|
||||||
# inform the user about the rejection
|
# inform the user about the rejection
|
||||||
message = Mail::Message.new(mail_string)
|
message = Mail::Message.new(mail_string)
|
||||||
|
|
|
@ -33,6 +33,14 @@ module Email
|
||||||
class OldDestinationError < ProcessingError; end
|
class OldDestinationError < ProcessingError; end
|
||||||
class ReplyToDigestError < ProcessingError; end
|
class ReplyToDigestError < ProcessingError; end
|
||||||
|
|
||||||
|
class TooManyRecipientsError < ProcessingError
|
||||||
|
attr_reader :recipients_count
|
||||||
|
|
||||||
|
def initialize(recipients_count:)
|
||||||
|
@recipients_count = recipients_count
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
attr_reader :incoming_email
|
attr_reader :incoming_email
|
||||||
attr_reader :raw_email
|
attr_reader :raw_email
|
||||||
attr_reader :mail
|
attr_reader :mail
|
||||||
|
@ -156,6 +164,11 @@ module Email
|
||||||
raise UserNotFoundError unless SiteSetting.enable_staged_users
|
raise UserNotFoundError unless SiteSetting.enable_staged_users
|
||||||
end
|
end
|
||||||
|
|
||||||
|
recipients = get_all_recipients(@mail)
|
||||||
|
if recipients.size > SiteSetting.maximum_recipients_per_new_group_email
|
||||||
|
raise TooManyRecipientsError.new(recipients_count: recipients.size)
|
||||||
|
end
|
||||||
|
|
||||||
body, elided = select_body
|
body, elided = select_body
|
||||||
body ||= ""
|
body ||= ""
|
||||||
|
|
||||||
|
@ -230,6 +243,23 @@ module Email
|
||||||
raise SilencedUserError if user.silenced?
|
raise SilencedUserError if user.silenced?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_all_recipients(mail)
|
||||||
|
recipients = Set.new
|
||||||
|
|
||||||
|
%i(to cc bcc).each do |field|
|
||||||
|
next if mail[field].blank?
|
||||||
|
|
||||||
|
mail[field].each do |address_field|
|
||||||
|
begin
|
||||||
|
address_field.decoded
|
||||||
|
recipients << address_field.address.downcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
recipients
|
||||||
|
end
|
||||||
|
|
||||||
def is_bounce?
|
def is_bounce?
|
||||||
@mail.bounced? || bounce_key
|
@mail.bounced? || bounce_key
|
||||||
end
|
end
|
||||||
|
|
|
@ -193,4 +193,26 @@ RSpec.describe Email::Processor do
|
||||||
))
|
))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'when group email recipients exceeds maximum_recipients_per_new_group_email site setting' do
|
||||||
|
let(:mail) { file_from_fixtures("cc.eml", "emails").read }
|
||||||
|
|
||||||
|
it 'rejects the email with the right response' do
|
||||||
|
SiteSetting.maximum_recipients_per_new_group_email = 3
|
||||||
|
|
||||||
|
processor = Email::Processor.new(mail)
|
||||||
|
processor.process!
|
||||||
|
|
||||||
|
rejection_raw = ActionMailer::Base.deliveries.first.body.to_s
|
||||||
|
|
||||||
|
expect(rejection_raw).to eq(
|
||||||
|
I18n.t("system_messages.email_reject_too_many_recipients.text_body_template",
|
||||||
|
destination: '["someone@else.com"]',
|
||||||
|
former_title: 'The more, the merrier',
|
||||||
|
max_recipients_count: 3,
|
||||||
|
base_url: Discourse.base_url,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -883,6 +883,11 @@ RSpec.describe Email::Receiver do
|
||||||
expect(Topic.last.ordered_posts[-1].post_type).to eq(Post.types[:moderator_action])
|
expect(Topic.last.ordered_posts[-1].post_type).to eq(Post.types[:moderator_action])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "rejects messages with too many recipients" do
|
||||||
|
SiteSetting.maximum_recipients_per_new_group_email = 3
|
||||||
|
expect { process(:cc) }.to raise_error(Email::Receiver::TooManyRecipientsError)
|
||||||
|
end
|
||||||
|
|
||||||
describe "reply-to header" do
|
describe "reply-to header" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.block_auto_generated_emails = false
|
SiteSetting.block_auto_generated_emails = false
|
||||||
|
|
Loading…
Reference in New Issue