Fixed inconsistencies between test and prod requests

This commit is contained in:
Eliot Berriot 2018-03-31 18:40:41 +02:00
parent de777764da
commit e1ebd4988b
No known key found for this signature in database
GPG Key ID: DD6965E2476E5C27
4 changed files with 83 additions and 41 deletions

View File

@ -9,15 +9,17 @@ from . import actors
from . import keys
from . import serializers
from . import signing
from . import utils
class SignatureAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
def authenticate_actor(self, request):
headers = utils.clean_wsgi_headers(request.META)
try:
signature = request.META['headers']['Signature']
signature = headers['Signature']
key_id = keys.get_key_id_from_signature_header(signature)
except KeyError:
raise exceptions.AuthenticationFailed('No signature')
return
except ValueError as e:
raise exceptions.AuthenticationFailed(str(e))
@ -33,14 +35,17 @@ class SignatureAuthentication(authentication.BaseAuthentication):
serializer = serializers.ActorSerializer(data=actor_data)
if not serializer.is_valid():
raise exceptions.AuthenticationFailed('Invalid actor payload')
raise exceptions.AuthenticationFailed('Invalid actor payload: {}'.format(serializer.errors))
try:
signing.verify_django(request, public_key.encode('utf-8'))
except cryptography.exceptions.InvalidSignature:
raise exceptions.AuthenticationFailed('Invalid signature')
return serializer.build()
def authenticate(self, request):
actor = self.authenticate_actor(request)
user = AnonymousUser()
ac = serializer.build()
setattr(request, 'actor', ac)
setattr(request, 'actor', actor)
return (user, None)

View File

@ -1,6 +1,12 @@
import logging
import requests
import requests_http_signature
from . import exceptions
from . import utils
logger = logging.getLogger(__name__)
def verify(request, public_key):
return requests_http_signature.HTTPSignatureAuth.verify(
@ -15,21 +21,35 @@ def verify_django(django_request, public_key):
Given a django WSGI request, create an underlying requests.PreparedRequest
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()):
# we include lower-cased version of the headers for compatibility
# with requests_http_signature
headers[h.lower()] = v
try:
signature = headers['signature']
signature = headers['Signature']
except KeyError:
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(
method=django_request.method,
url='http://noop',
url=url,
data=django_request.body,
headers=headers)
for h in request.headers.keys():
v = request.headers[h]
if v:
request.headers[h] = str(v)
prepared_request = request.prepare()
return verify(request, public_key)

View File

@ -20,14 +20,17 @@ def test_authenticate(nodb_factories, mocker, api_request):
})
signed_request = nodb_factories['federation.SignedRequest'](
auth__key=private,
auth__key_id=actor_url + '#main-key'
auth__key_id=actor_url + '#main-key',
auth__headers=[
'date',
]
)
prepared = signed_request.prepare()
django_request = api_request.get(
'/',
headers={
'Date': prepared.headers['date'],
'Signature': prepared.headers['signature'],
**{
'HTTP_DATE': prepared.headers['date'],
'HTTP_SIGNATURE': prepared.headers['signature'],
}
)
authenticator = authentication.SignatureAuthentication()

View File

@ -44,56 +44,67 @@ def test_verify_fails_with_wrong_key(nodb_factories):
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()
signed_request = factories['federation.SignedRequest'](
auth__key=private_key
auth__key=private_key,
auth__headers=[
'date',
]
)
prepared = signed_request.prepare()
django_request = api_request.get(
django_request = fake_request.get(
'/',
headers={
'Date': prepared.headers['date'],
'Signature': prepared.headers['signature'],
**{
'HTTP_DATE': prepared.headers['date'],
'HTTP_SIGNATURE': prepared.headers['signature'],
}
)
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()
signed_request = factories['federation.SignedRequest'](
auth__key=private_key,
method='post',
data=b'hello=world'
data=b'hello=world',
auth__headers=[
'date',
'digest',
]
)
prepared = signed_request.prepare()
django_request = api_request.post(
django_request = fake_request.post(
'/',
headers={
'Date': prepared.headers['date'],
'Digest': prepared.headers['digest'],
'Signature': prepared.headers['signature'],
**{
'HTTP_DATE': prepared.headers['date'],
'HTTP_DIGEST': prepared.headers['digest'],
'HTTP_SIGNATURE': prepared.headers['signature'],
}
)
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()
signed_request = factories['federation.SignedRequest'](
auth__key=private_key,
method='post',
data=b'hello=world'
data=b'hello=world',
auth__headers=[
'date',
'digest',
]
)
prepared = signed_request.prepare()
django_request = api_request.post(
django_request = fake_request.post(
'/',
headers={
'Date': prepared.headers['date'],
'Digest': prepared.headers['digest'] + 'noop',
'Signature': prepared.headers['signature'],
**{
'HTTP_DATE': prepared.headers['date'],
'HTTP_DIGEST': prepared.headers['digest'] + 'noop',
'HTTP_SIGNATURE': prepared.headers['signature'],
}
)
@ -101,17 +112,20 @@ def test_can_verify_django_request_digest_failure(factories, api_request):
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()
signed_request = factories['federation.SignedRequest'](
auth__key=private_key
auth__key=private_key,
auth__headers=[
'date',
]
)
prepared = signed_request.prepare()
django_request = api_request.get(
django_request = fake_request.get(
'/',
headers={
'Date': 'Wrong',
'Signature': prepared.headers['signature'],
**{
'HTTP_DATE': 'Wrong',
'HTTP_SIGNATURE': prepared.headers['signature'],
}
)
with pytest.raises(cryptography.exceptions.InvalidSignature):