query signing check
This commit is contained in:
parent
67092eb4f4
commit
8b0721980d
|
@ -1,6 +1,7 @@
|
|||
class AppSettings(object):
|
||||
__DEFAULTS = {
|
||||
'ENABLED': True,
|
||||
'API_KEY': '12345',
|
||||
}
|
||||
|
||||
def __init__(self, prefix):
|
||||
|
@ -18,6 +19,6 @@ class AppSettings(object):
|
|||
# Ugly? Guido recommends this himself ...
|
||||
# http://mail.python.org/pipermail/python-ideas/2012-May/014969.html
|
||||
import sys
|
||||
app_settings = AppSettings('A2_PLUGIN_TEMPLATE_')
|
||||
app_settings = AppSettings('A2_PLUGIN_USERINFO_')
|
||||
app_settings.__name__ = __name__
|
||||
sys.modules[__name__] = app_settings
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
from urlparse import urlparse
|
||||
from functools import wraps
|
||||
|
||||
from django.http import HttpResponseForbidden
|
||||
|
||||
from . import app_settings, signature
|
||||
|
||||
def valid_signature(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
api_key = app_settings.API_KEY
|
||||
request = args[1]
|
||||
signed_query = urlparse(request.get_full_path()).query
|
||||
try:
|
||||
assert signature.check_query(signed_query, api_key) is True
|
||||
return func(*args, **kwargs)
|
||||
except (KeyError, AssertionError):
|
||||
return HttpResponseForbidden()
|
||||
return wrapper
|
|
@ -0,0 +1,42 @@
|
|||
import datetime
|
||||
import base64
|
||||
import hmac
|
||||
import hashlib
|
||||
import urllib
|
||||
import random
|
||||
import urlparse
|
||||
|
||||
def sign_string(s, key, algo='sha256', timedelta=30):
|
||||
digestmod = getattr(hashlib, algo)
|
||||
hash = hmac.HMAC(key, digestmod=digestmod, msg=s)
|
||||
return hash.digest()
|
||||
|
||||
def check_query(query, key, known_nonce=None, timedelta=30):
|
||||
parsed = urlparse.parse_qs(query)
|
||||
signature = base64.b64decode(parsed['signature'][0])
|
||||
algo = parsed['algo'][0]
|
||||
timestamp = parsed['timestamp'][0]
|
||||
timestamp = datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%SZ')
|
||||
nonce = parsed['nonce']
|
||||
unsigned_query = query.split('&signature=')[0]
|
||||
if known_nonce is not None and known_nonce(nonce):
|
||||
return False
|
||||
if abs(datetime.datetime.utcnow() - timestamp) > datetime.timedelta(seconds=timedelta):
|
||||
return False
|
||||
return check_string(unsigned_query, signature, key, algo=algo)
|
||||
|
||||
def check_string(s, signature, key, algo='sha256'):
|
||||
# constant time compare
|
||||
signature2 = sign_string(s, key, algo=algo)
|
||||
if len(signature2) != len(signature):
|
||||
return False
|
||||
res = 0
|
||||
for a, b in zip(signature, signature2):
|
||||
res |= ord(a) ^ ord(b)
|
||||
return res == 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_key = '12345'
|
||||
signed_query = sign_query('NameId=_12345&orig=montpellier', test_key)
|
||||
assert check_query(signed_query, test_key, timedelta=0) is False
|
||||
assert check_query(signed_query, test_key) is True
|
|
@ -4,10 +4,13 @@ from jsonresponse import to_json
|
|||
|
||||
from authentic2 import compat
|
||||
|
||||
from .decorators import valid_signature
|
||||
|
||||
user_model = compat.get_user_model()
|
||||
|
||||
class UserFullData(View):
|
||||
|
||||
@valid_signature
|
||||
@to_json('api')
|
||||
def get(self, request, nameid):
|
||||
user = user_model.objects.get(libertyfederation__name_id_content=nameid, deleteduser__isnull=True)
|
||||
|
|
Reference in New Issue