Fix bugs in cherrypy auth_digest

This commit is contained in:
Kovid Goyal 2012-04-03 15:39:19 +05:30
parent e278da9219
commit 103854e242

View File

@ -33,7 +33,8 @@ qop_auth = 'auth'
qop_auth_int = 'auth-int' qop_auth_int = 'auth-int'
valid_qops = (qop_auth, qop_auth_int) valid_qops = (qop_auth, qop_auth_int)
valid_algorithms = ('MD5', 'MD5-sess') valid_algorithms = ('MD5', 'MD5-sess', 'md5', 'md5-sess') # Changed by Kovid to
# add lowercase
def TRACE(msg): def TRACE(msg):
@ -67,7 +68,7 @@ def get_ha1_dict(user_ha1_dict):
argument to digest_auth(). argument to digest_auth().
""" """
def get_ha1(realm, username): def get_ha1(realm, username):
return user_ha1_dict.get(user) return user_ha1_dict.get(username) # Changed by Kovid to fix typo
return get_ha1 return get_ha1
@ -107,10 +108,10 @@ def synthesize_nonce(s, key, timestamp=None):
key key
A secret string known only to the server. A secret string known only to the server.
timestamp timestamp
An integer seconds-since-the-epoch timestamp An integer seconds-since-the-epoch timestamp
""" """
if timestamp is None: if timestamp is None:
timestamp = int(time.time()) timestamp = int(time.time())
@ -190,10 +191,10 @@ class HttpDigestAuthorization (object):
s s
A string related to the resource, such as the hostname of the server. A string related to the resource, such as the hostname of the server.
key key
A secret string known only to the server. A secret string known only to the server.
Both s and key must be the same values which were used to synthesize the nonce Both s and key must be the same values which were used to synthesize the nonce
we are trying to validate. we are trying to validate.
""" """
@ -256,7 +257,7 @@ class HttpDigestAuthorization (object):
4.3. This refers to the entity the user agent sent in the request which 4.3. This refers to the entity the user agent sent in the request which
has the Authorization header. Typically GET requests don't have an entity, has the Authorization header. Typically GET requests don't have an entity,
and POST requests do. and POST requests do.
""" """
ha2 = self.HA2(entity_body) ha2 = self.HA2(entity_body)
# Request-Digest -- RFC 2617 3.2.2.1 # Request-Digest -- RFC 2617 3.2.2.1
@ -302,16 +303,16 @@ def www_authenticate(realm, key, algorithm='MD5', nonce=None, qop=qop_auth, stal
def digest_auth(realm, get_ha1, key, debug=False): def digest_auth(realm, get_ha1, key, debug=False):
"""A CherryPy tool which hooks at before_handler to perform """A CherryPy tool which hooks at before_handler to perform
HTTP Digest Access Authentication, as specified in :rfc:`2617`. HTTP Digest Access Authentication, as specified in :rfc:`2617`.
If the request has an 'authorization' header with a 'Digest' scheme, this If the request has an 'authorization' header with a 'Digest' scheme, this
tool authenticates the credentials supplied in that header. If tool authenticates the credentials supplied in that header. If
the request has no 'authorization' header, or if it does but the scheme is the request has no 'authorization' header, or if it does but the scheme is
not "Digest", or if authentication fails, the tool sends a 401 response with not "Digest", or if authentication fails, the tool sends a 401 response with
a 'WWW-Authenticate' Digest header. a 'WWW-Authenticate' Digest header.
realm realm
A string containing the authentication realm. A string containing the authentication realm.
get_ha1 get_ha1
A callable which looks up a username in a credentials store A callable which looks up a username in a credentials store
and returns the HA1 string, which is defined in the RFC to be and returns the HA1 string, which is defined in the RFC to be
@ -320,13 +321,13 @@ def digest_auth(realm, get_ha1, key, debug=False):
where username is obtained from the request's 'authorization' header. where username is obtained from the request's 'authorization' header.
If username is not found in the credentials store, get_ha1() returns If username is not found in the credentials store, get_ha1() returns
None. None.
key key
A secret string known only to the server, used in the synthesis of nonces. A secret string known only to the server, used in the synthesis of nonces.
""" """
request = cherrypy.serving.request request = cherrypy.serving.request
auth_header = request.headers.get('authorization') auth_header = request.headers.get('authorization')
nonce_is_stale = False nonce_is_stale = False
if auth_header is not None: if auth_header is not None:
@ -334,10 +335,10 @@ def digest_auth(realm, get_ha1, key, debug=False):
auth = HttpDigestAuthorization(auth_header, request.method, debug=debug) auth = HttpDigestAuthorization(auth_header, request.method, debug=debug)
except ValueError: except ValueError:
raise cherrypy.HTTPError(400, "The Authorization header could not be parsed.") raise cherrypy.HTTPError(400, "The Authorization header could not be parsed.")
if debug: if debug:
TRACE(str(auth)) TRACE(str(auth))
if auth.validate_nonce(realm, key): if auth.validate_nonce(realm, key):
ha1 = get_ha1(realm, auth.username) ha1 = get_ha1(realm, auth.username)
if ha1 is not None: if ha1 is not None:
@ -355,7 +356,7 @@ def digest_auth(realm, get_ha1, key, debug=False):
if debug: if debug:
TRACE("authentication of %s successful" % auth.username) TRACE("authentication of %s successful" % auth.username)
return return
# Respond with 401 status and a WWW-Authenticate header # Respond with 401 status and a WWW-Authenticate header
header = www_authenticate(realm, key, stale=nonce_is_stale) header = www_authenticate(realm, key, stale=nonce_is_stale)
if debug: if debug: