Merge pull request #824 from Frzk/unauthorized-exception

Simplified the `Unauthorized.__init__` signature.
This commit is contained in:
Raphael Deem 2017-07-21 23:50:36 -07:00 committed by GitHub
commit 1aad527956
2 changed files with 37 additions and 10 deletions

View File

@ -209,25 +209,43 @@ class Unauthorized(SanicException):
Unauthorized exception (401 HTTP status code). Unauthorized exception (401 HTTP status code).
:param scheme: Name of the authentication scheme to be used. :param scheme: Name of the authentication scheme to be used.
:param realm: Description of the protected area. (optional)
:param challenge: A dict containing values to add to the WWW-Authenticate :param challenge: A dict containing values to add to the WWW-Authenticate
header that is generated. This is especially useful when header that is generated. This is especially useful when dealing with
dealing with the Digest scheme. (optional) the Digest scheme. (optional)
Examples::
# With a Basic auth-scheme, realm MUST be present:
challenge = {"realm": "Restricted Area"}
raise Unauthorized("Auth required.", "Basic", challenge)
# 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)
# With a Bearer auth-scheme, realm is optional:
challenge = {"realm": "Restricted Area"}
raise Unauthorized("Auth required.", "Bearer", challenge)
""" """
pass pass
def __init__(self, message, scheme, realm="", challenge=None): def __init__(self, message, scheme, challenge=None):
super().__init__(message) super().__init__(message)
adds = "" chal = ""
if challenge is not None: if challenge is not None:
values = ["{!s}={!r}".format(k, v) for k, v in challenge.items()] values = ["{!s}={!r}".format(k, v) for k, v in challenge.items()]
adds = ', '.join(values) chal = ', '.join(values)
adds = ', {}'.format(adds)
self.headers = { self.headers = {
"WWW-Authenticate": "{} realm='{}'{}".format(scheme, realm, adds) "WWW-Authenticate": "{} {}".format(scheme, chal).rstrip()
} }

View File

@ -33,17 +33,22 @@ def exception_app():
@app.route('/401/basic') @app.route('/401/basic')
def handler_401_basic(request): def handler_401_basic(request):
raise Unauthorized("Unauthorized", "Basic", "Sanic") raise Unauthorized("Unauthorized", "Basic", {"realm": "Sanic"})
@app.route('/401/digest') @app.route('/401/digest')
def handler_401_digest(request): def handler_401_digest(request):
challenge = { challenge = {
"realm": "Sanic",
"qop": "auth, auth-int", "qop": "auth, auth-int",
"algorithm": "MD5", "algorithm": "MD5",
"nonce": "abcdef", "nonce": "abcdef",
"opaque": "zyxwvu", "opaque": "zyxwvu",
} }
raise Unauthorized("Unauthorized", "Digest", "Sanic", challenge) raise Unauthorized("Unauthorized", "Digest", challenge)
@app.route('/401/bearer')
def handler_401_bearer(request):
raise Unauthorized("Unauthorized", "Bearer")
@app.route('/invalid') @app.route('/invalid')
def handler_invalid(request): def handler_invalid(request):
@ -136,6 +141,10 @@ def test_unauthorized_exception(exception_app):
assert "nonce='abcdef'" in auth_header assert "nonce='abcdef'" in auth_header
assert "opaque='zyxwvu'" in auth_header assert "opaque='zyxwvu'" in auth_header
request, response = exception_app.test_client.get('/401/bearer')
assert response.status == 401
assert response.headers.get('WWW-Authenticate') == "Bearer"
def test_handled_unhandled_exception(exception_app): def test_handled_unhandled_exception(exception_app):
"""Test that an exception not built into sanic is handled""" """Test that an exception not built into sanic is handled"""