Skip to content
Snippets Groups Projects
Verified Commit e1ebd498 authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Fixed inconsistencies between test and prod requests

parent de777764
No related branches found
No related tags found
No related merge requests found
...@@ -9,15 +9,17 @@ from . import actors ...@@ -9,15 +9,17 @@ from . import actors
from . import keys from . import keys
from . import serializers from . import serializers
from . import signing from . import signing
from . import utils
class SignatureAuthentication(authentication.BaseAuthentication): class SignatureAuthentication(authentication.BaseAuthentication):
def authenticate(self, request): def authenticate_actor(self, request):
headers = utils.clean_wsgi_headers(request.META)
try: try:
signature = request.META['headers']['Signature'] signature = headers['Signature']
key_id = keys.get_key_id_from_signature_header(signature) key_id = keys.get_key_id_from_signature_header(signature)
except KeyError: except KeyError:
raise exceptions.AuthenticationFailed('No signature') return
except ValueError as e: except ValueError as e:
raise exceptions.AuthenticationFailed(str(e)) raise exceptions.AuthenticationFailed(str(e))
...@@ -33,14 +35,17 @@ class SignatureAuthentication(authentication.BaseAuthentication): ...@@ -33,14 +35,17 @@ class SignatureAuthentication(authentication.BaseAuthentication):
serializer = serializers.ActorSerializer(data=actor_data) serializer = serializers.ActorSerializer(data=actor_data)
if not serializer.is_valid(): if not serializer.is_valid():
raise exceptions.AuthenticationFailed('Invalid actor payload') raise exceptions.AuthenticationFailed('Invalid actor payload: {}'.format(serializer.errors))
try: try:
signing.verify_django(request, public_key.encode('utf-8')) signing.verify_django(request, public_key.encode('utf-8'))
except cryptography.exceptions.InvalidSignature: except cryptography.exceptions.InvalidSignature:
raise exceptions.AuthenticationFailed('Invalid signature') raise exceptions.AuthenticationFailed('Invalid signature')
return serializer.build()
def authenticate(self, request):
actor = self.authenticate_actor(request)
user = AnonymousUser() user = AnonymousUser()
ac = serializer.build() setattr(request, 'actor', actor)
setattr(request, 'actor', ac)
return (user, None) return (user, None)
import logging
import requests import requests
import requests_http_signature import requests_http_signature
from . import exceptions
from . import utils
logger = logging.getLogger(__name__)
def verify(request, public_key): def verify(request, public_key):
return requests_http_signature.HTTPSignatureAuth.verify( return requests_http_signature.HTTPSignatureAuth.verify(
...@@ -15,21 +21,35 @@ def verify_django(django_request, public_key): ...@@ -15,21 +21,35 @@ def verify_django(django_request, public_key):
Given a django WSGI request, create an underlying requests.PreparedRequest Given a django WSGI request, create an underlying requests.PreparedRequest
instance we can verify instance we can verify
""" """
headers = django_request.META.get('headers', {}).copy() headers = utils.clean_wsgi_headers(django_request.META)
for h, v in list(headers.items()): for h, v in list(headers.items()):
# we include lower-cased version of the headers for compatibility # we include lower-cased version of the headers for compatibility
# with requests_http_signature # with requests_http_signature
headers[h.lower()] = v headers[h.lower()] = v
try: try:
signature = headers['signature'] signature = headers['Signature']
except KeyError: except KeyError:
raise exceptions.MissingSignature raise exceptions.MissingSignature
url = 'http://noop{}'.format(django_request.path)
query = django_request.META['QUERY_STRING']
if query:
url += '?{}'.format(query)
signature_headers = signature.split('headers="')[1].split('",')[0]
expected = signature_headers.split(' ')
logger.debug('Signature expected headers: %s', expected)
for header in expected:
try:
headers[header]
except KeyError:
logger.debug('Missing header: %s', header)
request = requests.Request( request = requests.Request(
method=django_request.method, method=django_request.method,
url='http://noop', url=url,
data=django_request.body, data=django_request.body,
headers=headers) headers=headers)
for h in request.headers.keys():
v = request.headers[h]
if v:
request.headers[h] = str(v)
prepared_request = request.prepare() prepared_request = request.prepare()
return verify(request, public_key) return verify(request, public_key)
...@@ -20,14 +20,17 @@ def test_authenticate(nodb_factories, mocker, api_request): ...@@ -20,14 +20,17 @@ def test_authenticate(nodb_factories, mocker, api_request):
}) })
signed_request = nodb_factories['federation.SignedRequest']( signed_request = nodb_factories['federation.SignedRequest'](
auth__key=private, auth__key=private,
auth__key_id=actor_url + '#main-key' auth__key_id=actor_url + '#main-key',
auth__headers=[
'date',
]
) )
prepared = signed_request.prepare() prepared = signed_request.prepare()
django_request = api_request.get( django_request = api_request.get(
'/', '/',
headers={ **{
'Date': prepared.headers['date'], 'HTTP_DATE': prepared.headers['date'],
'Signature': prepared.headers['signature'], 'HTTP_SIGNATURE': prepared.headers['signature'],
} }
) )
authenticator = authentication.SignatureAuthentication() authenticator = authentication.SignatureAuthentication()
......
...@@ -44,56 +44,67 @@ def test_verify_fails_with_wrong_key(nodb_factories): ...@@ -44,56 +44,67 @@ def test_verify_fails_with_wrong_key(nodb_factories):
signing.verify(prepared_request, wrong_public) signing.verify(prepared_request, wrong_public)
def test_can_verify_django_request(factories, api_request): def test_can_verify_django_request(factories, fake_request):
private_key, public_key = keys.get_key_pair() private_key, public_key = keys.get_key_pair()
signed_request = factories['federation.SignedRequest']( signed_request = factories['federation.SignedRequest'](
auth__key=private_key auth__key=private_key,
auth__headers=[
'date',
]
) )
prepared = signed_request.prepare() prepared = signed_request.prepare()
django_request = api_request.get( django_request = fake_request.get(
'/', '/',
headers={ **{
'Date': prepared.headers['date'], 'HTTP_DATE': prepared.headers['date'],
'Signature': prepared.headers['signature'], 'HTTP_SIGNATURE': prepared.headers['signature'],
} }
) )
assert signing.verify_django(django_request, public_key) is None assert signing.verify_django(django_request, public_key) is None
def test_can_verify_django_request_digest(factories, api_request): def test_can_verify_django_request_digest(factories, fake_request):
private_key, public_key = keys.get_key_pair() private_key, public_key = keys.get_key_pair()
signed_request = factories['federation.SignedRequest']( signed_request = factories['federation.SignedRequest'](
auth__key=private_key, auth__key=private_key,
method='post', method='post',
data=b'hello=world' data=b'hello=world',
auth__headers=[
'date',
'digest',
]
) )
prepared = signed_request.prepare() prepared = signed_request.prepare()
django_request = api_request.post( django_request = fake_request.post(
'/', '/',
headers={ **{
'Date': prepared.headers['date'], 'HTTP_DATE': prepared.headers['date'],
'Digest': prepared.headers['digest'], 'HTTP_DIGEST': prepared.headers['digest'],
'Signature': prepared.headers['signature'], 'HTTP_SIGNATURE': prepared.headers['signature'],
} }
) )
assert signing.verify_django(django_request, public_key) is None assert signing.verify_django(django_request, public_key) is None
def test_can_verify_django_request_digest_failure(factories, api_request): def test_can_verify_django_request_digest_failure(factories, fake_request):
private_key, public_key = keys.get_key_pair() private_key, public_key = keys.get_key_pair()
signed_request = factories['federation.SignedRequest']( signed_request = factories['federation.SignedRequest'](
auth__key=private_key, auth__key=private_key,
method='post', method='post',
data=b'hello=world' data=b'hello=world',
auth__headers=[
'date',
'digest',
]
) )
prepared = signed_request.prepare() prepared = signed_request.prepare()
django_request = api_request.post( django_request = fake_request.post(
'/', '/',
headers={ **{
'Date': prepared.headers['date'], 'HTTP_DATE': prepared.headers['date'],
'Digest': prepared.headers['digest'] + 'noop', 'HTTP_DIGEST': prepared.headers['digest'] + 'noop',
'Signature': prepared.headers['signature'], 'HTTP_SIGNATURE': prepared.headers['signature'],
} }
) )
...@@ -101,17 +112,20 @@ def test_can_verify_django_request_digest_failure(factories, api_request): ...@@ -101,17 +112,20 @@ def test_can_verify_django_request_digest_failure(factories, api_request):
signing.verify_django(django_request, public_key) signing.verify_django(django_request, public_key)
def test_can_verify_django_request_failure(factories, api_request): def test_can_verify_django_request_failure(factories, fake_request):
private_key, public_key = keys.get_key_pair() private_key, public_key = keys.get_key_pair()
signed_request = factories['federation.SignedRequest']( signed_request = factories['federation.SignedRequest'](
auth__key=private_key auth__key=private_key,
auth__headers=[
'date',
]
) )
prepared = signed_request.prepare() prepared = signed_request.prepare()
django_request = api_request.get( django_request = fake_request.get(
'/', '/',
headers={ **{
'Date': 'Wrong', 'HTTP_DATE': 'Wrong',
'Signature': prepared.headers['signature'], 'HTTP_SIGNATURE': prepared.headers['signature'],
} }
) )
with pytest.raises(cryptography.exceptions.InvalidSignature): with pytest.raises(cryptography.exceptions.InvalidSignature):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment