mirror of
https://github.com/rembo10/headphones.git
synced 2026-05-24 12:27:45 +01:00
@@ -34,6 +34,7 @@ __date__ = 'April 2009'
|
||||
|
||||
|
||||
def md5_hex(s):
|
||||
"""Return hexdigest of md5sum."""
|
||||
return md5(ntob(s, 'utf-8')).hexdigest()
|
||||
|
||||
|
||||
@@ -48,6 +49,7 @@ DEFAULT_CHARSET = 'UTF-8'
|
||||
|
||||
|
||||
def TRACE(msg):
|
||||
"""Log message in TOOLS.AUTH_DIGEST context."""
|
||||
cherrypy.log(msg, context='TOOLS.AUTH_DIGEST')
|
||||
|
||||
# Three helper functions for users of the tool, providing three variants
|
||||
@@ -55,8 +57,9 @@ def TRACE(msg):
|
||||
|
||||
|
||||
def get_ha1_dict_plain(user_password_dict):
|
||||
"""Return a get_ha1 function which obtains a plaintext password from a
|
||||
dictionary of the form: {username : password}.
|
||||
"""Return a get_ha1 function which obtains a plaintext password.
|
||||
|
||||
user_password_dict is a dictionary of the form: {username : password}.
|
||||
|
||||
If you want a simple dictionary-based authentication scheme, with plaintext
|
||||
passwords, use get_ha1_dict_plain(my_userpass_dict) as the value for the
|
||||
@@ -72,8 +75,9 @@ def get_ha1_dict_plain(user_password_dict):
|
||||
|
||||
|
||||
def get_ha1_dict(user_ha1_dict):
|
||||
"""Return a get_ha1 function which obtains a HA1 password hash from a
|
||||
dictionary of the form: {username : HA1}.
|
||||
"""Return a get_ha1 function which obtains a HA1 password hash.
|
||||
|
||||
user_ha1_dict is a dictionary of the form: {username : HA1}.
|
||||
|
||||
If you want a dictionary-based authentication scheme, but with
|
||||
pre-computed HA1 hashes instead of plain-text passwords, use
|
||||
@@ -87,7 +91,9 @@ def get_ha1_dict(user_ha1_dict):
|
||||
|
||||
|
||||
def get_ha1_file_htdigest(filename):
|
||||
"""Return a get_ha1 function which obtains a HA1 password hash from a
|
||||
"""Return a get_ha1 function.
|
||||
|
||||
The returned function obtains a HA1 password hash from a
|
||||
flat file with lines of the same format as that produced by the Apache
|
||||
htdigest utility. For example, for realm 'wonderland', username 'alice',
|
||||
and password '4x5istwelve', the htdigest line would be::
|
||||
@@ -113,7 +119,9 @@ def get_ha1_file_htdigest(filename):
|
||||
|
||||
|
||||
def synthesize_nonce(s, key, timestamp=None):
|
||||
"""Synthesize a nonce value which resists spoofing and can be checked
|
||||
"""Synthesize a nonce value.
|
||||
|
||||
A nonce value resists spoofing and can be checked
|
||||
for staleness. Returns a string suitable as the value for 'nonce' in
|
||||
the www-authenticate header.
|
||||
|
||||
@@ -135,7 +143,7 @@ def synthesize_nonce(s, key, timestamp=None):
|
||||
|
||||
|
||||
def H(s):
|
||||
"""The hash function H."""
|
||||
"""Return an ``md5`` HEX hash."""
|
||||
return md5_hex(s)
|
||||
|
||||
|
||||
@@ -152,7 +160,8 @@ def _try_decode_header(header, charset):
|
||||
|
||||
|
||||
class HttpDigestAuthorization(object):
|
||||
"""
|
||||
"""Digest Authorization implementation.
|
||||
|
||||
Parses a Digest Authorization header and performs
|
||||
re-calculation of the digest.
|
||||
"""
|
||||
@@ -160,10 +169,12 @@ class HttpDigestAuthorization(object):
|
||||
scheme = 'digest'
|
||||
|
||||
def errmsg(self, s):
|
||||
"""Make an error message for HTTP Digest Authorization."""
|
||||
return 'Digest Authorization header: %s' % s
|
||||
|
||||
@classmethod
|
||||
def matches(cls, header):
|
||||
"""Check if header scheme matches auth implementation."""
|
||||
scheme, _, _ = header.partition(' ')
|
||||
return scheme.lower() == cls.scheme
|
||||
|
||||
@@ -171,6 +182,7 @@ class HttpDigestAuthorization(object):
|
||||
self, auth_header, http_method,
|
||||
debug=False, accept_charset=DEFAULT_CHARSET[:],
|
||||
):
|
||||
"""Initialize an HTTP Digest Authorization parser."""
|
||||
self.http_method = http_method
|
||||
self.debug = debug
|
||||
|
||||
@@ -229,10 +241,12 @@ class HttpDigestAuthorization(object):
|
||||
'neither cnonce nor nc can be present'))
|
||||
|
||||
def __str__(self):
|
||||
"""Render an HTTP Digest Auth header as a string."""
|
||||
return 'authorization : %s' % self.auth_header
|
||||
|
||||
def validate_nonce(self, s, key):
|
||||
"""Validate the nonce.
|
||||
|
||||
Returns True if nonce was generated by synthesize_nonce() and the
|
||||
timestamp is not spoofed, else returns False.
|
||||
|
||||
@@ -298,7 +312,7 @@ class HttpDigestAuthorization(object):
|
||||
return H(a2)
|
||||
|
||||
def request_digest(self, ha1, entity_body=''):
|
||||
"""Calculates the Request-Digest. See :rfc:`2617` section 3.2.2.1.
|
||||
"""Calculate the Request-Digest. See :rfc:`2617` section 3.2.2.1.
|
||||
|
||||
ha1
|
||||
The HA1 string obtained from the credentials store.
|
||||
@@ -351,7 +365,7 @@ def www_authenticate(
|
||||
realm, key, algorithm='MD5', nonce=None, qop=qop_auth,
|
||||
stale=False, accept_charset=DEFAULT_CHARSET[:],
|
||||
):
|
||||
"""Constructs a WWW-Authenticate header for Digest authentication."""
|
||||
"""Construct a WWW-Authenticate header for Digest authentication."""
|
||||
if qop not in valid_qops:
|
||||
raise ValueError("Unsupported value for qop: '%s'" % qop)
|
||||
if algorithm not in valid_algorithms:
|
||||
@@ -374,7 +388,9 @@ def www_authenticate(
|
||||
|
||||
|
||||
def digest_auth(realm, get_ha1, key, debug=False, accept_charset='utf-8'):
|
||||
"""A CherryPy tool that hooks at before_handler to perform
|
||||
"""Perform HTTP Digest Access Authentication.
|
||||
|
||||
A CherryPy tool that hooks at ``before_handler`` to perform
|
||||
HTTP Digest Access Authentication, as specified in :rfc:`2617`.
|
||||
|
||||
If the request has an 'authorization' header with a 'Digest' scheme,
|
||||
|
||||
Reference in New Issue
Block a user