mirror of
https://github.com/kennethreitz/httpbin.git
synced 2026-06-05 23:00:18 +00:00
Added support for another Digest algorithm, SHA256.
This commit is contained in:
+7
-5
@@ -405,9 +405,11 @@ def hidden_basic_auth(user='user', passwd='passwd'):
|
||||
return jsonify(authenticated=True, user=user)
|
||||
|
||||
|
||||
@app.route('/digest-auth/<qop>/<user>/<passwd>')
|
||||
def digest_auth(qop=None, user='user', passwd='passwd'):
|
||||
@app.route('/digest-auth/<algorithm>/<qop>/<user>/<passwd>')
|
||||
def digest_auth(algorithm='MD5', qop=None, user='user', passwd='passwd'):
|
||||
"""Prompts the user for authorization using HTTP Digest auth"""
|
||||
if algorithm not in ('MD5', 'SHA-256'):
|
||||
algorithm = 'MD5'
|
||||
if qop not in ('auth', 'auth-int'):
|
||||
qop = None
|
||||
if 'Authorization' not in request.headers or \
|
||||
@@ -426,12 +428,12 @@ def digest_auth(qop=None, user='user', passwd='passwd'):
|
||||
str(time.time()).encode('ascii'),
|
||||
b':',
|
||||
os.urandom(10)
|
||||
]))
|
||||
opaque = H(os.urandom(10))
|
||||
]), "MD5")
|
||||
opaque = H(os.urandom(10), "MD5")
|
||||
|
||||
auth = WWWAuthenticate("digest")
|
||||
auth.set_digest('me@kennethreitz.com', nonce, opaque=opaque,
|
||||
qop=('auth', 'auth-int') if qop is None else (qop, ))
|
||||
qop=('auth', 'auth-int') if qop is None else (qop, ), algorithm=algorithm)
|
||||
response.headers['WWW-Authenticate'] = auth.to_header()
|
||||
response.headers['Set-Cookie'] = 'fake=fake_value'
|
||||
return response
|
||||
|
||||
+17
-12
@@ -9,7 +9,7 @@ This module provides helper functions for httpbin.
|
||||
|
||||
import json
|
||||
import base64
|
||||
from hashlib import md5
|
||||
from hashlib import md5, sha256
|
||||
from werkzeug.http import parse_authorization_header
|
||||
|
||||
from flask import request, make_response
|
||||
@@ -260,11 +260,14 @@ def check_basic_auth(user, passwd):
|
||||
# Digest auth helpers
|
||||
# qop is a quality of protection
|
||||
|
||||
def H(data):
|
||||
return md5(data).hexdigest()
|
||||
def H(data, algorithm):
|
||||
if algorithm == 'SHA-256':
|
||||
return sha256(data).hexdigest()
|
||||
else:
|
||||
return md5(data).hexdigest()
|
||||
|
||||
|
||||
def HA1(realm, username, password):
|
||||
def HA1(realm, username, password, algorithm):
|
||||
"""Create HA1 hash by realm, username, password
|
||||
|
||||
HA1 = md5(A1) = MD5(username:realm:password)
|
||||
@@ -273,10 +276,10 @@ def HA1(realm, username, password):
|
||||
realm = u''
|
||||
return H(b":".join([username.encode('utf-8'),
|
||||
realm.encode('utf-8'),
|
||||
password.encode('utf-8')]))
|
||||
password.encode('utf-8')]), algorithm)
|
||||
|
||||
|
||||
def HA2(credentails, request):
|
||||
def HA2(credentails, request, algorithm):
|
||||
"""Create HA2 md5 hash
|
||||
|
||||
If the qop directive's value is "auth" or is unspecified, then HA2:
|
||||
@@ -285,14 +288,14 @@ def HA2(credentails, request):
|
||||
HA2 = md5(A2) = MD5(method:digestURI:MD5(entityBody))
|
||||
"""
|
||||
if credentails.get("qop") == "auth" or credentails.get('qop') is None:
|
||||
return H(b":".join([request['method'].encode('utf-8'), request['uri'].encode('utf-8')]))
|
||||
return H(b":".join([request['method'].encode('utf-8'), request['uri'].encode('utf-8')]), algorithm)
|
||||
elif credentails.get("qop") == "auth-int":
|
||||
for k in 'method', 'uri', 'body':
|
||||
if k not in request:
|
||||
raise ValueError("%s required" % k)
|
||||
return H("%s:%s:%s" % (request['method'],
|
||||
request['uri'],
|
||||
H(request['body'])))
|
||||
H(request['body'])), algorithm)
|
||||
raise ValueError
|
||||
|
||||
|
||||
@@ -310,18 +313,20 @@ def response(credentails, password, request):
|
||||
- `request`: request dict
|
||||
"""
|
||||
response = None
|
||||
algorithm = credentails.get('algorithm')
|
||||
HA1_value = HA1(
|
||||
credentails.get('realm'),
|
||||
credentails.get('username'),
|
||||
password
|
||||
password,
|
||||
algorithm
|
||||
)
|
||||
HA2_value = HA2(credentails, request)
|
||||
HA2_value = HA2(credentails, request, algorithm)
|
||||
if credentails.get('qop') is None:
|
||||
response = H(b":".join([
|
||||
HA1_value.encode('utf-8'),
|
||||
credentails.get('nonce', '').encode('utf-8'),
|
||||
HA2_value.encode('utf-8')
|
||||
]))
|
||||
]), algorithm)
|
||||
elif credentails.get('qop') == 'auth' or credentails.get('qop') == 'auth-int':
|
||||
for k in 'nonce', 'nc', 'cnonce', 'qop':
|
||||
if k not in credentails:
|
||||
@@ -331,7 +336,7 @@ def response(credentails, password, request):
|
||||
credentails.get('nc').encode('utf-8'),
|
||||
credentails.get('cnonce').encode('utf-8'),
|
||||
credentails.get('qop').encode('utf-8'),
|
||||
HA2_value.encode('utf-8')]))
|
||||
HA2_value.encode('utf-8')]), algorithm)
|
||||
else:
|
||||
raise ValueError("qop value are wrong")
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<li><a href="{{ url_for('delete_cookies', k1='', k2='') }}"><code>/cookies/delete?name</code></a> Deletes one or more simple cookies.</li>
|
||||
<li><a href="{{ url_for('basic_auth', user='user', passwd='passwd') }}"><code>/basic-auth/:user/:passwd</code></a> Challenges HTTPBasic Auth.</li>
|
||||
<li><a href="{{ url_for('hidden_basic_auth', user='user', passwd='passwd') }}"><code>/hidden-basic-auth/:user/:passwd</code></a> 404'd BasicAuth.</li>
|
||||
<li><a href="{{ url_for('digest_auth', qop='auth', user='user', passwd='passwd') }}"><code>/digest-auth/:qop/:user/:passwd</code></a> Challenges HTTP Digest Auth.</li>
|
||||
<li><a href="{{ url_for('digest_auth', algorithm='MD5', qop='auth', user='user', passwd='passwd') }}"><code>/digest-auth/:algorithm/:qop/:user/:passwd</code></a> Challenges HTTP Digest Auth.</li>
|
||||
<li><a href="{{ url_for('stream_n_messages', n=20) }}"><code>/stream/:n</code></a> Streams <em>n</em>–100 lines.</li>
|
||||
<li><a href="{{ url_for('delay_response', delay=3) }}"><code>/delay/:n</code></a> Delays responding for <em>n</em>–10 seconds.</li>
|
||||
<li><a href="{{ url_for('drip', numbytes=5, duration=5, code=200) }}"><code>/drip?numbytes=n&duration=s&delay=s&code=code</code></a> Drips data over a duration after an optional initial delay, then (optionally) returns with the given status code.</li>
|
||||
|
||||
+6
-6
@@ -164,9 +164,9 @@ class HttpbinTestCase(unittest.TestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_digest_auth_with_wrong_password(self):
|
||||
auth_header = 'Digest username="user",realm="wrong",nonce="wrong",uri="/digest-auth/user/passwd",response="wrong",opaque="wrong"'
|
||||
auth_header = 'Digest username="user",realm="wrong",nonce="wrong",uri="/digest-auth/MD5/user/passwd",response="wrong",opaque="wrong"'
|
||||
response = self.app.get(
|
||||
'/digest-auth/auth/user/passwd',
|
||||
'/digest-auth/MD5/auth/user/passwd',
|
||||
environ_base={
|
||||
# httpbin's digest auth implementation uses the remote addr to
|
||||
# build the nonce
|
||||
@@ -181,7 +181,7 @@ class HttpbinTestCase(unittest.TestCase):
|
||||
def test_digest_auth(self):
|
||||
# make first request
|
||||
unauthorized_response = self.app.get(
|
||||
'/digest-auth/auth/user/passwd',
|
||||
'/digest-auth/MD5/auth/user/passwd',
|
||||
environ_base={
|
||||
# digest auth uses the remote addr to build the nonce
|
||||
'REMOTE_ADDR': '127.0.0.1',
|
||||
@@ -196,7 +196,7 @@ class HttpbinTestCase(unittest.TestCase):
|
||||
d = parse_dict_header(auth_info)
|
||||
a1 = b'user:' + d['realm'].encode('utf-8') + b':passwd'
|
||||
ha1 = md5(a1).hexdigest().encode('utf-8')
|
||||
a2 = b'GET:/digest-auth/auth/user/passwd'
|
||||
a2 = b'GET:/digest-auth/MD5/auth/user/passwd'
|
||||
ha2 = md5(a2).hexdigest().encode('utf-8')
|
||||
a3 = ha1 + b':' + d['nonce'].encode('utf-8') + b':' + ha2
|
||||
auth_response = md5(a3).hexdigest()
|
||||
@@ -204,14 +204,14 @@ class HttpbinTestCase(unittest.TestCase):
|
||||
d['realm'] + \
|
||||
'",nonce="' + \
|
||||
d['nonce'] + \
|
||||
'",uri="/digest-auth/auth/user/passwd",response="' + \
|
||||
'",uri="/digest-auth/MD5/auth/user/passwd",response="' + \
|
||||
auth_response + \
|
||||
'",opaque="' + \
|
||||
d['opaque'] + '"'
|
||||
|
||||
# make second request
|
||||
authorized_response = self.app.get(
|
||||
'/digest-auth/auth/user/passwd',
|
||||
'/digest-auth/MD5/auth/user/passwd',
|
||||
environ_base={
|
||||
# httpbin's digest auth implementation uses the remote addr to
|
||||
# build the nonce
|
||||
|
||||
Reference in New Issue
Block a user