Tags: #python #tornado #authorization #access
"""
Explanation:
tornado.web.RequestHandler calls an internal `_execute` method before
calling any other method such as `get` or `post` etc.
we wrap the internal `_execute` method so that it either returns
a 403 or 401 exception depending on the situation.
note: you can't raise a tornado.web.HTTPError from outside of the handler,
otherwise tornado will fail with an unhandled exception. so instead we set
the status and simply return the function.
Tutorial:
http://kevinsayscode.tumblr.com/post/7362319243/easy-basic-http-authentication-with-tornado
See also "Python Tornado Basic Auth Middleware":
https://gist.github.com/Integralist/911252f81830ff0ed650145c4d52f58e
"""
import tornado.ioloop
import tornado.web
def require_basic_auth(handler_class):
def wrap_execute(handler_execute):
def serve_error(handler, status):
handler._transforms = [] # necessary
handler.set_status(status)
handler.finish()
def _execute(self, transforms, *args, **kwargs):
expected_header = self.request.headers.get('X-User-Auth')
if expected_header is None:
return serve_error(self, 403)
# beepboop should be pulled from somewhere, not hardcoded!
if expected_header != 'beepboop':
return serve_error(self, 401)
return handler_execute(self, transforms, *args, **kwargs)
return _execute
handler_class._execute = wrap_execute(handler_class._execute)
return handler_class
@require_basic_auth
class MainHandler(tornado.web.RequestHandler):
# use !s inside format syntax to control string conversion
# https://docs.python.org/3.6/library/string.html#formatstrings
def get(self, basicauth_user, basicauth_pass):
msg = 'Hi there, {0!s}! Your password is {1}.'
body = msg.format(basicauth_user, basicauth_pass)
self.write(body)
def post(self, **kwargs):
basicauth_user = kwargs['basicauth_user']
basicauth_pass = kwargs['basicauth_pass']
msg = 'Hi there, {0!s}! Your password is {1}.'
body = msg.format(basicauth_user, basicauth_pass)
self.write(body)
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
app = make_app()
app.listen(9000)
tornado.ioloop.IOLoop.current().start()