doc(quickstart): Sync HTTPUnauthorized example with post-0.3 changes (#760)

Update the complex example given in the quickstart and README so that
it now works in light of a breaking change to the HTTPUnauthorized
class. Note this breaking change in the changelog.

Closes #757
This commit is contained in:
Kurt Griffiths
2016-04-21 08:43:54 -05:00
committed by Fran Fitzpatrick
parent 9b2b00a8e1
commit f44fba57ef
7 changed files with 61 additions and 33 deletions

View File

@@ -29,6 +29,10 @@ Breaking Changes
app = falcon.API()
app.req_options.auto_parse_form_urlencoded = True
- The ``HTTPUnauthorized`` initializer now requires an
additional argument, `challenges`. Per RFC 7235, a server returning a
401 must include a WWW-Authenticate header field containing at least
one challenge.
- The performance of composing the response body was
improved. As part of this work, the ``Response.body_encoded``
attribute was removed. This property was only intended to be used by

View File

@@ -152,14 +152,14 @@ API.
# things.py
# Let's get this party started
# Let's get this party started!
import falcon
# Falcon follows the REST architectural style, meaning (among
# other things) that you think in terms of resources and state
# transitions, which map to HTTP verbs.
class ThingsResource:
class ThingsResource(object):
def on_get(self, req, resp):
"""Handles GET requests"""
resp.status = falcon.HTTP_200 # This is the default status
@@ -251,8 +251,10 @@ bodies.
class AuthMiddleware(object):
def process_request(self, req, resp):
token = req.get_header('X-Auth-Token')
project = req.get_header('X-Project-ID')
token = req.get_header('Authorization')
account_id = req.get_header('Account-ID')
challenges = ['Token type="Fernet"']
if token is None:
description = ('Please provide an auth token '
@@ -260,18 +262,19 @@ bodies.
raise falcon.HTTPUnauthorized('Auth token required',
description,
challenges,
href='http://docs.example.com/auth')
if not self._token_is_valid(token, project):
if not self._token_is_valid(token, account_id):
description = ('The provided auth token is not valid. '
'Please request a new token and try again.')
raise falcon.HTTPUnauthorized('Authentication required',
description,
href='http://docs.example.com/auth',
scheme='Token; UUID')
challenges,
href='http://docs.example.com/auth')
def _token_is_valid(self, token, project):
def _token_is_valid(self, token, account_id):
return True # Suuuuuure it's valid...
@@ -337,7 +340,7 @@ bodies.
return hook
class ThingsResource:
class ThingsResource(object):
def __init__(self, db):
self.db = db
@@ -367,7 +370,7 @@ bodies.
# that would serialize to JSON under the covers.
req.context['result'] = result
resp.set_header('X-Powered-By', 'Small Furry Creatures')
resp.set_header('Powered-By', 'Falcon')
resp.status = falcon.HTTP_200
@falcon.before(max_body(64 * 1024))
@@ -406,11 +409,15 @@ bodies.
sink = SinkAdapter()
app.add_sink(sink, r'/search/(?P<engine>ddg|y)\Z')
# Useful for debugging problems in your API; works with pdb.set_trace()
# Useful for debugging problems in your API; works with pdb.set_trace(). You
# can also use Gunicorn to host your app. Gunicorn can be configured to
# auto-restart workers when it detects a code change, and it also works
# with pdb.
if __name__ == '__main__':
httpd = simple_server.make_server('127.0.0.1', 8000, app)
httpd.serve_forever()
Community
---------

View File

@@ -29,6 +29,10 @@ Breaking Changes
app = falcon.API()
app.req_options.auto_parse_form_urlencoded = True
- The :class:`~falcon.HTTPUnauthorized` initializer now requires an
additional argument, `challenges`. Per RFC 7235, a server returning a
401 must include a WWW-Authenticate header field containing at least
one challenge.
- The performance of composing the response body was
improved. As part of this work, the :attr:`Response.body_encoded`
attribute was removed. This property was only intended to be used by

View File

@@ -21,7 +21,7 @@ started writing an API:
# things.py
# Let's get this party started
# Let's get this party started!
import falcon
@@ -121,8 +121,10 @@ parameters, handling errors, and working with request and response bodies.
class AuthMiddleware(object):
def process_request(self, req, resp):
token = req.get_header('X-Auth-Token')
project = req.get_header('X-Project-ID')
token = req.get_header('Authorization')
account_id = req.get_header('Account-ID')
challenges = ['Token type="Fernet"']
if token is None:
description = ('Please provide an auth token '
@@ -130,18 +132,19 @@ parameters, handling errors, and working with request and response bodies.
raise falcon.HTTPUnauthorized('Auth token required',
description,
challenges,
href='http://docs.example.com/auth')
if not self._token_is_valid(token, project):
if not self._token_is_valid(token, account_id):
description = ('The provided auth token is not valid. '
'Please request a new token and try again.')
raise falcon.HTTPUnauthorized('Authentication required',
description,
href='http://docs.example.com/auth',
scheme='Token; UUID')
challenges,
href='http://docs.example.com/auth')
def _token_is_valid(self, token, project):
def _token_is_valid(self, token, account_id):
return True # Suuuuuure it's valid...
@@ -237,7 +240,7 @@ parameters, handling errors, and working with request and response bodies.
# that would serialize to JSON under the covers.
req.context['result'] = result
resp.set_header('X-Powered-By', 'Small Furry Creatures')
resp.set_header('Powered-By', 'Falcon')
resp.status = falcon.HTTP_200
@falcon.before(max_body(64 * 1024))
@@ -276,7 +279,10 @@ parameters, handling errors, and working with request and response bodies.
sink = SinkAdapter()
app.add_sink(sink, r'/search/(?P<engine>ddg|y)\Z')
# Useful for debugging problems in your API; works with pdb.set_trace()
# Useful for debugging problems in your API; works with pdb.set_trace(). You
# can also use Gunicorn to host your app. Gunicorn can be configured to
# auto-restart workers when it detects a code change, and it also works
# with pdb.
if __name__ == '__main__':
httpd = simple_server.make_server('127.0.0.1', 8000, app)
httpd.serve_forever()

View File

@@ -52,7 +52,8 @@ class HTTPUnauthorized(HTTPError):
a helpful suggestion or two.
challenges (iterable of str): One or more authentication
challenges to use as the value of the WWW-Authenticate header in
the response.
the response. See also:
http://tools.ietf.org/html/rfc7235#section-2.1
kwargs (optional): Same as for ``HTTPError``.
"""

View File

@@ -49,8 +49,10 @@ class SinkAdapter(object):
class AuthMiddleware(object):
def process_request(self, req, resp):
token = req.get_header('X-Auth-Token')
project = req.get_header('X-Project-ID')
token = req.get_header('Authorization')
account_id = req.get_header('Account-ID')
challenges = ['Token type="Fernet"']
if token is None:
description = ('Please provide an auth token '
@@ -58,18 +60,19 @@ class AuthMiddleware(object):
raise falcon.HTTPUnauthorized('Auth token required',
description,
challenges,
href='http://docs.example.com/auth')
if not self._token_is_valid(token, project):
if not self._token_is_valid(token, account_id):
description = ('The provided auth token is not valid. '
'Please request a new token and try again.')
raise falcon.HTTPUnauthorized('Authentication required',
description,
href='http://docs.example.com/auth',
scheme='Token; UUID')
challenges,
href='http://docs.example.com/auth')
def _token_is_valid(self, token, project):
def _token_is_valid(self, token, account_id):
return True # Suuuuuure it's valid...
@@ -135,7 +138,7 @@ def max_body(limit):
return hook
class ThingsResource:
class ThingsResource(object):
def __init__(self, db):
self.db = db
@@ -165,7 +168,7 @@ class ThingsResource:
# that would serialize to JSON under the covers.
req.context['result'] = result
resp.set_header('X-Powered-By', 'Small Furry Creatures')
resp.set_header('Powered-By', 'Falcon')
resp.status = falcon.HTTP_200
@falcon.before(max_body(64 * 1024))
@@ -204,7 +207,10 @@ app.add_error_handler(StorageError, StorageError.handle)
sink = SinkAdapter()
app.add_sink(sink, r'/search/(?P<engine>ddg|y)\Z')
# Useful for debugging problems in your API; works with pdb.set_trace()
# Useful for debugging problems in your API; works with pdb.set_trace(). You
# can also use Gunicorn to host your app. Gunicorn can be configured to
# auto-restart workers when it detects a code change, and it also works
# with pdb.
if __name__ == '__main__':
httpd = simple_server.make_server('127.0.0.1', 8000, app)
httpd.serve_forever()

View File

@@ -73,18 +73,18 @@ class UnauthorizedResource:
def on_get(self, req, resp):
raise falcon.HTTPUnauthorized('Authentication Required',
'Missing or invalid token header.',
'Missing or invalid authorization.',
['Basic realm="simple"'])
def on_post(self, req, resp):
raise falcon.HTTPUnauthorized('Authentication Required',
'Missing or invalid token header.',
'Missing or invalid authorization.',
['Newauth realm="apps"',
'Basic realm="simple"'])
def on_put(self, req, resp):
raise falcon.HTTPUnauthorized('Authentication Required',
'Missing or invalid token header.', [])
'Missing or invalid authorization.', [])
class NotFoundResource: