lingo: check api signature with adding items to basket (#9423)

This commit is contained in:
Serghei Mihai 2015-12-31 16:16:04 +01:00
parent 54c9af60d4
commit 5daad1b54c
4 changed files with 36 additions and 1 deletions

View File

@ -25,9 +25,12 @@ from django.template.response import TemplateResponse
from django.utils import timezone
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import View, ListView, TemplateView
from django.conf import settings
import eopayment
from combo.utils import check_query
try:
from mellon.models import UserSAMLIdentifier
except ImportError:
@ -63,7 +66,9 @@ class AddBasketItemApiView(View):
return d.quantize(Decimal('0.01'), ROUND_HALF_UP)
def post(self, request, *args, **kwargs):
# XXX: check request signature
key = getattr(settings, 'LINGO_API_SIGN_KEY', '12345')
if not check_query(request.META['QUERY_STRING'], key):
return HttpResponseForbidden()
request_body = json.loads(self.request.body)
extra = request_body.get('extra', {})

View File

@ -63,3 +63,27 @@ def ellipsize(text, length=50):
if len(text) < length:
return text
return text[:(length-10)] + '...'
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

View File

@ -9,5 +9,7 @@ KNOWN_SERVICES = {
}
}
LINGO_API_SIGN_KEY = '12345'
import tempfile
MEDIA_ROOT = tempfile.mkdtemp('combo-test')

View File

@ -8,11 +8,13 @@ import json
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.core.wsgi import get_wsgi_application
from django.conf import settings
from webtest import TestApp
from django.test import Client
from combo.apps.lingo.models import Regie, BasketItem, Transaction, RemoteItem
from combo.utils import sign_url
pytestmark = pytest.mark.django_db
@ -90,12 +92,14 @@ def test_add_amount_to_basket(regie, user):
data = {'amount': amount, 'display_name': 'test amount',
'url': 'http://example.com'}
url = '%s?email=%s' % (reverse('api-add-basket-item'), user_email)
url = sign_url(url, settings.LINGO_API_SIGN_KEY)
resp = client.post(url, json.dumps(data), content_type='application/json')
assert resp.status_code == 200
assert json.loads(resp.content) == {'result': 'success'}
assert BasketItem.objects.filter(amount=amount).exists()
data['extra'] = {'amount': '22.22'}
url = sign_url(url, settings.LINGO_API_SIGN_KEY)
resp = client.post(url, json.dumps(data), content_type='application/json')
assert resp.status_code == 200
assert json.loads(resp.content) == {'result': 'success'}