text: from tornado.web import *
  from hashlib import md5
  
  class DigestAuthMixin(object):
      def apply_checksum(self, data):
          return md5(data).hexdigest()
  
      def apply_digest(self, secret, data):
          return self.apply_checksum(secret + ":" + data)
  
      def A1(self, algorithm, auth_pass):
          """
           If 'algorithm' is "MD5" or unset, A1 is:
           A1 = unq(username-value) ":" unq(realm-value) ":" passwd
  
           if 'algorithm' is 'MD5-Sess', A1 is:
           A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
            ":" unq(nonce-value) ":" unq(cnonce-value)
  
          """
  
          username = self.params["username"]
          if algorithm == 'MD5' or not algorithm:
              return "%s:%s:%s" % (username, self.realm, auth_pass)
          elif algorithm == 'MD5-Sess':
              return self.apply_checksum('%s:%s:%s:%s:%s' % \
                                         (username,
                                         self.realm,
                                         auth_pass,
                                         self.params['nonce'],
                                         self.params['cnonce']))
  
  
      def A2(self):
          """
          If the "qop" directive's value is "auth" or is unspecified, then A2 is:
              A2 = Method ":" digest-uri-value
          Else,
              A2 = Method ":" digest-uri-value ":" H(entity-body)
  
          """
          if self.params['qop'] == 'auth' or not self.params['qop']:
              return self.request.method + ":" + self.request.uri
          elif self.params['qop'] == 'auth-int':
              #print "UNSUPPORTED 'qop' METHOD\n"
              return ":".join([self.request.method,
                               self.request.uri,
                               self.apply_checksum(self.request.body)])
          else:
              print "A2 GOT BAD VALUE FOR 'qop': %s\n" % self.params['qop']
  
      def response(self, auth_pass):
          if 'qop' in self.params:
              auth_comps = [self.params['nonce'],
                                 self.params['nc'],
                                 self.params['cnonce'],
                                 self.params['qop'],
                                 self.apply_checksum(self.A2())]
              return self.apply_digest(self.apply_checksum( \
                                      self.A1(self.params.get('algorithm'),
                                              auth_pass)),
                                       ':'.join(auth_comps))
          else:
              return self.apply_digest(self.apply_checksum( \
                                      self.A1(self.params.get('algorithm'),
                                              auth_pass)),
                                      ':'.join([self.params["nonce"],
                                                self.apply_checksum(self.A2())]))
  
      def _parse_header(self, authheader):
          try:
              n = len("Digest ")
              authheader = authheader[n:].strip()
              items = authheader.split(",")
              keyvalues = [i.split("=", 1) for i in items]
              keyvalues = [(k.strip(), v.strip().replace('"', '')) for k, v in keyvalues]
              self.params = dict(keyvalues)
          except:
              self.params = dict()
  
      def _create_nonce(self):
          return md5("%d:%s" % (time.time(), self.realm)).hexdigest()
  
      def createAuthHeader(self):
          self.set_status(401)
          nonce = self._create_nonce()
          self.set_header('WWW-Authenticate', 'Digest algorithm=MD5, realm="%s", qop=auth, nonce=%s' % (self.realm, nonce))
          self.write('please authenticate\n')
          self.finish()
  
          return False
  
      def get_authenticated_user(self, get_creds_callback, realm):
          creds = None
          expected_response = None
          actual_response = None
          auth = None
          self.params = dict()
          if not hasattr(self,'realm'):
              self.realm = realm
  
          try:
              auth = self.request.headers.get('Authorization')
              if not auth or not auth.startswith('Digest '):
                  return self.createAuthHeader()
              else:
                  self._parse_header(auth)
                  required_params = ['username', 'realm', 'nonce', 'uri', 'response', 'qop', 'nc', 'cnonce']
                  for k in required_params:
                      if k not in self.params:
                          print "REQUIRED PARAM %s MISSING\n" % k
                          return self.createAuthHeader()
                      elif not self.params[k]:
                          print "REQUIRED PARAM %s IS NONE OR EMPTY\n" % k
                          return self.createAuthHeader()
                      else:
                          continue
  
              creds = get_creds_callback(self.params['username'])
              if not creds:
                  # the username passed to get_creds_callback didn't
                  # match any valid users.
                  self.createAuthHeader()
              else:
                  expected_response = self.response(creds['auth_password'])
                  actual_response = self.params['response']
                  print "Expected: %s" % expected_response
                  print "Actual: %s" % actual_response
  
              if expected_response and actual_response:
                  if expected_response == actual_response:
                      self._current_user = self.params['username']
                      print "Digest Auth user '%s' successful for realm '%s'. URI: '%s', IP: '%s'" % (self.params['username'], self.realm, self.request.uri, self.request.remote_ip)
                      return True
                  else:
                      self.createAuthHeader()
  
          except Exception as out:
              print "FELL THROUGH: %s\n" % out
              print "AUTH HEADERS: %s" % auth
              print "SELF.PARAMS: ",self.params,"\n"
              print "CREDS: ", creds
              print "EXPECTED RESPONSE: %s" % expected_response
              print "ACTUAL RESPONSE: %s" % actual_response
              return self.createAuthHeader()
  
  
  def digest_auth(realm, auth_func):
      """A decorator used to protect methods with HTTP Digest authentication.
  
      """
      def digest_auth_decorator(func):
          def func_replacement(self, *args, **kwargs):
              # 'self' here is the RequestHandler object, which is inheriting
              # from DigestAuthMixin to get 'get_authenticated_user'
              if self.get_authenticated_user(auth_func, realm):
                  return func(self, *args, **kwargs)
          return func_replacement
      return digest_auth_decorator
  