forked from twilio/twilio-ruby
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Restore accidentally removed files. (twilio#352)
* Undeprecate Twilio.configure. * Restore Webhook Auth helper. This reverts commit 038a3f9.
- Loading branch information
Showing
6 changed files
with
182 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
module Rack | ||
# Middleware that authenticates webhooks from Twilio using the request | ||
# validator. | ||
# | ||
# The middleware takes an auth token with which to set up the request | ||
# validator and any number of paths. When a path matches the incoming request | ||
# path, the request will be checked for authentication. | ||
# | ||
# Example: | ||
# | ||
# require 'rack' | ||
# use Rack::TwilioWebhookAuthentication, ENV['AUTH_TOKEN'], /\/messages/ | ||
# | ||
# The above appends this middleware to the stack, using an auth token saved in | ||
# the ENV and only against paths that match /\/messages/. If the request | ||
# validates then it gets passed on to the action as normal. If the request | ||
# doesn't validate then the middleware responds immediately with a 403 status. | ||
|
||
class TwilioWebhookAuthentication | ||
def initialize(app, auth_token, *paths, &auth_token_lookup) | ||
@app = app | ||
@auth_token = auth_token | ||
define_singleton_method(:get_auth_token, auth_token_lookup) if block_given? | ||
@path_regex = Regexp.union(paths) | ||
end | ||
|
||
def call(env) | ||
return @app.call(env) unless env['PATH_INFO'].match(@path_regex) | ||
request = Rack::Request.new(env) | ||
original_url = request.url | ||
params = request.post? ? request.POST : {} | ||
auth_token = @auth_token || get_auth_token(params['AccountSid']) | ||
validator = Twilio::Security::RequestValidator.new(auth_token) | ||
signature = env['HTTP_X_TWILIO_SIGNATURE'] || '' | ||
if validator.validate(original_url, params, signature) | ||
@app.call(env) | ||
else | ||
[ | ||
403, | ||
{ 'Content-Type' => 'text/plain' }, | ||
['Twilio Request Validation Failed.'] | ||
] | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
require 'spec_helper' | ||
require 'rack/mock' | ||
|
||
describe Rack::TwilioWebhookAuthentication do | ||
before do | ||
@app = ->(_env) { [200, { 'Content-Type' => 'text/plain' }, ['Hello']] } | ||
end | ||
|
||
describe 'new' do | ||
it 'should initialize with an app, auth token and a path' do | ||
expect do | ||
Rack::TwilioWebhookAuthentication.new(@app, 'ABC', /\/voice/) | ||
end.not_to raise_error | ||
end | ||
|
||
it 'should initialize with an app, auth token and paths' do | ||
expect do | ||
Rack::TwilioWebhookAuthentication.new(@app, 'ABC', /\/voice/, /\/sms/) | ||
end.not_to raise_error | ||
end | ||
|
||
it 'should initialize with an app, dynamic token and paths' do | ||
expect do | ||
Rack::TwilioWebhookAuthentication.new(@app, nil, /\/voice/, /\/sms/) | ||
end.not_to raise_error | ||
end | ||
end | ||
|
||
describe 'calling against one path with dynamic auth token' do | ||
it 'should allow a request through if it validates' do | ||
auth_token = 'qwerty' | ||
account_sid = 12_345 | ||
expect_any_instance_of(Rack::Request).to receive(:post?).and_return(true) | ||
expect_any_instance_of(Rack::Request).to receive(:POST).and_return({ 'AccountSid' => account_sid }) | ||
@middleware = Rack::TwilioWebhookAuthentication.new(@app, nil, /\/voice/) { |asid| auth_token } | ||
request_validator = double('RequestValidator') | ||
expect(Twilio::Security::RequestValidator).to receive(:new).with(auth_token).and_return(request_validator) | ||
expect(request_validator).to receive(:validate).and_return(true) | ||
request = Rack::MockRequest.env_for('/voice') | ||
status, headers, body = @middleware.call(request) | ||
expect(status).to be(200) | ||
end | ||
end | ||
|
||
describe 'calling against one path' do | ||
before do | ||
@middleware = Rack::TwilioWebhookAuthentication.new(@app, 'ABC', /\/voice/) | ||
end | ||
|
||
it 'should not intercept when the path doesn\'t match' do | ||
expect(Twilio::Security::RequestValidator).to_not receive(:validate) | ||
request = Rack::MockRequest.env_for('/sms') | ||
status, headers, body = @middleware.call(request) | ||
expect(status).to be(200) | ||
end | ||
|
||
it 'should allow a request through if it validates' do | ||
expect_any_instance_of(Twilio::Security::RequestValidator).to( | ||
receive(:validate).and_return(true) | ||
) | ||
request = Rack::MockRequest.env_for('/voice') | ||
status, headers, body = @middleware.call(request) | ||
expect(status).to be(200) | ||
end | ||
|
||
it 'should short circuit a request to 403 if it does not validate' do | ||
expect_any_instance_of(Twilio::Security::RequestValidator).to( | ||
receive(:validate).and_return(false) | ||
) | ||
request = Rack::MockRequest.env_for('/voice') | ||
status, headers, body = @middleware.call(request) | ||
expect(status).to be(403) | ||
end | ||
end | ||
|
||
describe 'calling against many paths' do | ||
before do | ||
@middleware = Rack::TwilioWebhookAuthentication.new(@app, 'ABC', /\/voice/, /\/sms/) | ||
end | ||
|
||
it 'should not intercept when the path doesn\'t match' do | ||
expect(Twilio::Security::RequestValidator).to_not receive(:validate) | ||
request = Rack::MockRequest.env_for('icesms') | ||
status, headers, body = @middleware.call(request) | ||
expect(status).to be(200) | ||
end | ||
|
||
it 'shold allow a request through if it validates' do | ||
expect_any_instance_of(Twilio::Security::RequestValidator).to( | ||
receive(:validate).and_return(true) | ||
) | ||
request = Rack::MockRequest.env_for('/sms') | ||
status, headers, body = @middleware.call(request) | ||
expect(status).to be(200) | ||
end | ||
|
||
it 'should short circuit a request to 403 if it does not validate' do | ||
expect_any_instance_of(Twilio::Security::RequestValidator).to( | ||
receive(:validate).and_return(false) | ||
) | ||
request = Rack::MockRequest.env_for('/sms') | ||
status, headers, body = @middleware.call(request) | ||
expect(status).to be(403) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters