From 2d048428cf851b75c04d615c511a19c50e0bad5e Mon Sep 17 00:00:00 2001 From: ueg1990 Date: Tue, 14 Apr 2015 19:04:33 -0400 Subject: [PATCH] fix(HTTPUnauthorized): Add support for setting the realm to the HTTPUnauthorized class Based on RFC 7235, add challenges to HTTP Unauthorized to indicate authentication scheme. A server generating a 401 must send a WWW-Authenticate header field containing at least one challenge BREAKING CHANGE: Add new parameter to the __init__ method of class HTTPUnauthorized to add determine authentication scheme in the WWW-Authenticate header field when a 401 (Unauthorized) is generated Fixes #430 Fix pep8 error Fix docstring --- falcon/errors.py | 12 ++++++------ tests/test_httperror.py | 30 +++++++++++++++++++----------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/falcon/errors.py b/falcon/errors.py index 1e19577..f759d1a 100644 --- a/falcon/errors.py +++ b/falcon/errors.py @@ -49,18 +49,18 @@ class HTTPUnauthorized(HTTPError): title (str): Error title (e.g., 'Authentication Required'). description (str): Human-friendly description of the error, along with a helpful suggestion or two. - scheme (str): Authentication scheme to use as the value of the - WWW-Authenticate header in the response (default ``None``). + challenges (iterable of str): One or more authentication + challenges to use as the value of the WWW-Authenticate header in + the response. kwargs (optional): Same as for ``HTTPError``. """ - def __init__(self, title, description, **kwargs): + def __init__(self, title, description, challenges, **kwargs): headers = kwargs.setdefault('headers', {}) - scheme = kwargs.pop('scheme', None) - if scheme is not None: - headers['WWW-Authenticate'] = scheme + if challenges: + headers['WWW-Authenticate'] = ', '.join(challenges) HTTPError.__init__(self, status.HTTP_401, title, description, **kwargs) diff --git a/tests/test_httperror.py b/tests/test_httperror.py index 438fd7d..fd04bc0 100644 --- a/tests/test_httperror.py +++ b/tests/test_httperror.py @@ -74,14 +74,17 @@ class UnauthorizedResource: def on_get(self, req, resp): raise falcon.HTTPUnauthorized('Authentication Required', 'Missing or invalid token header.', - scheme='Token; UUID') + ['Basic realm="simple"']) - -class UnauthorizedResourceSchemaless: - - def on_get(self, req, resp): + def on_post(self, req, resp): raise falcon.HTTPUnauthorized('Authentication Required', - 'Missing or invalid token header.') + 'Missing or invalid token header.', + ['Newauth realm="apps"', + 'Basic realm="simple"']) + + def on_put(self, req, resp): + raise falcon.HTTPUnauthorized('Authentication Required', + 'Missing or invalid token header.', []) class NotFoundResource: @@ -454,15 +457,20 @@ class TestHTTPError(testing.TestBase): self.simulate_request('/401') self.assertEqual(self.srmock.status, falcon.HTTP_401) - self.assertIn(('www-authenticate', 'Token; UUID'), + self.assertIn(('www-authenticate', 'Basic realm="simple"'), self.srmock.headers) - def test_401_schemaless(self): - self.api.add_route('/401', UnauthorizedResourceSchemaless()) - self.simulate_request('/401') + self.simulate_request('/401', method='POST') self.assertEqual(self.srmock.status, falcon.HTTP_401) - self.assertNotIn(('www-authenticate', 'Token'), self.srmock.headers) + self.assertIn(('www-authenticate', 'Newauth realm="apps", ' + 'Basic realm="simple"'), + self.srmock.headers) + + self.simulate_request('/401', method='PUT') + + self.assertEqual(self.srmock.status, falcon.HTTP_401) + self.assertNotIn(('www-authenticate', []), self.srmock.headers) def test_404_without_body(self): self.api.add_route('/404', NotFoundResource())