Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions care/emr/tests/test_reset_password_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,28 @@ def test_password_confirm_rate_limiting(self):
status_code=429,
)

def test_reset_password_request_email_failure(self):
"""
Test that a 503 is returned when the email fails to send.
"""
from unittest.mock import patch

with patch(
"care.users.reset_password_views.send_password_reset_email",
side_effect=Exception("Connection unexpectedly closed"),
):
response = self.client.post(
self.reset_password_request_url,
{"username": "testuser"},
format="json",
)
self.assertEqual(response.status_code, 503)
self.assertContains(
response,
"Failed to send password reset email. Please try again.",
status_code=503,
)

def test_change_password_with_leading_whitespace(self):
"""
Test that password with leading whitespace is handled consistently.
Expand Down
55 changes: 24 additions & 31 deletions care/emr/utils/reset_password.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import hashlib
import logging
from datetime import timedelta

import jwt
Expand All @@ -11,8 +10,6 @@
from care.emr.resources.common.mail_type import MailTypeChoices
from care.users.models import User

logger = logging.getLogger(__name__)


def generate_password_reset_token(user):
"""Generate a JWT token with HMAC-SHA256 signature"""
Expand Down Expand Up @@ -68,32 +65,28 @@ def send_password_reset_email(user, mail_type):
"""
Sends the password reset email to the user.
"""
try:
token = generate_password_reset_token(user)
context = {
"current_user": user,
"username": user.username,
"email": user.email,
"reset_password_url": f"{settings.CURRENT_DOMAIN}/password_reset/{token}",
}
if mail_type == MailTypeChoices.create.value:
email_html_message = render_to_string(
settings.USER_CREATE_PASSWORD_EMAIL_TEMPLATE_PATH, context
)
subject = "Set Up Your Password for Care"
else:
email_html_message = render_to_string(
settings.USER_RESET_PASSWORD_EMAIL_TEMPLATE_PATH, context
)
subject = "Password Reset for Care"
msg = EmailMessage(
subject,
email_html_message,
settings.DEFAULT_FROM_EMAIL,
(user.email,),
token = generate_password_reset_token(user)
context = {
"current_user": user,
"username": user.username,
"email": user.email,
"reset_password_url": f"{settings.CURRENT_DOMAIN}/password_reset/{token}",
}
if mail_type == MailTypeChoices.create.value:
email_html_message = render_to_string(
settings.USER_CREATE_PASSWORD_EMAIL_TEMPLATE_PATH, context
)
msg.content_subtype = "html"
msg.send()

except Exception as err:
logger.error("Failed to send password reset email: %s", err)
subject = "Set Up Your Password for Care"
else:
email_html_message = render_to_string(
settings.USER_RESET_PASSWORD_EMAIL_TEMPLATE_PATH, context
)
subject = "Password Reset for Care"
msg = EmailMessage(
subject,
email_html_message,
settings.DEFAULT_FROM_EMAIL,
(user.email,),
)
msg.content_subtype = "html"
msg.send()
10 changes: 9 additions & 1 deletion care/users/reset_password_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,15 @@ def post(self, request, *args, **kwargs):
if user and user.is_active:
active_user_found = True
mail_type = MailTypeChoices.reset.value
send_password_reset_email(user, mail_type)
try:
send_password_reset_email(user, mail_type)
except Exception:
return Response(
{
"detail": "Failed to send password reset email. Please try again."
},
status=status.HTTP_503_SERVICE_UNAVAILABLE,
)

if not active_user_found and not getattr(
settings, "DJANGO_REST_PASSWORDRESET_NO_INFORMATION_LEAKAGE", False
Expand Down
Loading