Add Mailgun endpoint for receiving email tasks via Mailgun

This commit is contained in:
Greg Sutcliffe 2013-09-15 21:17:05 +01:00
parent daef1c440b
commit 8a2da01d51
8 changed files with 193 additions and 2 deletions

View file

@ -0,0 +1,38 @@
require 'openssl'
class MailgunController < ApplicationController
skip_before_filter :login_required, :only => [:mailgun]
before_filter :verify, :only => [:mailgun]
protect_from_forgery with: :null_session
def mailgun
unless params.include? 'body-mime'
Rails.logger.info "Cannot process Mailgun request, no body-mime sent"
render_failure "Unacceptable body-mime", 406
return
end
todo = MessageGateway.receive(params['body-mime'])
if todo
render :xml => todo.to_xml( *todo_xml_params )
else
render_failure "Todo not saved", 406
end
end
private
def verify
unless params['signature'] == OpenSSL::HMAC.hexdigest(
OpenSSL::Digest::Digest.new('sha256'),
SITE_CONFIG['mailgun_api_key'],
'%s%s' % [params['timestamp'], params['token']]
)
Rails.logger.info "Cannot verify Mailgun signature"
render_failure "Access denied", 406
return
end
end
end

View file

@ -27,6 +27,7 @@ class MessageGateway < ActionMailer::Base
todo = todo_builder.construct todo = todo_builder.construct
todo.save! todo.save!
Rails.logger.info "Saved email as todo for user #{user.login} in context #{context.name}" Rails.logger.info "Saved email as todo for user #{user.login} in context #{context.name}"
todo
end end
private private
@ -49,10 +50,26 @@ class MessageGateway < ActionMailer::Base
if user.nil? if user.nil?
user = User.where("preferences.sms_email" => address.strip[1.100]).includes(:preference).first user = User.where("preferences.sms_email" => address.strip[1.100]).includes(:preference).first
end end
if user.present? and !sender_is_in_mailmap?(user,email)
Rails.logger.warn "#{email.from[0]} not found in mailmap for #{user.login}"
return nil
end
Rails.logger.info(!user.nil? ? "Email belongs to #{user.login}" : "User unknown") Rails.logger.info(!user.nil? ? "Email belongs to #{user.login}" : "User unknown")
return user return user
end end
def sender_is_in_mailmap?(user,email)
if SITE_CONFIG['mailmap'].is_a? Hash and SITE_CONFIG['email_dispatch'] == 'to'
# Look for the sender in the map of allowed senders
SITE_CONFIG['mailmap'][user.preference.sms_email].include? email.from[0]
else
# We can't check the map if it's not defined, or if the lookup is the
# wrong way round, so just allow it
true
end
end
def get_user_from_email_address(email) def get_user_from_email_address(email)
SITE_CONFIG['email_dispatch'] == 'single_user' ? get_user_from_env_setting : get_user_from_mail_header(email) SITE_CONFIG['email_dispatch'] == 'single_user' ? get_user_from_env_setting : get_user_from_mail_header(email)
end end

View file

@ -3,6 +3,8 @@ Tracksapp::Application.routes.draw do
root :to => 'todos#index' root :to => 'todos#index'
post 'mailgun/mime' => 'mailgun#mailgun'
post 'login' => 'login#login' post 'login' => 'login#login'
get 'login' => 'login#login' get 'login' => 'login#login'
get 'login/check_expiry' => 'login#check_expiry' get 'login/check_expiry' => 'login#check_expiry'

View file

@ -58,7 +58,20 @@ open_signups: false
# (see http://docs.cloudmailin.com/validating_the_sender) # (see http://docs.cloudmailin.com/validating_the_sender)
# cloudmailin: asdasd # cloudmailin: asdasd
# Mailgun api key - used to verify incoming HTTP requests from Mailgun.org
# mailgun_api_key: key-abcdef1234567890
# change this to reflect the email address of the admin that you want to show # change this to reflect the email address of the admin that you want to show
# on the signup page # on the signup page
admin_email: my.email@domain.com admin_email: my.email@domain.com
# Map of allowed incoming email addresses to real users
# Requires email_dispatch == 'to'
# This allows you to specify _who_ can send email Todos to your list
# The format is your incoming email (as per preferences page) for the key, and
# an array-list of acceptable senders for that account
#mailmap:
# 'user@preferences.from.value':
# - 'acceptable1@personal.org'
# - 'acceptable2@work.com'

View file

@ -13,6 +13,7 @@ class IntegrationsControllerTest < ActionController::TestCase
def test_cloudmailin_integration_success def test_cloudmailin_integration_success
SITE_CONFIG['cloudmailin'] = "123456789" SITE_CONFIG['cloudmailin'] = "123456789"
SITE_CONFIG['email_dispatch'] = 'from'
post :cloudmailin, { post :cloudmailin, {
"html"=>"", "html"=>"",
"plain"=>"asdasd", "plain"=>"asdasd",

View file

@ -0,0 +1,66 @@
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
class MailgunControllerTest < ActionController::TestCase
def setup
@user = users(:sms_user)
@inbox = contexts(:inbox)
end
def load_message(filename)
File.read(File.join(Rails.root, 'test', 'fixtures', filename))
end
def test_mailgun_signature_verifies
SITE_CONFIG['mailgun_api_key'] = "123456789"
SITE_CONFIG['email_dispatch'] = 'from'
post :mailgun, {
"timestamp" => "1379539674",
"token" => "5km6cwo0e3bfvg78hw4s69znro09xhk1h8u6-s633yasc8hcr5",
"signature" => "da92708b8f2c9dcd7ecdc91d52946c01802833e6683e46fc00b3f081920dd5b1",
"body-mime" => load_message('mailgun_message1.txt')
}
assert_response :success
end
def test_mailgun_creates_todo_with_mailmap
SITE_CONFIG['mailgun_api_key'] = "123456789"
SITE_CONFIG['email_dispatch'] = 'to'
SITE_CONFIG['mailmap'] = {
'5555555555@tmomail.net' => ['incoming@othermail.com', 'notused@foo.org']
}
todo_count = Todo.count
post :mailgun, {
"timestamp" => "1379539674",
"token" => "5km6cwo0e3bfvg78hw4s69znro09xhk1h8u6-s633yasc8hcr5",
"signature" => "da92708b8f2c9dcd7ecdc91d52946c01802833e6683e46fc00b3f081920dd5b1",
"body-mime" => load_message('mailgun_message2.txt')
}
assert_response :success
assert_equal(todo_count+1, Todo.count)
message_todo = Todo.where(:description => "test").first
assert_not_nil(message_todo)
assert_equal(@inbox, message_todo.context)
assert_equal(@user, message_todo.user)
end
def test_mailgun_signature_fails
SITE_CONFIG['mailgun_api_key'] = "invalidkey"
SITE_CONFIG['email_dispatch'] = 'from'
post :mailgun, {
"timestamp" => "1379539674",
"token" => "5km6cwo0e3bfvg78hw4s69znro09xhk1h8u6-s633yasc8hcr5",
"signature" => "da92708b8f2c9dcd7ecdc91d52946c01802833e6683e46fc00b3f081920dd5b1",
"body-mime" => load_message('mailgun_message1.txt')
}
assert_response 406
end
end

27
test/fixtures/mailgun_message1.txt vendored Normal file
View file

@ -0,0 +1,27 @@
Received: by luna.mailgun.net with SMTP mgrt 8745468409521; Wed, 18 Sep 2013\n 20:51:11 +0000
X-Envelope-From: <5555555555@tmomail.net>
Received: from mail-pa0-f45.google.com (mail-pa0-f45.google.com\n [209.85.220.45]) by mxa.mailgun.org with ESMTP id\n 523a123d.7f3a16482330-in1; Wed, 18 Sep 2013 20:51:09 -0000 (UTC)
Received: by mail-pa0-f45.google.com with SMTP id bg4so8745646pad.18 for\n <test@test.mailgun.org>; Wed, 18 Sep 2013 13:51:08 -0700 (PDT)
Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com;\n s=20120113; h=mime-version:date:message-id:subject:from:to:content-type;\n bh=XwBMHRr9XT/f5w7Vzap//yQJ9VoVaVcjEQ5+xjgSoMk=;\n b=IsSDTe2jY8XucXOxviOrGg1upSm5IOddtOsHFpgcyND4F9glSIU+xixRK60/9Dbqae\n BT5On3C+Aoffpj/WUqnp1+hA89JYDbWhzXcJPhkW8FcFnPvAzQ0G/loV7+CafWLubEnp\n T61owZQAQgdkHsrZMf1ChmpghNMJuDLMJvYmiijIId+z7arBocjErhHqwRY4Y6lmFdTo\n s9uGtVJ7Av1g7m8KyGZY6kzh/XPhbr4B0tjIFpyp+bDFH2Nk290RAB/2garNBfQAPhzy\n hIvvbuz5MLFWSnW17eXdymHAEH6oSbRfar8ocxcY5T+hg++nfsegUJ6sPRG1G63qnsj4 dCig==
Mime-Version: 1.0
X-Received: by 10.68.17.230 with SMTP id r6mr21790845pbd.112.1379537468893;\n Wed, 18 Sep 2013 13:51:08 -0700 (PDT)
Received: by 10.70.43.236 with HTTP; Wed, 18 Sep 2013 13:51:08 -0700 (PDT)
Date: Wed, 18 Sep 2013 21:51:08 +0100
Message-Id: <CAE=3ySAxx8r7dcgaixQjbYS3J4mVG7FdEqAwDAcJ4bkHnDiT5w@mail.gmail.com>
Subject: test1
From: A User <5555555555@tmomail.net>
To: test@test.mailgun.org
Content-Type: multipart/alternative; boundary=\"bcaec520ee93c9b26104e6ae9838\"
X-Mailgun-Incoming: Yes
--bcaec520ee93c9b26104e6ae9838
Content-Type: text/plain; charset=ISO-8859-1
--bcaec520ee93c9b26104e6ae9838
Content-Type: text/html; charset=ISO-8859-1
<div dir=\"ltr\"><br></div>
--bcaec520ee93c9b26104e6ae9838--

27
test/fixtures/mailgun_message2.txt vendored Normal file
View file

@ -0,0 +1,27 @@
Received: by luna.mailgun.net with SMTP mgrt 8745468409521; Wed, 18 Sep 2013\n 20:51:11 +0000
X-Envelope-From: <incoming@othermail.com>
Received: from mail-pa0-f45.google.com (mail-pa0-f45.google.com\n [209.85.220.45]) by mxa.mailgun.org with ESMTP id\n 523a123d.7f3a16482330-in1; Wed, 18 Sep 2013 20:51:09 -0000 (UTC)
Received: by mail-pa0-f45.google.com with SMTP id bg4so8745646pad.18 for\n <5555555555@tmomail.net>; Wed, 18 Sep 2013 13:51:08 -0700 (PDT)
Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com;\n s=20120113; h=mime-version:date:message-id:subject:from:to:content-type;\n bh=XwBMHRr9XT/f5w7Vzap//yQJ9VoVaVcjEQ5+xjgSoMk=;\n b=IsSDTe2jY8XucXOxviOrGg1upSm5IOddtOsHFpgcyND4F9glSIU+xixRK60/9Dbqae\n BT5On3C+Aoffpj/WUqnp1+hA89JYDbWhzXcJPhkW8FcFnPvAzQ0G/loV7+CafWLubEnp\n T61owZQAQgdkHsrZMf1ChmpghNMJuDLMJvYmiijIId+z7arBocjErhHqwRY4Y6lmFdTo\n s9uGtVJ7Av1g7m8KyGZY6kzh/XPhbr4B0tjIFpyp+bDFH2Nk290RAB/2garNBfQAPhzy\n hIvvbuz5MLFWSnW17eXdymHAEH6oSbRfar8ocxcY5T+hg++nfsegUJ6sPRG1G63qnsj4 dCig==
Mime-Version: 1.0
X-Received: by 10.68.17.230 with SMTP id r6mr21790845pbd.112.1379537468893;\n Wed, 18 Sep 2013 13:51:08 -0700 (PDT)
Received: by 10.70.43.236 with HTTP; Wed, 18 Sep 2013 13:51:08 -0700 (PDT)
Date: Wed, 18 Sep 2013 21:51:08 +0100
Message-Id: <CAE=3ySAxx8r7dcgaixQjbYS3J4mVG7FdEqAwDAcJ4bkHnDiT5w@mail.gmail.com>
Subject: test
From: A User <incoming@othermail.com>
To: 5555555555@tmomail.net
Content-Type: multipart/alternative; boundary=\"bcaec520ee93c9b26104e6ae9838\"
X-Mailgun-Incoming: Yes
--bcaec520ee93c9b26104e6ae9838
Content-Type: text/plain; charset=ISO-8859-1
--bcaec520ee93c9b26104e6ae9838
Content-Type: text/html; charset=ISO-8859-1
<div dir=\"ltr\"><br></div>
--bcaec520ee93c9b26104e6ae9838--