diff --git a/sanic/exceptions.py b/sanic/exceptions.py index 21ab2a94..0edb0562 100644 --- a/sanic/exceptions.py +++ b/sanic/exceptions.py @@ -208,44 +208,39 @@ class Unauthorized(SanicException): """ Unauthorized exception (401 HTTP status code). + :param message: Message describing the exception. :param scheme: Name of the authentication scheme to be used. - :param challenge: A dict containing values to add to the WWW-Authenticate - header that is generated. This is especially useful when dealing with - the Digest scheme. (optional) + + When present, kwargs is used to complete the WWW-Authentication header. Examples:: # With a Basic auth-scheme, realm MUST be present: - challenge = {"realm": "Restricted Area"} - raise Unauthorized("Auth required.", "Basic", challenge) + raise Unauthorized("Auth required.", "Basic", realm="Restricted Area") # With a Digest auth-scheme, things are a bit more complicated: - challenge = { - "realm": "Restricted Area", - "qop": "auth, auth-int", - "algorithm": "MD5", - "nonce": "abcdef", - "opaque": "zyxwvu" - } - raise Unauthorized("Auth required.", "Digest", challenge) + raise Unauthorized("Auth required.", + "Digest", + realm="Restricted Area", + qop="auth, auth-int", + algorithm="MD5", + nonce="abcdef", + opaque="zyxwvu") - # With a Bearer auth-scheme, realm is optional: - challenge = {"realm": "Restricted Area"} - raise Unauthorized("Auth required.", "Bearer", challenge) + # With a Bearer auth-scheme, realm is optional so you can write: + raise Unauthorized("Auth required.", "Bearer") + + # or, if you want to specify the realm: + raise Unauthorized("Auth required.", "Bearer", realm="Restricted Area") """ - pass - - def __init__(self, message, scheme, challenge=None): + def __init__(self, message, scheme, **kwargs): super().__init__(message) - chal = "" - - if challenge is not None: - values = ["{!s}={!r}".format(k, v) for k, v in challenge.items()] - chal = ', '.join(values) + values = ["{!s}={!r}".format(k, v) for k, v in kwargs.items()] + challenge = ', '.join(values) self.headers = { - "WWW-Authenticate": "{} {}".format(scheme, chal).rstrip() + "WWW-Authenticate": "{} {}".format(scheme, challenge).rstrip() } diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 620e7891..1521c9ed 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -33,18 +33,17 @@ def exception_app(): @app.route('/401/basic') def handler_401_basic(request): - raise Unauthorized("Unauthorized", "Basic", {"realm": "Sanic"}) + raise Unauthorized("Unauthorized", "Basic", realm="Sanic") @app.route('/401/digest') def handler_401_digest(request): - challenge = { - "realm": "Sanic", - "qop": "auth, auth-int", - "algorithm": "MD5", - "nonce": "abcdef", - "opaque": "zyxwvu", - } - raise Unauthorized("Unauthorized", "Digest", challenge) + raise Unauthorized("Unauthorized", + "Digest", + realm="Sanic", + qop="auth, auth-int", + algorithm="MD5", + nonce="abcdef", + opaque="zyxwvu") @app.route('/401/bearer') def handler_401_bearer(request): @@ -122,7 +121,7 @@ def test_forbidden_exception(exception_app): request, response = exception_app.test_client.get('/403') assert response.status == 403 - + def test_unauthorized_exception(exception_app): """Test the built-in Unauthorized exception""" request, response = exception_app.test_client.get('/401/basic') @@ -132,7 +131,7 @@ def test_unauthorized_exception(exception_app): request, response = exception_app.test_client.get('/401/digest') assert response.status == 401 - + auth_header = response.headers.get('WWW-Authenticate') assert auth_header is not None assert auth_header.startswith('Digest')