-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add custom media type, add /auth/register/ endpoint, add more test he…
…lpers. * Implement registration on /auth/register/ * Add custom application/vnd.keybar+json media-type in preparation for api-versioning * Add more test-helpers for testing the API * Lots of cleanups along the way
- Loading branch information
Showing
12 changed files
with
188 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
from django.conf import settings | ||
from rest_framework.parsers import JSONParser | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from rest_framework import renderers | ||
|
||
|
||
class JSONRenderer(renderers.JSONRenderer): | ||
media_type = 'application/vnd.keybar+json' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,11 @@ | ||
from django.conf.urls import url | ||
|
||
from .endpoints.dummy import AuthenticatedDummyEndpoint | ||
|
||
from .endpoints.dummy import AuthenticatedDummyView | ||
from .endpoints.registration import RegisterView | ||
|
||
urlpatterns = [ | ||
url(r'dummy/$', AuthenticatedDummyEndpoint.as_view()) | ||
url(r'auth/register/$', RegisterView.as_view(), name='register'), | ||
|
||
# Dummy view, used for testing. | ||
url(r'dummy/$', AuthenticatedDummyView.as_view()), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import mock | ||
import pytest | ||
from django.core.urlresolvers import reverse | ||
|
||
from keybar.models.user import User | ||
from keybar.models.device import Device | ||
from keybar.utils.test import APIClient | ||
from keybar.utils.crypto import serialize_public_key | ||
from keybar.tests.factories.device import PUBLIC_KEY | ||
|
||
|
||
@pytest.mark.django_db | ||
class TestRegistration: | ||
|
||
def test_simple_register(self): | ||
url = reverse('api:register') | ||
|
||
response = APIClient().post(url, data={ | ||
'email': 'test@example.com', | ||
'password': 'test123456', | ||
'public_key': serialize_public_key(PUBLIC_KEY) | ||
}) | ||
|
||
assert response.status_code == 201 | ||
assert response.json() == { | ||
'email': 'test@example.com', | ||
'device_id': mock.ANY | ||
} | ||
assert User.objects.filter(email='test@example.com').exists() | ||
|
||
def test_register_invalid_password(self): | ||
url = reverse('api:register') | ||
|
||
response = APIClient().post(url, data={ | ||
'email': 'test@example.com', | ||
'password': '1234', | ||
'public_key': serialize_public_key(PUBLIC_KEY) | ||
}) | ||
|
||
assert response.status_code == 400 | ||
assert response.json() == { | ||
'password': [ | ||
'This password is too short. It must contain at least 10 characters.', | ||
'This password is too common.', | ||
'This password is entirely numeric.' | ||
] | ||
} | ||
|
||
# Make sure we never created a user or device | ||
assert User.objects.all().count() == 0 | ||
assert Device.objects.all().count() == 0 | ||
|
||
def test_register_invalid_email(self): | ||
url = reverse('api:register') | ||
|
||
response = APIClient().post(url, data={ | ||
'email': 'test', | ||
'password': 'test123456', | ||
'public_key': serialize_public_key(PUBLIC_KEY) | ||
}) | ||
|
||
assert response.status_code == 400 | ||
assert response.json() == { | ||
'email': ['Enter a valid email address.'] | ||
} | ||
|
||
assert User.objects.all().count() == 0 | ||
assert Device.objects.all().count() == 0 | ||
|
||
def test_register_invalid_public_key(self): | ||
url = reverse('api:register') | ||
|
||
response = APIClient().post(url, data={ | ||
'email': 'test@test.test', | ||
'password': 'test123456', | ||
'public_key': 'invalid' | ||
}) | ||
|
||
assert response.status_code == 400 | ||
assert response.json() == { | ||
'public_key': ['Invalid public key'] | ||
} | ||
|
||
assert User.objects.all().count() == 0 | ||
assert Device.objects.all().count() == 0 | ||
|
||
def test_register_invalid_email_unique(self): | ||
url = reverse('api:register') | ||
|
||
User.objects.create(email='test@test.test') | ||
response = APIClient().post(url, data={ | ||
'email': 'test@test.test', | ||
'password': 'test123456', | ||
'public_key': serialize_public_key(PUBLIC_KEY) | ||
}) | ||
|
||
assert response.status_code == 400 | ||
assert response.json() == { | ||
'email': ['User with this Email already exists.'] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import json | ||
|
||
from rest_framework.test import APIClient as BaseAPIClient | ||
|
||
from keybar.models.device import Device | ||
from keybar.utils.jwt import encode_token | ||
|
||
|
||
class APIClient(BaseAPIClient): | ||
""" | ||
Subclass to handle our custom accept headers required | ||
for proper versioning and data parsing. | ||
Requires a test-user with the following credentials to be setup in | ||
case of using an authenticated endpoint: | ||
* E-mail: test@test.test | ||
* Password: test123456 | ||
""" | ||
content_type = 'application/vnd.keybar+json' | ||
default_format = 'json' | ||
|
||
def __init__(self, *args, **kwargs): | ||
self.device_id = kwargs.pop('device_id', None) | ||
super().__init__(*args, **kwargs) | ||
|
||
def generic(self, method, path, data='', content_type=None, secure=False, **extra): | ||
extra.update({ | ||
'HTTP_HOST': 'testserver', | ||
'HTTP_ACCEPT': self.content_type, | ||
}) | ||
|
||
if extra.get('authorized', False): | ||
device = Device.objects.get(pk=self.device_id) | ||
token = encode_token(device.id, device.public_key) | ||
extra['HTTP_AUTHORIZATION'] = 'JWT {}'.format(token) | ||
|
||
return super().generic(method, path, data, self.content_type, secure, **extra) | ||
|
||
def _parse_json(self, response, **extra): | ||
content_type = response.get('Content-Type') | ||
types = ('application/json', self.content_type) | ||
|
||
if not any(type in content_type for type in types): | ||
raise ValueError( | ||
'Content-Type header is "{0}", not "application/json"' | ||
.format(response.get('Content-Type')) | ||
) | ||
|
||
return json.loads(response.content.decode(), **extra) |