Skip to content

Commit

Permalink
Working hacky decorator for hardcoded auth
Browse files Browse the repository at this point in the history
  • Loading branch information
almad committed Oct 26, 2008
1 parent ad41fe2 commit 8b603a5
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 10 deletions.
1 change: 1 addition & 0 deletions .hgignore
Expand Up @@ -8,3 +8,4 @@ syntax: glob
*.db
.settings
*~
out.err
15 changes: 12 additions & 3 deletions djangohttpdigest/decorators.py
@@ -1,6 +1,7 @@
from http import HttpResponseNotAuthorized
from django.http import HttpResponseBadRequest

from digest import get_digest_challenge
from http import HttpResponseNotAuthorized
from digest import get_digest_challenge, parse_authorization_header, check_hardcoded_authentication

def protect_digest(realm, username, password):
def _innerDecorator(function):
Expand All @@ -9,7 +10,15 @@ def _wrapper(request, *args, **kwargs):
# successfull auth
if request.META['AUTH_TYPE'].lower() != 'digest':
raise NotImplementedError("Only digest supported")
#return function(request, *args, **kwargs)
try:
parsed_header = parse_authorization_header(request.META['HTTP_AUTHORIZATION'])
except ValueError, err:
return HttpResponseBadRequest(err)

if check_hardcoded_authentication(parsed_header, request.method, request.path, request.GET.urlencode(), realm, username, password):
return function(request, *args, **kwargs)

# nothing received, return challenge
response = HttpResponseNotAuthorized("Not Authorized")
response['www-authenticate'] = get_digest_challenge(realm)
return response
Expand Down
49 changes: 46 additions & 3 deletions djangohttpdigest/digest.py
Expand Up @@ -2,12 +2,34 @@
Helper functions and algorithms for computing HTTP digest thingies.
"""
import time
import urllib2
from md5 import md5
from django.http import HttpResponseBadRequest

def parse_authorization_header(header):
#auth_scheme, auth_params = credentials.split(" ", 1)
pass
""" Parse requests authorization header into list.
Raise ValueError if some problem occurs. """
# digest is marked as part of header and causes problem
# parsing, so remove its

if header.startswith('Digest '):
header = header[len('Digest '):]

# Convert the auth params to a dict
items = urllib2.parse_http_list(header)
params = urllib2.parse_keqv_list(items)

required = ["username", "realm", "nonce", "uri", "response"]

for field in required:
if not params.has_key(field):
raise ValueError("Required field %s not found" % field)

# check for qop companions
# (RFC 2617, sect. 3.2.2)
if params.has_key("qop") and not params.has_key("cnonce") and params.has_key("cn"):
raise ValueError("qop sent without cnonce and cn")

return params

def get_digest_challenge(realm):
""" Return HTTP digest challenge, which has to be placed into www-authenticate header"""
Expand Down Expand Up @@ -35,4 +57,25 @@ def check_credentials(request):
else:
return False

def check_hardcoded_authentication(parsed_header, method, path, params, realm, username, password):
""" Do information sent in header authenticates against given credentials? """
assert parsed_header['qop'] == 'auth'

# compute A1 according to RFC 2617, section 3.2.2.2
a1 = md5("%s:%s:%s" % (username, realm, password)).hexdigest()
# A2, according to section 3.2.2.3
a2 = md5("%s:%s" % (method,path)).hexdigest()

request = "%s:%s:%s:%s:%s" % (
parsed_header["nonce"],
parsed_header["nc"],
parsed_header["cnonce"],
parsed_header["qop"],
a2
)


result_secret = md5("%s:%s" % (a1, request)).hexdigest()

return parsed_header['response'] == result_secret

4 changes: 2 additions & 2 deletions djangohttpdigest/tests/test_digest.py
@@ -1,7 +1,7 @@
import urllib2

from django.test import TestCase
from django.http import HttpRequest, HttpResponseBadRequest
from django.http import HttpRequest
from django.core.handlers.wsgi import WSGIRequest

from djangohttpdigest.digest import check_credentials, parse_authorization_header
Expand Down Expand Up @@ -32,4 +32,4 @@ def test_check_credentials(self):
def test_parse_authorization_header(self):
""" Authorization header parsing, for various inputs """

self.assertRaises(HttpResponseBadRequest, lambda:parse_authorization_header(''))
self.assertRaises(ValueError, lambda:parse_authorization_header(''))
2 changes: 1 addition & 1 deletion testproject/config.py
Expand Up @@ -17,7 +17,7 @@
MANAGERS = ADMINS

DATABASE_ENGINE = "sqlite3"
DATABASE_NAME = "/tmp/test.db"
DATABASE_NAME = "/home/almad/tmp/httpdigesttest.db"
DATABASE_USER = ""
DATABASE_PASSWORD = ""
DATABASE_HOST = "localhost"
Expand Down
1 change: 0 additions & 1 deletion testproject/testapi/views.py
Expand Up @@ -9,5 +9,4 @@ def simpleprotected(request):
view returns 401 on failure or for challenge, 200 with empty body
on successfull authorization.
"""
raise ValueError()
return HttpResponse('')

0 comments on commit 8b603a5

Please sign in to comment.