Skip to content

Commit

Permalink
python 3 support
Browse files Browse the repository at this point in the history
  • Loading branch information
Miguel Grinberg committed Aug 30, 2013
1 parent c0c614d commit c13ff0a
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 24 deletions.
18 changes: 13 additions & 5 deletions flask_httpauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from functools import wraps
from hashlib import md5
from random import random
from random import Random, SystemRandom
from flask import request, make_response, session

class HTTPAuth(object):
Expand Down Expand Up @@ -75,8 +75,16 @@ def authenticate(self, auth, password):
return client_password == password

class HTTPDigestAuth(HTTPAuth):
def __init__(self):
super(HTTPDigestAuth, self).__init__()
self.random = SystemRandom()
try:
self.random.random()
except NotImplementedError:
self.random = Random()

def get_nonce(self):
return md5(str(random())).hexdigest()
return md5(str(self.random.random()).encode('utf-8')).hexdigest()

def authenticate_header(self):
session["auth_nonce"] = self.get_nonce()
Expand All @@ -89,9 +97,9 @@ def authenticate(self, auth, password):
if auth.nonce != session.get("auth_nonce") or auth.opaque != session.get("auth_opaque"):
return False
a1 = auth.username + ":" + auth.realm + ":" + password
ha1 = md5(a1).hexdigest()
ha1 = md5(a1.encode('utf-8')).hexdigest()
a2 = request.method + ":" + auth.uri
ha2 = md5(a2).hexdigest()
ha2 = md5(a2.encode('utf-8')).hexdigest()
a3 = ha1 + ":" + auth.nonce + ":" + ha2
response = md5(a3).hexdigest()
response = md5(a3.encode('utf-8')).hexdigest()
return response == auth.response
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

setup(
name='Flask-HTTPAuth',
version='1.0.0',
version='1.1.0',
url='http://github.com/miguelgrinberg/flask-httpauth/',
license='BSD',
author='Miguel Grinberg',
Expand Down
41 changes: 23 additions & 18 deletions test_httpauth.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import unittest
import base64
import re
from hashlib import md5
from hashlib import md5 as basic_md5
from flask import Flask
from flask.ext.httpauth import HTTPBasicAuth, HTTPDigestAuth
from werkzeug.http import parse_dict_header

def md5(str):
if type(str).__name__ == 'str':
str = str.encode('utf-8')
return basic_md5(str)

class HTTPAuthTestCase(unittest.TestCase):
def setUp(self):
app = Flask(__name__)
Expand Down Expand Up @@ -110,56 +115,56 @@ def digest_auth_my_realm_route():

def test_no_auth(self):
response = self.client.get('/')
self.assertTrue(response.data == "index")
self.assertTrue(response.data.decode('utf-8') == "index")

def test_basic_auth_prompt(self):
response = self.client.get('/basic')
self.assertTrue(response.status_code == 401)
self.assertIn("WWW-Authenticate", response.headers)
self.assertTrue("WWW-Authenticate" in response.headers)
self.assertTrue(response.headers["WWW-Authenticate"] == 'Basic realm="Authentication Required"')

def test_basic_auth_prompt_with_custom_realm(self):
response = self.client.get('/basic-with-realm')
self.assertTrue(response.status_code == 401)
self.assertIn("WWW-Authenticate", response.headers)
self.assertTrue("WWW-Authenticate" in response.headers)
self.assertTrue(response.headers["WWW-Authenticate"] == 'Basic realm="My Realm"')
self.assertTrue(response.data == "custom error")
self.assertTrue(response.data.decode('utf-8') == "custom error")

def test_basic_auth_login_valid(self):
response = self.client.get('/basic',
headers = { "Authorization": "Basic " + base64.encodestring("john:hello").strip("\r\n") })
self.assertTrue(response.data == "basic_auth")
headers = { "Authorization": "Basic " + base64.b64encode(b'john:hello').decode('utf-8').strip("\r\n") })
self.assertTrue(response.data.decode('utf-8') == "basic_auth")
self.assertTrue(self.basic_auth.username == "john")

def test_basic_auth_login_invalid(self):
response = self.client.get('/basic-with-realm',
headers = { "Authorization": "Basic " + base64.encodestring("john:bye").strip("\r\n") })
headers = { "Authorization": "Basic " + base64.b64encode(b'john:bye').decode('utf-8').strip("\r\n") })
self.assertTrue(response.status_code == 401)
self.assertIn("WWW-Authenticate", response.headers)
self.assertTrue("WWW-Authenticate" in response.headers)
self.assertTrue(response.headers["WWW-Authenticate"] == 'Basic realm="My Realm"')

def test_basic_custom_auth_login_valid(self):
response = self.client.get('/basic-custom',
headers = { "Authorization": "Basic " + base64.encodestring("john:hello").strip("\r\n") })
self.assertTrue(response.data == "basic_custom_auth")
headers = { "Authorization": "Basic " + base64.b64encode(b'john:hello').decode('utf-8').strip("\r\n") })
self.assertTrue(response.data.decode('utf-8') == "basic_custom_auth")
self.assertTrue(self.basic_custom_auth.username == "john")

def test_basic_custom_auth_login_invalid(self):
response = self.client.get('/basic-custom',
headers = { "Authorization": "Basic " + base64.encodestring("john:bye").strip("\r\n") })
headers = { "Authorization": "Basic " + base64.b64encode(b'john:bye').decode('utf-8').strip("\r\n") })
self.assertTrue(response.status_code == 401)
self.assertIn("WWW-Authenticate", response.headers)
self.assertTrue("WWW-Authenticate" in response.headers)

def test_digest_auth_prompt(self):
response = self.client.get('/digest')
self.assertTrue(response.status_code == 401)
self.assertIn("WWW-Authenticate", response.headers)
self.assertTrue("WWW-Authenticate" in response.headers)
self.assertTrue(re.match(r'^Digest realm="Authentication Required",nonce="[0-9a-f]+",opaque="[0-9a-f]+"$', response.headers["WWW-Authenticate"]))

def test_digest_auth_prompt_with_custom_realm(self):
response = self.client.get('/digest-with-realm')
self.assertTrue(response.status_code == 401)
self.assertIn("WWW-Authenticate", response.headers)
self.assertTrue("WWW-Authenticate" in response.headers)
self.assertTrue(re.match(r'^Digest realm="My Realm",nonce="[0-9a-f]+",opaque="[0-9a-f]+"$', response.headers["WWW-Authenticate"]))

def test_digest_auth_login_valid(self):
Expand All @@ -178,7 +183,7 @@ def test_digest_auth_login_valid(self):

response = self.client.get('/digest',
headers = { "Authorization": 'Digest username="john",realm="' + d['realm'] + '",nonce="' + d['nonce'] + '",uri="/digest",response="' + auth_response + '",opaque="' + d['opaque'] + '"' })
self.assertTrue(response.data == "digest_auth")
self.assertTrue(response.data.decode('utf-8') == "digest_auth")
self.assertTrue(self.digest_auth.username == "john")

def test_digest_auth_login_bad_realm(self):
Expand All @@ -198,14 +203,14 @@ def test_digest_auth_login_bad_realm(self):
response = self.client.get('/digest',
headers = { "Authorization": 'Digest username="john",realm="' + d['realm'] + '",nonce="' + d['nonce'] + '",uri="/digest",response="' + auth_response + '",opaque="' + d['opaque'] + '"' })
self.assertTrue(response.status_code == 401)
self.assertIn("WWW-Authenticate", response.headers)
self.assertTrue("WWW-Authenticate" in response.headers)
self.assertTrue(re.match(r'^Digest realm="Authentication Required",nonce="[0-9a-f]+",opaque="[0-9a-f]+"$', response.headers["WWW-Authenticate"]))

def test_digest_auth_login_invalid(self):
response = self.client.get('/digest-with-realm',
headers = { "Authorization": 'Digest username="susan",realm="My Realm",nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",uri="/digest-with-realm",response="ca306c361a9055b968810067a37fb8cb",opaque="5ccc069c403ebaf9f0171e9517f40e41"' })
self.assertTrue(response.status_code == 401)
self.assertIn("WWW-Authenticate", response.headers)
self.assertTrue("WWW-Authenticate" in response.headers)
self.assertTrue(re.match(r'^Digest realm="My Realm",nonce="[0-9a-f]+",opaque="[0-9a-f]+"$', response.headers["WWW-Authenticate"]))

def suite():
Expand Down

0 comments on commit c13ff0a

Please sign in to comment.