Closed marine44 closed 10 years ago
Best way to do that at this point would be to override the Griddler::EmailsController and throw in a before_filter
that renders some status from the 400 block if the params don't match. #133 is in PR now to override the controller in the future, but it might be a bit before that gets merged since we're overhauling the gem currently.
Thanks for getting back to me! What you say sounds very good but is a little outside my skill level (I don't know how to override the Griddler::EmailsController). Unless it's easy for you to explain or demonstrate I might just wait until a future version of the gem.
You'd reopen the Griddler::EmailsController
with class Griddler::EmailsController; ...; end
, write a method that does what mailgun talks about for securing webhooks, and before_filter :that_method
in the subclassed controller.
Full example:
# in app/controllers/griddler/emails_controller.rb
class Griddler::EmailsController
before_filter :verify_webhook
private
def verify_webhook
# write your verification method here
end
end
Thanks - thats great to get me started - here is where I am at:
require 'openssl'
class Griddler::EmailsController
before_filter :verify_webhook
private
def verify_webhook
signature = params[:signature]
timestamp = params[:timestamp]
token = params[:token]
api_key = ENV['MAILGUN_API_KEY']
return signature == OpenSSL::HMAC.hexdigest(
OpenSSL::Digest::Digest.new('sha256'),
api_key,
'%s%s' % [timestamp, token])
end
end
But it's giving me:
* [out :: 119.9.20.198] undefined method `before_filter' for Griddler::EmailsController:Class * [out :: 119.9.20.198]( [out :: 119.9.20.198] NoMethodError [out :: 119.9.20.198])
Before we get too far, you don't want to return true/false, you need to redirect or render, as in my original suggestion.
try before_action
, and if that doesn't work make sure Griddler::EmailsController exists before you reopen it. Could be a load issue.
@calebthompson thanks so much but is there any chance you could elaborate your last response? Really keen to get this working otherwise there is no way for me to stop others from posting to the application...
TL;DR, the Rails docs will be really helpful in this for you: http://guides.rubyonrails.org/action_controller_overview.html#filters
Briefly, beforefilters in controllers, unlike before* in models, don't stop a workflow just because they return false. In a before_save, if you returned false from a callback then the model wouldn't save, but that's not how it works with controllers. In a controller, you'll need to redirect_to
somewhere based on the request. rendering in the filter should also keep the controller action from running.
So after you check the signature, you'd probably want to use something like head :forbidden
if the signature doesn't parse properly. http://stackoverflow.com/questions/8085353/rails3-how-to-render-403-in-before-filter-w-o-doublerender-error
Unfortunately, the way Rails 4 (and I think 3?) isolate Engines, you can't just re-open the controller. I just tried the recommendations above and it results in:
AbstractController::ActionNotFound (The action 'create' could not be found for Griddler::EmailsController):
It appears that because I have now defined Griddler::EmailsController
in my own app, the one from the griddler gem is never used/loaded.
What worked for me was inheriting
# in config/routes.rb
post '/incoming_email' => 'griddler/custom_emails#create'
# in app/controllers/griddler/custom_emails_controller.rb
class Griddler::CustomEmailsController < Griddler::EmailsController
before_action :foo
private
def foo
# do something
end
end
However, while this is a good way to deal with authentication, it still doesn't provide my email processor class with access to the full set of original params. For example, with Mailgun, they post additional things like their own version of the stripped out body and signature (vs. letting griddler do this). It would be nice to have access to all of those params just in case.
Is there any reason/potential downside to just making the attr_reader :params
line public?
@dmarkow I'm confused - since it's a subclass, normalized_params
is still available to you, right? And since it's a controller, isn't params
available as well?
Yes, params
is available in my custom controller, but I need it in my EmailProcessor
class, not the controller.
It feels to me that the Griddler::Email
class should expose the params it was based off, especially considering that only some of the original params are being mapped to attributes. But for my purposes, overriding the process_email(email)
method so I can send a second parameter to my EmailProcessor
class seems to work:
def process_email(email)
EmailProcessor.new(email, params).process
end
Is there a way to access the params from the original HTTP post from mailgun?
I want to access params[:timestamp], params[:token] and params[:signature] to allow me to verify the web hooks.
See "Securing Webhooks" at http://documentation.mailgun.com/user_manual.html#webhooks