From c0956a7e761aaf7c2bfd34e31e84e54f2628c2f6 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Tue, 4 Oct 2011 20:14:36 +0200 Subject: [PATCH 1/3] Adding cloudmailin support for adding tasks --- Gemfile | 1 + app/controllers/integrations_controller.rb | 61 +++++++++++++++++++--- config/routes.rb | 5 +- config/site.yml.tmpl | 5 ++ 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index 6638f39b..85bfdc90 100644 --- a/Gemfile +++ b/Gemfile @@ -18,6 +18,7 @@ gem "ruby-openid", :require => "openid" gem "sqlite3" gem 'bcrypt-ruby', '~> 2.1.4' gem 'htmlentities', '~> 4.3.0' +gem "mail" gem "webrat", ">=0.7.0", :groups => [:cucumber, :test] gem "database_cleaner", ">=0.5.0", :groups => [:cucumber, :selenium] diff --git a/app/controllers/integrations_controller.rb b/app/controllers/integrations_controller.rb index 93989c71..fb13ab3b 100644 --- a/app/controllers/integrations_controller.rb +++ b/app/controllers/integrations_controller.rb @@ -1,6 +1,8 @@ class IntegrationsController < ApplicationController - - skip_before_filter :login_required, :only => [:search_plugin, :google_gadget] + require 'mail' + + skip_before_filter :login_required, :only => [:cloudmailin, :search_plugin, :google_gadget] + before_filter :verify_cloudmailin_signature, :only => [:cloudmailin] def index @page_title = 'TRACKS::Integrations' @@ -26,14 +28,61 @@ class IntegrationsController < ApplicationController end def search_plugin - @icon_data = [File.open(RAILS_ROOT + '/public/images/done.png').read]. - pack('m').gsub(/\n/, '') - - render :layout => false + @icon_data = [File.open(RAILS_ROOT + '/public/images/done.png').read]. + pack('m').gsub(/\n/, '') + + render :layout => false end def google_gadget render :layout => false, :content_type => Mime::XML end + + def cloudmailin + message = Mail.new(params[:message]) + + # debug + #puts message.from.addresses.first + + # find user + user = User.find(:first, :include => [:preference], :conditions => ["preferences.sms_email = ?", message.from.addresses.first]) + if user.nil? + render :text => "No user found", :status => 404 + return false + end + context = user.prefs.sms_context + # prepare body + if message.body.multipart? + #body = message.body.parts[0].to_s + body = message.body.preamble + else + body = message.body.to_s + end + + # parse mail + if message.subject.to_s.empty? + description = body + notes = nil + else + description = message.subject.decoded.to_s + notes = body + end + + todo = Todo.from_rich_message(user, context.id, description, notes) + todo.save! + render :text => 'success', :status => 200 + end + +private + + def verify_cloudmailin_signature + provided = request.request_parameters.delete(:signature) + signature = Digest::MD5.hexdigest(request.request_parameters.sort.map{|k,v| v}.join + SITE_CONFIG['cloudmailin']) + + if provided != signature + render :text => "Message signature fail #{provided} != #{signature}", :status => 403 + return false + end + end end diff --git a/config/routes.rb b/config/routes.rb index b3897e5d..60f777cd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -86,8 +86,9 @@ ActionController::Routing::Routes.draw do |map| map.with_options :controller => :integrations do |i| i.integrations 'integrations', :action => 'index' i.rest_api_docs 'integrations/rest_api', :action => "rest_api" - i.search_plugin 'integrations/search_plugin.xml', :controller => 'integrations', :action => 'search_plugin', :format => 'xml' - i.google_gadget 'integrations/google_gadget.xml', :controller => 'integrations', :action => 'google_gadget', :format => 'xml' + i.search_plugin 'integrations/search_plugin.xml', :action => 'search_plugin', :format => 'xml' + i.google_gadget 'integrations/google_gadget.xml', :action => 'google_gadget', :format => 'xml' + i.cloudmailin 'integrations/cloudmailin', :action => 'cloudmailin' end map.with_options :controller => :preferences do |p| diff --git a/config/site.yml.tmpl b/config/site.yml.tmpl index 91389652..d5208a6c 100644 --- a/config/site.yml.tmpl +++ b/config/site.yml.tmpl @@ -51,3 +51,8 @@ open_signups: false # - 'localhost' # use_ssl: false # login_format: 'cn=%s,dc=example,dc=com' + +# When integrating your tracks instance with http://cloudmailin.com/ by using the /integrations/cloudmailin URL, +# this value is the cloudmailin-secret for verifying the authenticity of the request. +# (see http://docs.cloudmailin.com/validating_the_sender) +# cloudmailin: asdasd From 17bb939e9e56bde2700d4a1672e7d3017946954d Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Wed, 5 Oct 2011 23:53:49 +0200 Subject: [PATCH 2/3] adding test for cloudmailin and fixing some bugs found with it --- app/controllers/integrations_controller.rb | 37 +++++++++---------- .../integrations_controller_test.rb | 22 ++++++++--- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/app/controllers/integrations_controller.rb b/app/controllers/integrations_controller.rb index fb13ab3b..b5d874fb 100644 --- a/app/controllers/integrations_controller.rb +++ b/app/controllers/integrations_controller.rb @@ -2,7 +2,6 @@ class IntegrationsController < ApplicationController require 'mail' skip_before_filter :login_required, :only => [:cloudmailin, :search_plugin, :google_gadget] - before_filter :verify_cloudmailin_signature, :only => [:cloudmailin] def index @page_title = 'TRACKS::Integrations' @@ -39,22 +38,31 @@ class IntegrationsController < ApplicationController end def cloudmailin + # verify cloudmailin signature + provided = request.request_parameters.delete(:signature) + signature = Digest::MD5.hexdigest(request.request_parameters.sort{|a,b| a[0].to_s <=> b[0].to_s}.map{|k,v| v}.join + SITE_CONFIG['cloudmailin']) + + # if signature does not match, return 403 + if provided != signature + render :text => "Message signature fail #{provided} != #{signature}", :status => 403 + return false + end + + # parse message message = Mail.new(params[:message]) - - # debug - #puts message.from.addresses.first - + # find user - user = User.find(:first, :include => [:preference], :conditions => ["preferences.sms_email = ?", message.from.addresses.first]) + user = User.find(:first, :include => [:preference], :conditions => ["preferences.sms_email = ?", message.from]) if user.nil? render :text => "No user found", :status => 404 return false end + # load user settings context = user.prefs.sms_context + # prepare body if message.body.multipart? - #body = message.body.parts[0].to_s body = message.body.preamble else body = message.body.to_s @@ -65,24 +73,13 @@ class IntegrationsController < ApplicationController description = body notes = nil else - description = message.subject.decoded.to_s + description = message.subject.to_s notes = body end + # create todo todo = Todo.from_rich_message(user, context.id, description, notes) todo.save! render :text => 'success', :status => 200 end - -private - - def verify_cloudmailin_signature - provided = request.request_parameters.delete(:signature) - signature = Digest::MD5.hexdigest(request.request_parameters.sort.map{|k,v| v}.join + SITE_CONFIG['cloudmailin']) - - if provided != signature - render :text => "Message signature fail #{provided} != #{signature}", :status => 403 - return false - end - end end diff --git a/test/functional/integrations_controller_test.rb b/test/functional/integrations_controller_test.rb index d1f8db4e..c45c9d20 100644 --- a/test/functional/integrations_controller_test.rb +++ b/test/functional/integrations_controller_test.rb @@ -12,11 +12,6 @@ class IntegrationsControllerTest < ActionController::TestCase @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new end - - # Replace this with your real tests. - def test_truth - assert true - end def test_page_load login_as(:admin_user) @@ -24,4 +19,21 @@ class IntegrationsControllerTest < ActionController::TestCase assert_response :success end + def test_cloudmailin_integration + SITE_CONFIG['cloudmailin'] = "123456789" + post :cloudmailin, { + "html"=>"", + "plain"=>"asdasd", + "x_to_header"=>"[\"81496ecea21032d35a7a@cloudmailin.net\"]", + "disposable"=>"", + "from"=>"5555555555@tmomail.net", + "signature"=>"e85e908fb893394762047c21e54ce248", + "to"=>"<123123@cloudmailin.net>", + "subject"=>"asd", + "x_cc_header"=>"", + "message"=>"Received: from VMBX103.ihostexchange.net ([192.168.3.3]) by\r\n HUB103.ihostexchange.net ([66.46.182.53]) with mapi; Wed, 5 Oct 2011 17:12:44\r\n -0400\r\nFrom: SMS User <5555555555@tmomail.net>\r\nTo: Tracks <123123@cloudmailin.net>\r\nDate: Wed, 5 Oct 2011 17:12:43 -0400\r\nSubject: asd\r\nThread-Topic: asd\r\nThread-Index: AcyDo4aig2wghvcsTAOkleWqi4t/FQ==\r\nMessage-ID: <7D7CB176-7559-4997-A301-8DF9726264C7@tmomail.net>\r\nAccept-Language: de-DE, en-US\r\nContent-Language: en-US\r\nX-MS-Has-Attach:\r\nX-MS-TNEF-Correlator:\r\nacceptlanguage: de-DE, en-US\r\nContent-Type: text/plain; charset=\"us-ascii\"\r\nContent-Transfer-Encoding: quoted-printable\r\nMIME-Version: 1.0\r\n\r\nasdasd\r\n" + } + + assert_response :success + end end From 0d4116cc930c4d6b653a22426f58bc22a9052a81 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 6 Oct 2011 16:41:46 +0200 Subject: [PATCH 3/3] Removing signatures from output and adding two test cases: invalid signature and unknown sender --- app/controllers/integrations_controller.rb | 2 +- .../integrations_controller_test.rb | 38 ++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/app/controllers/integrations_controller.rb b/app/controllers/integrations_controller.rb index b5d874fb..70c93b17 100644 --- a/app/controllers/integrations_controller.rb +++ b/app/controllers/integrations_controller.rb @@ -44,7 +44,7 @@ class IntegrationsController < ApplicationController # if signature does not match, return 403 if provided != signature - render :text => "Message signature fail #{provided} != #{signature}", :status => 403 + render :text => "Message signature verification failed.", :status => 403 return false end diff --git a/test/functional/integrations_controller_test.rb b/test/functional/integrations_controller_test.rb index c45c9d20..4d36d69a 100644 --- a/test/functional/integrations_controller_test.rb +++ b/test/functional/integrations_controller_test.rb @@ -19,7 +19,7 @@ class IntegrationsControllerTest < ActionController::TestCase assert_response :success end - def test_cloudmailin_integration + def test_cloudmailin_integration_success SITE_CONFIG['cloudmailin'] = "123456789" post :cloudmailin, { "html"=>"", @@ -36,4 +36,40 @@ class IntegrationsControllerTest < ActionController::TestCase assert_response :success end + + def test_cloudmailin_integration_invalid_signature + SITE_CONFIG['cloudmailin'] = "12345678901234567890" + post :cloudmailin, { + "html"=>"", + "plain"=>"asdasd", + "x_to_header"=>"[\"81496ecea21032d35a7a@cloudmailin.net\"]", + "disposable"=>"", + "from"=>"5555555555@tmomail.net", + "signature"=>"e85e908fb893394762047c21e54ce248", + "to"=>"<123123@cloudmailin.net>", + "subject"=>"asd", + "x_cc_header"=>"", + "message"=>"Received: from VMBX103.ihostexchange.net ([192.168.3.3]) by\r\n HUB103.ihostexchange.net ([66.46.182.53]) with mapi; Wed, 5 Oct 2011 17:12:44\r\n -0400\r\nFrom: SMS User <5555555555@tmomail.net>\r\nTo: Tracks <123123@cloudmailin.net>\r\nDate: Wed, 5 Oct 2011 17:12:43 -0400\r\nSubject: asd\r\nThread-Topic: asd\r\nThread-Index: AcyDo4aig2wghvcsTAOkleWqi4t/FQ==\r\nMessage-ID: <7D7CB176-7559-4997-A301-8DF9726264C7@tmomail.net>\r\nAccept-Language: de-DE, en-US\r\nContent-Language: en-US\r\nX-MS-Has-Attach:\r\nX-MS-TNEF-Correlator:\r\nacceptlanguage: de-DE, en-US\r\nContent-Type: text/plain; charset=\"us-ascii\"\r\nContent-Transfer-Encoding: quoted-printable\r\nMIME-Version: 1.0\r\n\r\nasdasd\r\n" + } + + assert_response 403 + end + + def test_cloudmailin_integration_unknown_address + SITE_CONFIG['cloudmailin'] = "123456789" + post :cloudmailin, { + "html"=>"", + "plain"=>"asdasd", + "x_to_header"=>"[\"81496ecea21032d35a7a@cloudmailin.net\"]", + "disposable"=>"", + "from"=>"444444444444@tmomail.net", + "signature"=>"6d2df0e807bfa9b77d24c31dce6d4515", + "to"=>"<123123@cloudmailin.net>", + "subject"=>"asd", + "x_cc_header"=>"", + "message"=>"Received: from VMBX103.ihostexchange.net ([192.168.3.3]) by\r\n HUB103.ihostexchange.net ([66.46.182.53]) with mapi; Wed, 5 Oct 2011 17:12:44\r\n -0400\r\nFrom: SMS User <444444444444@tmomail.net>\r\nTo: Tracks <123123@cloudmailin.net>\r\nDate: Wed, 5 Oct 2011 17:12:43 -0400\r\nSubject: asd\r\nThread-Topic: asd\r\nThread-Index: AcyDo4aig2wghvcsTAOkleWqi4t/FQ==\r\nMessage-ID: <7D7CB176-7559-4997-A301-8DF9726264C7@tmomail.net>\r\nAccept-Language: de-DE, en-US\r\nContent-Language: en-US\r\nX-MS-Has-Attach:\r\nX-MS-TNEF-Correlator:\r\nacceptlanguage: de-DE, en-US\r\nContent-Type: text/plain; charset=\"us-ascii\"\r\nContent-Transfer-Encoding: quoted-printable\r\nMIME-Version: 1.0\r\n\r\nasdasd\r\n" + } + + assert_response 404 + end end