-
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.
feat: Add endpoint to request password reset.
Added an endpoint for users to request a password reset token using any of their verified email addresses. Refs #39
- Loading branch information
Showing
6 changed files
with
217 additions
and
0 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
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
78 changes: 78 additions & 0 deletions
78
email_auth/interfaces/rest/test/serializers/test_password_reset_request_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,78 @@ | ||
from unittest import mock | ||
|
||
import pytest | ||
|
||
from email_auth import models | ||
|
||
|
||
pytest.importorskip("rest_framework") | ||
|
||
# Imports that require the presence of "rest_framework" | ||
from email_auth.interfaces.rest import serializers # noqa | ||
|
||
|
||
def test_save_unregistered_email(mock_email_address_qs): | ||
""" | ||
If the provided email address doesn't exist in the system, saving | ||
should do nothing. | ||
""" | ||
address = "test@example.com" | ||
mock_email_address_qs.get.side_effect = models.EmailAddress.DoesNotExist | ||
|
||
data = {"email": address} | ||
serializer = serializers.PasswordResetRequestSerializer(data=data) | ||
|
||
assert serializer.is_valid() | ||
result = serializer.save() | ||
|
||
assert result is None | ||
assert serializer.data == data | ||
assert mock_email_address_qs.get.call_args[1] == { | ||
"address__iexact": address, | ||
"is_verified": True, | ||
} | ||
|
||
|
||
def test_save_unverified_email(mock_email_address_qs): | ||
""" | ||
If the provided email address has not been verified yet, saving the | ||
serializer should do nothing. | ||
""" | ||
address = "test@example.com" | ||
mock_email_address_qs.get.side_effect = models.EmailAddress.DoesNotExist | ||
|
||
data = {"email": address} | ||
serializer = serializers.PasswordResetRequestSerializer(data=data) | ||
|
||
assert serializer.is_valid() | ||
result = serializer.save() | ||
|
||
assert result is None | ||
assert serializer.data == data | ||
assert mock_email_address_qs.get.call_args[1] == { | ||
"address__iexact": address, | ||
"is_verified": True, | ||
} | ||
|
||
|
||
@mock.patch("email_auth.models.PasswordReset.send_email") | ||
def test_save_verified_email(_, mock_email_address_qs): | ||
""" | ||
If a verified email is provided, saving the serializer should send | ||
a new password reset token to the provided address. | ||
""" | ||
email = models.EmailAddress(address="test@example.com") | ||
mock_email_address_qs.get.return_value = email | ||
|
||
data = {"email": email.address} | ||
serializer = serializers.PasswordResetRequestSerializer(data=data) | ||
|
||
assert serializer.is_valid() | ||
result = serializer.save() | ||
|
||
assert serializer.data == data | ||
assert result.send_email.call_count == 1 | ||
assert mock_email_address_qs.get.call_args[1] == { | ||
"address__iexact": email.address, | ||
"is_verified": True, | ||
} |
84 changes: 84 additions & 0 deletions
84
email_auth/interfaces/rest/test/views/test_password_reset_request_view.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,84 @@ | ||
import pytest | ||
import requests | ||
from django.contrib.auth import get_user_model | ||
|
||
from email_auth import models | ||
|
||
|
||
pytest.importorskip("rest_framework") | ||
|
||
from email_auth.interfaces.rest import serializers, views # noqa | ||
|
||
|
||
def test_get_serializer_class(): | ||
""" | ||
Test the serializer class used by the view. | ||
""" | ||
view = views.PasswordResetRequestView() | ||
expected = serializers.PasswordResetRequestSerializer | ||
|
||
assert view.get_serializer_class() == expected | ||
|
||
|
||
@pytest.mark.functional_test | ||
def test_request_password_reset(live_server, mailoutbox, settings): | ||
""" | ||
If the user provides a verified email address to the endpoint, an | ||
email containing a link to the password reset page should be sent. | ||
""" | ||
reset_url_template = "http://localhost/reset-password/{key}" | ||
settings.EMAIL_AUTH = {"PASSWORD_RESET_URL": reset_url_template} | ||
|
||
user = get_user_model().objects.create_user(username="Test User") | ||
email = models.EmailAddress.objects.create( | ||
address="test@example.com", is_verified=True, user=user | ||
) | ||
|
||
data = {"email": email.address} | ||
url = f"{live_server}/rest/password-reset-requests/" | ||
response = requests.post(url, data) | ||
|
||
assert response.status_code == 201 | ||
assert response.json() == data | ||
assert len(mailoutbox) == 1 | ||
|
||
msg = mailoutbox[0] | ||
reset = models.PasswordReset.objects.get() | ||
|
||
assert msg.to == [data["email"]] | ||
assert reset_url_template.format(key=reset.token) in msg.body | ||
|
||
|
||
@pytest.mark.functional_test | ||
def test_request_password_reset_missing_email(live_server, mailoutbox): | ||
""" | ||
If the user provides an email address that does not exist in the | ||
system, no action should be taken. | ||
""" | ||
data = {"email": "test@example.com"} | ||
url = f"{live_server}/rest/password-reset-requests/" | ||
response = requests.post(url, data) | ||
|
||
assert response.status_code == 201 | ||
assert response.json() == data | ||
assert len(mailoutbox) == 0 | ||
|
||
|
||
@pytest.mark.functional_test | ||
def test_request_password_reset_unverified_email(live_server, mailoutbox): | ||
""" | ||
If the user provides an email address that does not exist in the | ||
system, no action should be taken. | ||
""" | ||
user = get_user_model().objects.create_user(username="Test User") | ||
email = models.EmailAddress.objects.create( | ||
address="test@example.com", is_verified=False, user=user | ||
) | ||
|
||
data = {"email": email.address} | ||
url = f"{live_server}/rest/password-reset-requests/" | ||
response = requests.post(url, data) | ||
|
||
assert response.status_code == 201 | ||
assert response.json() == data | ||
assert len(mailoutbox) == 0 |
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