mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-19 19:09:22 +02:00
Merge pull request #1493 from cisagov/dk/1458-uppercase-emails
Issue #1458 - lowercase emails on add user to domain; case insensitive match
This commit is contained in:
commit
b657db8821
4 changed files with 69 additions and 18 deletions
|
@ -28,6 +28,17 @@ class DomainAddUserForm(forms.Form):
|
||||||
|
|
||||||
email = forms.EmailField(label="Email")
|
email = forms.EmailField(label="Email")
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
"""clean form data by lowercasing email"""
|
||||||
|
cleaned_data = super().clean()
|
||||||
|
|
||||||
|
# Lowercase the value of the 'email' field
|
||||||
|
email_value = cleaned_data.get("email")
|
||||||
|
if email_value:
|
||||||
|
cleaned_data["email"] = email_value.lower()
|
||||||
|
|
||||||
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
class DomainNameserverForm(forms.Form):
|
class DomainNameserverForm(forms.Form):
|
||||||
"""Form for changing nameservers."""
|
"""Form for changing nameservers."""
|
||||||
|
|
|
@ -101,7 +101,7 @@ class User(AbstractUser):
|
||||||
"""When a user first arrives on the site, we need to retrieve any domain
|
"""When a user first arrives on the site, we need to retrieve any domain
|
||||||
invitations that match their email address."""
|
invitations that match their email address."""
|
||||||
for invitation in DomainInvitation.objects.filter(
|
for invitation in DomainInvitation.objects.filter(
|
||||||
email=self.email, status=DomainInvitation.DomainInvitationStatus.INVITED
|
email__iexact=self.email, status=DomainInvitation.DomainInvitationStatus.INVITED
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
invitation.retrieve()
|
invitation.retrieve()
|
||||||
|
|
|
@ -654,3 +654,17 @@ class TestUser(TestCase):
|
||||||
"""A new user who's neither transitioned nor invited should
|
"""A new user who's neither transitioned nor invited should
|
||||||
return True when tested with class method needs_identity_verification"""
|
return True when tested with class method needs_identity_verification"""
|
||||||
self.assertTrue(User.needs_identity_verification(self.user.email, self.user.username))
|
self.assertTrue(User.needs_identity_verification(self.user.email, self.user.username))
|
||||||
|
|
||||||
|
def test_check_domain_invitations_on_login_caps_email(self):
|
||||||
|
"""A DomainInvitation with an email address with capital letters should match
|
||||||
|
a User record whose email address is not in caps"""
|
||||||
|
# create DomainInvitation with CAPS email that matches User email
|
||||||
|
# on a case-insensitive match
|
||||||
|
caps_email = "MAYOR@igorville.gov"
|
||||||
|
# mock the domain invitation save routine
|
||||||
|
with patch("registrar.models.DomainInvitation.save") as save_mock:
|
||||||
|
DomainInvitation.objects.get_or_create(email=caps_email, domain=self.domain)
|
||||||
|
self.user.check_domain_invitations_on_login()
|
||||||
|
# if check_domain_invitations_on_login properly matches exactly one
|
||||||
|
# Domain Invitation, then save routine should be called exactly once
|
||||||
|
save_mock.assert_called_once()
|
||||||
|
|
|
@ -1355,29 +1355,55 @@ class TestDomainManagers(TestDomainOverview):
|
||||||
out the boto3 SES email sending here.
|
out the boto3 SES email sending here.
|
||||||
"""
|
"""
|
||||||
# make sure there is no user with this email
|
# make sure there is no user with this email
|
||||||
EMAIL = "mayor@igorville.gov"
|
email_address = "mayor@igorville.gov"
|
||||||
User.objects.filter(email=EMAIL).delete()
|
User.objects.filter(email=email_address).delete()
|
||||||
|
|
||||||
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
|
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
|
||||||
|
|
||||||
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
||||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||||
add_page.form["email"] = EMAIL
|
add_page.form["email"] = email_address
|
||||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
success_result = add_page.form.submit()
|
success_result = add_page.form.submit()
|
||||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
success_page = success_result.follow()
|
success_page = success_result.follow()
|
||||||
|
|
||||||
self.assertContains(success_page, EMAIL)
|
self.assertContains(success_page, email_address)
|
||||||
self.assertContains(success_page, "Cancel") # link to cancel invitation
|
self.assertContains(success_page, "Cancel") # link to cancel invitation
|
||||||
self.assertTrue(DomainInvitation.objects.filter(email=EMAIL).exists())
|
self.assertTrue(DomainInvitation.objects.filter(email=email_address).exists())
|
||||||
|
|
||||||
|
@boto3_mocking.patching
|
||||||
|
def test_domain_invitation_created_for_caps_email(self):
|
||||||
|
"""Add user on a nonexistent email with CAPS creates an invitation to lowercase email.
|
||||||
|
|
||||||
|
Adding a non-existent user sends an email as a side-effect, so mock
|
||||||
|
out the boto3 SES email sending here.
|
||||||
|
"""
|
||||||
|
# make sure there is no user with this email
|
||||||
|
email_address = "mayor@igorville.gov"
|
||||||
|
caps_email_address = "MAYOR@igorville.gov"
|
||||||
|
User.objects.filter(email=email_address).delete()
|
||||||
|
|
||||||
|
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
|
||||||
|
|
||||||
|
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
||||||
|
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||||
|
add_page.form["email"] = caps_email_address
|
||||||
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
|
success_result = add_page.form.submit()
|
||||||
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
|
success_page = success_result.follow()
|
||||||
|
|
||||||
|
self.assertContains(success_page, email_address)
|
||||||
|
self.assertContains(success_page, "Cancel") # link to cancel invitation
|
||||||
|
self.assertTrue(DomainInvitation.objects.filter(email=email_address).exists())
|
||||||
|
|
||||||
@boto3_mocking.patching
|
@boto3_mocking.patching
|
||||||
def test_domain_invitation_email_sent(self):
|
def test_domain_invitation_email_sent(self):
|
||||||
"""Inviting a non-existent user sends them an email."""
|
"""Inviting a non-existent user sends them an email."""
|
||||||
# make sure there is no user with this email
|
# make sure there is no user with this email
|
||||||
EMAIL = "mayor@igorville.gov"
|
email_address = "mayor@igorville.gov"
|
||||||
User.objects.filter(email=EMAIL).delete()
|
User.objects.filter(email=email_address).delete()
|
||||||
|
|
||||||
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
|
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
|
||||||
|
|
||||||
|
@ -1386,28 +1412,28 @@ class TestDomainManagers(TestDomainOverview):
|
||||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||||
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
||||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||||
add_page.form["email"] = EMAIL
|
add_page.form["email"] = email_address
|
||||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
add_page.form.submit()
|
add_page.form.submit()
|
||||||
# check the mock instance to see if `send_email` was called right
|
# check the mock instance to see if `send_email` was called right
|
||||||
mock_client_instance.send_email.assert_called_once_with(
|
mock_client_instance.send_email.assert_called_once_with(
|
||||||
FromEmailAddress=settings.DEFAULT_FROM_EMAIL,
|
FromEmailAddress=settings.DEFAULT_FROM_EMAIL,
|
||||||
Destination={"ToAddresses": [EMAIL]},
|
Destination={"ToAddresses": [email_address]},
|
||||||
Content=ANY,
|
Content=ANY,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_domain_invitation_cancel(self):
|
def test_domain_invitation_cancel(self):
|
||||||
"""Posting to the delete view deletes an invitation."""
|
"""Posting to the delete view deletes an invitation."""
|
||||||
EMAIL = "mayor@igorville.gov"
|
email_address = "mayor@igorville.gov"
|
||||||
invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=EMAIL)
|
invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=email_address)
|
||||||
self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id}))
|
self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id}))
|
||||||
with self.assertRaises(DomainInvitation.DoesNotExist):
|
with self.assertRaises(DomainInvitation.DoesNotExist):
|
||||||
DomainInvitation.objects.get(id=invitation.id)
|
DomainInvitation.objects.get(id=invitation.id)
|
||||||
|
|
||||||
def test_domain_invitation_cancel_no_permissions(self):
|
def test_domain_invitation_cancel_no_permissions(self):
|
||||||
"""Posting to the delete view as a different user should fail."""
|
"""Posting to the delete view as a different user should fail."""
|
||||||
EMAIL = "mayor@igorville.gov"
|
email_address = "mayor@igorville.gov"
|
||||||
invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=EMAIL)
|
invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=email_address)
|
||||||
|
|
||||||
other_user = User()
|
other_user = User()
|
||||||
other_user.save()
|
other_user.save()
|
||||||
|
@ -1419,20 +1445,20 @@ class TestDomainManagers(TestDomainOverview):
|
||||||
@boto3_mocking.patching
|
@boto3_mocking.patching
|
||||||
def test_domain_invitation_flow(self):
|
def test_domain_invitation_flow(self):
|
||||||
"""Send an invitation to a new user, log in and load the dashboard."""
|
"""Send an invitation to a new user, log in and load the dashboard."""
|
||||||
EMAIL = "mayor@igorville.gov"
|
email_address = "mayor@igorville.gov"
|
||||||
User.objects.filter(email=EMAIL).delete()
|
User.objects.filter(email=email_address).delete()
|
||||||
|
|
||||||
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
||||||
|
|
||||||
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
|
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
|
||||||
|
|
||||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||||
add_page.form["email"] = EMAIL
|
add_page.form["email"] = email_address
|
||||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
add_page.form.submit()
|
add_page.form.submit()
|
||||||
|
|
||||||
# user was invited, create them
|
# user was invited, create them
|
||||||
new_user = User.objects.create(username=EMAIL, email=EMAIL)
|
new_user = User.objects.create(username=email_address, email=email_address)
|
||||||
# log them in to `self.app`
|
# log them in to `self.app`
|
||||||
self.app.set_user(new_user.username)
|
self.app.set_user(new_user.username)
|
||||||
# and manually call the on each login callback
|
# and manually call the on each login callback
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue