actionmailbox 6.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +46 -0
- data/MIT-LICENSE +21 -0
- data/README.md +13 -0
- data/app/controllers/action_mailbox/base_controller.rb +34 -0
- data/app/controllers/action_mailbox/ingresses/mailgun/inbound_emails_controller.rb +103 -0
- data/app/controllers/action_mailbox/ingresses/mandrill/inbound_emails_controller.rb +82 -0
- data/app/controllers/action_mailbox/ingresses/postmark/inbound_emails_controller.rb +62 -0
- data/app/controllers/action_mailbox/ingresses/relay/inbound_emails_controller.rb +65 -0
- data/app/controllers/action_mailbox/ingresses/sendgrid/inbound_emails_controller.rb +54 -0
- data/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb +35 -0
- data/app/controllers/rails/conductor/action_mailbox/reroutes_controller.rb +19 -0
- data/app/controllers/rails/conductor/base_controller.rb +14 -0
- data/app/jobs/action_mailbox/incineration_job.rb +25 -0
- data/app/jobs/action_mailbox/routing_job.rb +13 -0
- data/app/models/action_mailbox/inbound_email.rb +49 -0
- data/app/models/action_mailbox/inbound_email/incineratable.rb +20 -0
- data/app/models/action_mailbox/inbound_email/incineratable/incineration.rb +26 -0
- data/app/models/action_mailbox/inbound_email/message_id.rb +38 -0
- data/app/models/action_mailbox/inbound_email/routable.rb +24 -0
- data/app/views/layouts/rails/conductor.html.erb +8 -0
- data/app/views/rails/conductor/action_mailbox/inbound_emails/index.html.erb +15 -0
- data/app/views/rails/conductor/action_mailbox/inbound_emails/new.html.erb +47 -0
- data/app/views/rails/conductor/action_mailbox/inbound_emails/show.html.erb +15 -0
- data/config/routes.rb +19 -0
- data/db/migrate/20180917164000_create_action_mailbox_tables.rb +13 -0
- data/lib/action_mailbox.rb +17 -0
- data/lib/action_mailbox/base.rb +118 -0
- data/lib/action_mailbox/callbacks.rb +34 -0
- data/lib/action_mailbox/engine.rb +33 -0
- data/lib/action_mailbox/gem_version.rb +17 -0
- data/lib/action_mailbox/mail_ext.rb +6 -0
- data/lib/action_mailbox/mail_ext/address_equality.rb +9 -0
- data/lib/action_mailbox/mail_ext/address_wrapping.rb +9 -0
- data/lib/action_mailbox/mail_ext/addresses.rb +29 -0
- data/lib/action_mailbox/mail_ext/from_source.rb +7 -0
- data/lib/action_mailbox/mail_ext/recipients.rb +9 -0
- data/lib/action_mailbox/relayer.rb +75 -0
- data/lib/action_mailbox/router.rb +42 -0
- data/lib/action_mailbox/router/route.rb +42 -0
- data/lib/action_mailbox/routing.rb +22 -0
- data/lib/action_mailbox/test_case.rb +12 -0
- data/lib/action_mailbox/test_helper.rb +48 -0
- data/lib/action_mailbox/version.rb +10 -0
- data/lib/rails/generators/installer.rb +10 -0
- data/lib/rails/generators/mailbox/USAGE +12 -0
- data/lib/rails/generators/mailbox/mailbox_generator.rb +32 -0
- data/lib/rails/generators/mailbox/templates/application_mailbox.rb.tt +3 -0
- data/lib/rails/generators/mailbox/templates/mailbox.rb.tt +4 -0
- data/lib/rails/generators/test_unit/mailbox_generator.rb +20 -0
- data/lib/rails/generators/test_unit/templates/mailbox_test.rb.tt +11 -0
- data/lib/tasks/ingress.rake +72 -0
- data/lib/tasks/install.rake +20 -0
- metadata +186 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f760915fec826c10aea46573b37d574dff15a37ff55ab4c3709de444aba59765
|
4
|
+
data.tar.gz: 860e24cbc7f89114761410cfbbb1a83cc7e43e273946bb6308433d725aedd241
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: db933c1bea644124576ff433a957e3ee6dbaf08fd8c4d72b9a9fb6b6758a63ff2c343e7c5b31a89886297958f685524b14413540ea4ac64b6a036f78f856a17e
|
7
|
+
data.tar.gz: ce37067d7c26c6e705f566074343745346f6c7d020a071b0c1cfd21480f067ae48cfef6476105a1e793524a4f36b0c31a85c835467745bafb11a338c518e0124
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
## Rails 6.0.2 (December 13, 2019) ##
|
2
|
+
|
3
|
+
* No changes.
|
4
|
+
|
5
|
+
|
6
|
+
## Rails 6.0.1 (November 5, 2019) ##
|
7
|
+
|
8
|
+
* No changes.
|
9
|
+
|
10
|
+
|
11
|
+
## Rails 6.0.0 (August 16, 2019) ##
|
12
|
+
|
13
|
+
* Fix Bcc header not being included with emails from `create_inbound_email_from` test helpers.
|
14
|
+
|
15
|
+
*jduff*
|
16
|
+
|
17
|
+
|
18
|
+
## Rails 6.0.0.rc2 (July 22, 2019) ##
|
19
|
+
|
20
|
+
* No changes.
|
21
|
+
|
22
|
+
|
23
|
+
## Rails 6.0.0.rc1 (April 24, 2019) ##
|
24
|
+
|
25
|
+
* No changes.
|
26
|
+
|
27
|
+
|
28
|
+
## Rails 6.0.0.beta3 (March 11, 2019) ##
|
29
|
+
|
30
|
+
* No changes.
|
31
|
+
|
32
|
+
|
33
|
+
## Rails 6.0.0.beta2 (February 25, 2019) ##
|
34
|
+
|
35
|
+
* Allow skipping incineration of processed emails.
|
36
|
+
|
37
|
+
This can be done by setting `config.action_mailbox.incinerate` to `false`.
|
38
|
+
|
39
|
+
*Pratik Naik*
|
40
|
+
|
41
|
+
|
42
|
+
## Rails 6.0.0.beta1 (January 18, 2019) ##
|
43
|
+
|
44
|
+
* Added to Rails.
|
45
|
+
|
46
|
+
*DHH*
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2019 Basecamp, LLC
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Action Mailbox
|
2
|
+
|
3
|
+
Action Mailbox routes incoming emails to controller-like mailboxes for processing in Rails. It ships with ingresses for Mailgun, Mandrill, Postmark, and SendGrid. You can also handle inbound mails directly via the built-in Exim, Postfix, and Qmail ingresses.
|
4
|
+
|
5
|
+
The inbound emails are turned into `InboundEmail` records using Active Record and feature lifecycle tracking, storage of the original email on cloud storage via Active Storage, and responsible data handling with on-by-default incineration.
|
6
|
+
|
7
|
+
These inbound emails are routed asynchronously using Active Job to one or several dedicated mailboxes, which are capable of interacting directly with the rest of your domain model.
|
8
|
+
|
9
|
+
You can read more about Action Mailbox in the [Action Mailbox Basics](https://edgeguides.rubyonrails.org/action_mailbox_basics.html) guide.
|
10
|
+
|
11
|
+
## License
|
12
|
+
|
13
|
+
Action Mailbox is released under the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionMailbox
|
4
|
+
# The base class for all Action Mailbox ingress controllers.
|
5
|
+
class BaseController < ActionController::Base
|
6
|
+
skip_forgery_protection if default_protect_from_forgery
|
7
|
+
|
8
|
+
before_action :ensure_configured
|
9
|
+
|
10
|
+
private
|
11
|
+
def ensure_configured
|
12
|
+
unless ActionMailbox.ingress == ingress_name
|
13
|
+
head :not_found
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def ingress_name
|
18
|
+
self.class.name.remove(/\AActionMailbox::Ingresses::/, /::InboundEmailsController\z/).underscore.to_sym
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def authenticate_by_password
|
23
|
+
if password.present?
|
24
|
+
http_basic_authenticate_or_request_with name: "actionmailbox", password: password, realm: "Action Mailbox"
|
25
|
+
else
|
26
|
+
raise ArgumentError, "Missing required ingress credentials"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def password
|
31
|
+
Rails.application.credentials.dig(:action_mailbox, :ingress_password) || ENV["RAILS_INBOUND_EMAIL_PASSWORD"]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionMailbox
|
4
|
+
# Ingests inbound emails from Mailgun. Requires the following parameters:
|
5
|
+
#
|
6
|
+
# - +body-mime+: The full RFC 822 message
|
7
|
+
# - +timestamp+: The current time according to Mailgun as the number of seconds passed since the UNIX epoch
|
8
|
+
# - +token+: A randomly-generated, 50-character string
|
9
|
+
# - +signature+: A hexadecimal HMAC-SHA256 of the timestamp concatenated with the token, generated using the Mailgun API key
|
10
|
+
#
|
11
|
+
# Authenticates requests by validating their signatures.
|
12
|
+
#
|
13
|
+
# Returns:
|
14
|
+
#
|
15
|
+
# - <tt>204 No Content</tt> if an inbound email is successfully recorded and enqueued for routing to the appropriate mailbox
|
16
|
+
# - <tt>401 Unauthorized</tt> if the request's signature could not be validated, or if its timestamp is more than 2 minutes old
|
17
|
+
# - <tt>404 Not Found</tt> if Action Mailbox is not configured to accept inbound emails from Mailgun
|
18
|
+
# - <tt>422 Unprocessable Entity</tt> if the request is missing required parameters
|
19
|
+
# - <tt>500 Server Error</tt> if the Mailgun API key is missing, or one of the Active Record database,
|
20
|
+
# the Active Storage service, or the Active Job backend is misconfigured or unavailable
|
21
|
+
#
|
22
|
+
# == Usage
|
23
|
+
#
|
24
|
+
# 1. Give Action Mailbox your {Mailgun API key}[https://help.mailgun.com/hc/en-us/articles/203380100-Where-can-I-find-my-API-key-and-SMTP-credentials-]
|
25
|
+
# so it can authenticate requests to the Mailgun ingress.
|
26
|
+
#
|
27
|
+
# Use <tt>rails credentials:edit</tt> to add your API key to your application's encrypted credentials under
|
28
|
+
# +action_mailbox.mailgun_api_key+, where Action Mailbox will automatically find it:
|
29
|
+
#
|
30
|
+
# action_mailbox:
|
31
|
+
# mailgun_api_key: ...
|
32
|
+
#
|
33
|
+
# Alternatively, provide your API key in the +MAILGUN_INGRESS_API_KEY+ environment variable.
|
34
|
+
#
|
35
|
+
# 2. Tell Action Mailbox to accept emails from Mailgun:
|
36
|
+
#
|
37
|
+
# # config/environments/production.rb
|
38
|
+
# config.action_mailbox.ingress = :mailgun
|
39
|
+
#
|
40
|
+
# 3. {Configure Mailgun}[https://documentation.mailgun.com/en/latest/user_manual.html#receiving-forwarding-and-storing-messages]
|
41
|
+
# to forward inbound emails to +/rails/action_mailbox/mailgun/inbound_emails/mime+.
|
42
|
+
#
|
43
|
+
# If your application lived at <tt>https://example.com</tt>, you would specify the fully-qualified URL
|
44
|
+
# <tt>https://example.com/rails/action_mailbox/mailgun/inbound_emails/mime</tt>.
|
45
|
+
class Ingresses::Mailgun::InboundEmailsController < ActionMailbox::BaseController
|
46
|
+
before_action :authenticate
|
47
|
+
|
48
|
+
def create
|
49
|
+
ActionMailbox::InboundEmail.create_and_extract_message_id! params.require("body-mime")
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def authenticate
|
54
|
+
head :unauthorized unless authenticated?
|
55
|
+
end
|
56
|
+
|
57
|
+
def authenticated?
|
58
|
+
if key.present?
|
59
|
+
Authenticator.new(
|
60
|
+
key: key,
|
61
|
+
timestamp: params.require(:timestamp),
|
62
|
+
token: params.require(:token),
|
63
|
+
signature: params.require(:signature)
|
64
|
+
).authenticated?
|
65
|
+
else
|
66
|
+
raise ArgumentError, <<~MESSAGE.squish
|
67
|
+
Missing required Mailgun API key. Set action_mailbox.mailgun_api_key in your application's
|
68
|
+
encrypted credentials or provide the MAILGUN_INGRESS_API_KEY environment variable.
|
69
|
+
MESSAGE
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def key
|
74
|
+
Rails.application.credentials.dig(:action_mailbox, :mailgun_api_key) || ENV["MAILGUN_INGRESS_API_KEY"]
|
75
|
+
end
|
76
|
+
|
77
|
+
class Authenticator
|
78
|
+
attr_reader :key, :timestamp, :token, :signature
|
79
|
+
|
80
|
+
def initialize(key:, timestamp:, token:, signature:)
|
81
|
+
@key, @timestamp, @token, @signature = key, Integer(timestamp), token, signature
|
82
|
+
end
|
83
|
+
|
84
|
+
def authenticated?
|
85
|
+
signed? && recent?
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def signed?
|
90
|
+
ActiveSupport::SecurityUtils.secure_compare signature, expected_signature
|
91
|
+
end
|
92
|
+
|
93
|
+
# Allow for 2 minutes of drift between Mailgun time and local server time.
|
94
|
+
def recent?
|
95
|
+
Time.at(timestamp) >= 2.minutes.ago
|
96
|
+
end
|
97
|
+
|
98
|
+
def expected_signature
|
99
|
+
OpenSSL::HMAC.hexdigest OpenSSL::Digest::SHA256.new, key, "#{timestamp}#{token}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionMailbox
|
4
|
+
# Ingests inbound emails from Mandrill.
|
5
|
+
#
|
6
|
+
# Requires a +mandrill_events+ parameter containing a JSON array of Mandrill inbound email event objects.
|
7
|
+
# Each event is expected to have a +msg+ object containing a full RFC 822 message in its +raw_msg+ property.
|
8
|
+
#
|
9
|
+
# Returns:
|
10
|
+
#
|
11
|
+
# - <tt>204 No Content</tt> if an inbound email is successfully recorded and enqueued for routing to the appropriate mailbox
|
12
|
+
# - <tt>401 Unauthorized</tt> if the request's signature could not be validated
|
13
|
+
# - <tt>404 Not Found</tt> if Action Mailbox is not configured to accept inbound emails from Mandrill
|
14
|
+
# - <tt>422 Unprocessable Entity</tt> if the request is missing required parameters
|
15
|
+
# - <tt>500 Server Error</tt> if the Mandrill API key is missing, or one of the Active Record database,
|
16
|
+
# the Active Storage service, or the Active Job backend is misconfigured or unavailable
|
17
|
+
class Ingresses::Mandrill::InboundEmailsController < ActionMailbox::BaseController
|
18
|
+
before_action :authenticate
|
19
|
+
|
20
|
+
def create
|
21
|
+
raw_emails.each { |raw_email| ActionMailbox::InboundEmail.create_and_extract_message_id! raw_email }
|
22
|
+
head :ok
|
23
|
+
rescue JSON::ParserError => error
|
24
|
+
logger.error error.message
|
25
|
+
head :unprocessable_entity
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def raw_emails
|
30
|
+
events.select { |event| event["event"] == "inbound" }.collect { |event| event.dig("msg", "raw_msg") }
|
31
|
+
end
|
32
|
+
|
33
|
+
def events
|
34
|
+
JSON.parse params.require(:mandrill_events)
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def authenticate
|
39
|
+
head :unauthorized unless authenticated?
|
40
|
+
end
|
41
|
+
|
42
|
+
def authenticated?
|
43
|
+
if key.present?
|
44
|
+
Authenticator.new(request, key).authenticated?
|
45
|
+
else
|
46
|
+
raise ArgumentError, <<~MESSAGE.squish
|
47
|
+
Missing required Mandrill API key. Set action_mailbox.mandrill_api_key in your application's
|
48
|
+
encrypted credentials or provide the MANDRILL_INGRESS_API_KEY environment variable.
|
49
|
+
MESSAGE
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def key
|
54
|
+
Rails.application.credentials.dig(:action_mailbox, :mandrill_api_key) || ENV["MANDRILL_INGRESS_API_KEY"]
|
55
|
+
end
|
56
|
+
|
57
|
+
class Authenticator
|
58
|
+
attr_reader :request, :key
|
59
|
+
|
60
|
+
def initialize(request, key)
|
61
|
+
@request, @key = request, key
|
62
|
+
end
|
63
|
+
|
64
|
+
def authenticated?
|
65
|
+
ActiveSupport::SecurityUtils.secure_compare given_signature, expected_signature
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
def given_signature
|
70
|
+
request.headers["X-Mandrill-Signature"]
|
71
|
+
end
|
72
|
+
|
73
|
+
def expected_signature
|
74
|
+
Base64.strict_encode64 OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, key, message)
|
75
|
+
end
|
76
|
+
|
77
|
+
def message
|
78
|
+
request.url + request.POST.sort.flatten.join
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionMailbox
|
4
|
+
# Ingests inbound emails from Postmark. Requires a +RawEmail+ parameter containing a full RFC 822 message.
|
5
|
+
#
|
6
|
+
# Authenticates requests using HTTP basic access authentication. The username is always +actionmailbox+, and the
|
7
|
+
# password is read from the application's encrypted credentials or an environment variable. See the Usage section below.
|
8
|
+
#
|
9
|
+
# Note that basic authentication is insecure over unencrypted HTTP. An attacker that intercepts cleartext requests to
|
10
|
+
# the Postmark ingress can learn its password. You should only use the Postmark ingress over HTTPS.
|
11
|
+
#
|
12
|
+
# Returns:
|
13
|
+
#
|
14
|
+
# - <tt>204 No Content</tt> if an inbound email is successfully recorded and enqueued for routing to the appropriate mailbox
|
15
|
+
# - <tt>401 Unauthorized</tt> if the request's signature could not be validated
|
16
|
+
# - <tt>404 Not Found</tt> if Action Mailbox is not configured to accept inbound emails from Postmark
|
17
|
+
# - <tt>422 Unprocessable Entity</tt> if the request is missing the required +RawEmail+ parameter
|
18
|
+
# - <tt>500 Server Error</tt> if the ingress password is not configured, or if one of the Active Record database,
|
19
|
+
# the Active Storage service, or the Active Job backend is misconfigured or unavailable
|
20
|
+
#
|
21
|
+
# == Usage
|
22
|
+
#
|
23
|
+
# 1. Tell Action Mailbox to accept emails from Postmark:
|
24
|
+
#
|
25
|
+
# # config/environments/production.rb
|
26
|
+
# config.action_mailbox.ingress = :postmark
|
27
|
+
#
|
28
|
+
# 2. Generate a strong password that Action Mailbox can use to authenticate requests to the Postmark ingress.
|
29
|
+
#
|
30
|
+
# Use <tt>rails credentials:edit</tt> to add the password to your application's encrypted credentials under
|
31
|
+
# +action_mailbox.ingress_password+, where Action Mailbox will automatically find it:
|
32
|
+
#
|
33
|
+
# action_mailbox:
|
34
|
+
# ingress_password: ...
|
35
|
+
#
|
36
|
+
# Alternatively, provide the password in the +RAILS_INBOUND_EMAIL_PASSWORD+ environment variable.
|
37
|
+
#
|
38
|
+
# 3. {Configure Postmark}[https://postmarkapp.com/manual#configure-your-inbound-webhook-url] to forward inbound emails
|
39
|
+
# to +/rails/action_mailbox/postmark/inbound_emails+ with the username +actionmailbox+ and the password you
|
40
|
+
# previously generated. If your application lived at <tt>https://example.com</tt>, you would configure your
|
41
|
+
# Postmark inbound webhook with the following fully-qualified URL:
|
42
|
+
#
|
43
|
+
# https://actionmailbox:PASSWORD@example.com/rails/action_mailbox/postmark/inbound_emails
|
44
|
+
#
|
45
|
+
# *NOTE:* When configuring your Postmark inbound webhook, be sure to check the box labeled *"Include raw email
|
46
|
+
# content in JSON payload"*. Action Mailbox needs the raw email content to work.
|
47
|
+
class Ingresses::Postmark::InboundEmailsController < ActionMailbox::BaseController
|
48
|
+
before_action :authenticate_by_password
|
49
|
+
|
50
|
+
def create
|
51
|
+
ActionMailbox::InboundEmail.create_and_extract_message_id! params.require("RawEmail")
|
52
|
+
rescue ActionController::ParameterMissing => error
|
53
|
+
logger.error <<~MESSAGE
|
54
|
+
#{error.message}
|
55
|
+
|
56
|
+
When configuring your Postmark inbound webhook, be sure to check the box
|
57
|
+
labeled "Include raw email content in JSON payload".
|
58
|
+
MESSAGE
|
59
|
+
head :unprocessable_entity
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionMailbox
|
4
|
+
# Ingests inbound emails relayed from an SMTP server.
|
5
|
+
#
|
6
|
+
# Authenticates requests using HTTP basic access authentication. The username is always +actionmailbox+, and the
|
7
|
+
# password is read from the application's encrypted credentials or an environment variable. See the Usage section below.
|
8
|
+
#
|
9
|
+
# Note that basic authentication is insecure over unencrypted HTTP. An attacker that intercepts cleartext requests to
|
10
|
+
# the ingress can learn its password. You should only use this ingress over HTTPS.
|
11
|
+
#
|
12
|
+
# Returns:
|
13
|
+
#
|
14
|
+
# - <tt>204 No Content</tt> if an inbound email is successfully recorded and enqueued for routing to the appropriate mailbox
|
15
|
+
# - <tt>401 Unauthorized</tt> if the request could not be authenticated
|
16
|
+
# - <tt>404 Not Found</tt> if Action Mailbox is not configured to accept inbound emails relayed from an SMTP server
|
17
|
+
# - <tt>415 Unsupported Media Type</tt> if the request does not contain an RFC 822 message
|
18
|
+
# - <tt>500 Server Error</tt> if the ingress password is not configured, or if one of the Active Record database,
|
19
|
+
# the Active Storage service, or the Active Job backend is misconfigured or unavailable
|
20
|
+
#
|
21
|
+
# == Usage
|
22
|
+
#
|
23
|
+
# 1. Tell Action Mailbox to accept emails from an SMTP relay:
|
24
|
+
#
|
25
|
+
# # config/environments/production.rb
|
26
|
+
# config.action_mailbox.ingress = :relay
|
27
|
+
#
|
28
|
+
# 2. Generate a strong password that Action Mailbox can use to authenticate requests to the ingress.
|
29
|
+
#
|
30
|
+
# Use <tt>rails credentials:edit</tt> to add the password to your application's encrypted credentials under
|
31
|
+
# +action_mailbox.ingress_password+, where Action Mailbox will automatically find it:
|
32
|
+
#
|
33
|
+
# action_mailbox:
|
34
|
+
# ingress_password: ...
|
35
|
+
#
|
36
|
+
# Alternatively, provide the password in the +RAILS_INBOUND_EMAIL_PASSWORD+ environment variable.
|
37
|
+
#
|
38
|
+
# 3. Configure your SMTP server to pipe inbound emails to the appropriate ingress command, providing the +URL+ of the
|
39
|
+
# relay ingress and the +INGRESS_PASSWORD+ you previously generated.
|
40
|
+
#
|
41
|
+
# If your application lives at <tt>https://example.com</tt>, you would configure the Postfix SMTP server to pipe
|
42
|
+
# inbound emails to the following command:
|
43
|
+
#
|
44
|
+
# bin/rails action_mailbox:ingress:postfix URL=https://example.com/rails/action_mailbox/postfix/inbound_emails INGRESS_PASSWORD=...
|
45
|
+
#
|
46
|
+
# Built-in ingress commands are available for these popular SMTP servers:
|
47
|
+
#
|
48
|
+
# - Exim (<tt>bin/rails action_mailbox:ingress:exim)
|
49
|
+
# - Postfix (<tt>bin/rails action_mailbox:ingress:postfix)
|
50
|
+
# - Qmail (<tt>bin/rails action_mailbox:ingress:qmail)
|
51
|
+
class Ingresses::Relay::InboundEmailsController < ActionMailbox::BaseController
|
52
|
+
before_action :authenticate_by_password, :require_valid_rfc822_message
|
53
|
+
|
54
|
+
def create
|
55
|
+
ActionMailbox::InboundEmail.create_and_extract_message_id! request.body.read
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def require_valid_rfc822_message
|
60
|
+
unless request.content_type == "message/rfc822"
|
61
|
+
head :unsupported_media_type
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionMailbox
|
4
|
+
# Ingests inbound emails from SendGrid. Requires an +email+ parameter containing a full RFC 822 message.
|
5
|
+
#
|
6
|
+
# Authenticates requests using HTTP basic access authentication. The username is always +actionmailbox+, and the
|
7
|
+
# password is read from the application's encrypted credentials or an environment variable. See the Usage section below.
|
8
|
+
#
|
9
|
+
# Note that basic authentication is insecure over unencrypted HTTP. An attacker that intercepts cleartext requests to
|
10
|
+
# the SendGrid ingress can learn its password. You should only use the SendGrid ingress over HTTPS.
|
11
|
+
#
|
12
|
+
# Returns:
|
13
|
+
#
|
14
|
+
# - <tt>204 No Content</tt> if an inbound email is successfully recorded and enqueued for routing to the appropriate mailbox
|
15
|
+
# - <tt>401 Unauthorized</tt> if the request's signature could not be validated
|
16
|
+
# - <tt>404 Not Found</tt> if Action Mailbox is not configured to accept inbound emails from SendGrid
|
17
|
+
# - <tt>422 Unprocessable Entity</tt> if the request is missing the required +email+ parameter
|
18
|
+
# - <tt>500 Server Error</tt> if the ingress password is not configured, or if one of the Active Record database,
|
19
|
+
# the Active Storage service, or the Active Job backend is misconfigured or unavailable
|
20
|
+
#
|
21
|
+
# == Usage
|
22
|
+
#
|
23
|
+
# 1. Tell Action Mailbox to accept emails from SendGrid:
|
24
|
+
#
|
25
|
+
# # config/environments/production.rb
|
26
|
+
# config.action_mailbox.ingress = :sendgrid
|
27
|
+
#
|
28
|
+
# 2. Generate a strong password that Action Mailbox can use to authenticate requests to the SendGrid ingress.
|
29
|
+
#
|
30
|
+
# Use <tt>rails credentials:edit</tt> to add the password to your application's encrypted credentials under
|
31
|
+
# +action_mailbox.ingress_password+, where Action Mailbox will automatically find it:
|
32
|
+
#
|
33
|
+
# action_mailbox:
|
34
|
+
# ingress_password: ...
|
35
|
+
#
|
36
|
+
# Alternatively, provide the password in the +RAILS_INBOUND_EMAIL_PASSWORD+ environment variable.
|
37
|
+
#
|
38
|
+
# 3. {Configure SendGrid Inbound Parse}[https://sendgrid.com/docs/for-developers/parsing-email/setting-up-the-inbound-parse-webhook/]
|
39
|
+
# to forward inbound emails to +/rails/action_mailbox/sendgrid/inbound_emails+ with the username +actionmailbox+ and
|
40
|
+
# the password you previously generated. If your application lived at <tt>https://example.com</tt>, you would
|
41
|
+
# configure SendGrid with the following fully-qualified URL:
|
42
|
+
#
|
43
|
+
# https://actionmailbox:PASSWORD@example.com/rails/action_mailbox/sendgrid/inbound_emails
|
44
|
+
#
|
45
|
+
# *NOTE:* When configuring your SendGrid Inbound Parse webhook, be sure to check the box labeled *"Post the raw,
|
46
|
+
# full MIME message."* Action Mailbox needs the raw MIME message to work.
|
47
|
+
class Ingresses::Sendgrid::InboundEmailsController < ActionMailbox::BaseController
|
48
|
+
before_action :authenticate_by_password
|
49
|
+
|
50
|
+
def create
|
51
|
+
ActionMailbox::InboundEmail.create_and_extract_message_id! params.require(:email)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|