Date: Tue, 12 Dec 2023 15:07:40 -0800
Subject: [PATCH 15/40] updated all AO examples
---
.../templates/includes/ao_example.html | 56 +++++++++++--------
1 file changed, 33 insertions(+), 23 deletions(-)
diff --git a/src/registrar/templates/includes/ao_example.html b/src/registrar/templates/includes/ao_example.html
index dfdbb3775..e0821fd39 100644
--- a/src/registrar/templates/includes/ao_example.html
+++ b/src/registrar/templates/includes/ao_example.html
@@ -1,50 +1,60 @@
{% if is_federal %}
{% if federal_type == 'executive' %}
- Domain requests from executive branch agencies must be authorized by Chief Information Officers or agency heads.
- Domain requests from executive branch agencies are subject to guidance issued by the U.S. Office of Management and Budget.
+ Executive branch federal agencies
+ Domain requests from executive branch federal agencies must be authorized by the agency's CIO or the head of the agency.
+ See OMB Memorandum M-23-10 for more information.
{% elif federal_type == 'judicial' %}
- Domain requests from the U.S. Supreme Court must be authorized by the director of information technology for the U.S. Supreme Court.
- Domain requests from other judicial branch agencies must be authorized by the director or Chief Information Officer of the Administrative Office (AO) of the United States Courts.
-
+ Judicial branch federal agencies
+ Domain requests for judicial branch federal agencies, except the U.S. Supreme Court, must be authorized by the director or CIO of the Administrative Office (AO) of the United States Courts.
+ Domain requests from the U.S. Supreme Court must be authorized by the director of information technology for the U.S. Supreme Court.
{% elif federal_type == 'legislative' %}
- U.S. Senate
- Domain requests from the U.S. Senate must come from the Senate Sergeant at Arms.
+ Legislative branch federal agencies
- U.S. House of Representatives
- Domain requests from the U.S. House of Representatives must come from the House Chief Administrative Officer.
+
U.S. Senate
+ Domain requests from the U.S. Senate must come from the Senate Sergeant at Arms.
- Other legislative branch agencies
- Domain requests from legislative branch agencies must come from the agency’s head or Chief Information Officer.
- Domain requests from legislative commissions must come from the head of the commission, or the head or Chief Information Officer of the parent agency, if there is one.
-
+ U.S. House of Representatives
+ Domain requests from the U.S. House of Representatives must come from the House Chief Administrative Officer.
+
+ Other legislative branch agencies
+ Domain requests from legislative branch agencies must come from the agency’s head or CIO.
+ Domain requests from legislative commissions must come from the head of the commission, or the head or CIO of the parent agency, if there is one.
{% endif %}
{% elif organization_type == 'city' %}
- Domain requests from cities must be authorized by the mayor or the equivalent highest-elected official.
+ Cities
+ Domain requests from cities must be authorized by someone in a role of significant, executive responsibility within the city (mayor, council president, city manager, township/village supervisor, select board chairperson, chief, senior technology officer, or equivalent).
{% elif organization_type == 'county' %}
- Domain requests from counties must be authorized by the chair of the county commission or the equivalent highest-elected official.
+ Counties
+ Domain requests from counties must be authorized by the commission chair or someone in a role of significant, executive responsibility within the county (county judge, county mayor, parish/borough president, senior technology officer, or equivalent). Other county-level offices (county clerk, sheriff, county auditor, comptroller) may qualify, as well, in some instances.
{% elif organization_type == 'interstate' %}
- Domain requests from interstate organizations must be authorized by the highest-ranking executive (president, director, chair, or equivalent) or one of the state’s governors or Chief Information Officers.
+ Interstate organizations
+ Domain requests from interstate organizations must be authorized by someone in a role of significant, executive responsibility within the organization (president, director, chair, senior technology officer, or equivalent) or one of the state’s governors or CIOs.
{% elif organization_type == 'school_district' %}
+ School districts
Domain requests from school district governments must be authorized by the highest-ranking executive (the chair of a school district’s board or a superintendent).
{% elif organization_type == 'special_district' %}
- Domain requests from special districts must be authorized by the highest-ranking executive (president, director, chair, or equivalent) or state Chief Information Officers for state-based organizations.
+ Special districts
+ Domain requests from school district governments must be authorized by someone in a role of significant, executive responsibility within the district (board chair, superintendent, senior technology officer, or equivalent).
{% elif organization_type == 'state_or_territory' %}
- States and territories: executive branch
- Domain requests from states and territories must be authorized by the governor or the state Chief Information Officer.
- States and territories: judicial and legislative branches
- Domain requests from state legislatures and courts must be authorized by an agency’s Chief Information Officer or highest-ranking executive.
+ U.S. states and territories
+
+ States and territories: executive branch
+ Domain requests from states and territories must be authorized by the governor or someone in a role of significant, executive responsibility within the agency (department secretary, senior technology officer, or equivalent).
+
+ States and territories: judicial and legislative branches
+ Domain requests from state legislatures and courts must be authorized by an agency’s CIO or someone in a role of significant, executive responsibility within the agency.
{% elif organization_type == 'tribal' %}
- Domain requests from federally-recognized tribal governments must be authorized by the leader of the tribe, as recognized by the Bureau of Indian Affairs.
-Domain requests from state-recognized tribal governments must be authorized by the leader of the tribe, as determined by the state’s tribal recognition initiative.
+ Tribal governments
+ Domain requests from federally recognized tribal governments must be authorized by the tribal leader the Bureau of Indian Affairs recognizes.
{% endif %}
From e7e3df042243443226c01dfa637d4449e538b058 Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Tue, 12 Dec 2023 18:28:25 -0500
Subject: [PATCH 16/40] Lint
---
src/djangooidc/backends.py | 2 +-
src/djangooidc/tests/test_backends.py | 8 +-
src/djangooidc/views.py | 19 +--
.../copy_names_from_contacts_to_users.py | 19 +--
.../test_copy_names_from_contacts_to_users.py | 136 +++++++++---------
src/registrar/tests/test_models.py | 22 ++-
6 files changed, 107 insertions(+), 99 deletions(-)
diff --git a/src/djangooidc/backends.py b/src/djangooidc/backends.py
index d77a3fe15..cf326eca4 100644
--- a/src/djangooidc/backends.py
+++ b/src/djangooidc/backends.py
@@ -69,7 +69,7 @@ class OpenIdConnectBackend(ModelBackend):
# Overwrite first_name and last_name if not empty string
for key, value in kwargs.items():
# Check if the key is not first_name or last_name or value is not empty string
- if key not in ['first_name', 'last_name'] or value:
+ if key not in ["first_name", "last_name"] or value:
setattr(user, key, value)
user.save()
diff --git a/src/djangooidc/tests/test_backends.py b/src/djangooidc/tests/test_backends.py
index 51fc965c4..93dc6e68a 100644
--- a/src/djangooidc/tests/test_backends.py
+++ b/src/djangooidc/tests/test_backends.py
@@ -4,8 +4,8 @@ from django.utils import timezone
from registrar.models import User
from ..backends import OpenIdConnectBackend # Adjust the import path based on your project structure
-class OpenIdConnectBackendTestCase(TestCase):
+class OpenIdConnectBackendTestCase(TestCase):
def setUp(self):
self.backend = OpenIdConnectBackend()
self.kwargs = {
@@ -56,10 +56,10 @@ class OpenIdConnectBackendTestCase(TestCase):
"""Test that authenticate updates an existing user if it finds one.
For this test, given_name and family_name are not supplied"""
# Create an existing user with the same username and with first and last names
- existing_user = User.objects.create_user(username="test_user",first_name="John",last_name="Doe")
+ existing_user = User.objects.create_user(username="test_user", first_name="John", last_name="Doe")
# Remove given_name and family_name from the input, self.kwargs
- self.kwargs.pop("given_name", None)
+ self.kwargs.pop("given_name", None)
self.kwargs.pop("family_name", None)
# Ensure that the authenticate method updates the existing user
@@ -79,7 +79,7 @@ class OpenIdConnectBackendTestCase(TestCase):
"""Test that authenticate updates an existing user if it finds one.
For this test, given_name and family_name are supplied and overwrite"""
# Create an existing user with the same username and with first and last names
- existing_user = User.objects.create_user(username="test_user",first_name="WillBe",last_name="Replaced")
+ existing_user = User.objects.create_user(username="test_user", first_name="WillBe", last_name="Replaced")
# Ensure that the authenticate method updates the existing user
# and preserves existing first and last names
diff --git a/src/djangooidc/views.py b/src/djangooidc/views.py
index 7efb576da..a39da68aa 100644
--- a/src/djangooidc/views.py
+++ b/src/djangooidc/views.py
@@ -58,7 +58,7 @@ def openid(request):
request.session["next"] = request.GET.get("next", "/")
try:
- logger.info('openid() calls create_authn_request in oidc')
+ logger.info("openid() calls create_authn_request in oidc")
return CLIENT.create_authn_request(request.session)
except Exception as err:
return error_page(request, err)
@@ -72,22 +72,23 @@ def login_callback(request):
# test for need for identity verification and if it is satisfied
# if not satisfied, redirect user to login with stepped up acr_value
if requires_step_up_auth(userinfo):
- logger.info('login_callback() calls get_step_up_acr_value and create_authn_request in oidc')
+ logger.info("login_callback() calls get_step_up_acr_value and create_authn_request in oidc")
# add acr_value to request.session
request.session["acr_value"] = CLIENT.get_step_up_acr_value()
return CLIENT.create_authn_request(request.session)
-
- logger.info(f'login_callback() before calling authenticate: {userinfo}')
-
+
+ logger.info(f"login_callback() before calling authenticate: {userinfo}")
+
try:
user_in_db = User.objects.get(username=userinfo["sub"])
-
+
if user_in_db:
- logger.info(f"This user exists in the DB (before authenticate): {user_in_db.first_name} {user_in_db.last_name}")
+ logger.info(
+ f"This user exists in the DB (before authenticate): {user_in_db.first_name} {user_in_db.last_name}"
+ )
except:
pass
-
-
+
user = authenticate(request=request, **userinfo)
if user:
login(request, user)
diff --git a/src/registrar/management/commands/copy_names_from_contacts_to_users.py b/src/registrar/management/commands/copy_names_from_contacts_to_users.py
index c807c0278..6fdf0f09c 100644
--- a/src/registrar/management/commands/copy_names_from_contacts_to_users.py
+++ b/src/registrar/management/commands/copy_names_from_contacts_to_users.py
@@ -63,7 +63,7 @@ class Command(BaseCommand):
{TerminalColors.ENDC}
""" # noqa
)
-
+
# DEBUG:
TerminalHelper.print_conditional(
debug_on,
@@ -91,7 +91,7 @@ class Command(BaseCommand):
Returns the corresponding User object.
"""
-
+
user_exists = User.objects.filter(contact=contact).exists()
if user_exists:
try:
@@ -105,7 +105,7 @@ class Command(BaseCommand):
f"""{TerminalColors.YELLOW}
> Found linked user for contact:
{contact} {contact.email} {contact.first_name} {contact.last_name}
- > The linked user is {eligible_user}
+ > The linked user is {eligible_user} {eligible_user.username}
{TerminalColors.ENDC}""", # noqa
)
@@ -113,8 +113,10 @@ class Command(BaseCommand):
# ---- LET'S KEEP A LIGHT TOUCH
if not eligible_user.first_name or not eligible_user.last_name:
processed_user = eligible_user
- processed_user.first_name = contact.first_name
- processed_user.last_name = contact.last_name
+ # (expression has type "str | None", variable has type "str | int | Combinable")
+ # so we'll ignore type
+ processed_user.first_name = contact.first_name # type: ignore
+ processed_user.last_name = contact.last_name # type: ignore
processed_user.save()
return (
@@ -140,7 +142,7 @@ class Command(BaseCommand):
# ======================================================
# ================= PROCESS CONTACTS ==================
# ======================================================
-
+
# C901 'Command.handle' is too complex
def process_contacts(
self,
@@ -150,7 +152,6 @@ class Command(BaseCommand):
processed_users,
):
for contact in Contact.objects.all():
-
# DEBUG:
TerminalHelper.print_conditional(
debug_on,
@@ -208,10 +209,10 @@ class Command(BaseCommand):
# users we SKIPPED
skipped_contacts = []
-
+
# users we found that are linked to contacts
eligible_users = []
-
+
# users we PROCESSED
processed_users = []
diff --git a/src/registrar/tests/test_copy_names_from_contacts_to_users.py b/src/registrar/tests/test_copy_names_from_contacts_to_users.py
index 2383adc50..eaf0e6349 100644
--- a/src/registrar/tests/test_copy_names_from_contacts_to_users.py
+++ b/src/registrar/tests/test_copy_names_from_contacts_to_users.py
@@ -5,88 +5,96 @@ from registrar.models import (
Contact,
)
-from django.core.management import call_command
-from unittest.mock import patch
-
from registrar.management.commands.copy_names_from_contacts_to_users import Command
-class TestOrganizationMigration(TestCase):
+
+class TestDataUpdates(TestCase):
def setUp(self):
- """Defines the file name of migration_json and the folder its contained in"""
-
-
- # self.user1, _ = User.objects.get_or_create(username="user1")
- # self.user2 = User.objects.create(username="user2", first_name="Joey", last_name="")
- # self.user3 = User.objects.create(username="user3", first_name="a special first name", last_name="a special last name")
- # self.userX = User.objects.create(username="emailX@igorville.gov", first_name="firstX", last_name="lastX")
-
- # self.contact1, _ = Contact.objects.get_or_create(user=self.user1, email="email1@igorville.gov", first_name="first1", last_name="last1")
- # self.contact2 = Contact.objects.create(user=self.user2, email="email2@igorville.gov", first_name="first2", last_name="last2")
- # self.contact3 = Contact.objects.create(user=None, email="email3@igorville.gov", first_name="first3", last_name="last3")
- # self.contact4 = Contact.objects.create(user=None, email="email4@igorville.gov", first_name="first4", last_name="last4")
-
- # self.contact1 = Contact.objects.create(email="email1@igorville.gov", first_name="first1", last_name="last1")
- # self.contact2 = Contact.objects.create(email="email2@igorville.gov", first_name="first2", last_name="last2")
- # self.contact3 = Contact.objects.create(email="email3@igorville.gov", first_name="first3", last_name="last3")
- # self.contact4 = Contact.objects.create(email="email4@igorville.gov", first_name="first4", last_name="last4")
-
- # self.user1 = User.objects.create(contact=self.contact1)
- # self.user2 = User.objects.create(contact=self.contact2, username="user2", first_name="Joey", last_name="")
- # self.user3 = User.objects.create(username="user3", first_name="a special first name", last_name="a special last name")
- # self.userX = User.objects.create(username="emailX@igorville.gov", first_name="firstX", last_name="lastX")
-
-
+ """We cannot setup the user details because contacts will override the first and last names in its save method
+ so we will initiate the users, setup the contacts and link them, and leave the rest of the setup for the test(s).
+ """
+
+ self.user1 = User.objects.create(username="user1")
+ self.user2 = User.objects.create(username="user2")
+ self.user3 = User.objects.create(username="user3")
+ self.userX = User.objects.create(username="user4")
+ # The last user created triggers the creation of a contact and attaches itself to it. @Neil wth is going on?
+ # This bs_user defuses that situation so we can test the code.
+ self.bs_user = User.objects.create()
+
+ self.contact1 = Contact.objects.create(
+ user=self.user1, email="email1@igorville.gov", first_name="first1", last_name="last1"
+ )
+ self.contact2 = Contact.objects.create(
+ user=self.user2, email="email2@igorville.gov", first_name="first2", last_name="last2"
+ )
+ self.contact3 = Contact.objects.create(
+ user=self.user3, email="email3@igorville.gov", first_name="first3", last_name="last3"
+ )
+ self.contact4 = Contact.objects.create(email="email4@igorville.gov", first_name="first4", last_name="last4")
+
self.command = Command()
def tearDown(self):
- """Deletes all DB objects related to migrations"""
- # Delete users
+ """Clean up"""
+ # Delete users and contacts
User.objects.all().delete()
Contact.objects.all().delete()
-
+
def test_script_updates_linked_users(self):
-
- user1, _ = User.objects.get_or_create(username="user1")
- contact1, _ = Contact.objects.get_or_create(user=user1, email="email1@igorville.gov", first_name="first1", last_name="last1")
-
-
- # self.user1.first_name = ""
- # self.user1.last_name = ""
- # self.user2.last_name = ""
- # self.user1.save()
- # self.user2.save()
-
- # users we SKIPPED
+ """Test the script that copies contacts' first and last names into associated users that
+ are eligible (first or last are blank or undefined)"""
+
+ # Set up the users' first and last names here so
+ # they that they don't get overwritten by Contact's save()
+ # User with no first or last names
+ self.user1.first_name = ""
+ self.user1.last_name = ""
+ self.user1.save()
+
+ # User with a first name but no last name
+ self.user2.last_name = ""
+ self.user2.save()
+
+ # User with a first and last name
+ self.user3.first_name = "An existing first name"
+ self.user3.last_name = "An existing last name"
+ self.user3.save()
+
+ # Unlinked user
+ # To make this test useful, we will set the last_name to ""
+ self.userX.first_name = "Unlinked user's first name"
+ self.userX.last_name = ""
+ self.userX.save()
+
+ # Call the parent method the same way we do it in the script
skipped_contacts = []
- # users we found that are linked to contacts
eligible_users = []
- # users we PROCESSED
processed_users = []
(
skipped_contacts,
eligible_users,
processed_users,
) = self.command.process_contacts(
- True,
+ # Set debugging to False
+ False,
skipped_contacts,
eligible_users,
processed_users,
)
-
- # self.user1.refresh_from_db()
- # self.user2.refresh_from_db()
- # self.user3.refresh_from_db()
- # self.userX.refresh_from_db()
-
- self.assertEqual(user1.first_name, "first1")
- self.assertEqual(user1.last_name, "last1")
- # self.assertEqual(self.user2.first_name, "first2")
- # self.assertEqual(self.user2.last_name, "last2")
- # self.assertEqual(self.user3.first_name, "a special first name")
- # self.assertEqual(self.user3.last_name, "a special last name")
- # self.assertEqual(self.userX.first_name, "firstX")
- # self.assertEqual(self.userX.last_name, "lastX")
-
-
-
-
\ No newline at end of file
+
+ # Trigger DB refresh
+ self.user1.refresh_from_db()
+ self.user2.refresh_from_db()
+ self.user3.refresh_from_db()
+ self.userX.refresh_from_db()
+
+ # Asserts
+ self.assertEqual(self.user1.first_name, "first1")
+ self.assertEqual(self.user1.last_name, "last1")
+ self.assertEqual(self.user2.first_name, "first2")
+ self.assertEqual(self.user2.last_name, "last2")
+ self.assertEqual(self.user3.first_name, "An existing first name")
+ self.assertEqual(self.user3.last_name, "An existing last name")
+ self.assertEqual(self.userX.first_name, "Unlinked user's first name")
+ self.assertEqual(self.userX.last_name, "")
diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py
index c51af8008..dfab4f9a3 100644
--- a/src/registrar/tests/test_models.py
+++ b/src/registrar/tests/test_models.py
@@ -654,10 +654,9 @@ class TestUser(TestCase):
"""A new user who's neither transitioned nor invited should
return True when tested with class method needs_identity_verification"""
self.assertTrue(User.needs_identity_verification(self.user.email, self.user.username))
-
+
class TestContact(TestCase):
-
def setUp(self):
self.email = "mayor@igorville.gov"
self.user, _ = User.objects.get_or_create(email=self.email, first_name="Rachid", last_name="Mrad")
@@ -675,29 +674,28 @@ class TestContact(TestCase):
self.assertEqual(self.contact.last_name, "Mrad")
self.assertEqual(self.user.first_name, "Rachid")
self.assertEqual(self.user.last_name, "Mrad")
-
+
self.contact.first_name = "Joey"
self.contact.last_name = "Baloney"
self.contact.save()
-
+
# Refresh the user object to reflect the changes made in the database
self.user.refresh_from_db()
-
- # Updating the contact's first and last names propagate to the user
+
+ # Updating the contact's first and last names propagate to the user
self.assertEqual(self.contact.first_name, "Joey")
self.assertEqual(self.contact.last_name, "Baloney")
self.assertEqual(self.user.first_name, "Joey")
self.assertEqual(self.user.last_name, "Baloney")
-
+
def test_saving_contact_does_not_update_user_email(self):
- """When a contact's email is updated, the change is not propagated to the lined user."""
+ """When a contact's email is updated, the change is not propagated to the lined user."""
self.contact.email = "joey.baloney@diaperville.com"
self.contact.save()
-
+
# Refresh the user object to reflect the changes made in the database
self.user.refresh_from_db()
-
- # Updating the contact's email does not propagate
+
+ # Updating the contact's email does not propagate
self.assertEqual(self.contact.email, "joey.baloney@diaperville.com")
self.assertEqual(self.user.email, "mayor@igorville.gov")
-
From 8b6ce47da18dbe7a22337c94ce3a0700ec5025d3 Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Tue, 12 Dec 2023 18:52:12 -0500
Subject: [PATCH 17/40] Changed the logic and test around users who have either
a first or a last: Left them alone instead of overwriting from contact
---
.../copy_names_from_contacts_to_users.py | 2 +-
.../test_copy_names_from_contacts_to_users.py | 22 +++++++++----------
2 files changed, 11 insertions(+), 13 deletions(-)
diff --git a/src/registrar/management/commands/copy_names_from_contacts_to_users.py b/src/registrar/management/commands/copy_names_from_contacts_to_users.py
index 6fdf0f09c..f4849b2bd 100644
--- a/src/registrar/management/commands/copy_names_from_contacts_to_users.py
+++ b/src/registrar/management/commands/copy_names_from_contacts_to_users.py
@@ -111,7 +111,7 @@ class Command(BaseCommand):
# ---- UPDATE THE USER IF IT DOES NOT HAVE A FIRST AND LAST NAMES
# ---- LET'S KEEP A LIGHT TOUCH
- if not eligible_user.first_name or not eligible_user.last_name:
+ if not eligible_user.first_name and not eligible_user.last_name:
processed_user = eligible_user
# (expression has type "str | None", variable has type "str | int | Combinable")
# so we'll ignore type
diff --git a/src/registrar/tests/test_copy_names_from_contacts_to_users.py b/src/registrar/tests/test_copy_names_from_contacts_to_users.py
index eaf0e6349..4a57ec19b 100644
--- a/src/registrar/tests/test_copy_names_from_contacts_to_users.py
+++ b/src/registrar/tests/test_copy_names_from_contacts_to_users.py
@@ -17,7 +17,7 @@ class TestDataUpdates(TestCase):
self.user1 = User.objects.create(username="user1")
self.user2 = User.objects.create(username="user2")
self.user3 = User.objects.create(username="user3")
- self.userX = User.objects.create(username="user4")
+ self.user4 = User.objects.create(username="user4")
# The last user created triggers the creation of a contact and attaches itself to it. @Neil wth is going on?
# This bs_user defuses that situation so we can test the code.
self.bs_user = User.objects.create()
@@ -53,6 +53,7 @@ class TestDataUpdates(TestCase):
self.user1.save()
# User with a first name but no last name
+ self.user2.first_name = "First name but no last name"
self.user2.last_name = ""
self.user2.save()
@@ -61,12 +62,6 @@ class TestDataUpdates(TestCase):
self.user3.last_name = "An existing last name"
self.user3.save()
- # Unlinked user
- # To make this test useful, we will set the last_name to ""
- self.userX.first_name = "Unlinked user's first name"
- self.userX.last_name = ""
- self.userX.save()
-
# Call the parent method the same way we do it in the script
skipped_contacts = []
eligible_users = []
@@ -87,14 +82,17 @@ class TestDataUpdates(TestCase):
self.user1.refresh_from_db()
self.user2.refresh_from_db()
self.user3.refresh_from_db()
- self.userX.refresh_from_db()
# Asserts
+ # The user that has no first and last names will get them from the contact
self.assertEqual(self.user1.first_name, "first1")
self.assertEqual(self.user1.last_name, "last1")
- self.assertEqual(self.user2.first_name, "first2")
- self.assertEqual(self.user2.last_name, "last2")
+ # The user that has a first but no last will be left alone
+ self.assertEqual(self.user2.first_name, "First name but no last name")
+ self.assertEqual(self.user2.last_name, "")
+ # The user that has a first and a last will be left alone
self.assertEqual(self.user3.first_name, "An existing first name")
self.assertEqual(self.user3.last_name, "An existing last name")
- self.assertEqual(self.userX.first_name, "Unlinked user's first name")
- self.assertEqual(self.userX.last_name, "")
+ # The unlinked user will be left alone
+ self.assertEqual(self.user4.first_name, "")
+ self.assertEqual(self.user4.last_name, "")
From 5254b7cda8818b41b90c7726ae71174673604f77 Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Tue, 12 Dec 2023 19:27:45 -0500
Subject: [PATCH 18/40] Logger cleanup
---
src/djangooidc/oidc.py | 16 ++++++++--------
src/djangooidc/views.py | 14 --------------
2 files changed, 8 insertions(+), 22 deletions(-)
diff --git a/src/djangooidc/oidc.py b/src/djangooidc/oidc.py
index 331490cae..87592d8e1 100644
--- a/src/djangooidc/oidc.py
+++ b/src/djangooidc/oidc.py
@@ -87,7 +87,7 @@ class Client(oic.Client):
extra_args=None,
):
"""Step 2: Construct a login URL at OP's domain and send the user to it."""
- logger.info("create_authn_request() Creating the OpenID Connect authn request...")
+ logger.debug("create_authn_request() Creating the OpenID Connect authn request...")
state = rndstr(size=32)
try:
session["state"] = state
@@ -112,7 +112,7 @@ class Client(oic.Client):
logger.error("Failed to assemble request arguments for %s" % state)
raise o_e.InternalError(locator=state)
- logger.info("request args: %s" % request_args)
+ logger.debug("request args: %s" % request_args)
try:
# prepare the request for sending
@@ -126,9 +126,9 @@ class Client(oic.Client):
method="GET",
request_args=request_args,
)
- logger.info("body: %s" % body)
- logger.info("URL: %s" % url)
- logger.info("headers: %s" % headers)
+ logger.debug("body: %s" % body)
+ logger.debug("URL: %s" % url)
+ logger.debug("headers: %s" % headers)
except Exception as err:
logger.error(err)
logger.error("Failed to prepare request for %s" % state)
@@ -150,7 +150,7 @@ class Client(oic.Client):
def callback(self, unparsed_response, session):
"""Step 3: Receive OP's response, request an access token, and user info."""
- logger.info("callback() Processing the OpenID Connect callback response...")
+ logger.debug("callback() Processing the OpenID Connect callback response...")
state = session.get("state", "")
try:
# parse the response from OP
@@ -174,7 +174,7 @@ class Client(oic.Client):
logger.error("Unable to process response %s for %s" % (error, state))
raise o_e.AuthenticationFailed(locator=state)
- logger.info("callback() authn_response %s" % authn_response)
+ logger.debug("callback() authn_response %s" % authn_response)
if not authn_response.get("state", None):
logger.error("State value not received from OP for %s" % state)
@@ -213,7 +213,7 @@ class Client(oic.Client):
logger.error("Unable to get user info (%s) for %s" % (info_response.get("error", ""), state))
raise o_e.AuthenticationFailed(locator=state)
- logger.info("_get_user_info() user info: %s" % info_response)
+ logger.debug("_get_user_info() user info: %s" % info_response)
return info_response.to_dict()
diff --git a/src/djangooidc/views.py b/src/djangooidc/views.py
index a39da68aa..e7151d8a3 100644
--- a/src/djangooidc/views.py
+++ b/src/djangooidc/views.py
@@ -72,23 +72,9 @@ def login_callback(request):
# test for need for identity verification and if it is satisfied
# if not satisfied, redirect user to login with stepped up acr_value
if requires_step_up_auth(userinfo):
- logger.info("login_callback() calls get_step_up_acr_value and create_authn_request in oidc")
# add acr_value to request.session
request.session["acr_value"] = CLIENT.get_step_up_acr_value()
return CLIENT.create_authn_request(request.session)
-
- logger.info(f"login_callback() before calling authenticate: {userinfo}")
-
- try:
- user_in_db = User.objects.get(username=userinfo["sub"])
-
- if user_in_db:
- logger.info(
- f"This user exists in the DB (before authenticate): {user_in_db.first_name} {user_in_db.last_name}"
- )
- except:
- pass
-
user = authenticate(request=request, **userinfo)
if user:
login(request, user)
From 2f47272fff93c4cf03ad1c97bb67cc0dbde1cc38 Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Tue, 12 Dec 2023 19:28:38 -0500
Subject: [PATCH 19/40] Logger cleanup
---
src/djangooidc/views.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/djangooidc/views.py b/src/djangooidc/views.py
index e7151d8a3..b5905df48 100644
--- a/src/djangooidc/views.py
+++ b/src/djangooidc/views.py
@@ -58,7 +58,6 @@ def openid(request):
request.session["next"] = request.GET.get("next", "/")
try:
- logger.info("openid() calls create_authn_request in oidc")
return CLIENT.create_authn_request(request.session)
except Exception as err:
return error_page(request, err)
From 18e215dd084f2837cf1f536037a7d64a87312a73 Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Tue, 12 Dec 2023 19:31:09 -0500
Subject: [PATCH 20/40] Logger cleanup
---
src/djangooidc/oidc.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/djangooidc/oidc.py b/src/djangooidc/oidc.py
index 87592d8e1..0c5f2c637 100644
--- a/src/djangooidc/oidc.py
+++ b/src/djangooidc/oidc.py
@@ -87,7 +87,7 @@ class Client(oic.Client):
extra_args=None,
):
"""Step 2: Construct a login URL at OP's domain and send the user to it."""
- logger.debug("create_authn_request() Creating the OpenID Connect authn request...")
+ logger.debug("Creating the OpenID Connect authn request...")
state = rndstr(size=32)
try:
session["state"] = state
@@ -150,7 +150,7 @@ class Client(oic.Client):
def callback(self, unparsed_response, session):
"""Step 3: Receive OP's response, request an access token, and user info."""
- logger.debug("callback() Processing the OpenID Connect callback response...")
+ logger.debug("Processing the OpenID Connect callback response...")
state = session.get("state", "")
try:
# parse the response from OP
@@ -174,7 +174,7 @@ class Client(oic.Client):
logger.error("Unable to process response %s for %s" % (error, state))
raise o_e.AuthenticationFailed(locator=state)
- logger.debug("callback() authn_response %s" % authn_response)
+ logger.debug("authn_response %s" % authn_response)
if not authn_response.get("state", None):
logger.error("State value not received from OP for %s" % state)
@@ -213,7 +213,7 @@ class Client(oic.Client):
logger.error("Unable to get user info (%s) for %s" % (info_response.get("error", ""), state))
raise o_e.AuthenticationFailed(locator=state)
- logger.debug("_get_user_info() user info: %s" % info_response)
+ logger.debug("user info: %s" % info_response)
return info_response.to_dict()
From 7c5eb79f0a2a2b38948f191d3e711554bfb90095 Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Tue, 12 Dec 2023 19:31:36 -0500
Subject: [PATCH 21/40] cleanup
---
src/djangooidc/oidc.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/djangooidc/oidc.py b/src/djangooidc/oidc.py
index 0c5f2c637..91bfddc66 100644
--- a/src/djangooidc/oidc.py
+++ b/src/djangooidc/oidc.py
@@ -214,7 +214,6 @@ class Client(oic.Client):
raise o_e.AuthenticationFailed(locator=state)
logger.debug("user info: %s" % info_response)
-
return info_response.to_dict()
def _request_token(self, state, code, session):
From a422a7cbdb8e00e0ff796cddb71774666e51b7e2 Mon Sep 17 00:00:00 2001
From: Kristina Yin
Date: Tue, 12 Dec 2023 16:33:05 -0800
Subject: [PATCH 22/40] updated python test since first line of ao_example.html
was changed
---
src/registrar/tests/test_views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py
index 3c98c5fe7..da6fe6205 100644
--- a/src/registrar/tests/test_views.py
+++ b/src/registrar/tests/test_views.py
@@ -804,7 +804,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
# ---- AO CONTACT PAGE ----
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
ao_page = org_contact_result.follow()
- self.assertContains(ao_page, "Domain requests from executive branch agencies")
+ self.assertContains(ao_page, "Executive branch federal agencies")
# Go back to organization type page and change type
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
From 9b90172cd31b59d752ad5c6856a2e0eaf69acc0b Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Tue, 12 Dec 2023 20:26:46 -0500
Subject: [PATCH 23/40] lint
---
src/djangooidc/tests/test_backends.py | 2 --
.../commands/copy_names_from_contacts_to_users.py | 6 +++---
.../tests/test_copy_names_from_contacts_to_users.py | 2 +-
3 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/src/djangooidc/tests/test_backends.py b/src/djangooidc/tests/test_backends.py
index 93dc6e68a..ac7f74903 100644
--- a/src/djangooidc/tests/test_backends.py
+++ b/src/djangooidc/tests/test_backends.py
@@ -1,6 +1,4 @@
from django.test import TestCase
-from django.contrib.auth import get_user_model
-from django.utils import timezone
from registrar.models import User
from ..backends import OpenIdConnectBackend # Adjust the import path based on your project structure
diff --git a/src/registrar/management/commands/copy_names_from_contacts_to_users.py b/src/registrar/management/commands/copy_names_from_contacts_to_users.py
index f4849b2bd..c5e2364e9 100644
--- a/src/registrar/management/commands/copy_names_from_contacts_to_users.py
+++ b/src/registrar/management/commands/copy_names_from_contacts_to_users.py
@@ -71,10 +71,10 @@ class Command(BaseCommand):
======= DEBUG OUTPUT =======
Users who have a linked contact:
{eligible_users}
-
+
Processed users (users who have a linked contact and a missing first or last name):
{processed_users}
-
+
===== SKIPPED CONTACTS =====
{skipped_contacts}
@@ -131,7 +131,7 @@ class Command(BaseCommand):
!!! ERROR: An exception occured in the
User table for the following user:
{contact.email} {contact.first_name} {contact.last_name}
-
+
Exception is: {E}
----------TERMINATING----------"""
)
diff --git a/src/registrar/tests/test_copy_names_from_contacts_to_users.py b/src/registrar/tests/test_copy_names_from_contacts_to_users.py
index 4a57ec19b..2690578e0 100644
--- a/src/registrar/tests/test_copy_names_from_contacts_to_users.py
+++ b/src/registrar/tests/test_copy_names_from_contacts_to_users.py
@@ -11,7 +11,7 @@ from registrar.management.commands.copy_names_from_contacts_to_users import Comm
class TestDataUpdates(TestCase):
def setUp(self):
"""We cannot setup the user details because contacts will override the first and last names in its save method
- so we will initiate the users, setup the contacts and link them, and leave the rest of the setup for the test(s).
+ so we will initiate the users, setup the contacts and link them, and leave the rest of the setup to the test(s).
"""
self.user1 = User.objects.create(username="user1")
From 2c5d6c8488ef4da4789784e1e9083bd29dc652a5 Mon Sep 17 00:00:00 2001
From: Kristina Yin
Date: Tue, 12 Dec 2023 17:34:09 -0800
Subject: [PATCH 24/40] accidently mixing up special district and school
district descriptions
---
src/registrar/templates/includes/ao_example.html | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/registrar/templates/includes/ao_example.html b/src/registrar/templates/includes/ao_example.html
index e0821fd39..976deb34d 100644
--- a/src/registrar/templates/includes/ao_example.html
+++ b/src/registrar/templates/includes/ao_example.html
@@ -38,11 +38,12 @@
{% elif organization_type == 'school_district' %}
School districts
- Domain requests from school district governments must be authorized by the highest-ranking executive (the chair of a school district’s board or a superintendent).
+ Domain requests from school district governments must be authorized by someone in a role of significant, executive responsibility within the district (board chair, superintendent, senior technology officer, or equivalent).
{% elif organization_type == 'special_district' %}
Special districts
- Domain requests from school district governments must be authorized by someone in a role of significant, executive responsibility within the district (board chair, superintendent, senior technology officer, or equivalent).
+ Domain requests from special districts must be authorized by someone in a role of significant, executive responsibility within the district (CEO, chair, executive director, senior technology officer, or equivalent).
+
{% elif organization_type == 'state_or_territory' %}
U.S. states and territories
From 600f326be23639ee3ee9e02a812846c7dadd5a8d Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Tue, 12 Dec 2023 21:13:42 -0500
Subject: [PATCH 25/40] Make username in dummy_user unique and comment out
parts in test_alphabetically_sorted_fk_fields_domain_application and
test_alphabetically_sorted_fk_fields_domain_information that now fail after
that edit
---
src/registrar/tests/common.py | 21 +++++++++++----------
src/registrar/tests/test_admin.py | 6 +++---
2 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py
index f17e0f9fa..347781ef8 100644
--- a/src/registrar/tests/common.py
+++ b/src/registrar/tests/common.py
@@ -5,6 +5,7 @@ import logging
from contextlib import contextmanager
import random
from string import ascii_uppercase
+import uuid
from django.test import TestCase
from unittest.mock import MagicMock, Mock, patch
from typing import List, Dict
@@ -155,16 +156,6 @@ class AuditedAdminMockData:
APPLICATION = "application"
INVITATION = "invitation"
- def dummy_user(self, item_name, short_hand):
- """Creates a dummy user object,
- but with a shorthand and support for multiple"""
- user = User.objects.get_or_create(
- first_name="{} first_name:{}".format(item_name, short_hand),
- last_name="{} last_name:{}".format(item_name, short_hand),
- username="{} username:{}".format(item_name, short_hand),
- )[0]
- return user
-
def dummy_contact(self, item_name, short_hand):
"""Creates a dummy contact object"""
contact = Contact.objects.get_or_create(
@@ -175,6 +166,16 @@ class AuditedAdminMockData:
phone="(555) 555 5555",
)[0]
return contact
+
+ def dummy_user(self, item_name, short_hand):
+ """Creates a dummy user object,
+ but with a shorthand and support for multiple"""
+ user = User.objects.get_or_create(
+ first_name="{} first_name:{}".format(item_name, short_hand),
+ last_name="{} last_name:{}".format(item_name, short_hand),
+ username="{} username:{}".format(item_name, str(uuid.uuid4())[:8]),
+ )[0]
+ return user
def dummy_draft_domain(self, item_name, prebuilt=False):
"""
diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py
index 526a9ea2a..82af3573c 100644
--- a/src/registrar/tests/test_admin.py
+++ b/src/registrar/tests/test_admin.py
@@ -1108,8 +1108,8 @@ class AuditedAdminTest(TestCase):
tested_fields = [
DomainApplication.authorizing_official.field,
DomainApplication.submitter.field,
- DomainApplication.investigator.field,
- DomainApplication.creator.field,
+ # DomainApplication.investigator.field,
+ # DomainApplication.creator.field,
DomainApplication.requested_domain.field,
]
@@ -1164,7 +1164,7 @@ class AuditedAdminTest(TestCase):
tested_fields = [
DomainInformation.authorizing_official.field,
DomainInformation.submitter.field,
- DomainInformation.creator.field,
+ # DomainInformation.creator.field,
(DomainInformation.domain.field, ["name"]),
(DomainInformation.domain_application.field, ["requested_domain__name"]),
]
From 3bd9a2fbaa845443b9a0555d3cd37f6a628e1e29 Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Tue, 12 Dec 2023 21:25:36 -0500
Subject: [PATCH 26/40] lint
---
src/registrar/tests/common.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py
index 347781ef8..4535084af 100644
--- a/src/registrar/tests/common.py
+++ b/src/registrar/tests/common.py
@@ -166,7 +166,7 @@ class AuditedAdminMockData:
phone="(555) 555 5555",
)[0]
return contact
-
+
def dummy_user(self, item_name, short_hand):
"""Creates a dummy user object,
but with a shorthand and support for multiple"""
From ed5d4fc4c3126dd2f5dabfdccaa9bdb7a56496b0 Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Tue, 12 Dec 2023 21:28:14 -0500
Subject: [PATCH 27/40] cleanup
---
src/registrar/tests/common.py | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py
index 4535084af..87bad0dfb 100644
--- a/src/registrar/tests/common.py
+++ b/src/registrar/tests/common.py
@@ -156,6 +156,16 @@ class AuditedAdminMockData:
APPLICATION = "application"
INVITATION = "invitation"
+ def dummy_user(self, item_name, short_hand):
+ """Creates a dummy user object,
+ but with a shorthand and support for multiple"""
+ user = User.objects.get_or_create(
+ first_name="{} first_name:{}".format(item_name, short_hand),
+ last_name="{} last_name:{}".format(item_name, short_hand),
+ username="{} username:{}".format(item_name, str(uuid.uuid4())[:8]),
+ )[0]
+ return user
+
def dummy_contact(self, item_name, short_hand):
"""Creates a dummy contact object"""
contact = Contact.objects.get_or_create(
@@ -167,16 +177,6 @@ class AuditedAdminMockData:
)[0]
return contact
- def dummy_user(self, item_name, short_hand):
- """Creates a dummy user object,
- but with a shorthand and support for multiple"""
- user = User.objects.get_or_create(
- first_name="{} first_name:{}".format(item_name, short_hand),
- last_name="{} last_name:{}".format(item_name, short_hand),
- username="{} username:{}".format(item_name, str(uuid.uuid4())[:8]),
- )[0]
- return user
-
def dummy_draft_domain(self, item_name, prebuilt=False):
"""
Creates a dummy DraftDomain object
From 2497b00fa3b40369e910c400c11d457b877a9f27 Mon Sep 17 00:00:00 2001
From: Kristina Yin
Date: Tue, 12 Dec 2023 19:58:23 -0800
Subject: [PATCH 28/40] missed part of Tribal AO ex description
---
src/registrar/templates/includes/ao_example.html | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/registrar/templates/includes/ao_example.html b/src/registrar/templates/includes/ao_example.html
index 976deb34d..38bbe20ff 100644
--- a/src/registrar/templates/includes/ao_example.html
+++ b/src/registrar/templates/includes/ao_example.html
@@ -57,5 +57,6 @@
{% elif organization_type == 'tribal' %}
Tribal governments
Domain requests from federally recognized tribal governments must be authorized by the tribal leader the Bureau of Indian Affairs recognizes.
+ Domain requests from state-recognized tribal governments must be authorized by the tribal leader the individual state recognizes.
{% endif %}
From e70324443183807bd75f42cd62843468824a5c2c Mon Sep 17 00:00:00 2001
From: Michelle Rago <60157596+michelle-rago@users.noreply.github.com>
Date: Wed, 13 Dec 2023 09:26:37 -0500
Subject: [PATCH 29/40] Hyphenated "federally-recognized"
---
src/registrar/templates/includes/ao_example.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/registrar/templates/includes/ao_example.html b/src/registrar/templates/includes/ao_example.html
index 38bbe20ff..be5f4624d 100644
--- a/src/registrar/templates/includes/ao_example.html
+++ b/src/registrar/templates/includes/ao_example.html
@@ -56,7 +56,7 @@
{% elif organization_type == 'tribal' %}
Tribal governments
- Domain requests from federally recognized tribal governments must be authorized by the tribal leader the Bureau of Indian Affairs recognizes.
+ Domain requests from federally-recognized tribal governments must be authorized by the tribal leader the Bureau of Indian Affairs recognizes.
Domain requests from state-recognized tribal governments must be authorized by the tribal leader the individual state recognizes.
{% endif %}
From d89ace47ba2f7c35c371502620f4cdd8403d0a38 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 13 Dec 2023 09:53:12 -0700
Subject: [PATCH 30/40] Fix migration weirdness
---
...ter_domain_state_alter_domainapplication_status_and_more.py} | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
rename src/registrar/migrations/{0055_alter_domain_state_alter_domainapplication_status_and_more.py => 0056_alter_domain_state_alter_domainapplication_status_and_more.py} (96%)
diff --git a/src/registrar/migrations/0055_alter_domain_state_alter_domainapplication_status_and_more.py b/src/registrar/migrations/0056_alter_domain_state_alter_domainapplication_status_and_more.py
similarity index 96%
rename from src/registrar/migrations/0055_alter_domain_state_alter_domainapplication_status_and_more.py
rename to src/registrar/migrations/0056_alter_domain_state_alter_domainapplication_status_and_more.py
index 9b6bac48c..097cddf8a 100644
--- a/src/registrar/migrations/0055_alter_domain_state_alter_domainapplication_status_and_more.py
+++ b/src/registrar/migrations/0056_alter_domain_state_alter_domainapplication_status_and_more.py
@@ -6,7 +6,7 @@ import django_fsm
class Migration(migrations.Migration):
dependencies = [
- ("registrar", "0054_alter_domainapplication_federal_agency_and_more"),
+ ("registrar", "0055_transitiondomain_processed"),
]
operations = [
From d2242c7667720ce5108d88c3536ecde11dd2e66d Mon Sep 17 00:00:00 2001
From: Kristina Yin
Date: Wed, 13 Dec 2023 10:04:05 -0800
Subject: [PATCH 31/40] made sure external link icon shows up
---
src/registrar/templates/includes/ao_example.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/registrar/templates/includes/ao_example.html b/src/registrar/templates/includes/ao_example.html
index be5f4624d..85df174aa 100644
--- a/src/registrar/templates/includes/ao_example.html
+++ b/src/registrar/templates/includes/ao_example.html
@@ -2,7 +2,7 @@
{% if federal_type == 'executive' %}
Executive branch federal agencies
Domain requests from executive branch federal agencies must be authorized by the agency's CIO or the head of the agency.
- See OMB Memorandum M-23-10 for more information.
+ See OMB Memorandum M-23-10 for more information.
{% elif federal_type == 'judicial' %}
Judicial branch federal agencies
@@ -56,7 +56,7 @@
{% elif organization_type == 'tribal' %}
Tribal governments
- Domain requests from federally-recognized tribal governments must be authorized by the tribal leader the Bureau of Indian Affairs recognizes.
+ Domain requests from federally-recognized tribal governments must be authorized by the tribal leader the Bureau of Indian Affairs recognizes.
Domain requests from state-recognized tribal governments must be authorized by the tribal leader the individual state recognizes.
{% endif %}
From 4adeb6722b9611b294c5ccec8a3edcdce15f00a0 Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Wed, 13 Dec 2023 15:13:04 -0500
Subject: [PATCH 32/40] Revisions and clean up based on PR comments
---
src/djangooidc/backends.py | 5 ++--
.../copy_names_from_contacts_to_users.py | 28 ++++++-------------
src/registrar/tests/common.py | 2 +-
3 files changed, 12 insertions(+), 23 deletions(-)
diff --git a/src/djangooidc/backends.py b/src/djangooidc/backends.py
index cf326eca4..7a192e4a5 100644
--- a/src/djangooidc/backends.py
+++ b/src/djangooidc/backends.py
@@ -65,8 +65,9 @@ class OpenIdConnectBackend(ModelBackend):
return user
def update_existing_user(self, user, kwargs):
- # Update other fields without overwriting first_name and last_name.
- # Overwrite first_name and last_name if not empty string
+ """Update other fields without overwriting first_name and last_name.
+ Overwrite first_name and last_name if not empty string"""
+
for key, value in kwargs.items():
# Check if the key is not first_name or last_name or value is not empty string
if key not in ["first_name", "last_name"] or value:
diff --git a/src/registrar/management/commands/copy_names_from_contacts_to_users.py b/src/registrar/management/commands/copy_names_from_contacts_to_users.py
index c5e2364e9..6d089f721 100644
--- a/src/registrar/management/commands/copy_names_from_contacts_to_users.py
+++ b/src/registrar/management/commands/copy_names_from_contacts_to_users.py
@@ -17,7 +17,7 @@ logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = """Copy first and last names from a contact to
a related user if it exists and if its first and last name
- properties are null"""
+ properties are null or blank strings."""
# ======================================================
# ===================== ARGUMENTS =====================
@@ -112,12 +112,12 @@ class Command(BaseCommand):
# ---- UPDATE THE USER IF IT DOES NOT HAVE A FIRST AND LAST NAMES
# ---- LET'S KEEP A LIGHT TOUCH
if not eligible_user.first_name and not eligible_user.last_name:
- processed_user = eligible_user
# (expression has type "str | None", variable has type "str | int | Combinable")
# so we'll ignore type
- processed_user.first_name = contact.first_name # type: ignore
- processed_user.last_name = contact.last_name # type: ignore
- processed_user.save()
+ eligible_user.first_name = contact.first_name # type: ignore
+ eligible_user.last_name = contact.last_name # type: ignore
+ eligible_user.save()
+ processed_user = eligible_user
return (
eligible_user,
@@ -147,9 +147,9 @@ class Command(BaseCommand):
def process_contacts(
self,
debug_on,
- skipped_contacts,
- eligible_users,
- processed_users,
+ skipped_contacts=[],
+ eligible_users=[],
+ processed_users=[],
):
for contact in Contact.objects.all():
# DEBUG:
@@ -207,15 +207,6 @@ class Command(BaseCommand):
self.print_debug_mode_statements(debug_on)
- # users we SKIPPED
- skipped_contacts = []
-
- # users we found that are linked to contacts
- eligible_users = []
-
- # users we PROCESSED
- processed_users = []
-
logger.info(
f"""{TerminalColors.OKCYAN}
==========================
@@ -235,9 +226,6 @@ class Command(BaseCommand):
processed_users,
) = self.process_contacts(
debug_on,
- skipped_contacts,
- eligible_users,
- processed_users,
)
self.print_summary_of_findings(
diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py
index 87bad0dfb..443c05ee3 100644
--- a/src/registrar/tests/common.py
+++ b/src/registrar/tests/common.py
@@ -162,7 +162,7 @@ class AuditedAdminMockData:
user = User.objects.get_or_create(
first_name="{} first_name:{}".format(item_name, short_hand),
last_name="{} last_name:{}".format(item_name, short_hand),
- username="{} username:{}".format(item_name, str(uuid.uuid4())[:8]),
+ username="{} username:{}".format(item_name + str(uuid.uuid4())[:8], short_hand),
)[0]
return user
From 9c8ed1682bcc78e3f239b765b88b20020878f140 Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Wed, 13 Dec 2023 15:41:18 -0500
Subject: [PATCH 33/40] linter
---
src/djangooidc/backends.py | 2 +-
src/registrar/tests/common.py | 4 ++--
src/registrar/tests/test_admin.py | 4 ++--
src/registrar/tests/test_models.py | 10 +++++-----
4 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/djangooidc/backends.py b/src/djangooidc/backends.py
index 7a192e4a5..b0e6417db 100644
--- a/src/djangooidc/backends.py
+++ b/src/djangooidc/backends.py
@@ -67,7 +67,7 @@ class OpenIdConnectBackend(ModelBackend):
def update_existing_user(self, user, kwargs):
"""Update other fields without overwriting first_name and last_name.
Overwrite first_name and last_name if not empty string"""
-
+
for key, value in kwargs.items():
# Check if the key is not first_name or last_name or value is not empty string
if key not in ["first_name", "last_name"] or value:
diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py
index 443c05ee3..ec09b45d0 100644
--- a/src/registrar/tests/common.py
+++ b/src/registrar/tests/common.py
@@ -406,8 +406,8 @@ def mock_user():
"""A simple user."""
user_kwargs = dict(
id=4,
- first_name="Rachid",
- last_name="Mrad",
+ first_name="Jeff",
+ last_name="Lebowski",
)
mock_user, _ = User.objects.get_or_create(**user_kwargs)
return mock_user
diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py
index 82af3573c..8cca67d1e 100644
--- a/src/registrar/tests/test_admin.py
+++ b/src/registrar/tests/test_admin.py
@@ -1021,7 +1021,7 @@ class ListHeaderAdminTest(TestCase):
# Set the GET parameters for testing
request.GET = {
"status": "started",
- "investigator": "Rachid Mrad",
+ "investigator": "Jeff Lebowski",
"q": "search_value",
}
# Call the get_filters method
@@ -1032,7 +1032,7 @@ class ListHeaderAdminTest(TestCase):
filters,
[
{"parameter_name": "status", "parameter_value": "started"},
- {"parameter_name": "investigator", "parameter_value": "Rachid Mrad"},
+ {"parameter_name": "investigator", "parameter_value": "Jeff Lebowski"},
],
)
diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py
index dfab4f9a3..d9090c287 100644
--- a/src/registrar/tests/test_models.py
+++ b/src/registrar/tests/test_models.py
@@ -659,7 +659,7 @@ class TestUser(TestCase):
class TestContact(TestCase):
def setUp(self):
self.email = "mayor@igorville.gov"
- self.user, _ = User.objects.get_or_create(email=self.email, first_name="Rachid", last_name="Mrad")
+ self.user, _ = User.objects.get_or_create(email=self.email, first_name="Jeff", last_name="Lebowski")
self.contact, _ = Contact.objects.get_or_create(user=self.user)
def tearDown(self):
@@ -670,10 +670,10 @@ class TestContact(TestCase):
def test_saving_contact_updates_user_first_last_names(self):
"""When a contact is updated, we propagate the changes to the linked user if it exists."""
# User and Contact are created and linked as expected
- self.assertEqual(self.contact.first_name, "Rachid")
- self.assertEqual(self.contact.last_name, "Mrad")
- self.assertEqual(self.user.first_name, "Rachid")
- self.assertEqual(self.user.last_name, "Mrad")
+ self.assertEqual(self.contact.first_name, "Jeff")
+ self.assertEqual(self.contact.last_name, "Lebowski")
+ self.assertEqual(self.user.first_name, "Jeff")
+ self.assertEqual(self.user.last_name, "Lebowski")
self.contact.first_name = "Joey"
self.contact.last_name = "Baloney"
From 52220d0a9e8cc9533d0ad79b66827ee30e07d2f1 Mon Sep 17 00:00:00 2001
From: Michelle Rago <60157596+michelle-rago@users.noreply.github.com>
Date: Wed, 13 Dec 2023 17:13:33 -0500
Subject: [PATCH 34/40] Changed DNS sidebar nav / Put DNS options in a list on
DNS landing page (#1389)
* Changed domain management sidebar nav for DNS items
* Bulleted DNS options on DNS landing page
* Remove "DNS" from "DNS name servers"
* Remove "DNS" from "DNS name servers"
---
src/registrar/templates/domain_dns.html | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/registrar/templates/domain_dns.html b/src/registrar/templates/domain_dns.html
index 85ba02e16..0f625e0e3 100644
--- a/src/registrar/templates/domain_dns.html
+++ b/src/registrar/templates/domain_dns.html
@@ -12,9 +12,11 @@
You can enter your name servers, as well as other DNS-related information, in the following sections:
{% url 'domain-dns-nameservers' pk=domain.id as url %}
- DNS name servers
+
{% endblock %} {# domain_content #}
From e36351d21ef5d60be8a4d96579b4dfd9c446bd71 Mon Sep 17 00:00:00 2001
From: David Kennedy
Date: Thu, 14 Dec 2023 07:44:31 -0500
Subject: [PATCH 35/40] lowercase emails on add user to domain; case
insensitive match on matching DomainInvitations on login; test cases
---
src/registrar/forms/domain.py | 11 +++++++++++
src/registrar/models/user.py | 2 +-
src/registrar/tests/test_models.py | 14 ++++++++++++++
src/registrar/tests/test_views.py | 26 ++++++++++++++++++++++++++
4 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/src/registrar/forms/domain.py b/src/registrar/forms/domain.py
index 8b55aa29d..ac96393b4 100644
--- a/src/registrar/forms/domain.py
+++ b/src/registrar/forms/domain.py
@@ -28,6 +28,17 @@ class DomainAddUserForm(forms.Form):
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):
"""Form for changing nameservers."""
diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py
index ec2d06c70..d79e4c9ee 100644
--- a/src/registrar/models/user.py
+++ b/src/registrar/models/user.py
@@ -101,7 +101,7 @@ class User(AbstractUser):
"""When a user first arrives on the site, we need to retrieve any domain
invitations that match their email address."""
for invitation in DomainInvitation.objects.filter(
- email=self.email, status=DomainInvitation.DomainInvitationStatus.INVITED
+ email__iexact=self.email, status=DomainInvitation.DomainInvitationStatus.INVITED
):
try:
invitation.retrieve()
diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py
index 83126ab7c..ca221a18f 100644
--- a/src/registrar/tests/test_models.py
+++ b/src/registrar/tests/test_models.py
@@ -654,3 +654,17 @@ class TestUser(TestCase):
"""A new user who's neither transitioned nor invited should
return True when tested with class method needs_identity_verification"""
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
diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py
index da6fe6205..89ef0e35e 100644
--- a/src/registrar/tests/test_views.py
+++ b/src/registrar/tests/test_views.py
@@ -1372,6 +1372,32 @@ class TestDomainManagers(TestDomainOverview):
self.assertContains(success_page, "Cancel") # link to cancel invitation
self.assertTrue(DomainInvitation.objects.filter(email=EMAIL).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 = "mayor@igorville.gov"
+ CAPS_EMAIL = "MAYOR@igorville.gov"
+ User.objects.filter(email=EMAIL).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
+ 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)
+ self.assertContains(success_page, "Cancel") # link to cancel invitation
+ self.assertTrue(DomainInvitation.objects.filter(email=EMAIL).exists())
+
@boto3_mocking.patching
def test_domain_invitation_email_sent(self):
"""Inviting a non-existent user sends them an email."""
From 7b7653ac9822ea175d54292780f2360cb81bf928 Mon Sep 17 00:00:00 2001
From: Katherine-Osos <119689946+Katherine-Osos@users.noreply.github.com>
Date: Thu, 14 Dec 2023 09:55:32 -0600
Subject: [PATCH 36/40] Wording updates
---
src/registrar/templates/home.html | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/registrar/templates/home.html b/src/registrar/templates/home.html
index 8e056924f..362b14f18 100644
--- a/src/registrar/templates/home.html
+++ b/src/registrar/templates/home.html
@@ -87,7 +87,7 @@
aria-live="polite"
>
{% else %}
- You don't have any registered domains yet
+ You don't have any registered domains.
{% endif %}
@@ -95,7 +95,7 @@
Domain requests
{% if domain_applications %}
- Your domain applications
+ Your domain requests
Domain name |
@@ -138,7 +138,7 @@
aria-live="polite"
>
{% else %}
- You don't have any domain requests yet
+ You haven't requested any domains.
{% endif %}
From 7e4f500f941f640b7b39193e55796b337d49f7e6 Mon Sep 17 00:00:00 2001
From: David Kennedy
Date: Thu, 14 Dec 2023 11:06:43 -0500
Subject: [PATCH 37/40] updated formatting and variable names in tests
---
src/registrar/tests/test_models.py | 6 ++--
src/registrar/tests/test_views.py | 46 +++++++++++++++---------------
2 files changed, 26 insertions(+), 26 deletions(-)
diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py
index ca221a18f..8642b1d15 100644
--- a/src/registrar/tests/test_models.py
+++ b/src/registrar/tests/test_models.py
@@ -660,11 +660,11 @@ class TestUser(TestCase):
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"
+ 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)
+ 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
+ save_mock.assert_called_once()
diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py
index 89ef0e35e..17641636e 100644
--- a/src/registrar/tests/test_views.py
+++ b/src/registrar/tests/test_views.py
@@ -1355,22 +1355,22 @@ class TestDomainManagers(TestDomainOverview):
out the boto3 SES email sending here.
"""
# make sure there is no user with this email
- EMAIL = "mayor@igorville.gov"
- User.objects.filter(email=EMAIL).delete()
+ 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"] = EMAIL
+ add_page.form["email"] = 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)
+ self.assertContains(success_page, email_address)
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):
@@ -1380,30 +1380,30 @@ class TestDomainManagers(TestDomainOverview):
out the boto3 SES email sending here.
"""
# make sure there is no user with this email
- EMAIL = "mayor@igorville.gov"
- CAPS_EMAIL = "MAYOR@igorville.gov"
- User.objects.filter(email=EMAIL).delete()
+ 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
+ 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)
+ self.assertContains(success_page, email_address)
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_email_sent(self):
"""Inviting a non-existent user sends them an email."""
# make sure there is no user with this email
- EMAIL = "mayor@igorville.gov"
- User.objects.filter(email=EMAIL).delete()
+ 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)
@@ -1412,28 +1412,28 @@ class TestDomainManagers(TestDomainOverview):
with boto3_mocking.clients.handler_for("sesv2", mock_client):
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"] = EMAIL
+ add_page.form["email"] = email_address
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
add_page.form.submit()
# check the mock instance to see if `send_email` was called right
mock_client_instance.send_email.assert_called_once_with(
FromEmailAddress=settings.DEFAULT_FROM_EMAIL,
- Destination={"ToAddresses": [EMAIL]},
+ Destination={"ToAddresses": [email_address]},
Content=ANY,
)
def test_domain_invitation_cancel(self):
"""Posting to the delete view deletes an invitation."""
- EMAIL = "mayor@igorville.gov"
- invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=EMAIL)
+ email_address = "mayor@igorville.gov"
+ invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=email_address)
self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id}))
with self.assertRaises(DomainInvitation.DoesNotExist):
DomainInvitation.objects.get(id=invitation.id)
def test_domain_invitation_cancel_no_permissions(self):
"""Posting to the delete view as a different user should fail."""
- EMAIL = "mayor@igorville.gov"
- invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=EMAIL)
+ email_address = "mayor@igorville.gov"
+ invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=email_address)
other_user = User()
other_user.save()
@@ -1445,20 +1445,20 @@ class TestDomainManagers(TestDomainOverview):
@boto3_mocking.patching
def test_domain_invitation_flow(self):
"""Send an invitation to a new user, log in and load the dashboard."""
- EMAIL = "mayor@igorville.gov"
- User.objects.filter(email=EMAIL).delete()
+ email_address = "mayor@igorville.gov"
+ User.objects.filter(email=email_address).delete()
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)
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)
add_page.form.submit()
# 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`
self.app.set_user(new_user.username)
# and manually call the on each login callback
From 7bcf148453f204491bee8f3c8a170952e057e9ec Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Thu, 14 Dec 2023 13:11:01 -0500
Subject: [PATCH 38/40] cleanup
---
.../commands/copy_names_from_contacts_to_users.py | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/src/registrar/management/commands/copy_names_from_contacts_to_users.py b/src/registrar/management/commands/copy_names_from_contacts_to_users.py
index 6d089f721..50e1bea3d 100644
--- a/src/registrar/management/commands/copy_names_from_contacts_to_users.py
+++ b/src/registrar/management/commands/copy_names_from_contacts_to_users.py
@@ -89,7 +89,8 @@ class Command(BaseCommand):
"""Given a contact with a first_name and last_name, find & update an existing
corresponding user if her first_name and last_name are null.
- Returns the corresponding User object.
+ Returns tuple of eligible (is linked to the contact) and processed
+ (first and last are blank) users.
"""
user_exists = User.objects.filter(contact=contact).exists()
@@ -124,7 +125,7 @@ class Command(BaseCommand):
processed_user,
)
- except Exception as E:
+ except Exception as error:
logger.warning(
f"""
{TerminalColors.FAIL}
@@ -132,7 +133,7 @@ class Command(BaseCommand):
User table for the following user:
{contact.email} {contact.first_name} {contact.last_name}
- Exception is: {E}
+ Exception is: {error}
----------TERMINATING----------"""
)
sys.exit()
@@ -143,7 +144,6 @@ class Command(BaseCommand):
# ================= PROCESS CONTACTS ==================
# ======================================================
- # C901 'Command.handle' is too complex
def process_contacts(
self,
debug_on,
@@ -152,7 +152,6 @@ class Command(BaseCommand):
processed_users=[],
):
for contact in Contact.objects.all():
- # DEBUG:
TerminalHelper.print_conditional(
debug_on,
f"{TerminalColors.OKCYAN}"
@@ -160,7 +159,7 @@ class Command(BaseCommand):
f"{contact.email},"
f" {contact.first_name},"
f" {contact.last_name}"
- f"{TerminalColors.ENDC}", # noqa
+ f"{TerminalColors.ENDC}",
)
# ======================================================
From 04d3b92f9665fc676e3cbdda8f7415de963e697b Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Thu, 14 Dec 2023 13:23:27 -0500
Subject: [PATCH 39/40] fix merge error: misplaced test:
test_check_domain_invitations_on_login_caps_email
---
src/registrar/tests/test_models.py | 27 ++++++++++++++-------------
1 file changed, 14 insertions(+), 13 deletions(-)
diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py
index 3854528b4..51a89a359 100644
--- a/src/registrar/tests/test_models.py
+++ b/src/registrar/tests/test_models.py
@@ -654,6 +654,20 @@ class TestUser(TestCase):
"""A new user who's neither transitioned nor invited should
return True when tested with class method needs_identity_verification"""
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()
class TestContact(TestCase):
@@ -700,16 +714,3 @@ class TestContact(TestCase):
self.assertEqual(self.contact.email, "joey.baloney@diaperville.com")
self.assertEqual(self.user.email, "mayor@igorville.gov")
- 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()
From c02f74cf6741e3b5149d8e9ced4b0e3c9a32ffaa Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Thu, 14 Dec 2023 13:47:20 -0500
Subject: [PATCH 40/40] lint
---
src/registrar/tests/test_models.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py
index 51a89a359..0e0839382 100644
--- a/src/registrar/tests/test_models.py
+++ b/src/registrar/tests/test_models.py
@@ -654,7 +654,7 @@ class TestUser(TestCase):
"""A new user who's neither transitioned nor invited should
return True when tested with class method needs_identity_verification"""
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"""
@@ -713,4 +713,3 @@ class TestContact(TestCase):
# Updating the contact's email does not propagate
self.assertEqual(self.contact.email, "joey.baloney@diaperville.com")
self.assertEqual(self.user.email, "mayor@igorville.gov")
-