Commits

Anonymous committed b20f50c

Created digestor, moved challenge retrieving to it

Comments (0)

Files changed (3)

djangohttpdigest/decorators.py

 from django.http import HttpResponseBadRequest
 
 from http import HttpResponseNotAuthorized
-from digest import get_digest_challenge, parse_authorization_header, check_hardcoded_authentication
+from digest import Digestor, parse_authorization_header, check_hardcoded_authentication
 
 def protect_digest(realm, username, password):
     def _innerDecorator(function):
         def _wrapper(request, *args, **kwargs):
+            
+            digestor = Digestor(realm=realm)
+            
             if request.META.has_key('HTTP_AUTHORIZATION'):
                 # successfull auth
                 if request.META['AUTH_TYPE'].lower() != 'digest':
             
             # nothing received, return challenge
             response = HttpResponseNotAuthorized("Not Authorized")
-            response['www-authenticate'] = get_digest_challenge(realm)
+            response['www-authenticate'] = digestor.get_digest_challenge()
             return response
         return _wrapper
     return _innerDecorator

djangohttpdigest/digest.py

 """
 Helper functions and algorithms for computing HTTP digest thingies.
 """
-import time
+from time import time
 import urllib2
 from md5 import md5
+#from sha import sha
+
+class Digestor(object):
+    """ Main class for handling digest algorithms as described in RFC 2617 """
+    
+    # map string representation of algorithm to hash function
+    algorithm_implementation_map = {
+        'md5' : md5,
+#        'sha' : sha,
+    }
+    
+    def __init__(self, realm=None, qop=None, opaque=None, algorithm=None):
+        object.__init__(self)
+        
+        self.algorithm = algorithm or 'md5'
+        self.opaque = opaque or 'ToDoMoveThisToSettings'
+        self.qop = qop or 'auth'
+        self.realm = realm or None
+        
+        assert self.algorithm in self.algorithm_implementation_map
+    
+    def get_digest_challenge(self):
+        """ Return HTTP digest challenge, which has to be placed into www-authenticate header"""
+        
+        nonce = self.algorithm_implementation_map[self.algorithm]("%s:%s" % (time(), self.realm)).hexdigest()
+        
+        return 'Digest realm="%(realm)s", qop="%(qop)s", nonce="%(nonce)s", opaque="%(opaque)s"' % {
+            'realm' : self.realm,
+            'qop' : self.qop,
+            'nonce' : nonce,
+            'opaque' : self.opaque
+        }
 
 def parse_authorization_header(header):
     """ Parse requests authorization header into list.
 
     return params
 
-def get_digest_challenge(realm):
-    """ Return HTTP digest challenge, which has to be placed into www-authenticate header"""
-    
-    algorithm = 'md5'
-    qop = 'auth'
-    opaque = 'ToDoMoveThisToSettings'
-    
-    nonce = md5("%s:%s" % (time.time(), realm)).hexdigest()
-    
-    return 'Digest realm="%(realm)s", qop="%(qop)s", nonce="%(nonce)s", opaque="%(opaque)s"' % {
-        'realm' : realm,
-        'qop' : qop,
-        'nonce' : nonce,
-        'opaque' : opaque
-    }
-
 def check_credentials(request):
     """
     Check if request contains credentials.

djangohttpdigest/tests/test_digest.py

+import re
 import urllib2
 
 from django.test import TestCase
 from django.http import HttpRequest
 from django.core.handlers.wsgi import WSGIRequest
 
-from djangohttpdigest.digest import check_credentials, parse_authorization_header
+from djangohttpdigest.digest import Digestor, check_credentials, parse_authorization_header
 
+class TestDigestor(TestCase):
+    """ Test digestor, our wrapping class for handling digests """
+    
+    def setUp(self):
+        self.digestor = Digestor(realm='testrealm')
+    
+    def test_get_digest_challenge(self):
+        challenge = self.digestor.get_digest_challenge()
+        
+        # check our challenge is compatible with urllib2's resolving
+        if re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', re.I).match(challenge):
+            pass
+        else:
+            self.fail("Challenge %s does not match urllib2's regexp" % challenge)
+    
 class TestSimpleDigest(TestCase):
     
     environment = {
     
     def test_parse_authorization_header(self):
         """ Authorization header parsing, for various inputs """
-        
         self.assertRaises(ValueError, lambda:parse_authorization_header(''))