Welcome, guest | Sign In | My Account | Store | Cart
# A decorator that lets you require HTTP basic authentication from visitors.
# Kevin Kelley <kelleyk@kelleyk.net> 2011
# Use however makes you happy, but if it breaks, you get to keep both pieces.

# Post with explanation, commentary, etc.:
# http://kelleyk.com/post/7362319243/easy-basic-http-authentication-with-tornado

import base64, logging
import tornado.web
import twilio # From https://github.com/twilio/twilio-python

def require_basic_auth(handler_class):
   
def wrap_execute(handler_execute):
       
def require_basic_auth(handler, kwargs):
            auth_header
= handler.request.headers.get('Authorization')
           
if auth_header is None or not auth_header.startswith('Basic '):
                handler
.set_status(401)
                handler
.set_header('WWW-Authenticate', 'Basic realm=Restricted')
                handler
._transforms = []
                handler
.finish()
               
return False
            auth_decoded
= base64.decodestring(auth_header[6:])
            kwargs
['basicauth_user'], kwargs['basicauth_pass'] = auth_decoded.split(':', 2)
           
return True
       
def _execute(self, transforms, *args, **kwargs):
           
if not require_basic_auth(self, kwargs):
               
return False
           
return handler_execute(self, transforms, *args, **kwargs)
       
return _execute

    handler_class
._execute = wrap_execute(handler_class._execute)
   
return handler_class


twilio_account_sid
= 'INSERT YOUR ACCOUNT ID HERE'
twilio_account_token
= 'INSERT YOUR ACCOUNT TOKEN HERE'

@require_basic_auth
class TwilioRequestHandler(tornado.web.RequestHandler):
   
def post(self, basicauth_user, basicauth_pass):
       
"""
        Receive a Twilio request, return a TwiML response
        """

       
# We check in two ways that it's really Twilio POSTing to this URL:
       
# 1. Check that Twilio is sending the username and password we specified
       
#    for it at https://www.twilio.com/user/account/phone-numbers/incoming
       
# 2. Check that Twilio has signed its request with our secret account token
        username
= 'CONFIGURE USERNAME AT TWILIO.COM AND ENTER IT HERE'
        password
= 'CONFIGURE PASSWORD AT TWILIO.COM AND ENTER IT HERE'
       
if basicauth_user != username or basicauth_pass != password:
           
raise tornado.web.HTTPError(401, "Invalid username and password for HTTP basic authentication")

       
# Construct the URL to this handler.
       
# self.request.full_url() doesn't work, because Twilio sort of has a bug:
       
# We tell it to POST to our URL with HTTP Authentication like this:
       
#   http://username:password@b-date.me/api/twilio_request_handler
       
# ... and Twilio uses *that* URL, with username and password included, as
       
# part of its signature.
       
# Also, if we're proxied by Nginx, then Nginx handles the HTTPS protocol and
       
# connects to Tornado over HTTP
        protocol
= 'https' if self.request.headers.get('X-Twilio-Ssl') == 'Enabled' else self.request.protocol
        url
= '%s://%s:%s@%s%s' % (
            protocol
, username, password, self.request.host, self.request.path,
       
)

       
if not twilio.Utils(twilio_account_sid, twilio_account_token).validateRequest(
            url
,
           
# arguments has lists like { 'key': [ 'value', ... ] }, so flatten them
           
{
                k
: self.request.arguments[k][0]
               
for k in self.request.arguments
           
},
           
self.request.headers.get('X-Twilio-Signature'),
       
):
            logging
.error("Invalid Twilio signature to %s: %s" % (
               
self.request.full_url(), self.request
           
))
           
raise tornado.web.HTTPError(401, "Invalid Twilio signature")

       
# Do your actual processing of Twilio's POST here, using self.get_argument()
       

History