-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #28 from SELab-2/test-authentication
Authentication tests
- Loading branch information
Showing
8 changed files
with
305 additions
and
19 deletions.
There are no files selected for viewing
Binary file not shown.
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 |
---|---|---|
|
@@ -6,7 +6,7 @@ | |
email: [email protected] | ||
first_name: John | ||
last_name: Doe | ||
last_enrolled: 1 | ||
last_enrolled: 2023 | ||
create_time: 2024-02-29 20:35:45.690556+00:00 | ||
faculties: | ||
- Wetenschappen | ||
|
@@ -18,7 +18,7 @@ | |
email: [email protected] | ||
first_name: Tom | ||
last_name: Boonen | ||
last_enrolled: 1 | ||
last_enrolled: 2023 | ||
create_time: 2024-02-29 20:35:45.686541+00:00 | ||
faculties: | ||
- Psychologie_PedagogischeWetenschappen | ||
|
@@ -30,7 +30,7 @@ | |
email: [email protected] | ||
first_name: Peter | ||
last_name: Sagan | ||
last_enrolled: 1 | ||
last_enrolled: 2023 | ||
create_time: 2024-02-29 20:35:45.689543+00:00 | ||
faculties: | ||
- Psychologie_PedagogischeWetenschappen | ||
|
@@ -42,7 +42,7 @@ | |
email: [email protected] | ||
first_name: Bartje | ||
last_name: Verhaege | ||
last_enrolled: 1 | ||
last_enrolled: 2023 | ||
create_time: 2024-02-29 20:35:45.691565+00:00 | ||
faculties: | ||
- Geneeskunde_Gezondheidswetenschappen | ||
|
@@ -54,7 +54,7 @@ | |
email: [email protected] | ||
first_name: Bart | ||
last_name: Simpson | ||
last_enrolled: 1 | ||
last_enrolled: 2023 | ||
create_time: 2024-02-29 20:35:45.687541+00:00 | ||
faculties: | ||
- Wetenschappen | ||
|
@@ -66,7 +66,7 @@ | |
email: [email protected] | ||
first_name: Kim | ||
last_name: Clijsters | ||
last_enrolled: 1 | ||
last_enrolled: 2023 | ||
create_time: 2024-02-29 20:35:45.688545+00:00 | ||
faculties: | ||
- Psychologie_PedagogischeWetenschappen |
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
194 changes: 194 additions & 0 deletions
194
backend/authentication/tests/test_authentication_serializer.py
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,194 @@ | ||
from django.test import TestCase | ||
|
||
from unittest.mock import patch, Mock | ||
|
||
from rest_framework_simplejwt.tokens import RefreshToken | ||
|
||
from authentication.cas.client import client | ||
from authentication.serializers import CASTokenObtainSerializer, UserSerializer | ||
from authentication.signals import user_created, user_login | ||
|
||
|
||
TICKET = 'ST-da8e1747f248a54a5f078e3905b88a9767f11d7aedcas6' | ||
WRONG_TICKET = 'ST-da8e1747f248a54a5f078e3905b88a9767f11d7aedcas5' | ||
|
||
ID = '1234' | ||
USERNAME = 'ddickwd' | ||
EMAIL = '[email protected]' | ||
FIRST_NAME = 'Dummy' | ||
LAST_NAME = 'McDickwad' | ||
|
||
|
||
class UserSerializerModelTests(TestCase): | ||
|
||
def test_invalid_email_makes_user_serializer_invalid(self): | ||
""" | ||
The is_valid() method of a UserSerializer whose supplied User's email is not | ||
formatted as an email address should return False. | ||
""" | ||
user = UserSerializer(data={ | ||
'id': ID, | ||
'username': USERNAME, | ||
'email': 'dummy', | ||
'first_name': FIRST_NAME, | ||
'last_name': LAST_NAME, | ||
}) | ||
user2 = UserSerializer(data={ | ||
'id': ID, | ||
'username': USERNAME, | ||
'email': 'dummy@dummy', | ||
'first_name': FIRST_NAME, | ||
'last_name': LAST_NAME, | ||
}) | ||
user3 = UserSerializer(data={ | ||
'id': ID, | ||
'username': USERNAME, | ||
'email': 21, | ||
'first_name': FIRST_NAME, | ||
'last_name': LAST_NAME, | ||
}) | ||
self.assertFalse(user.is_valid()) | ||
self.assertFalse(user2.is_valid()) | ||
self.assertFalse(user3.is_valid()) | ||
|
||
def test_valid_email_makes_valid_serializer(self): | ||
""" | ||
When the serializer is provided with a valid email, the serializer becomes valid, | ||
thus the is_valid() method returns True. | ||
""" | ||
user = UserSerializer(data={ | ||
'id': ID, | ||
'username': USERNAME, | ||
'email': EMAIL, | ||
'first_name': FIRST_NAME, | ||
'last_name': LAST_NAME, | ||
}) | ||
self.assertTrue(user.is_valid()) | ||
|
||
|
||
def customize_data(ugent_id, uid, mail): | ||
|
||
class Response: | ||
__slots__ = ('error', 'data') | ||
|
||
def __init__(self): | ||
self.error = None | ||
self.data = {} | ||
|
||
def service_validate( | ||
ticket=None, | ||
service_url=None, | ||
headers=None,): | ||
response = Response() | ||
if ticket != TICKET: | ||
response.error = 'This is an error' | ||
else: | ||
response.data['attributes'] = { | ||
'ugentID': ugent_id, | ||
'uid': uid, | ||
'mail': mail, | ||
'givenname': FIRST_NAME, | ||
'surname': LAST_NAME, | ||
'faculty': 'Sciences', | ||
'lastenrolled': '2023 - 2024', | ||
'lastlogin': '', | ||
'createtime': '' | ||
} | ||
return response | ||
|
||
return service_validate | ||
|
||
|
||
class SerializersTests(TestCase): | ||
def test_wrong_length_ticket_generates_error(self): | ||
""" | ||
When the provided ticket has the wrong length, a ValidationError should be raised | ||
when validating the serializer. | ||
""" | ||
serializer = CASTokenObtainSerializer(data={ | ||
'token': RefreshToken(), | ||
'ticket': 'ST' | ||
}) | ||
self.assertFalse(serializer.is_valid()) | ||
|
||
@patch.object(client, | ||
'perform_service_validate', | ||
customize_data(ID, USERNAME, EMAIL)) | ||
def test_wrong_ticket_generates_error(self): | ||
""" | ||
When the wrong ticket is provided, a ValidationError should be raised when trying to validate | ||
the serializer. | ||
""" | ||
serializer = CASTokenObtainSerializer(data={ | ||
'token': RefreshToken(), | ||
'ticket': WRONG_TICKET | ||
}) | ||
self.assertFalse(serializer.is_valid()) | ||
|
||
@patch.object(client, | ||
'perform_service_validate', | ||
customize_data(ID, USERNAME, "dummy@dummy")) | ||
def test_wrong_user_arguments_generate_error(self): | ||
""" | ||
If the user arguments returned by CAS are not valid, then a ValidationError | ||
should be raised when validating the serializer. | ||
""" | ||
serializer = CASTokenObtainSerializer(data={ | ||
'token': RefreshToken(), | ||
'ticket': TICKET | ||
}) | ||
self.assertFalse(serializer.is_valid()) | ||
|
||
@patch.object(client, | ||
'perform_service_validate', | ||
customize_data(ID, USERNAME, EMAIL)) | ||
def test_new_user_activates_user_created_signal(self): | ||
""" | ||
If the authenticated user is new to the app, then the user_created signal should | ||
be sent when trying to validate the token.""" | ||
|
||
mock = Mock() | ||
user_created.connect(mock, dispatch_uid="STDsAllAround") | ||
serializer = CASTokenObtainSerializer(data={ | ||
'token': RefreshToken(), | ||
'ticket': TICKET | ||
}) | ||
# this next line triggers the retrieval of User information and logs in the user | ||
serializer.is_valid() | ||
self.assertEquals(mock.call_count, 1) | ||
|
||
@patch.object(client, | ||
'perform_service_validate', | ||
customize_data(ID, USERNAME, EMAIL)) | ||
def test_old_user_does_not_activate_user_created_signal(self): | ||
""" | ||
If the authenticated user is new to the app, then the user_created signal should | ||
be sent when trying to validate the token.""" | ||
|
||
mock = Mock() | ||
user_created.connect(mock, dispatch_uid="STDsAllAround") | ||
serializer = CASTokenObtainSerializer(data={ | ||
'token': RefreshToken(), | ||
'ticket': TICKET | ||
}) | ||
# this next line triggers the retrieval of User information and logs in the user | ||
serializer.is_valid() | ||
self.assertEquals(mock.call_count, 0) | ||
|
||
@patch.object(client, | ||
'perform_service_validate', | ||
customize_data(ID, USERNAME, EMAIL)) | ||
def test_login_signal(self): | ||
""" | ||
When the token is correct and all user data is correct, while trying to validate | ||
the token, then the user_login signal should be sent. | ||
""" | ||
mock = Mock() | ||
user_login.connect(mock, dispatch_uid="STDsAllAround") | ||
serializer = CASTokenObtainSerializer(data={ | ||
'token': RefreshToken(), | ||
'ticket': TICKET | ||
}) | ||
# this next line triggers the retrieval of User information and logs in the user | ||
serializer.is_valid() | ||
self.assertEquals(mock.call_count, 1) |
100 changes: 100 additions & 0 deletions
100
backend/authentication/tests/test_authentication_views.py
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 json | ||
from rest_framework.reverse import reverse | ||
from rest_framework.test import APITestCase | ||
from rest_framework_simplejwt.tokens import AccessToken | ||
from authentication.models import User | ||
from ypovoli import settings | ||
|
||
|
||
class TestWhomAmIView(APITestCase): | ||
def setUp(self): | ||
"""Create a user and generate a token for that user""" | ||
user_data = { | ||
'id': '1234', | ||
'username': 'ddickwd', | ||
'email': '[email protected]', | ||
'first_name': 'dummy', | ||
'last_name': 'McDickwad', | ||
} | ||
self.user = User.objects.create(**user_data) | ||
access_token = AccessToken().for_user(self.user) | ||
self.token = f'Bearer {access_token}' | ||
|
||
def test_who_am_i_view_get_returns_user_if_existing_and_authenticated(self): | ||
""" | ||
WhoAmIView should return the User info when requested if User | ||
exists in database and token is supplied. | ||
""" | ||
self.client.credentials(HTTP_AUTHORIZATION=self.token) | ||
|
||
response = self.client.get(reverse('auth.whoami')) | ||
self.assertEqual(response.status_code, 200) | ||
content = json.loads(response.content.decode('utf-8')) | ||
self.assertEqual(content['id'], self.user.id) | ||
|
||
def test_who_am_i_view_get_does_not_return_viewer_if_deleted_but_authenticated(self): | ||
""" | ||
WhoAmIView should return that the user was not found if | ||
authenticated user was deleted from the database. | ||
""" | ||
self.user.delete() | ||
self.client.credentials(HTTP_AUTHORIZATION=self.token) | ||
|
||
response = self.client.get(reverse('auth.whoami')) | ||
self.assertEqual(response.status_code, 405) | ||
|
||
def test_who_am_i_view_returns_401_when_not_authenticated(self): | ||
"""WhoAmIView should return a 401 status code when the user is not authenticated""" | ||
response = self.client.get(reverse('auth.whoami')) | ||
self.assertEqual(response.status_code, 401) | ||
|
||
|
||
class TestLogoutView(APITestCase): | ||
def setUp(self): | ||
user_data = { | ||
'id': '1234', | ||
'username': 'ddickwd', | ||
'email': '[email protected]', | ||
'first_name': 'dummy', | ||
'last_name': 'McDickwad', | ||
} | ||
self.user = User.objects.create(**user_data) | ||
|
||
def test_logout_view_authenticated_logout_url(self): | ||
"""LogoutView should return a logout url redirect if authenticated user sends a post request.""" | ||
access_token = AccessToken().for_user(self.user) | ||
self.token = f'Bearer {access_token}' | ||
self.client.credentials(HTTP_AUTHORIZATION=self.token) | ||
response = self.client.post(reverse('auth.logout')) | ||
self.assertEqual(response.status_code, 302) | ||
url = '{server_url}/logout?service={service_url}'.format( | ||
server_url=settings.CAS_ENDPOINT, | ||
service_url=settings.API_ENDPOINT | ||
) | ||
self.assertEqual(response['Location'], url) | ||
|
||
def test_logout_view_not_authenticated_logout_url(self): | ||
"""LogoutView should return a 401 error when trying to access it while not authenticated.""" | ||
response = self.client.post(reverse('auth.logout')) | ||
self.assertEqual(response.status_code, 401) | ||
|
||
|
||
class TestLoginView(APITestCase): | ||
def test_login_view_returns_login_url(self): | ||
"""LoginView should return a login url redirect if a post request is sent.""" | ||
response = self.client.get(reverse('auth.login')) | ||
self.assertEqual(response.status_code, 302) | ||
url = '{server_url}/login?service={service_url}'.format( | ||
server_url=settings.CAS_ENDPOINT, | ||
service_url=settings.CAS_RESPONSE | ||
) | ||
self.assertEqual(response['Location'], url) | ||
|
||
|
||
class TestTokenEchoView(APITestCase): | ||
def test_token_echo_echoes_token(self): | ||
"""TokenEchoView should echo the User's current token""" | ||
ticket = 'This is a ticket.' | ||
response = self.client.get(reverse('auth.echo'), data={'ticket': ticket}) | ||
content = response.rendered_content.decode('utf-8').strip('"') | ||
self.assertEqual(content, ticket) |
Oops, something went wrong.