Skip to content

Commit

Permalink
Merge 7138ba0 into 2d86944
Browse files Browse the repository at this point in the history
  • Loading branch information
BrianSerem committed Mar 8, 2019
2 parents 2d86944 + 7138ba0 commit d30e16b
Show file tree
Hide file tree
Showing 21 changed files with 447 additions and 85 deletions.
7 changes: 7 additions & 0 deletions authors/apps/authentication/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ def render(self, data, media_type=None, renderer_context=None):
return super().render(data)

# Finally, we can render our data under the "user" namespace.
image_prefix = "https://res.cloudinary.com/dbsri2qtr/"
try:
image_suffix = data['profile']['image']
data['profile']['image'] = image_prefix + image_suffix

except KeyError:
pass
return json.dumps({
'user': data
})
32 changes: 10 additions & 22 deletions authors/apps/authentication/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from .models import User
from authors.apps.profiles.serializers import ProfileSerializer


class RegistrationSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -137,44 +138,31 @@ class UserSerializer(serializers.ModelSerializer):
min_length=8,
write_only=True
)
profile = ProfileSerializer()

class Meta:
model = User
fields = ('email', 'username', 'password', 'token',)

# The `read_only_fields` option is an alternative for explicitly
# specifying the field with `read_only=True` like we did for password
# above. The reason we want to use `read_only_fields` here is because
# we don't need to specify anything else about the field. For the
# password field, we needed to specify the `min_length` and
# `max_length` properties too, but that isn't the case for the token
# field.

fields = ('email', 'username', 'password', 'token', 'profile',)
read_only_fields = ('token',)

def update(self, instance, validated_data):
"""Performs an update on a User."""

# Passwords should not be handled with `setattr`, unlike other fields.
# This is because Django provides a function that handles hashing and
# salting passwords, which is important for security. What that means
# here is that we need to remove the password field from the
# `validated_data` dictionary before iterating over it.
password = validated_data.pop('password', None)
profile_data = validated_data.pop('profile', {})

for (key, value) in validated_data.items():
# For the keys remaining in `validated_data`, we will set them on
# the current `User` instance one at a time.
setattr(instance, key, value)

if password is not None:
# `.set_password()` is the method mentioned above. It handles all
# of the security stuff that we shouldn't be concerned with.
instance.set_password(password)

# Finally, after everything has been updated, we must explicitly save
# the model. It's worth pointing out that `.set_password()` does not
# save the model.
instance.save()

for (key, value) in profile_data.items():
setattr(instance.profile, key, value)

# Save profile
instance.profile.save()

return instance
25 changes: 17 additions & 8 deletions authors/apps/authentication/tests/test_jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@

class JWTAuthenticationTest(TestCase):
"""Test the JWT Authentication implementation"""

def setUp(self):
self.user = User.objects.create(username='user1', email='user1@mail.com', password='password')
self.user = User.objects.create(
username='user1', email='user1@mail.com', password='password')
self.login_data = {'user': {
'email': 'user2@mail.com',
'password': 'password'
Expand All @@ -27,7 +29,8 @@ def test_user_gets_a_token_when_they_log_in(self):
'username': 'user2',
'password': 'password'
}}, format='json')
res = self.client.post(reverse('authentication:login'), self.login_data, format='json')
res = self.client.post(
reverse('authentication:login'), self.login_data, format='json')
self.assertEqual(res.status_code, status.HTTP_200_OK)
self.assertIn('token', res.data)

Expand All @@ -41,36 +44,42 @@ def test_failure_if_user_passes_no_token(self):
"""Test if a user can access a secured endpoint without providing a token"""
res = self.client.get(reverse('authentication:get users'))
self.assertEqual(res.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(res.data['detail'], 'Authentication credentials were not provided.')
self.assertEqual(
res.data['detail'], 'Authentication credentials were not provided.')

def test_failure_if_user_provides_invalid_token(self):
"""Test if an invalid token can be decoded"""
fake_token = self.user_token + 'ivalid'
headers = {'HTTP_AUTHORIZATION': f'Bearer {fake_token}'}
res = self.client.get(reverse('authentication:get users'), **headers)
self.assertEqual(res.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(res.data['detail'], 'Ivalid token provided. Authentication failure.')
self.assertEqual(
res.data['detail'], 'Ivalid token provided. Authentication failure.')

def test_failure_if_user_does_not_exist(self):
"""We register a user to get the token, then delete the user from the database. When a user tries to pass the token to access the endpoint, they should be forbidden from proceeding."""
test_user = User.objects.create(username='test_user', email='test_user@mail.com', password='password')
test_user = User.objects.create(
username='test_user', email='test_user@mail.com', password='password')
test_token = test_user.token
test_user.delete()
client = APIClient()
headers = {'HTTP_AUTHORIZATION': f'Bearer {test_token}'}
res = client.get(reverse('authentication:get users'), **headers)
self.assertEqual(res.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(res.data['detail'], 'User matching this token was not found.')
self.assertEqual(res.data['detail'],
'User matching this token was not found.')

def test_failure_because_user_is_inactive(self):
"""Test if an inactive user can be authenticated"""
inactive_user = User.objects.create(username='inactive_one', email='inactive@mail.com', password='password')
inactive_user = User.objects.create(
username='inactive_one', email='inactive@mail.com', password='password')
inactive_user.is_active = False
inactive_user.save()
headers = {'HTTP_AUTHORIZATION': f'Bearer {inactive_user.token}'}
res = self.client.get(reverse('authentication:get users'), **headers)
self.assertEqual(res.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(res.data['detail'], 'Forbidden! This user has been deactivated.')
self.assertEqual(res.data['detail'],
'Forbidden! This user has been deactivated.')

def test_authentication_failure_because_header_is_None(self):
"""Test if authentication fails when a request has authorization
Expand Down
22 changes: 15 additions & 7 deletions authors/apps/authentication/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,51 @@

from authors.apps.authentication.models import User


class UserManagerTest(TestCase):
"""This class defines tests for UserManager class"""

def test_successfully_create_user(self):
""""""
user1 = User.objects.create_user(username="user1", email="user1@mail.com", password="password")
user1 = User.objects.create_user(
username="user1", email="user1@mail.com", password="password")
self.assertIsInstance(user1, User)

def test_registration_failure_due_to_no_username(self):
"""Test if user can register without a username"""
with self.assertRaises(TypeError) as e:
User.objects.create_user(email="user1@mail.com", username=None, password="password")
User.objects.create_user(
email="user1@mail.com", username=None, password="password")
self.assertEqual(str(e.exception), 'Users must have a username.')


def test_registration_failure_due_to_no_email(self):
"""Test if a user can register without an email"""
with self.assertRaises(TypeError) as e:
User.objects.create_user(username="user2", email=None, password="password")
User.objects.create_user(
username="user2", email=None, password="password")
self.assertEqual(str(e.exception), 'Users must have an email address.')

def test_registration_superuser_fail_due_to_no_password(self):
"""Test if a superuser can register without a password"""
with self.assertRaises(TypeError) as e:
User.objects.create_superuser(username="superuser", email="superuser@mail.com", password=None)
User.objects.create_superuser(
username="superuser", email="superuser@mail.com", password=None)
self.assertEqual(str(e.exception), 'Superusers must have a password.')

def test_successfully_create_superuser(self):
"""Test if we can successfully create a superuser"""
user1 = User.objects.create_superuser(username="superuser", email="superuser@mail.com", password="password")
user1 = User.objects.create_superuser(
username="superuser", email="superuser@mail.com", password="password")
self.assertTrue(user1.is_superuser)
self.assertTrue(user1.is_staff)


class UserTest(TestCase):
"""This class defines tests for user models"""

def setUp(self):
self.user1 = User.objects.create_user(username="user", email="user@mail.com", password="password")
self.user1 = User.objects.create_user(
username="user", email="user@mail.com", password="password")

def test_user_string_representation(self):
"""Test if proper string representation is returned for users"""
Expand Down
4 changes: 3 additions & 1 deletion authors/apps/authentication/tests/test_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

class RegistrationSerializerTests(TestCase):
"""This class defines tests for the RegistrationSerializer class"""

def setUp(self):
self.user_data = {
"username": "bob",
Expand All @@ -27,6 +28,7 @@ def test_create_method(self):

class LoginSerializerTests(TestCase):
"""This class defines tests for the LoginSerializer class"""

def test_validating_login_serializer_without_email(self):
"""Test if users can login without providing an email"""
login_data = {"password": "hardpassword"}
Expand Down Expand Up @@ -81,7 +83,7 @@ class UserSerializersTests(TestCase):

def setUp(self):
self.user = User.objects.create_user(username="bob", email="bob@email.com",
password="hardpassword")
password="hardpassword")
self.user.save()
self.serializer = UserSerializer()

Expand Down
30 changes: 20 additions & 10 deletions authors/apps/authentication/tests/test_user_registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ def test_blank_username(self):
"username": ["Username field cannot be blank"]
}
}
response = self.client.post(self.url, blank_username_data, format='json')
response = self.client.post(
self.url, blank_username_data, format='json')
self.assertEqual(response.data, blank_username_data_response)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Expand All @@ -52,7 +53,8 @@ def test_no_username_field(self):
"username": ["Username is required"]
}
}
response = self.client.post(self.url, no_username_field_data, format='json')
response = self.client.post(
self.url, no_username_field_data, format='json')
self.assertEqual(response.data, no_username_field_data_response)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Expand All @@ -77,7 +79,8 @@ def test_existing_username(self):
}
}
self.client.post(self.url, username_data, format='json')
response = self.client.post(self.url, existing_username_data, format='json')
response = self.client.post(
self.url, existing_username_data, format='json')
self.assertEqual(response.data, existing_username_data_response)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Expand Down Expand Up @@ -110,7 +113,8 @@ def test_no_email_field(self):
"email": ["Email is required"]
}
}
response = self.client.post(self.url, no_email_field_data, format='json')
response = self.client.post(
self.url, no_email_field_data, format='json')
self.assertEqual(response.data, no_email_field_data_response)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Expand Down Expand Up @@ -152,7 +156,8 @@ def test_existing_email(self):
}
}
self.client.post(self.url, email_data, format='json')
response = self.client.post(self.url, existing_email_data, format='json')
response = self.client.post(
self.url, existing_email_data, format='json')
self.assertEqual(response.data, existing_email_data_response)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Expand All @@ -169,7 +174,8 @@ def test_blank_password(self):
"password": ["Password field cannot be blank"]
}
}
response = self.client.post(self.url, blank_password_data, format='json')
response = self.client.post(
self.url, blank_password_data, format='json')
self.assertEqual(response.data, blank_password_data_response)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Expand All @@ -185,7 +191,8 @@ def test_no_password_field(self):
"password": ["Password is required"]
}
}
response = self.client.post(self.url, no_password_field_data, format='json')
response = self.client.post(
self.url, no_password_field_data, format='json')
self.assertEqual(response.data, no_password_field_data_response)

def test_not_alphanumeric_password(self):
Expand All @@ -201,7 +208,8 @@ def test_not_alphanumeric_password(self):
"password": ["Password should be alphanumeric"]
}
}
response = self.client.post(self.url, not_alphanumeric_password_data, format='json')
response = self.client.post(
self.url, not_alphanumeric_password_data, format='json')
self.assertEqual(response.data, not_alphanumeric_password_data_response)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Expand All @@ -218,7 +226,8 @@ def test_short_password(self):
"password": ["Password should be at least 8 characters long"]
}
}
response = self.client.post(self.url, not_alphanumeric_password_data, format='json')
response = self.client.post(
self.url, not_alphanumeric_password_data, format='json')
self.assertEqual(response.data, not_alphanumeric_password_data_response)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Expand All @@ -235,6 +244,7 @@ def test_long_password(self):
"password": ["Password should not be longer than 128 characters"]
}
}
response = self.client.post(self.url, not_alphanumeric_password_data, format='json')
response = self.client.post(
self.url, not_alphanumeric_password_data, format='json')
self.assertEqual(response.data, not_alphanumeric_password_data_response)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
Loading

0 comments on commit d30e16b

Please sign in to comment.