actionmailbox 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +159 -0
- data/LICENSE +21 -0
- data/README.md +278 -0
- data/Rakefile +27 -0
- data/actionmailbox.gemspec +27 -0
- data/app/controllers/action_mailbox/base_controller.rb +43 -0
- data/app/controllers/action_mailbox/ingresses/amazon/inbound_emails_controller.rb +50 -0
- data/app/controllers/action_mailbox/ingresses/mailgun/inbound_emails_controller.rb +99 -0
- data/app/controllers/action_mailbox/ingresses/mandrill/inbound_emails_controller.rb +78 -0
- data/app/controllers/action_mailbox/ingresses/postfix/inbound_emails_controller.rb +55 -0
- data/app/controllers/action_mailbox/ingresses/sendgrid/inbound_emails_controller.rb +50 -0
- data/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb +27 -0
- data/app/controllers/rails/conductor/action_mailbox/reroutes_controller.rb +15 -0
- data/app/controllers/rails/conductor/base_controller.rb +10 -0
- data/app/jobs/action_mailbox/incineration_job.rb +18 -0
- data/app/jobs/action_mailbox/routing_job.rb +9 -0
- data/app/models/action_mailbox/inbound_email.rb +43 -0
- data/app/models/action_mailbox/inbound_email/incineratable.rb +18 -0
- data/app/models/action_mailbox/inbound_email/incineratable/incineration.rb +22 -0
- data/app/models/action_mailbox/inbound_email/message_id.rb +36 -0
- data/app/models/action_mailbox/inbound_email/routable.rb +22 -0
- data/app/views/layouts/rails/conductor.html.erb +7 -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 +42 -0
- data/app/views/rails/conductor/action_mailbox/inbound_emails/show.html.erb +15 -0
- data/bin/test +5 -0
- data/config/routes.rb +19 -0
- data/db/migrate/20180917164000_create_action_mailbox_tables.rb +11 -0
- data/lib/action_mailbox.rb +15 -0
- data/lib/action_mailbox/base.rb +111 -0
- data/lib/action_mailbox/callbacks.rb +32 -0
- data/lib/action_mailbox/engine.rb +34 -0
- data/lib/action_mailbox/mail_ext.rb +4 -0
- data/lib/action_mailbox/mail_ext/address_equality.rb +5 -0
- data/lib/action_mailbox/mail_ext/address_wrapping.rb +5 -0
- data/lib/action_mailbox/mail_ext/addresses.rb +25 -0
- data/lib/action_mailbox/mail_ext/from_source.rb +5 -0
- data/lib/action_mailbox/mail_ext/recipients.rb +5 -0
- data/lib/action_mailbox/postfix_relayer.rb +67 -0
- data/lib/action_mailbox/router.rb +38 -0
- data/lib/action_mailbox/router/route.rb +38 -0
- data/lib/action_mailbox/routing.rb +20 -0
- data/lib/action_mailbox/test_case.rb +8 -0
- data/lib/action_mailbox/test_helper.rb +42 -0
- data/lib/action_mailbox/version.rb +3 -0
- data/lib/tasks/ingress.rake +24 -0
- data/lib/tasks/install.rake +20 -0
- data/lib/templates/installer.rb +4 -0
- data/lib/templates/mailboxes/application_mailbox.rb +3 -0
- data/test/controllers/ingresses/amazon/inbound_emails_controller_test.rb +20 -0
- data/test/controllers/ingresses/mailgun/inbound_emails_controller_test.rb +89 -0
- data/test/controllers/ingresses/mandrill/inbound_emails_controller_test.rb +58 -0
- data/test/controllers/ingresses/postfix/inbound_emails_controller_test.rb +54 -0
- data/test/controllers/ingresses/sendgrid/inbound_emails_controller_test.rb +44 -0
- data/test/dummy/.babelrc +18 -0
- data/test/dummy/.gitignore +3 -0
- data/test/dummy/.postcssrc.yml +3 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/config/manifest.js +3 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/assets/stylesheets/scaffold.css +80 -0
- data/test/dummy/app/channels/application_cable/channel.rb +4 -0
- data/test/dummy/app/channels/application_cable/connection.rb +4 -0
- data/test/dummy/app/controllers/application_controller.rb +2 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/javascript/packs/application.js +0 -0
- data/test/dummy/app/jobs/application_job.rb +2 -0
- data/test/dummy/app/mailboxes/application_mailbox.rb +2 -0
- data/test/dummy/app/mailboxes/messages_mailbox.rb +4 -0
- data/test/dummy/app/mailers/application_mailer.rb +4 -0
- data/test/dummy/app/models/application_record.rb +3 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +36 -0
- data/test/dummy/bin/update +31 -0
- data/test/dummy/bin/yarn +11 -0
- data/test/dummy/config.ru +5 -0
- data/test/dummy/config/application.rb +19 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/cable.yml +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +63 -0
- data/test/dummy/config/environments/production.rb +96 -0
- data/test/dummy/config/environments/test.rb +46 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/test/dummy/config/initializers/assets.rb +14 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/content_security_policy.rb +22 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +33 -0
- data/test/dummy/config/puma.rb +34 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/config/storage.yml +35 -0
- data/test/dummy/config/webpack/development.js +3 -0
- data/test/dummy/config/webpack/environment.js +3 -0
- data/test/dummy/config/webpack/production.js +3 -0
- data/test/dummy/config/webpack/test.js +3 -0
- data/test/dummy/config/webpacker.yml +65 -0
- data/test/dummy/db/migrate/20180208205311_create_action_mailroom_tables.rb +11 -0
- data/test/dummy/db/migrate/20180212164506_create_active_storage_tables.active_storage.rb +26 -0
- data/test/dummy/db/schema.rb +43 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/package.json +11 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/test/dummy/public/apple-touch-icon.png +0 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/storage/.keep +0 -0
- data/test/dummy/yarn.lock +6071 -0
- data/test/fixtures/files/welcome.eml +631 -0
- data/test/jobs/incineration_job_test.rb +17 -0
- data/test/test_helper.rb +54 -0
- data/test/unit/inbound_email/incineration_test.rb +45 -0
- data/test/unit/inbound_email/message_id_test.rb +13 -0
- data/test/unit/inbound_email_test.rb +13 -0
- data/test/unit/mail_ext/address_equality_test.rb +9 -0
- data/test/unit/mail_ext/address_wrapping_test.rb +11 -0
- data/test/unit/mail_ext/recipients_test.rb +33 -0
- data/test/unit/mailbox/bouncing_test.rb +29 -0
- data/test/unit/mailbox/callbacks_test.rb +75 -0
- data/test/unit/mailbox/routing_test.rb +30 -0
- data/test/unit/mailbox/state_test.rb +49 -0
- data/test/unit/postfix_relayer_test.rb +90 -0
- data/test/unit/router_test.rb +137 -0
- metadata +355 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
require "active_support/callbacks"
|
2
|
+
|
3
|
+
module ActionMailbox
|
4
|
+
# Defines the callbacks related to processing.
|
5
|
+
module Callbacks
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
include ActiveSupport::Callbacks
|
8
|
+
|
9
|
+
TERMINATOR = ->(mailbox, chain) do
|
10
|
+
chain.call
|
11
|
+
mailbox.finished_processing?
|
12
|
+
end
|
13
|
+
|
14
|
+
included do
|
15
|
+
define_callbacks :process, terminator: TERMINATOR, skip_after_callbacks_if_terminated: true
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def before_processing(*methods, &block)
|
20
|
+
set_callback(:process, :before, *methods, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
def after_processing(*methods, &block)
|
24
|
+
set_callback(:process, :after, *methods, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def around_processing(*methods, &block)
|
28
|
+
set_callback(:process, :around, *methods, &block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "rails/engine"
|
2
|
+
|
3
|
+
module ActionMailbox
|
4
|
+
class Engine < Rails::Engine
|
5
|
+
isolate_namespace ActionMailbox
|
6
|
+
config.eager_load_namespaces << ActionMailbox
|
7
|
+
|
8
|
+
config.action_mailbox = ActiveSupport::OrderedOptions.new
|
9
|
+
config.action_mailbox.incinerate_after = 30.days
|
10
|
+
|
11
|
+
config.action_mailbox.queues = ActiveSupport::InheritableOptions.new \
|
12
|
+
incineration: :action_mailbox_incineration, routing: :action_mailbox_routing
|
13
|
+
|
14
|
+
initializer "action_mailbox.config" do
|
15
|
+
config.after_initialize do |app|
|
16
|
+
ActionMailbox.logger = app.config.action_mailbox.logger || Rails.logger
|
17
|
+
ActionMailbox.incinerate_after = app.config.action_mailbox.incinerate_after || 30.days
|
18
|
+
ActionMailbox.queues = app.config.action_mailbox.queues || {}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
initializer "action_mailbox.ingress" do
|
23
|
+
config.after_initialize do |app|
|
24
|
+
if ActionMailbox.ingress = app.config.action_mailbox.ingress.presence
|
25
|
+
config.to_prepare do
|
26
|
+
if ingress_controller_class = "ActionMailbox::Ingresses::#{ActionMailbox.ingress.to_s.classify}::InboundEmailsController".safe_constantize
|
27
|
+
ingress_controller_class.prepare
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Mail::Message
|
2
|
+
def from_address
|
3
|
+
header[:from]&.address_list&.addresses&.first
|
4
|
+
end
|
5
|
+
|
6
|
+
def recipients_addresses
|
7
|
+
to_addresses + cc_addresses + bcc_addresses + x_original_to_addresses
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_addresses
|
11
|
+
Array(header[:to]&.address_list&.addresses)
|
12
|
+
end
|
13
|
+
|
14
|
+
def cc_addresses
|
15
|
+
Array(header[:cc]&.address_list&.addresses)
|
16
|
+
end
|
17
|
+
|
18
|
+
def bcc_addresses
|
19
|
+
Array(header[:bcc]&.address_list&.addresses)
|
20
|
+
end
|
21
|
+
|
22
|
+
def x_original_to_addresses
|
23
|
+
Array(header[:x_original_to]).collect { |header| Mail::Address.new header.to_s }
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_mailbox/version"
|
4
|
+
require "net/http"
|
5
|
+
require "uri"
|
6
|
+
|
7
|
+
module ActionMailbox
|
8
|
+
class PostfixRelayer
|
9
|
+
class Result < Struct.new(:output)
|
10
|
+
def success?
|
11
|
+
!failure?
|
12
|
+
end
|
13
|
+
|
14
|
+
def failure?
|
15
|
+
output.match?(/\A[45]\.\d{1,3}\.\d{1,3}(\s|\z)/)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
CONTENT_TYPE = "message/rfc822"
|
20
|
+
USER_AGENT = "Action Mailbox Postfix relayer v#{ActionMailbox::VERSION}"
|
21
|
+
|
22
|
+
attr_reader :uri, :username, :password
|
23
|
+
|
24
|
+
def initialize(url:, username: "actionmailbox", password:)
|
25
|
+
@uri, @username, @password = URI(url), username, password
|
26
|
+
end
|
27
|
+
|
28
|
+
def relay(source)
|
29
|
+
case response = post(source)
|
30
|
+
when Net::HTTPSuccess
|
31
|
+
Result.new "2.0.0 Successfully relayed message to Postfix ingress"
|
32
|
+
when Net::HTTPUnauthorized
|
33
|
+
Result.new "4.7.0 Invalid credentials for Postfix ingress"
|
34
|
+
else
|
35
|
+
Result.new "4.0.0 HTTP #{response.code}"
|
36
|
+
end
|
37
|
+
rescue IOError, SocketError, SystemCallError => error
|
38
|
+
Result.new "4.4.2 Network error relaying to Postfix ingress: #{error.message}"
|
39
|
+
rescue Timeout::Error
|
40
|
+
Result.new "4.4.2 Timed out relaying to Postfix ingress"
|
41
|
+
rescue => error
|
42
|
+
Result.new "4.0.0 Error relaying to Postfix ingress: #{error.message}"
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def post(source)
|
47
|
+
client.post uri, source,
|
48
|
+
"Content-Type" => CONTENT_TYPE,
|
49
|
+
"User-Agent" => USER_AGENT,
|
50
|
+
"Authorization" => "Basic #{Base64.strict_encode64(username + ":" + password)}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def client
|
54
|
+
@client ||= Net::HTTP.new(uri.host, uri.port).tap do |connection|
|
55
|
+
if uri.scheme == "https"
|
56
|
+
require "openssl"
|
57
|
+
|
58
|
+
connection.use_ssl = true
|
59
|
+
connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
60
|
+
end
|
61
|
+
|
62
|
+
connection.open_timeout = 1
|
63
|
+
connection.read_timeout = 10
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# Encapsulates the routes that live on the ApplicationMailbox and performs the actual routing when
|
2
|
+
# an inbound_email is received.
|
3
|
+
class ActionMailbox::Router
|
4
|
+
class RoutingError < StandardError; end
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@routes = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_routes(routes)
|
11
|
+
routes.each do |(address, mailbox_name)|
|
12
|
+
add_route address, to: mailbox_name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_route(address, to:)
|
17
|
+
routes.append Route.new(address, to: to)
|
18
|
+
end
|
19
|
+
|
20
|
+
def route(inbound_email)
|
21
|
+
if mailbox = match_to_mailbox(inbound_email)
|
22
|
+
mailbox.receive(inbound_email)
|
23
|
+
else
|
24
|
+
inbound_email.bounced!
|
25
|
+
|
26
|
+
raise RoutingError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
attr_reader :routes
|
32
|
+
|
33
|
+
def match_to_mailbox(inbound_email)
|
34
|
+
routes.detect { |route| route.match?(inbound_email) }.try(:mailbox_class)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
require "action_mailbox/router/route"
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# Encapsulates a route, which can then be matched against an inbound_email and provide a lookup of the matching
|
2
|
+
# mailbox class. See examples for the different route addresses and how to use them in the `ActionMailbox::Base`
|
3
|
+
# documentation.
|
4
|
+
class ActionMailbox::Router::Route
|
5
|
+
attr_reader :address, :mailbox_name
|
6
|
+
|
7
|
+
def initialize(address, to:)
|
8
|
+
@address, @mailbox_name = address, to
|
9
|
+
|
10
|
+
ensure_valid_address
|
11
|
+
end
|
12
|
+
|
13
|
+
def match?(inbound_email)
|
14
|
+
case address
|
15
|
+
when :all
|
16
|
+
true
|
17
|
+
when String
|
18
|
+
inbound_email.mail.recipients.any? { |recipient| address.casecmp?(recipient) }
|
19
|
+
when Regexp
|
20
|
+
inbound_email.mail.recipients.any? { |recipient| address.match?(recipient) }
|
21
|
+
when Proc
|
22
|
+
address.call(inbound_email)
|
23
|
+
else
|
24
|
+
address.match?(inbound_email)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def mailbox_class
|
29
|
+
"#{mailbox_name.to_s.camelize}Mailbox".constantize
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def ensure_valid_address
|
34
|
+
unless [ Symbol, String, Regexp, Proc ].any? { |klass| address.is_a?(klass) } || address.respond_to?(:match?)
|
35
|
+
raise ArgumentError, "Expected a Symbol, String, Regexp, Proc, or matchable, got #{address.inspect}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ActionMailbox
|
2
|
+
# See `ActionMailbox::Base` for how to specify routing.
|
3
|
+
module Routing
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
cattr_accessor :router, default: ActionMailbox::Router.new
|
8
|
+
end
|
9
|
+
|
10
|
+
class_methods do
|
11
|
+
def routing(routes)
|
12
|
+
router.add_routes(routes)
|
13
|
+
end
|
14
|
+
|
15
|
+
def route(inbound_email)
|
16
|
+
router.route(inbound_email)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "mail"
|
2
|
+
|
3
|
+
module ActionMailbox
|
4
|
+
module TestHelper
|
5
|
+
# Create an `InboundEmail` record using an eml fixture in the format of message/rfc822
|
6
|
+
# referenced with +fixture_name+ located in +test/fixtures/files/fixture_name+.
|
7
|
+
def create_inbound_email_from_fixture(fixture_name, status: :processing)
|
8
|
+
create_inbound_email_from_source file_fixture(fixture_name).read, status: status
|
9
|
+
end
|
10
|
+
|
11
|
+
# Create an `InboundEmail` by specifying it using `Mail.new` options. Example:
|
12
|
+
#
|
13
|
+
# create_inbound_email_from_mail(from: "david@loudthinking.com", subject: "Hello!")
|
14
|
+
def create_inbound_email_from_mail(status: :processing, **mail_options)
|
15
|
+
create_inbound_email_from_source Mail.new(mail_options).to_s, status: status
|
16
|
+
end
|
17
|
+
|
18
|
+
# Create an `InboundEmail` using the raw rfc822 `source` as text.
|
19
|
+
def create_inbound_email_from_source(source, status: :processing)
|
20
|
+
ActionMailbox::InboundEmail.create_and_extract_message_id! source, status: status
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
# Create an `InboundEmail` from fixture using the same arguments as `create_inbound_email_from_fixture`
|
25
|
+
# and immediately route it to processing.
|
26
|
+
def receive_inbound_email_from_fixture(*args)
|
27
|
+
create_inbound_email_from_fixture(*args).tap(&:route)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Create an `InboundEmail` from fixture using the same arguments as `create_inbound_email_from_mail`
|
31
|
+
# and immediately route it to processing.
|
32
|
+
def receive_inbound_email_from_mail(**kwargs)
|
33
|
+
create_inbound_email_from_mail(**kwargs).tap(&:route)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Create an `InboundEmail` from fixture using the same arguments as `create_inbound_email_from_source`
|
37
|
+
# and immediately route it to processing.
|
38
|
+
def receive_inbound_email_from_source(**kwargs)
|
39
|
+
create_inbound_email_from_source(**kwargs).tap(&:route)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :action_mailbox do
|
4
|
+
namespace :ingress do
|
5
|
+
desc "Pipe an inbound email from STDIN to the Postfix ingress (URL and INGRESS_PASSWORD required)"
|
6
|
+
task :postfix do
|
7
|
+
require "active_support"
|
8
|
+
require "active_support/core_ext/object/blank"
|
9
|
+
require "action_mailbox/postfix_relayer"
|
10
|
+
|
11
|
+
url, password = ENV.values_at("URL", "INGRESS_PASSWORD")
|
12
|
+
|
13
|
+
if url.blank? || password.blank?
|
14
|
+
print "4.3.5 URL and INGRESS_PASSWORD are required"
|
15
|
+
exit 1
|
16
|
+
end
|
17
|
+
|
18
|
+
ActionMailbox::PostfixRelayer.new(url: url, password: password).relay(STDIN.read).tap do |result|
|
19
|
+
print result.output
|
20
|
+
exit result.success?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :action_mailbox do
|
4
|
+
# Prevent migration installation task from showing up twice.
|
5
|
+
Rake::Task["install:migrations"].clear_comments
|
6
|
+
|
7
|
+
desc "Copy over the migration"
|
8
|
+
task install: %w[ environment run_installer copy_migrations ]
|
9
|
+
|
10
|
+
task :run_installer do
|
11
|
+
installer_template = File.expand_path("../templates/installer.rb", __dir__)
|
12
|
+
system "#{RbConfig.ruby} ./bin/rails app:template LOCATION=#{installer_template}"
|
13
|
+
end
|
14
|
+
|
15
|
+
task :copy_migrations do
|
16
|
+
Rake::Task["active_storage:install:migrations"].invoke
|
17
|
+
Rake::Task["railties:install:migrations"].reenable # Otherwise you can't run 2 migration copy tasks in one invocation
|
18
|
+
Rake::Task["action_mailbox:install:migrations"].invoke
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,4 @@
|
|
1
|
+
say "Copying application_mailbox.rb to app/mailboxes"
|
2
|
+
copy_file "#{__dir__}/mailboxes/application_mailbox.rb", "app/mailboxes/application_mailbox.rb"
|
3
|
+
|
4
|
+
environment "# Prepare the ingress controller used to receive mail\n# config.action_mailbox.ingress = :amazon\n\n", env: 'production'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
ActionMailbox::Ingresses::Amazon::InboundEmailsController.verifier =
|
4
|
+
Module.new { def self.authentic?(message); true; end }
|
5
|
+
|
6
|
+
class ActionMailbox::Ingresses::Amazon::InboundEmailsControllerTest < ActionDispatch::IntegrationTest
|
7
|
+
setup { ActionMailbox.ingress = :amazon }
|
8
|
+
|
9
|
+
test "receiving an inbound email from Amazon" do
|
10
|
+
assert_difference -> { ActionMailbox::InboundEmail.count }, +1 do
|
11
|
+
post rails_amazon_inbound_emails_url, params: { content: file_fixture("../files/welcome.eml").read }, as: :json
|
12
|
+
end
|
13
|
+
|
14
|
+
assert_response :no_content
|
15
|
+
|
16
|
+
inbound_email = ActionMailbox::InboundEmail.last
|
17
|
+
assert_equal file_fixture("../files/welcome.eml").read, inbound_email.raw_email.download
|
18
|
+
assert_equal "0CB459E0-0336-41DA-BC88-E6E28C697DDB@37signals.com", inbound_email.message_id
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
ENV["MAILGUN_INGRESS_API_KEY"] = "tbsy84uSV1Kt3ZJZELY2TmShPRs91E3yL4tzf96297vBCkDWgL"
|
4
|
+
|
5
|
+
class ActionMailbox::Ingresses::Mailgun::InboundEmailsControllerTest < ActionDispatch::IntegrationTest
|
6
|
+
setup { ActionMailbox.ingress = :mailgun }
|
7
|
+
|
8
|
+
test "receiving an inbound email from Mailgun" do
|
9
|
+
assert_difference -> { ActionMailbox::InboundEmail.count }, +1 do
|
10
|
+
travel_to "2018-10-09 15:15:00 EDT"
|
11
|
+
post rails_mailgun_inbound_emails_url, params: {
|
12
|
+
timestamp: 1539112500,
|
13
|
+
token: "7VwW7k6Ak7zcTwoSoNm7aTtbk1g67MKAnsYLfUB7PdszbgR5Xi",
|
14
|
+
signature: "ef24c5225322217bb065b80bb54eb4f9206d764e3e16abab07f0a64d1cf477cc",
|
15
|
+
"body-mime" => file_fixture("../files/welcome.eml").read
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
assert_response :no_content
|
20
|
+
|
21
|
+
inbound_email = ActionMailbox::InboundEmail.last
|
22
|
+
assert_equal file_fixture("../files/welcome.eml").read, inbound_email.raw_email.download
|
23
|
+
assert_equal "0CB459E0-0336-41DA-BC88-E6E28C697DDB@37signals.com", inbound_email.message_id
|
24
|
+
end
|
25
|
+
|
26
|
+
test "rejecting a delayed inbound email from Mailgun" do
|
27
|
+
assert_no_difference -> { ActionMailbox::InboundEmail.count } do
|
28
|
+
travel_to "2018-10-09 15:26:00 EDT"
|
29
|
+
post rails_mailgun_inbound_emails_url, params: {
|
30
|
+
timestamp: 1539112500,
|
31
|
+
token: "7VwW7k6Ak7zcTwoSoNm7aTtbk1g67MKAnsYLfUB7PdszbgR5Xi",
|
32
|
+
signature: "ef24c5225322217bb065b80bb54eb4f9206d764e3e16abab07f0a64d1cf477cc",
|
33
|
+
"body-mime" => file_fixture("../files/welcome.eml").read
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
assert_response :unauthorized
|
38
|
+
end
|
39
|
+
|
40
|
+
test "rejecting a forged inbound email from Mailgun" do
|
41
|
+
assert_no_difference -> { ActionMailbox::InboundEmail.count } do
|
42
|
+
travel_to "2018-10-09 15:15:00 EDT"
|
43
|
+
post rails_mailgun_inbound_emails_url, params: {
|
44
|
+
timestamp: 1539112500,
|
45
|
+
token: "Zx8mJBiGmiiyyfWnho3zKyjCg2pxLARoCuBM7X9AKCioShGiMX",
|
46
|
+
signature: "ef24c5225322217bb065b80bb54eb4f9206d764e3e16abab07f0a64d1cf477cc",
|
47
|
+
"body-mime" => file_fixture("../files/welcome.eml").read
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
assert_response :unauthorized
|
52
|
+
end
|
53
|
+
|
54
|
+
test "raising when the configured Mailgun API key is nil" do
|
55
|
+
switch_key_to nil do
|
56
|
+
assert_raises ArgumentError do
|
57
|
+
travel_to "2018-10-09 15:15:00 EDT"
|
58
|
+
post rails_mailgun_inbound_emails_url, params: {
|
59
|
+
timestamp: 1539112500,
|
60
|
+
token: "7VwW7k6Ak7zcTwoSoNm7aTtbk1g67MKAnsYLfUB7PdszbgR5Xi",
|
61
|
+
signature: "ef24c5225322217bb065b80bb54eb4f9206d764e3e16abab07f0a64d1cf477cc",
|
62
|
+
"body-mime" => file_fixture("../files/welcome.eml").read
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
test "raising when the configured Mailgun API key is blank" do
|
69
|
+
switch_key_to "" do
|
70
|
+
assert_raises ArgumentError do
|
71
|
+
travel_to "2018-10-09 15:15:00 EDT"
|
72
|
+
post rails_mailgun_inbound_emails_url, params: {
|
73
|
+
timestamp: 1539112500,
|
74
|
+
token: "7VwW7k6Ak7zcTwoSoNm7aTtbk1g67MKAnsYLfUB7PdszbgR5Xi",
|
75
|
+
signature: "ef24c5225322217bb065b80bb54eb4f9206d764e3e16abab07f0a64d1cf477cc",
|
76
|
+
"body-mime" => file_fixture("../files/welcome.eml").read
|
77
|
+
}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
def switch_key_to(new_key)
|
84
|
+
previous_key, ENV["MAILGUN_INGRESS_API_KEY"] = ENV["MAILGUN_INGRESS_API_KEY"], new_key
|
85
|
+
yield
|
86
|
+
ensure
|
87
|
+
ENV["MAILGUN_INGRESS_API_KEY"] = previous_key
|
88
|
+
end
|
89
|
+
end
|