diff --git a/app/app/settings.py b/app/app/settings.py index b90657f..cbd24b0 100755 --- a/app/app/settings.py +++ b/app/app/settings.py @@ -40,6 +40,7 @@ 'django.contrib.staticfiles', 'core', 'rest_framework', + 'rest_framework.authtoken', 'drf_spectacular', 'user' ] diff --git a/app/user/serializers.py b/app/user/serializers.py index c705a74..6f45c10 100644 --- a/app/user/serializers.py +++ b/app/user/serializers.py @@ -1,4 +1,5 @@ -from django.contrib.auth import get_user_model +from django.contrib.auth import get_user_model, authenticate +from django.utils.translation import gettext as _ from rest_framework import serializers @@ -17,3 +18,27 @@ class Meta: def create(self, validated_data): return get_user_model().objects.create_user(**validated_data) + + +class AuthTokenSerializer(serializers.Serializer): + email = serializers.EmailField() + password = serializers.CharField( + style={'input_type': 'password'}, + trim_whitespace=False + ) + + def validate(self, attrs): + email = attrs.get('email') + password = attrs.get('password') + user = authenticate( + request=self.context.get('request'), + username=email, + password=password + ) + + if not user: + msg = _('Unable to authenticate with provided credentials.') + raise serializers.ValidationError(msg, code='authorization') + + attrs['user'] = user + return attrs diff --git a/app/user/tests/test_user_api.py b/app/user/tests/test_user_api.py index 8c911df..e505699 100644 --- a/app/user/tests/test_user_api.py +++ b/app/user/tests/test_user_api.py @@ -6,6 +6,7 @@ from rest_framework import status CREATE_USER_URL = reverse('user:create') +TOKEN_URL = reverse('user:token') def create_user(**params): @@ -17,6 +18,10 @@ class PublicUserAPI(TestCase): def setUp(self): self.client = APIClient() + """ + Create User Tests + """ + def test_create_user_success(self): payload = { 'email': 'test@example.com', @@ -59,3 +64,56 @@ def test_password_too_short_error(self): email=payload['email'] ).exists() self.assertFalse(user_exists) + + """ + Create Token Tests + """ + + def test_create_token_for_user(self): + user_details = { + 'email': 'test@example.com', + 'password': 'testpass123', + 'name': 'Test Name' + } + create_user(**user_details) + + payload = { + 'email': user_details['email'], + 'password': user_details['password'] + } + res = self.client.post(TOKEN_URL, payload) + + self.assertIn('token', res.data) + self.assertEqual(res.status_code, status.HTTP_200_OK) + + def test_create_token_bad_credentials(self): + user_details = { + 'email': 'test@example.com', + 'password': 'goodpass123' + } + create_user(**user_details) + + payload = { + 'email': user_details['email'], + 'password': 'badpass123' + } + res = self.client.post(TOKEN_URL, payload) + + self.assertNotIn('token', res.data) + self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) + + def test_create_token_blank_password(self): + user_details = { + 'email': 'test@example.com', + 'password': 'testpass123' + } + create_user(**user_details) + + payload = { + 'email': user_details['email'], + 'password': '' + } + res = self.client.post(TOKEN_URL, payload) + + self.assertNotIn('token', res.data) + self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) diff --git a/app/user/urls.py b/app/user/urls.py index 86465b6..982e5dd 100644 --- a/app/user/urls.py +++ b/app/user/urls.py @@ -5,5 +5,6 @@ app_name = 'user' urlpatterns = [ - path('create/', views.CreateUserView.as_view(), name='create') + path('create/', views.CreateUserView.as_view(), name='create'), + path('token/', views.CreateTokenView.as_view(), name='token') ] diff --git a/app/user/views.py b/app/user/views.py index 92a7e3c..93fe1bd 100755 --- a/app/user/views.py +++ b/app/user/views.py @@ -1,6 +1,14 @@ from rest_framework import generics -from user.serializers import UserSerializer +from rest_framework.authtoken.views import ObtainAuthToken +from rest_framework.settings import api_settings + +from user.serializers import UserSerializer, AuthTokenSerializer class CreateUserView(generics.CreateAPIView): serializer_class = UserSerializer + + +class CreateTokenView(ObtainAuthToken): + serializer_class = AuthTokenSerializer + renderer_class = api_settings.DEFAULT_RENDERER_CLASSES