Read ingress passwords/API keys from encrypted credentials
Fall back to ENV for people who prefer that approach.
This commit is contained in:
parent
cb041ddc7e
commit
7755f9b381
@ -1,15 +1,33 @@
|
||||
class ActionMailbox::BaseController < ActionController::Base
|
||||
skip_forgery_protection
|
||||
|
||||
before_action :ensure_configured
|
||||
|
||||
private
|
||||
def authenticate
|
||||
if username.present? && password.present?
|
||||
http_basic_authenticate_or_request_with username: username, password: password, realm: "Action Mailbox"
|
||||
def ensure_configured
|
||||
unless ActionMailbox.ingress == ingress_name
|
||||
head :not_found
|
||||
end
|
||||
end
|
||||
|
||||
def ingress_name
|
||||
self.class.name[/^ActionMailbox::Ingresses::(.*?)::/, 1].underscore.to_sym
|
||||
end
|
||||
|
||||
|
||||
def authenticate_by_password
|
||||
if password.present?
|
||||
http_basic_authenticate_or_request_with username: "actionmailbox", password: password, realm: "Action Mailbox"
|
||||
else
|
||||
raise ArgumentError, "Missing required ingress credentials"
|
||||
end
|
||||
end
|
||||
|
||||
def password
|
||||
Rails.application.credentials.dig(:action_mailbox, :ingress_password) || ENV["RAILS_INBOUND_EMAIL_PASSWORD"]
|
||||
end
|
||||
|
||||
|
||||
# TODO: Extract to ActionController::HttpAuthentication
|
||||
def http_basic_authenticate_or_request_with(username:, password:, realm: nil)
|
||||
authenticate_or_request_with_http_basic(realm || "Application") do |given_username, given_password|
|
||||
|
@ -11,21 +11,30 @@ def ensure_authenticated
|
||||
end
|
||||
|
||||
def authenticated?
|
||||
Authenticator.new(
|
||||
timestamp: params.require(:timestamp),
|
||||
token: params.require(:token),
|
||||
signature: params.require(:signature)
|
||||
).authenticated?
|
||||
if key.present?
|
||||
Authenticator.new(
|
||||
key: key,
|
||||
timestamp: params.require(:timestamp),
|
||||
token: params.require(:token),
|
||||
signature: params.require(:signature)
|
||||
).authenticated?
|
||||
else
|
||||
raise ArgumentError, <<~MESSAGE.squish
|
||||
Missing required Mailgun API key. Set action_mailbox.mailgun_api_key in your application's
|
||||
encrypted credentials or provide the MAILGUN_INGRESS_API_KEY environment variable.
|
||||
MESSAGE
|
||||
end
|
||||
end
|
||||
|
||||
def key
|
||||
Rails.application.credentials.dig(:action_mailbox, :mailgun_api_key) || ENV["MAILGUN_INGRESS_API_KEY"]
|
||||
end
|
||||
|
||||
class Authenticator
|
||||
cattr_accessor :key
|
||||
attr_reader :timestamp, :token, :signature
|
||||
attr_reader :key, :timestamp, :token, :signature
|
||||
|
||||
def initialize(timestamp:, token:, signature:)
|
||||
@timestamp, @token, @signature = Integer(timestamp), token, signature
|
||||
|
||||
ensure_presence_of_key
|
||||
def initialize(key:, timestamp:, token:, signature:)
|
||||
@key, @timestamp, @token, @signature = key, Integer(timestamp), token, signature
|
||||
end
|
||||
|
||||
def authenticated?
|
||||
@ -33,13 +42,6 @@ def authenticated?
|
||||
end
|
||||
|
||||
private
|
||||
def ensure_presence_of_key
|
||||
unless key.present?
|
||||
raise ArgumentError, "Missing required Mailgun API key"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def signed?
|
||||
ActiveSupport::SecurityUtils.secure_compare signature, expected_signature
|
||||
end
|
||||
|
@ -24,17 +24,25 @@ def ensure_authenticated
|
||||
end
|
||||
|
||||
def authenticated?
|
||||
Authenticator.new(request).authenticated?
|
||||
if key.present?
|
||||
Authenticator.new(request, key).authenticated?
|
||||
else
|
||||
raise ArgumentError, <<~MESSAGE.squish
|
||||
Missing required Mandrill API key. Set action_mailbox.mandrill_api_key in your application's
|
||||
encrypted credentials or provide the MANDRILL_INGRESS_API_KEY environment variable.
|
||||
MESSAGE
|
||||
end
|
||||
end
|
||||
|
||||
def key
|
||||
Rails.application.credentials.dig(:action_mailbox, :mandrill_api_key) || ENV["MANDRILL_INGRESS_API_KEY"]
|
||||
end
|
||||
|
||||
class Authenticator
|
||||
cattr_accessor :key
|
||||
attr_reader :request
|
||||
attr_reader :request, :key
|
||||
|
||||
def initialize(request)
|
||||
@request = request
|
||||
|
||||
ensure_presence_of_key
|
||||
def initialize(request, key)
|
||||
@request, @key = request, key
|
||||
end
|
||||
|
||||
def authenticated?
|
||||
@ -42,13 +50,6 @@ def authenticated?
|
||||
end
|
||||
|
||||
private
|
||||
def ensure_presence_of_key
|
||||
unless key.present?
|
||||
raise ArgumentError, "Missing required Mandrill API key"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def given_signature
|
||||
request.headers["X-Mandrill-Signature"]
|
||||
end
|
||||
|
@ -1,8 +1,5 @@
|
||||
class ActionMailbox::Ingresses::Postfix::InboundEmailsController < ActionMailbox::BaseController
|
||||
cattr_accessor :username, default: "actionmailbox"
|
||||
cattr_accessor :password
|
||||
|
||||
before_action :authenticate, :require_valid_rfc822_message
|
||||
before_action :authenticate_by_password, :require_valid_rfc822_message
|
||||
|
||||
def create
|
||||
ActionMailbox::InboundEmail.create_and_extract_message_id! request.body.read
|
||||
|
@ -1,8 +1,5 @@
|
||||
class ActionMailbox::Ingresses::Sendgrid::InboundEmailsController < ActionMailbox::BaseController
|
||||
cattr_accessor :username, default: "actionmailbox"
|
||||
cattr_accessor :password
|
||||
|
||||
before_action :authenticate
|
||||
before_action :authenticate_by_password
|
||||
|
||||
def create
|
||||
ActionMailbox::InboundEmail.create_and_extract_message_id! params.require(:email)
|
||||
|
@ -6,6 +6,7 @@ module ActionMailbox
|
||||
autoload :Base
|
||||
autoload :Router
|
||||
|
||||
mattr_accessor :ingress
|
||||
mattr_accessor :logger
|
||||
mattr_accessor :incinerate_after, default: 30.days
|
||||
end
|
||||
|
@ -10,6 +10,7 @@ class Engine < Rails::Engine
|
||||
|
||||
initializer "action_mailbox.config" do
|
||||
config.after_initialize do |app|
|
||||
ActionMailbox.ingress = app.config.action_mailbox.ingress
|
||||
ActionMailbox.logger = app.config.action_mailbox.logger || Rails.logger
|
||||
ActionMailbox.incinerate_after = app.config.action_mailbox.incinerate_after || 30.days
|
||||
end
|
||||
|
@ -4,6 +4,8 @@
|
||||
Module.new { def self.authentic?(message); true; end }
|
||||
|
||||
class ActionMailbox::Ingresses::Amazon::InboundEmailsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup { ActionMailbox.ingress = :amazon }
|
||||
|
||||
test "receiving an inbound email from Amazon" do
|
||||
assert_difference -> { ActionMailbox::InboundEmail.count }, +1 do
|
||||
post rails_amazon_inbound_emails_url, params: { content: file_fixture("../files/welcome.eml").read }, as: :json
|
||||
|
@ -1,8 +1,10 @@
|
||||
require "test_helper"
|
||||
|
||||
ActionMailbox::Ingresses::Mailgun::InboundEmailsController::Authenticator.key = "tbsy84uSV1Kt3ZJZELY2TmShPRs91E3yL4tzf96297vBCkDWgL"
|
||||
ENV["MAILGUN_INGRESS_API_KEY"] = "tbsy84uSV1Kt3ZJZELY2TmShPRs91E3yL4tzf96297vBCkDWgL"
|
||||
|
||||
class ActionMailbox::Ingresses::Mailgun::InboundEmailsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup { ActionMailbox.ingress = :mailgun }
|
||||
|
||||
test "receiving an inbound email from Mailgun" do
|
||||
assert_difference -> { ActionMailbox::InboundEmail.count }, +1 do
|
||||
travel_to "2018-10-09 15:15:00 EDT"
|
||||
@ -78,12 +80,10 @@ class ActionMailbox::Ingresses::Mailgun::InboundEmailsControllerTest < ActionDis
|
||||
end
|
||||
|
||||
private
|
||||
delegate :key, :key=, to: ActionMailbox::Ingresses::Mailgun::InboundEmailsController::Authenticator
|
||||
|
||||
def switch_key_to(new_key)
|
||||
previous_key, self.key = key, new_key
|
||||
previous_key, ENV["MAILGUN_INGRESS_API_KEY"] = ENV["MAILGUN_INGRESS_API_KEY"], new_key
|
||||
yield
|
||||
ensure
|
||||
self.key = previous_key
|
||||
ENV["MAILGUN_INGRESS_API_KEY"] = previous_key
|
||||
end
|
||||
end
|
||||
|
@ -1,9 +1,10 @@
|
||||
require "test_helper"
|
||||
|
||||
ActionMailbox::Ingresses::Mandrill::InboundEmailsController::Authenticator.key = "1l9Qf7lutEf7h73VXfBwhw"
|
||||
ENV["MANDRILL_INGRESS_API_KEY"] = "1l9Qf7lutEf7h73VXfBwhw"
|
||||
|
||||
class ActionMailbox::Ingresses::Mandrill::InboundEmailsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
ActionMailbox.ingress = :mandrill
|
||||
@events = JSON.generate([{ event: "inbound", msg: { raw_msg: file_fixture("../files/welcome.eml").read } }])
|
||||
end
|
||||
|
||||
@ -48,12 +49,10 @@ class ActionMailbox::Ingresses::Mandrill::InboundEmailsControllerTest < ActionDi
|
||||
end
|
||||
|
||||
private
|
||||
delegate :key, :key=, to: ActionMailbox::Ingresses::Mandrill::InboundEmailsController::Authenticator
|
||||
|
||||
def switch_key_to(new_key)
|
||||
previous_key, self.key = key, new_key
|
||||
previous_key, ENV["MANDRILL_INGRESS_API_KEY"] = ENV["MANDRILL_INGRESS_API_KEY"], new_key
|
||||
yield
|
||||
ensure
|
||||
self.key = previous_key
|
||||
ENV["MANDRILL_INGRESS_API_KEY"] = previous_key
|
||||
end
|
||||
end
|
||||
|
@ -1,8 +1,8 @@
|
||||
require "test_helper"
|
||||
|
||||
ActionMailbox::Ingresses::Postfix::InboundEmailsController.password = "tbsy84uSV1Kt3ZJZELY2TmShPRs91E3yL4tzf96297vBCkDWgL"
|
||||
|
||||
class ActionMailbox::Ingresses::Postfix::InboundEmailsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup { ActionMailbox.ingress = :postfix }
|
||||
|
||||
test "receiving an inbound email from Postfix" do
|
||||
assert_difference -> { ActionMailbox::InboundEmail.count }, +1 do
|
||||
post rails_postfix_inbound_emails_url, headers: { "Authorization" => credentials, "Content-Type" => "message/rfc822" },
|
||||
@ -51,18 +51,4 @@ class ActionMailbox::Ingresses::Postfix::InboundEmailsControllerTest < ActionDis
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
delegate :username, :password, :password=, to: ActionMailbox::Ingresses::Postfix::InboundEmailsController
|
||||
|
||||
def credentials
|
||||
ActionController::HttpAuthentication::Basic.encode_credentials username, password
|
||||
end
|
||||
|
||||
def switch_password_to(new_password)
|
||||
previous_password, self.password = password, new_password
|
||||
yield
|
||||
ensure
|
||||
self.password = previous_password
|
||||
end
|
||||
end
|
||||
|
@ -1,8 +1,8 @@
|
||||
require "test_helper"
|
||||
|
||||
ActionMailbox::Ingresses::Sendgrid::InboundEmailsController.password = "tbsy84uSV1Kt3ZJZELY2TmShPRs91E3yL4tzf96297vBCkDWgL"
|
||||
|
||||
class ActionMailbox::Ingresses::Sendgrid::InboundEmailsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup { ActionMailbox.ingress = :sendgrid }
|
||||
|
||||
test "receiving an inbound email from Sendgrid" do
|
||||
assert_difference -> { ActionMailbox::InboundEmail.count }, +1 do
|
||||
post rails_sendgrid_inbound_emails_url,
|
||||
@ -43,16 +43,7 @@ class ActionMailbox::Ingresses::Sendgrid::InboundEmailsControllerTest < ActionDi
|
||||
end
|
||||
|
||||
private
|
||||
delegate :username, :password, :password=, to: ActionMailbox::Ingresses::Sendgrid::InboundEmailsController
|
||||
|
||||
def credentials
|
||||
ActionController::HttpAuthentication::Basic.encode_credentials username, password
|
||||
end
|
||||
|
||||
def switch_password_to(new_password)
|
||||
previous_password, self.password = password, new_password
|
||||
yield
|
||||
ensure
|
||||
self.password = previous_password
|
||||
ActionController::HttpAuthentication::Basic.encode_credentials "actionmailbox", ENV["RAILS_INBOUND_EMAIL_PASSWORD"]
|
||||
end
|
||||
end
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Configure Rails Environment
|
||||
ENV["RAILS_ENV"] = "test"
|
||||
ENV["RAILS_INBOUND_EMAIL_PASSWORD"] = "tbsy84uSV1Kt3ZJZELY2TmShPRs91E3yL4tzf96297vBCkDWgL"
|
||||
|
||||
require_relative "../test/dummy/config/environment"
|
||||
ActiveRecord::Migrator.migrations_paths = [File.expand_path("../test/dummy/db/migrate", __dir__)]
|
||||
@ -7,14 +7,11 @@
|
||||
|
||||
require "byebug"
|
||||
|
||||
# Filter out Minitest backtrace while allowing backtrace from other libraries
|
||||
# to be shown.
|
||||
Minitest.backtrace_filter = Minitest::BacktraceFilter.new
|
||||
|
||||
require "rails/test_unit/reporter"
|
||||
Rails::TestUnitReporter.executable = 'bin/test'
|
||||
|
||||
# Load fixtures from the engine
|
||||
if ActiveSupport::TestCase.respond_to?(:fixture_path=)
|
||||
ActiveSupport::TestCase.fixture_path = File.expand_path("fixtures", __dir__)
|
||||
ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
|
||||
@ -28,6 +25,20 @@ class ActiveSupport::TestCase
|
||||
include ActionMailbox::TestHelper, ActiveJob::TestHelper
|
||||
end
|
||||
|
||||
class ActionDispatch::IntegrationTest
|
||||
private
|
||||
def credentials
|
||||
ActionController::HttpAuthentication::Basic.encode_credentials "actionmailbox", ENV["RAILS_INBOUND_EMAIL_PASSWORD"]
|
||||
end
|
||||
|
||||
def switch_password_to(new_password)
|
||||
previous_password, ENV["RAILS_INBOUND_EMAIL_PASSWORD"] = ENV["RAILS_INBOUND_EMAIL_PASSWORD"], new_password
|
||||
yield
|
||||
ensure
|
||||
ENV["RAILS_INBOUND_EMAIL_PASSWORD"] = previous_password
|
||||
end
|
||||
end
|
||||
|
||||
if ARGV.include?("-v")
|
||||
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
||||
ActiveJob::Base.logger = Logger.new(STDOUT)
|
||||
|
Loading…
Reference in New Issue
Block a user