From 5cba82b34382175bfd1914ea20f8bd13fac21036 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 18 Apr 2024 15:30:45 -0600 Subject: [PATCH 01/62] Basic logic --- src/registrar/admin.py | 4 +- src/registrar/models/user.py | 99 ++++++++++++++----- .../admin/includes/contact_detail_list.html | 2 +- .../admin/includes/detail_table_fieldset.html | 4 +- 4 files changed, 83 insertions(+), 26 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index b62ea80b8..6869df94a 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -490,7 +490,7 @@ class MyUserAdmin(BaseUserAdmin): fieldsets = ( ( None, - {"fields": ("username", "password", "status")}, + {"fields": ("username", "password", "status", "verification_type")}, ), ("Personal Info", {"fields": ("first_name", "last_name", "email")}), ( @@ -508,6 +508,8 @@ class MyUserAdmin(BaseUserAdmin): ("Important dates", {"fields": ("last_login", "date_joined")}), ) + readonly_fields = ("verification_type") + # Hide Username (uuid), Groups and Permissions # Q: Now that we're using Groups and Permissions, # do we expose those to analysts to view? diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index 2688ef57f..c69672100 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -1,3 +1,4 @@ +from enum import Enum import logging from django.contrib.auth.models import AbstractUser @@ -23,6 +24,16 @@ class User(AbstractUser): but can be customized later. """ + class VerificationTypeChoices(models.TextChoices): + """ + Users achieve access to our system in a few different ways. + These choices reflect those pathways. + """ + GRANDFATHERED = "grandfathered", "Legacy user" + VERIFIED_BY_STAFF = "verified_by_staff", "Verified by staff" + REGULAR = "regular", "Verified by Login.gov" + INVITED = "invited", "Invited by a domain manager" + # #### Constants for choice fields #### RESTRICTED = "restricted" STATUS_CHOICES = ((RESTRICTED, RESTRICTED),) @@ -48,6 +59,13 @@ class User(AbstractUser): db_index=True, ) + verification_type = models.CharField( + choices=VerificationTypeChoices, + null=True, + blank=True, + help_text="The means through which this user was verified", + ) + def __str__(self): # this info is pulled from Login.gov if self.first_name or self.last_name: @@ -95,6 +113,22 @@ class User(AbstractUser): def has_contact_info(self): return bool(self.contact.title or self.contact.email or self.contact.phone) + + @classmethod + def get_existing_user_from_uuid(cls, uuid): + existing_user = None + try: + existing_user = cls.objects.get(username=uuid) + if existing_user and UserDomainRole.objects.filter(user=existing_user).exists(): + return (False, existing_user) + except cls.DoesNotExist: + # Do nothing when the user is not found, as we're checking for existence. + pass + except Exception as err: + raise err + + return (True, existing_user) + @classmethod def needs_identity_verification(cls, email, uuid): """A method used by our oidc classes to test whether a user needs email/uuid verification @@ -102,33 +136,52 @@ class User(AbstractUser): # An existing user who is a domain manager of a domain (that is, # they have an entry in UserDomainRole for their User) - try: - existing_user = cls.objects.get(username=uuid) - if existing_user and UserDomainRole.objects.filter(user=existing_user).exists(): - return False - except cls.DoesNotExist: - # Do nothing when the user is not found, as we're checking for existence. - pass - except Exception as err: - raise err + user_exists, existing_user = cls.existing_user(uuid) + if not user_exists: + return False - # A new incoming user who is a domain manager for one of the domains - # that we inputted from Verisign (that is, their email address appears - # in the username field of a TransitionDomain) + # The user needs identity verification if they don't meet + # any special criteria, i.e. we are validating them "regularly" + existing_user.verification_type = cls.get_verification_type_from_email(email) + return existing_user.verification_type == cls.VerificationTypeChoices.REGULAR + + @classmethod + def get_verification_type_from_email(cls, email, invitation_status=DomainInvitation.DomainInvitationStatus.INVITED): + """Retrieves the verification type based off of a provided email address""" + + verification_type = None if TransitionDomain.objects.filter(username=email).exists(): - return False + # A new incoming user who is a domain manager for one of the domains + # that we inputted from Verisign (that is, their email address appears + # in the username field of a TransitionDomain) + verification_type = cls.VerificationTypeChoices.GRANDFATHERED + elif VerifiedByStaff.objects.filter(email=email).exists(): + # New users flagged by Staff to bypass ial2 + verification_type = cls.VerificationTypeChoices.VERIFIED_BY_STAFF + elif DomainInvitation.objects.filter(email=email, status=invitation_status).exists(): + # A new incoming user who is being invited to be a domain manager (that is, + # their email address is in DomainInvitation for an invitation that is not yet "retrieved"). + verification_type = cls.VerificationTypeChoices.INVITED + else: + verification_type = cls.VerificationTypeChoices.REGULAR + + return verification_type - # New users flagged by Staff to bypass ial2 - if VerifiedByStaff.objects.filter(email=email).exists(): - return False + def user_verification_type(self, check_if_user_exists=False): + if self.verification_type is None: + # Would need to check audit log + retrieved = DomainInvitation.DomainInvitationStatus.RETRIEVED + user_exists, _ = self.existing_user(self.username) + verification_type = self.get_verification_type_from_email(self.email, invitation_status=retrieved) - # A new incoming user who is being invited to be a domain manager (that is, - # their email address is in DomainInvitation for an invitation that is not yet "retrieved"). - invited = DomainInvitation.DomainInvitationStatus.INVITED - if DomainInvitation.objects.filter(email=email, status=invited).exists(): - return False - - return True + # This should check if the type is unknown, use check_if_user_exists? + if verification_type == self.VerificationTypeChoices.REGULAR and not user_exists: + raise ValueError(f"No verification_type was found for {self} with id: {self.pk}") + else: + self.verification_type = verification_type + return self.verification_type + else: + return self.verification_type def check_domain_invitations_on_login(self): """When a user first arrives on the site, we need to retrieve any domain diff --git a/src/registrar/templates/django/admin/includes/contact_detail_list.html b/src/registrar/templates/django/admin/includes/contact_detail_list.html index 0ac9c4c49..5ac5452e3 100644 --- a/src/registrar/templates/django/admin/includes/contact_detail_list.html +++ b/src/registrar/templates/django/admin/includes/contact_detail_list.html @@ -3,7 +3,7 @@
{% if show_formatted_name %} - {% if contact.get_formatted_name %} + {% if user.get_formatted_name %} {{ user.get_formatted_name }}
{% else %} None
diff --git a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html index fb7303352..7b468faec 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -6,7 +6,9 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endcomment %} {% block field_readonly %} {% with all_contacts=original_object.other_contacts.all %} - {% if field.field.name == "other_contacts" %} + {% if field.field.name == "creator" %} +
{{ field.contents }} ({{ user.verification_type }})
+ {% elif field.field.name == "other_contacts" %} {% if all_contacts.count > 2 %}
{% for contact in all_contacts %} From 3e7f143a1e8911f6dd70878a01154a3b8873fcd0 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 19 Apr 2024 15:30:37 -0600 Subject: [PATCH 02/62] Fix verification type --- src/djangooidc/views.py | 3 ++ src/registrar/admin.py | 15 ++++++---- src/registrar/fixtures_users.py | 4 +++ .../migrations/0085_user_verification_type.py | 28 +++++++++++++++++++ src/registrar/models/user.py | 18 ++++++------ 5 files changed, 53 insertions(+), 15 deletions(-) create mode 100644 src/registrar/migrations/0085_user_verification_type.py diff --git a/src/djangooidc/views.py b/src/djangooidc/views.py index ab81ccff1..b887b7a14 100644 --- a/src/djangooidc/views.py +++ b/src/djangooidc/views.py @@ -99,8 +99,11 @@ def login_callback(request): return CLIENT.create_authn_request(request.session) user = authenticate(request=request, **userinfo) if user: + # Set the verification type + user.set_user_verification_type() login(request, user) logger.info("Successfully logged in user %s" % user) + # Clear the flag if the exception is not caught request.session.pop("redirect_attempted", None) return redirect(request.session.get("next", "/")) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 6869df94a..dd016b470 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -508,7 +508,7 @@ class MyUserAdmin(BaseUserAdmin): ("Important dates", {"fields": ("last_login", "date_joined")}), ) - readonly_fields = ("verification_type") + readonly_fields = ("verification_type",) # Hide Username (uuid), Groups and Permissions # Q: Now that we're using Groups and Permissions, @@ -516,7 +516,7 @@ class MyUserAdmin(BaseUserAdmin): analyst_fieldsets = ( ( None, - {"fields": ("password", "status")}, + {"fields": ("password", "status", "verification_type")}, ), ("Personal Info", {"fields": ("first_name", "last_name", "email")}), ( @@ -636,11 +636,14 @@ class MyUserAdmin(BaseUserAdmin): return [] def get_readonly_fields(self, request, obj=None): + readonly_fields = list(self.readonly_fields) + if request.user.has_perm("registrar.full_access_permission"): - return () # No read-only fields for all access users - # Return restrictive Read-only fields for analysts and - # users who might not belong to groups - return self.analyst_readonly_fields + return readonly_fields + else: + # Return restrictive Read-only fields for analysts and + # users who might not belong to groups + return self.analyst_readonly_fields class HostIPInline(admin.StackedInline): diff --git a/src/registrar/fixtures_users.py b/src/registrar/fixtures_users.py index 99fe4910e..4c2b85233 100644 --- a/src/registrar/fixtures_users.py +++ b/src/registrar/fixtures_users.py @@ -6,6 +6,7 @@ from registrar.models import ( User, UserGroup, ) +from registrar.models.verified_by_staff import VerifiedByStaff fake = Faker() logger = logging.getLogger(__name__) @@ -187,6 +188,9 @@ class UserFixture: logger.info(f"Going to load {len(users)} users in group {group_name}") for user_data in users: try: + + # TODO - Add the fixture user to the VerifiedByStaff table + # (To track how this user was verified) user, _ = User.objects.get_or_create(username=user_data["username"]) user.is_superuser = False user.first_name = user_data["first_name"] diff --git a/src/registrar/migrations/0085_user_verification_type.py b/src/registrar/migrations/0085_user_verification_type.py new file mode 100644 index 000000000..2790ea3b7 --- /dev/null +++ b/src/registrar/migrations/0085_user_verification_type.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.10 on 2024-04-19 21:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0084_create_groups_v11"), + ] + + operations = [ + migrations.AddField( + model_name="user", + name="verification_type", + field=models.CharField( + blank=True, + choices=[ + ("grandfathered", "Legacy user"), + ("verified_by_staff", "Verified by staff"), + ("regular", "Verified by Login.gov"), + ("invited", "Invited by a domain manager"), + ], + help_text="The means through which this user was verified", + null=True, + ), + ), + ] diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index c69672100..9320a80ff 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -60,7 +60,7 @@ class User(AbstractUser): ) verification_type = models.CharField( - choices=VerificationTypeChoices, + choices=VerificationTypeChoices.choices, null=True, blank=True, help_text="The means through which this user was verified", @@ -115,19 +115,19 @@ class User(AbstractUser): @classmethod - def get_existing_user_from_uuid(cls, uuid): + def existing_user(cls, uuid): existing_user = None try: existing_user = cls.objects.get(username=uuid) if existing_user and UserDomainRole.objects.filter(user=existing_user).exists(): - return (False, existing_user) + return False except cls.DoesNotExist: # Do nothing when the user is not found, as we're checking for existence. pass except Exception as err: raise err - return (True, existing_user) + return True @classmethod def needs_identity_verification(cls, email, uuid): @@ -136,14 +136,14 @@ class User(AbstractUser): # An existing user who is a domain manager of a domain (that is, # they have an entry in UserDomainRole for their User) - user_exists, existing_user = cls.existing_user(uuid) + user_exists = cls.existing_user(uuid) if not user_exists: return False # The user needs identity verification if they don't meet # any special criteria, i.e. we are validating them "regularly" - existing_user.verification_type = cls.get_verification_type_from_email(email) - return existing_user.verification_type == cls.VerificationTypeChoices.REGULAR + verification_type = cls.get_verification_type_from_email(email) + return verification_type == cls.VerificationTypeChoices.REGULAR @classmethod def get_verification_type_from_email(cls, email, invitation_status=DomainInvitation.DomainInvitationStatus.INVITED): @@ -167,11 +167,11 @@ class User(AbstractUser): return verification_type - def user_verification_type(self, check_if_user_exists=False): + def set_user_verification_type(self): if self.verification_type is None: # Would need to check audit log retrieved = DomainInvitation.DomainInvitationStatus.RETRIEVED - user_exists, _ = self.existing_user(self.username) + user_exists = self.existing_user(self.username) verification_type = self.get_verification_type_from_email(self.email, invitation_status=retrieved) # This should check if the type is unknown, use check_if_user_exists? From a047380b59111916522de1d3ef605b9f793b0467 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 19 Apr 2024 15:44:26 -0600 Subject: [PATCH 03/62] Fix logic --- src/djangooidc/views.py | 7 +++++-- src/registrar/models/user.py | 18 ++++-------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/djangooidc/views.py b/src/djangooidc/views.py index b887b7a14..12b82f242 100644 --- a/src/djangooidc/views.py +++ b/src/djangooidc/views.py @@ -99,8 +99,11 @@ def login_callback(request): return CLIENT.create_authn_request(request.session) user = authenticate(request=request, **userinfo) if user: - # Set the verification type - user.set_user_verification_type() + # Set the verification type if it doesn't already exist + if not user.verification_type: + user.set_user_verification_type() + user.save() + login(request, user) logger.info("Successfully logged in user %s" % user) diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index 9320a80ff..423f93595 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -168,20 +168,10 @@ class User(AbstractUser): return verification_type def set_user_verification_type(self): - if self.verification_type is None: - # Would need to check audit log - retrieved = DomainInvitation.DomainInvitationStatus.RETRIEVED - user_exists = self.existing_user(self.username) - verification_type = self.get_verification_type_from_email(self.email, invitation_status=retrieved) - - # This should check if the type is unknown, use check_if_user_exists? - if verification_type == self.VerificationTypeChoices.REGULAR and not user_exists: - raise ValueError(f"No verification_type was found for {self} with id: {self.pk}") - else: - self.verification_type = verification_type - return self.verification_type - else: - return self.verification_type + # Would need to check audit log + retrieved = DomainInvitation.DomainInvitationStatus.RETRIEVED + verification_type = self.get_verification_type_from_email(self.email, invitation_status=retrieved) + self.verification_type = verification_type def check_domain_invitations_on_login(self): """When a user first arrives on the site, we need to retrieve any domain From 8b27c44b89f30632018f627fa6d6df882f5b3868 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 19 Apr 2024 15:47:03 -0600 Subject: [PATCH 04/62] Cleanup --- src/registrar/models/user.py | 40 +++++++++++++++--------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index 423f93595..b67421d3f 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -113,10 +113,13 @@ class User(AbstractUser): def has_contact_info(self): return bool(self.contact.title or self.contact.email or self.contact.phone) - @classmethod - def existing_user(cls, uuid): - existing_user = None + def needs_identity_verification(cls, email, uuid): + """A method used by our oidc classes to test whether a user needs email/uuid verification + or the full identity PII verification""" + + # An existing user who is a domain manager of a domain (that is, + # they have an entry in UserDomainRole for their User) try: existing_user = cls.objects.get(username=uuid) if existing_user and UserDomainRole.objects.filter(user=existing_user).exists(): @@ -126,24 +129,21 @@ class User(AbstractUser): pass except Exception as err: raise err - - return True - @classmethod - def needs_identity_verification(cls, email, uuid): - """A method used by our oidc classes to test whether a user needs email/uuid verification - or the full identity PII verification""" - - # An existing user who is a domain manager of a domain (that is, - # they have an entry in UserDomainRole for their User) - user_exists = cls.existing_user(uuid) - if not user_exists: - return False + # We can't set the verification type here because the user may not + # always exist at this point. We do it down the line. + verification_type = cls.get_verification_type_from_email(email) # The user needs identity verification if they don't meet # any special criteria, i.e. we are validating them "regularly" - verification_type = cls.get_verification_type_from_email(email) - return verification_type == cls.VerificationTypeChoices.REGULAR + needs_verification = verification_type == cls.VerificationTypeChoices.REGULAR + return needs_verification + + def set_user_verification_type(self): + # Would need to check audit log + retrieved = DomainInvitation.DomainInvitationStatus.RETRIEVED + verification_type = self.get_verification_type_from_email(self.email, invitation_status=retrieved) + self.verification_type = verification_type @classmethod def get_verification_type_from_email(cls, email, invitation_status=DomainInvitation.DomainInvitationStatus.INVITED): @@ -167,12 +167,6 @@ class User(AbstractUser): return verification_type - def set_user_verification_type(self): - # Would need to check audit log - retrieved = DomainInvitation.DomainInvitationStatus.RETRIEVED - verification_type = self.get_verification_type_from_email(self.email, invitation_status=retrieved) - self.verification_type = verification_type - def check_domain_invitations_on_login(self): """When a user first arrives on the site, we need to retrieve any domain invitations that match their email address.""" From aa9127875ad29181c838953520d15d858693fb48 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 10:40:21 -0600 Subject: [PATCH 05/62] Refine logic for fixture users --- src/djangooidc/views.py | 12 ++++++++++-- src/registrar/fixtures_users.py | 7 ++++--- src/registrar/models/user.py | 4 ++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/djangooidc/views.py b/src/djangooidc/views.py index 12b82f242..815df4ecf 100644 --- a/src/djangooidc/views.py +++ b/src/djangooidc/views.py @@ -99,8 +99,16 @@ def login_callback(request): return CLIENT.create_authn_request(request.session) user = authenticate(request=request, **userinfo) if user: - # Set the verification type if it doesn't already exist - if not user.verification_type: + + # Fixture users kind of exist in a superposition of verification types, + # because while the system "verified" them, if they login, + # we don't know how the user themselves was verified through login.gov until + # they actually try logging in. This edge-case only matters in non-production environments. + fixture_user = User.VerificationTypeChoices.FIXTURE_USER + is_fixture_user = user.verification_type and user.verification_type == fixture_user + + # Set the verification type if it doesn't already exist or if its a fixture user + if not user.verification_type or is_fixture_user: user.set_user_verification_type() user.save() diff --git a/src/registrar/fixtures_users.py b/src/registrar/fixtures_users.py index 4c2b85233..a901b83e6 100644 --- a/src/registrar/fixtures_users.py +++ b/src/registrar/fixtures_users.py @@ -188,9 +188,6 @@ class UserFixture: logger.info(f"Going to load {len(users)} users in group {group_name}") for user_data in users: try: - - # TODO - Add the fixture user to the VerifiedByStaff table - # (To track how this user was verified) user, _ = User.objects.get_or_create(username=user_data["username"]) user.is_superuser = False user.first_name = user_data["first_name"] @@ -199,6 +196,10 @@ class UserFixture: user.email = user_data["email"] user.is_staff = True user.is_active = True + # This verification type will get reverted to "regular" (or whichever is applicables) + # once the user logs in for the first time (as they then got verified through different means). + # In the meantime, we can still describe how the user got here in the first place. + user.verification_type = User.VerificationTypeChoices.FIXTURE_USER group = UserGroup.objects.get(name=group_name) user.groups.add(group) user.save() diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index b67421d3f..79bf905b9 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -33,6 +33,10 @@ class User(AbstractUser): VERIFIED_BY_STAFF = "verified_by_staff", "Verified by staff" REGULAR = "regular", "Verified by Login.gov" INVITED = "invited", "Invited by a domain manager" + # We need a type for fixture users (rather than using verified by staff) + # because those users still do get "verified" through normal means + # after they login. + FIXTURE_USER = "fixture_user", "Created by fixtures" # #### Constants for choice fields #### RESTRICTED = "restricted" From 68e44eb98eb936ad2a1e8c0c219a92eb86b48d47 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 10:41:08 -0600 Subject: [PATCH 06/62] Fix migrations after merge --- ...r_verification_type.py => 0087_user_verification_type.py} | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename src/registrar/migrations/{0085_user_verification_type.py => 0087_user_verification_type.py} (78%) diff --git a/src/registrar/migrations/0085_user_verification_type.py b/src/registrar/migrations/0087_user_verification_type.py similarity index 78% rename from src/registrar/migrations/0085_user_verification_type.py rename to src/registrar/migrations/0087_user_verification_type.py index 2790ea3b7..599d067d5 100644 --- a/src/registrar/migrations/0085_user_verification_type.py +++ b/src/registrar/migrations/0087_user_verification_type.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-04-19 21:02 +# Generated by Django 4.2.10 on 2024-04-22 16:40 from django.db import migrations, models @@ -6,7 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("registrar", "0084_create_groups_v11"), + ("registrar", "0086_domaininformation_updated_federal_agency_and_more"), ] operations = [ @@ -20,6 +20,7 @@ class Migration(migrations.Migration): ("verified_by_staff", "Verified by staff"), ("regular", "Verified by Login.gov"), ("invited", "Invited by a domain manager"), + ("fixture_user", "Created by fixtures"), ], help_text="The means through which this user was verified", null=True, From 2a7eb6db817d0bd38b4c08431bc788ba02358969 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 11:08:59 -0600 Subject: [PATCH 07/62] Fix test + lint --- src/registrar/fixtures_users.py | 2 +- src/registrar/models/user.py | 9 +++++---- src/registrar/tests/test_admin.py | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/registrar/fixtures_users.py b/src/registrar/fixtures_users.py index a901b83e6..9669ce071 100644 --- a/src/registrar/fixtures_users.py +++ b/src/registrar/fixtures_users.py @@ -6,7 +6,7 @@ from registrar.models import ( User, UserGroup, ) -from registrar.models.verified_by_staff import VerifiedByStaff + fake = Faker() logger = logging.getLogger(__name__) diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index 20a84bec4..adc198d3a 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -29,12 +29,13 @@ class User(AbstractUser): Users achieve access to our system in a few different ways. These choices reflect those pathways. """ + GRANDFATHERED = "grandfathered", "Legacy user" VERIFIED_BY_STAFF = "verified_by_staff", "Verified by staff" REGULAR = "regular", "Verified by Login.gov" INVITED = "invited", "Invited by a domain manager" # We need a type for fixture users (rather than using verified by staff) - # because those users still do get "verified" through normal means + # because those users still do get "verified" through normal means # after they login. FIXTURE_USER = "fixture_user", "Created by fixtures" @@ -153,7 +154,7 @@ class User(AbstractUser): @classmethod def get_verification_type_from_email(cls, email, invitation_status=DomainInvitation.DomainInvitationStatus.INVITED): """Retrieves the verification type based off of a provided email address""" - + verification_type = None if TransitionDomain.objects.filter(username=email).exists(): # A new incoming user who is a domain manager for one of the domains @@ -164,12 +165,12 @@ class User(AbstractUser): # New users flagged by Staff to bypass ial2 verification_type = cls.VerificationTypeChoices.VERIFIED_BY_STAFF elif DomainInvitation.objects.filter(email=email, status=invitation_status).exists(): - # A new incoming user who is being invited to be a domain manager (that is, + # A new incoming user who is being invited to be a domain manager (that is, # their email address is in DomainInvitation for an invitation that is not yet "retrieved"). verification_type = cls.VerificationTypeChoices.INVITED else: verification_type = cls.VerificationTypeChoices.REGULAR - + return verification_type def check_domain_invitations_on_login(self): diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index bca1a94cb..20ada4d14 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -2820,7 +2820,7 @@ class MyUserAdminTest(TestCase): request.user = create_user() fieldsets = self.admin.get_fieldsets(request) expected_fieldsets = ( - (None, {"fields": ("password", "status")}), + (None, {"fields": ("password", "status", "verification_type")}), ("Personal Info", {"fields": ("first_name", "last_name", "email")}), ("Permissions", {"fields": ("is_active", "groups")}), ("Important dates", {"fields": ("last_login", "date_joined")}), From 2f494466fc5d2f5b1e2320d043fa08968aae9c56 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:09:16 -0600 Subject: [PATCH 08/62] Check created at date for invited --- src/registrar/models/user.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index adc198d3a..f775c77ad 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -28,6 +28,13 @@ class User(AbstractUser): """ Users achieve access to our system in a few different ways. These choices reflect those pathways. + + Overview of verification types: + - GRANDFATHERED: User exists in the `TransitionDomain` table + - VERIFIED_BY_STAFF: User exists in the `VerifiedByStaff` table + - INVITED: User exists in the `DomainInvitation` table + - REGULAR: User was verified through IAL2 + - FIXTURE_USER: User was created by fixtures """ GRANDFATHERED = "grandfathered", "Legacy user" @@ -146,9 +153,23 @@ class User(AbstractUser): return needs_verification def set_user_verification_type(self): - # Would need to check audit log + """ + Given pre-existing data from TransitionDomain, VerifiedByStaff, and DomainInvitation, + set the verification "type" defined in VerificationTypeChoices. + """ retrieved = DomainInvitation.DomainInvitationStatus.RETRIEVED verification_type = self.get_verification_type_from_email(self.email, invitation_status=retrieved) + + # An existing user may have been invited to a domain after they got verified. + # We need to check for this condition. + if verification_type == User.VerificationTypeChoices.INVITED: + invitation = DomainInvitation.objects.filter(email=self.email, status=retrieved).order_by("created_at").first() + + # If you joined BEFORE the oldest invitation was created, then you were verified normally. + # (See logic in get_verification_type_from_email) + if self.date_joined < invitation.created_at: + verification_type = User.VerificationTypeChoices.REGULAR + self.verification_type = verification_type @classmethod From c24a235d8ec6e2c3c23dfd66c807bf7669f279c5 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:51:22 -0600 Subject: [PATCH 09/62] Add script to populate data --- docs/operations/data_migration.md | 34 ++++++++++++-- .../commands/populate_verification_type.py | 46 +++++++++++++++++++ 2 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 src/registrar/management/commands/populate_verification_type.py diff --git a/docs/operations/data_migration.md b/docs/operations/data_migration.md index 0846208de..e4543a28c 100644 --- a/docs/operations/data_migration.md +++ b/docs/operations/data_migration.md @@ -602,18 +602,18 @@ That data are synthesized from the generic_org_type field and the is_election_bo The latest domain_election_board csv can be found [here](https://drive.google.com/file/d/1aDeCqwHmBnXBl2arvoFCN0INoZmsEGsQ/view). After downloading this file, place it in `src/migrationdata` -#### Step 2: Upload the domain_election_board file to your sandbox +#### Step 3: Upload the domain_election_board file to your sandbox Follow [Step 1: Transfer data to sandboxes](#step-1-transfer-data-to-sandboxes) and [Step 2: Transfer uploaded files to the getgov directory](#step-2-transfer-uploaded-files-to-the-getgov-directory) from the [Set Up Migrations on Sandbox](#set-up-migrations-on-sandbox) portion of this doc. -#### Step 2: SSH into your environment +#### Step 4: SSH into your environment ```cf ssh getgov-{space}``` Example: `cf ssh getgov-za` -#### Step 3: Create a shell instance +#### Step 5: Create a shell instance ```/tmp/lifecycle/shell``` -#### Step 4: Running the script +#### Step 6: Running the script ```./manage.py populate_organization_type {domain_election_board_filename}``` - The domain_election_board_filename file must adhere to this format: @@ -642,3 +642,29 @@ Example (assuming that this is being ran from src/): | | Parameter | Description | |:-:|:------------------------------------|:-------------------------------------------------------------------| | 1 | **domain_election_board_filename** | A file containing every domain that is an election office. + + +## Populate Verification Type +This section outlines how to run the `populate_verification_type` script. +The script is used to update the verification_type field on User when it is None. + +### Running on sandboxes + +#### Step 1: Login to CloudFoundry +```cf login -a api.fr.cloud.gov --sso``` + +#### Step 2: SSH into your environment +```cf ssh getgov-{space}``` + +Example: `cf ssh getgov-za` + +#### Step 3: Create a shell instance +```/tmp/lifecycle/shell``` + +#### Step 4: Running the script +```./manage.py populate_verification_type``` + +### Running locally + +#### Step 1: Running the script +```docker-compose exec app ./manage.py populate_verification_type``` diff --git a/src/registrar/management/commands/populate_verification_type.py b/src/registrar/management/commands/populate_verification_type.py new file mode 100644 index 000000000..959fefe6c --- /dev/null +++ b/src/registrar/management/commands/populate_verification_type.py @@ -0,0 +1,46 @@ +import argparse +import logging +from typing import List +from django.core.management import BaseCommand +from registrar.management.commands.utility.terminal_helper import TerminalColors, TerminalHelper, ScriptDataHelper +from registrar.models import User + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand): + help = "Loops through each valid User object and updates its verification_type value" + + def handle(self, **kwargs): + """Loops through each valid User object and updates its verification_type value""" + + users = User.objects.filter(verification_type__isnull=True) + + # Code execution will stop here if the user prompts "N" + TerminalHelper.prompt_for_execution( + system_exit_on_terminate=True, + info_to_inspect=f""" + ==Proposed Changes== + Number of User objects to change: {len(users)} + This field will be updated on each record: verification_type + """, + prompt_title="Do you wish to patch verification_type data?", + ) + logger.info("Updating...") + + user_to_update: List[User] = [] + user_failed_to_update: List[User] = [] + for user in users: + try: + user.set_user_verification_type() + user_to_update.append(user) + except Exception as err: + user_failed_to_update.append(user) + logger.error(err) + logger.error(f"{TerminalColors.FAIL}" f"Failed to update {user}" f"{TerminalColors.ENDC}") + + # Do a bulk update on the first_ready field + ScriptDataHelper.bulk_update_fields(User, user_to_update, ["verification_type"]) + + # Log what happened + TerminalHelper.log_script_run_summary(user_to_update, user_failed_to_update, skipped=[], debug=True) From 5aa4e8f484295f1270613bdd7a0478eeff27fb11 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:32:42 -0600 Subject: [PATCH 10/62] Add script template --- .../commands/populate_verification_type.py | 46 ++++------------- .../commands/utility/terminal_helper.py | 51 +++++++++++++++++++ 2 files changed, 62 insertions(+), 35 deletions(-) diff --git a/src/registrar/management/commands/populate_verification_type.py b/src/registrar/management/commands/populate_verification_type.py index 959fefe6c..ceaaccc2e 100644 --- a/src/registrar/management/commands/populate_verification_type.py +++ b/src/registrar/management/commands/populate_verification_type.py @@ -1,46 +1,22 @@ -import argparse import logging from typing import List from django.core.management import BaseCommand -from registrar.management.commands.utility.terminal_helper import TerminalColors, TerminalHelper, ScriptDataHelper +from registrar.management.commands.utility.terminal_helper import ScriptTemplate, TerminalColors from registrar.models import User logger = logging.getLogger(__name__) -class Command(BaseCommand): +class Command(ScriptTemplate): help = "Loops through each valid User object and updates its verification_type value" - def handle(self, **kwargs): + def handle(self): """Loops through each valid User object and updates its verification_type value""" - - users = User.objects.filter(verification_type__isnull=True) - - # Code execution will stop here if the user prompts "N" - TerminalHelper.prompt_for_execution( - system_exit_on_terminate=True, - info_to_inspect=f""" - ==Proposed Changes== - Number of User objects to change: {len(users)} - This field will be updated on each record: verification_type - """, - prompt_title="Do you wish to patch verification_type data?", - ) - logger.info("Updating...") - - user_to_update: List[User] = [] - user_failed_to_update: List[User] = [] - for user in users: - try: - user.set_user_verification_type() - user_to_update.append(user) - except Exception as err: - user_failed_to_update.append(user) - logger.error(err) - logger.error(f"{TerminalColors.FAIL}" f"Failed to update {user}" f"{TerminalColors.ENDC}") - - # Do a bulk update on the first_ready field - ScriptDataHelper.bulk_update_fields(User, user_to_update, ["verification_type"]) - - # Log what happened - TerminalHelper.log_script_run_summary(user_to_update, user_failed_to_update, skipped=[], debug=True) + filter_condition = { + "verification_type__isnull": True + } + ScriptTemplate.mass_populate_field(User, filter_condition, ["verification_type"]) + + def populate_field(self, field_to_update): + """Defines how we update the verification_type field""" + field_to_update.set_user_verification_type() diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index b54209750..f42ebc1cb 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -2,6 +2,7 @@ import logging import sys from django.core.paginator import Paginator from typing import List +from django.core.management import BaseCommand from registrar.utility.enums import LogCode logger = logging.getLogger(__name__) @@ -58,6 +59,56 @@ class ScriptDataHelper: model_class.objects.bulk_update(page.object_list, fields_to_update) +class ScriptTemplate(BaseCommand): + """ + Contains common script actions for our scripts which can be prefilled as templates. + """ + + @staticmethod + def mass_populate_field(sender, filter_conditions, fields_to_update): + """Loops through each valid "sender" object - specified by filter_conditions - and + updates fields defined by fields_to_update using populate_function. + + You must define populate_field before you can use this function. + """ + + objects = sender.objects.filter(**filter_conditions) + + # Code execution will stop here if the user prompts "N" + TerminalHelper.prompt_for_execution( + system_exit_on_terminate=True, + info_to_inspect=f""" + ==Proposed Changes== + Number of {sender} objects to change: {len(objects)} + These fields will be updated on each record: {fields_to_update} + """, + prompt_title="Do you wish to patch this data?", + ) + logger.info("Updating...") + + to_update: List[sender] = [] + failed_to_update: List[sender] = [] + for updated_object in objects: + try: + ScriptTemplate.populate_field(updated_object) + to_update.append(updated_object) + except Exception as err: + to_update.append(updated_object) + logger.error(err) + logger.error(f"{TerminalColors.FAIL}" f"Failed to update {updated_object}" f"{TerminalColors.ENDC}") + + # Do a bulk update on the first_ready field + ScriptDataHelper.bulk_update_fields(sender, to_update, fields_to_update) + + # Log what happened + TerminalHelper.log_script_run_summary(to_update, failed_to_update, skipped=[], debug=True) + + @staticmethod + def populate_field(field_to_update): + """Defines how we update each field. Must be defined before using mass_populate_field.""" + raise NotImplementedError("This method should be implemented by the child class.") + + class TerminalHelper: @staticmethod def log_script_run_summary(to_update, failed_to_update, skipped, debug: bool, log_header=None): From 705febe37a7b254952c9a7cce6c68e221bbfdc2d Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 15:25:53 -0600 Subject: [PATCH 11/62] Add kwargs --- src/registrar/management/commands/populate_verification_type.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/management/commands/populate_verification_type.py b/src/registrar/management/commands/populate_verification_type.py index ceaaccc2e..a59f16570 100644 --- a/src/registrar/management/commands/populate_verification_type.py +++ b/src/registrar/management/commands/populate_verification_type.py @@ -10,7 +10,7 @@ logger = logging.getLogger(__name__) class Command(ScriptTemplate): help = "Loops through each valid User object and updates its verification_type value" - def handle(self): + def handle(self, **kwargs): """Loops through each valid User object and updates its verification_type value""" filter_condition = { "verification_type__isnull": True From 9b3466475f0fe7f2dbe0008a860acd648d13bd32 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 15:34:03 -0600 Subject: [PATCH 12/62] Change override --- .../management/commands/populate_verification_type.py | 2 +- .../management/commands/utility/terminal_helper.py | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/registrar/management/commands/populate_verification_type.py b/src/registrar/management/commands/populate_verification_type.py index a59f16570..63ef641d8 100644 --- a/src/registrar/management/commands/populate_verification_type.py +++ b/src/registrar/management/commands/populate_verification_type.py @@ -15,7 +15,7 @@ class Command(ScriptTemplate): filter_condition = { "verification_type__isnull": True } - ScriptTemplate.mass_populate_field(User, filter_condition, ["verification_type"]) + self.mass_populate_field(User, filter_condition, ["verification_type"]) def populate_field(self, field_to_update): """Defines how we update the verification_type field""" diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index f42ebc1cb..305be8d8d 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -64,8 +64,7 @@ class ScriptTemplate(BaseCommand): Contains common script actions for our scripts which can be prefilled as templates. """ - @staticmethod - def mass_populate_field(sender, filter_conditions, fields_to_update): + def mass_populate_field(self, sender, filter_conditions, fields_to_update): """Loops through each valid "sender" object - specified by filter_conditions - and updates fields defined by fields_to_update using populate_function. @@ -90,7 +89,7 @@ class ScriptTemplate(BaseCommand): failed_to_update: List[sender] = [] for updated_object in objects: try: - ScriptTemplate.populate_field(updated_object) + self.populate_field(updated_object) to_update.append(updated_object) except Exception as err: to_update.append(updated_object) @@ -102,9 +101,8 @@ class ScriptTemplate(BaseCommand): # Log what happened TerminalHelper.log_script_run_summary(to_update, failed_to_update, skipped=[], debug=True) - - @staticmethod - def populate_field(field_to_update): + + def populate_field(self, field_to_update): """Defines how we update each field. Must be defined before using mass_populate_field.""" raise NotImplementedError("This method should be implemented by the child class.") From 3f6a9ec1ce08ca5e441a1919ecc651c11e5e6c91 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 12:13:55 -0600 Subject: [PATCH 13/62] Fix unit test --- src/registrar/tests/test_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 20ada4d14..8e9701d59 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -2820,7 +2820,7 @@ class MyUserAdminTest(TestCase): request.user = create_user() fieldsets = self.admin.get_fieldsets(request) expected_fieldsets = ( - (None, {"fields": ("password", "status", "verification_type")}), + (None, {"fields": ("status", "verification_type")}), ("Personal Info", {"fields": ("first_name", "last_name", "email")}), ("Permissions", {"fields": ("is_active", "groups")}), ("Important dates", {"fields": ("last_login", "date_joined")}), From 9ce10c370e48f07880b9d3822c26fdaf7b51560b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 12:18:08 -0600 Subject: [PATCH 14/62] Revert "Fix unit test" This reverts commit 3f6a9ec1ce08ca5e441a1919ecc651c11e5e6c91. --- src/registrar/tests/test_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 8e9701d59..20ada4d14 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -2820,7 +2820,7 @@ class MyUserAdminTest(TestCase): request.user = create_user() fieldsets = self.admin.get_fieldsets(request) expected_fieldsets = ( - (None, {"fields": ("status", "verification_type")}), + (None, {"fields": ("password", "status", "verification_type")}), ("Personal Info", {"fields": ("first_name", "last_name", "email")}), ("Permissions", {"fields": ("is_active", "groups")}), ("Important dates", {"fields": ("last_login", "date_joined")}), From 54b615d7f88cbce3efc18bed285954bc299395ed Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 13:46:20 -0600 Subject: [PATCH 15/62] Unit tests --- src/djangooidc/tests/test_views.py | 132 +++++++++++++++++++++++++++++ src/registrar/models/user.py | 3 +- 2 files changed, 134 insertions(+), 1 deletion(-) diff --git a/src/djangooidc/tests/test_views.py b/src/djangooidc/tests/test_views.py index f10afcbaf..fc93db82e 100644 --- a/src/djangooidc/tests/test_views.py +++ b/src/djangooidc/tests/test_views.py @@ -4,8 +4,10 @@ from django.http import HttpResponse from django.test import Client, TestCase, RequestFactory from django.urls import reverse +from api.tests.common import less_console_noise_decorator from djangooidc.exceptions import StateMismatch, InternalError from ..views import login_callback +from registrar.models import User, Contact, VerifiedByStaff, DomainInvitation, TransitionDomain, Domain from .common import less_console_noise @@ -15,6 +17,14 @@ class ViewsTest(TestCase): def setUp(self): self.client = Client() self.factory = RequestFactory() + + def tearDown(self): + User.objects.all().delete() + Contact.objects.all().delete() + DomainInvitation.objects.all().delete() + VerifiedByStaff.objects.all().delete() + TransitionDomain.objects.all().delete() + Domain.objects.all().delete() def say_hi(*args): return HttpResponse("Hi") @@ -228,6 +238,128 @@ class ViewsTest(TestCase): # assert that redirect is to / when no 'next' is set self.assertEqual(response.status_code, 302) self.assertEqual(response.url, "/") + + @less_console_noise_decorator + def test_login_callback_sets_verification_type_regular(self, mock_client): + """Test that openid sets the verification type to regular on the returned user""" + # SETUP + session = self.client.session + session.save() + # MOCK + # mock that callback returns user_info; this is the expected behavior + mock_client.callback.side_effect = self.user_info + # patch that the request does not require step up auth + with patch("djangooidc.views._requires_step_up_auth", return_value=False), patch("djangooidc.views._initialize_client") as mock_init_client: + with patch("djangooidc.views._client_is_none", return_value=True): + # TEST + # test the login callback url + response = self.client.get(reverse("openid_login_callback")) + + # assert that _initialize_client was called + mock_init_client.assert_called_once() + + # Assert that we get a redirect + self.assertEqual(response.status_code, 302) + self.assertEqual(response.url, "/") + + # Test the created user object + created_user = User.objects.get(email="test@example.com") + self.assertEqual(created_user.verification_type, User.VerificationTypeChoices.REGULAR) + + @less_console_noise_decorator + def test_login_callback_sets_verification_type_invited(self, mock_client): + """Test that openid sets the verification type to invited on the returned user + when they exist in the DomainInvitation table""" + # SETUP + session = self.client.session + session.save() + + domain, _ = Domain.objects.get_or_create(name="test123.gov") + invitation, _ = DomainInvitation.objects.get_or_create(email="test@example.com", domain=domain) + # MOCK + # mock that callback returns user_info; this is the expected behavior + mock_client.callback.side_effect = self.user_info + # patch that the request does not require step up auth + with patch("djangooidc.views._requires_step_up_auth", return_value=False), patch("djangooidc.views._initialize_client") as mock_init_client: + with patch("djangooidc.views._client_is_none", return_value=True): + # TEST + # test the login callback url + response = self.client.get(reverse("openid_login_callback")) + + # assert that _initialize_client was called + mock_init_client.assert_called_once() + + # Assert that we get a redirect + self.assertEqual(response.status_code, 302) + self.assertEqual(response.url, "/") + + # Test the created user object + created_user = User.objects.get(email="test@example.com") + self.assertEqual(created_user.email, invitation.email) + self.assertEqual(created_user.verification_type, User.VerificationTypeChoices.INVITED) + + @less_console_noise_decorator + def test_login_callback_sets_verification_type_grandfathered(self, mock_client): + """Test that openid sets the verification type to grandfathered on a user which exists in our TransitionDomain table""" + # SETUP + session = self.client.session + session.save() + # MOCK + # mock that callback returns user_info; this is the expected behavior + mock_client.callback.side_effect = self.user_info + + td, _ = TransitionDomain.objects.get_or_create(username="test@example.com", domain_name="test123.gov") + + # patch that the request does not require step up auth + with patch("djangooidc.views._requires_step_up_auth", return_value=False), patch("djangooidc.views._initialize_client") as mock_init_client: + with patch("djangooidc.views._client_is_none", return_value=True): + # TEST + # test the login callback url + response = self.client.get(reverse("openid_login_callback")) + + # assert that _initialize_client was called + mock_init_client.assert_called_once() + + # Assert that we get a redirect + self.assertEqual(response.status_code, 302) + self.assertEqual(response.url, "/") + + # Test the created user object + created_user = User.objects.get(email="test@example.com") + self.assertEqual(created_user.email, td.username) + self.assertEqual(created_user.verification_type, User.VerificationTypeChoices.GRANDFATHERED) + + @less_console_noise_decorator + def test_login_callback_sets_verification_type_verified_by_staff(self, mock_client): + """Test that openid sets the verification type to verified_by_staff + on a user which exists in our VerifiedByStaff table""" + # SETUP + session = self.client.session + session.save() + # MOCK + # mock that callback returns user_info; this is the expected behavior + mock_client.callback.side_effect = self.user_info + + vip, _ = VerifiedByStaff.objects.get_or_create(email="test@example.com") + + # patch that the request does not require step up auth + with patch("djangooidc.views._requires_step_up_auth", return_value=False), patch("djangooidc.views._initialize_client") as mock_init_client: + with patch("djangooidc.views._client_is_none", return_value=True): + # TEST + # test the login callback url + response = self.client.get(reverse("openid_login_callback")) + + # assert that _initialize_client was called + mock_init_client.assert_called_once() + + # Assert that we get a redirect + self.assertEqual(response.status_code, 302) + self.assertEqual(response.url, "/") + + # Test the created user object + created_user = User.objects.get(email="test@example.com") + self.assertEqual(created_user.email, vip.email) + self.assertEqual(created_user.verification_type, User.VerificationTypeChoices.VERIFIED_BY_STAFF) def test_login_callback_no_step_up_auth(self, mock_client): """Walk through login_callback when _requires_step_up_auth returns False diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index f775c77ad..45532f8ea 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -3,6 +3,7 @@ import logging from django.contrib.auth.models import AbstractUser from django.db import models +from django.db.models import Q from registrar.models.user_domain_role import UserDomainRole @@ -177,7 +178,7 @@ class User(AbstractUser): """Retrieves the verification type based off of a provided email address""" verification_type = None - if TransitionDomain.objects.filter(username=email).exists(): + if TransitionDomain.objects.filter(Q(username=email) | Q(email=email)).exists(): # A new incoming user who is a domain manager for one of the domains # that we inputted from Verisign (that is, their email address appears # in the username field of a TransitionDomain) From d1fcb922c635b087d5c356e0f344845a0b3ab63a Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 14:17:08 -0600 Subject: [PATCH 16/62] Unit tests --- .../commands/populate_verification_type.py | 9 +- .../commands/utility/terminal_helper.py | 2 +- src/registrar/models/user.py | 9 +- .../tests/test_management_scripts.py | 91 ++++++++++++++++++- 4 files changed, 103 insertions(+), 8 deletions(-) diff --git a/src/registrar/management/commands/populate_verification_type.py b/src/registrar/management/commands/populate_verification_type.py index 63ef641d8..da493b8cf 100644 --- a/src/registrar/management/commands/populate_verification_type.py +++ b/src/registrar/management/commands/populate_verification_type.py @@ -12,11 +12,12 @@ class Command(ScriptTemplate): def handle(self, **kwargs): """Loops through each valid User object and updates its verification_type value""" - filter_condition = { - "verification_type__isnull": True - } + filter_condition = {"verification_type__isnull": True} self.mass_populate_field(User, filter_condition, ["verification_type"]) - + def populate_field(self, field_to_update): """Defines how we update the verification_type field""" field_to_update.set_user_verification_type() + logger.info( + f"{TerminalColors.OKCYAN}Updating {field_to_update} => {field_to_update.verification_type}{TerminalColors.OKCYAN}" + ) diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index 305be8d8d..1d411e403 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -65,7 +65,7 @@ class ScriptTemplate(BaseCommand): """ def mass_populate_field(self, sender, filter_conditions, fields_to_update): - """Loops through each valid "sender" object - specified by filter_conditions - and + """Loops through each valid "sender" object - specified by filter_conditions - and updates fields defined by fields_to_update using populate_function. You must define populate_field before you can use this function. diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index 45532f8ea..eb21971f8 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -158,13 +158,18 @@ class User(AbstractUser): Given pre-existing data from TransitionDomain, VerifiedByStaff, and DomainInvitation, set the verification "type" defined in VerificationTypeChoices. """ + email_or_username = self.email or self.username retrieved = DomainInvitation.DomainInvitationStatus.RETRIEVED - verification_type = self.get_verification_type_from_email(self.email, invitation_status=retrieved) + verification_type = self.get_verification_type_from_email(email_or_username, invitation_status=retrieved) # An existing user may have been invited to a domain after they got verified. # We need to check for this condition. if verification_type == User.VerificationTypeChoices.INVITED: - invitation = DomainInvitation.objects.filter(email=self.email, status=retrieved).order_by("created_at").first() + invitation = ( + DomainInvitation.objects.filter(email=email_or_username, status=retrieved) + .order_by("created_at") + .first() + ) # If you joined BEFORE the oldest invitation was created, then you were verified normally. # (See logic in get_verification_type_from_email) diff --git a/src/registrar/tests/test_management_scripts.py b/src/registrar/tests/test_management_scripts.py index 26161b272..68b7f04c9 100644 --- a/src/registrar/tests/test_management_scripts.py +++ b/src/registrar/tests/test_management_scripts.py @@ -14,8 +14,9 @@ from registrar.models import ( TransitionDomain, DomainInformation, UserDomainRole, + VerifiedByStaff, + PublicContact, ) -from registrar.models.public_contact import PublicContact from django.core.management import call_command from unittest.mock import patch, call @@ -25,6 +26,94 @@ from .common import MockEppLib, less_console_noise, completed_domain_request from api.tests.common import less_console_noise_decorator +class TestPopulateVerificationType(MockEppLib): + """Tests for the populate_organization_type script""" + + def setUp(self): + """Creates a fake domain object""" + super().setUp() + + # Get the domain requests + self.domain_request_1 = completed_domain_request( + name="lasers.gov", + generic_org_type=DomainRequest.OrganizationChoices.FEDERAL, + is_election_board=True, + status=DomainRequest.DomainRequestStatus.IN_REVIEW, + ) + + # Approve the request + self.domain_request_1.approve() + + # Get the domains + self.domain_1 = Domain.objects.get(name="lasers.gov") + + # Get users + self.regular_user, _ = User.objects.get_or_create(username="testuser@igormail.gov") + + vip, _ = VerifiedByStaff.objects.get_or_create(email="vipuser@igormail.gov") + self.verified_by_staff_user, _ = User.objects.get_or_create(username="vipuser@igormail.gov") + + grandfathered, _ = TransitionDomain.objects.get_or_create( + username="grandpa@igormail.gov", domain_name=self.domain_1.name + ) + self.grandfathered_user, _ = User.objects.get_or_create(username="grandpa@igormail.gov") + + invited, _ = DomainInvitation.objects.get_or_create(email="invited@igormail.gov", domain=self.domain_1) + self.invited_user, _ = User.objects.get_or_create(username="invited@igormail.gov") + + self.untouched_user, _ = User.objects.get_or_create( + username="iaminvincible@igormail.gov", verification_type=User.VerificationTypeChoices.GRANDFATHERED + ) + + def tearDown(self): + """Deletes all DB objects related to migrations""" + super().tearDown() + + # Delete domains and related information + Domain.objects.all().delete() + DomainInformation.objects.all().delete() + DomainRequest.objects.all().delete() + User.objects.all().delete() + Contact.objects.all().delete() + Website.objects.all().delete() + + @less_console_noise_decorator + def run_populate_verification_type(self): + """ + This method executes the populate_organization_type command. + + The 'call_command' function from Django's management framework is then used to + execute the populate_organization_type command with the specified arguments. + """ + with patch( + "registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa + return_value=True, + ): + call_command("populate_verification_type") + + @less_console_noise_decorator + def test_verification_type_script_populates_data(self): + """Ensures that the verification type script actually populates data""" + + # Run the script + self.run_populate_verification_type() + + # Scripts don't work as we'd expect in our test environment, we need to manually + # trigger the refresh event + self.regular_user.refresh_from_db() + self.grandfathered_user.refresh_from_db() + self.invited_user.refresh_from_db() + self.verified_by_staff_user.refresh_from_db() + self.untouched_user.refresh_from_db() + + # Test all users + self.assertEqual(self.regular_user.verification_type, User.VerificationTypeChoices.REGULAR) + self.assertEqual(self.grandfathered_user.verification_type, User.VerificationTypeChoices.GRANDFATHERED) + self.assertEqual(self.invited_user.verification_type, User.VerificationTypeChoices.INVITED) + self.assertEqual(self.verified_by_staff_user.verification_type, User.VerificationTypeChoices.VERIFIED_BY_STAFF) + self.assertEqual(self.untouched_user.verification_type, User.VerificationTypeChoices.GRANDFATHERED) + + class TestPopulateOrganizationType(MockEppLib): """Tests for the populate_organization_type script""" From 883595ba446a3df7c431897f39afcea6b9acb614 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 14:25:48 -0600 Subject: [PATCH 17/62] Add unit tests --- src/registrar/models/user.py | 4 ++-- src/registrar/tests/test_management_scripts.py | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index eb21971f8..3a37f5b61 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -158,7 +158,7 @@ class User(AbstractUser): Given pre-existing data from TransitionDomain, VerifiedByStaff, and DomainInvitation, set the verification "type" defined in VerificationTypeChoices. """ - email_or_username = self.email or self.username + email_or_username = self.email if self.email else self.username retrieved = DomainInvitation.DomainInvitationStatus.RETRIEVED verification_type = self.get_verification_type_from_email(email_or_username, invitation_status=retrieved) @@ -173,7 +173,7 @@ class User(AbstractUser): # If you joined BEFORE the oldest invitation was created, then you were verified normally. # (See logic in get_verification_type_from_email) - if self.date_joined < invitation.created_at: + if invitation is not None and self.date_joined < invitation.created_at: verification_type = User.VerificationTypeChoices.REGULAR self.verification_type = verification_type diff --git a/src/registrar/tests/test_management_scripts.py b/src/registrar/tests/test_management_scripts.py index 68b7f04c9..617e305a1 100644 --- a/src/registrar/tests/test_management_scripts.py +++ b/src/registrar/tests/test_management_scripts.py @@ -58,13 +58,21 @@ class TestPopulateVerificationType(MockEppLib): ) self.grandfathered_user, _ = User.objects.get_or_create(username="grandpa@igormail.gov") - invited, _ = DomainInvitation.objects.get_or_create(email="invited@igormail.gov", domain=self.domain_1) + invited, _ = DomainInvitation.objects.get_or_create( + email="invited@igormail.gov", domain=self.domain_1, status=DomainInvitation.DomainInvitationStatus.RETRIEVED + ) self.invited_user, _ = User.objects.get_or_create(username="invited@igormail.gov") self.untouched_user, _ = User.objects.get_or_create( username="iaminvincible@igormail.gov", verification_type=User.VerificationTypeChoices.GRANDFATHERED ) + # Fixture users should be untouched by the script. These will auto update once the + # user logs in / creates an account. + self.fixture_user, _ = User.objects.get_or_create( + username="fixture@igormail.gov", verification_type=User.VerificationTypeChoices.FIXTURE_USER + ) + def tearDown(self): """Deletes all DB objects related to migrations""" super().tearDown() @@ -112,6 +120,7 @@ class TestPopulateVerificationType(MockEppLib): self.assertEqual(self.invited_user.verification_type, User.VerificationTypeChoices.INVITED) self.assertEqual(self.verified_by_staff_user.verification_type, User.VerificationTypeChoices.VERIFIED_BY_STAFF) self.assertEqual(self.untouched_user.verification_type, User.VerificationTypeChoices.GRANDFATHERED) + self.assertEqual(self.fixture_user.verification_type, User.VerificationTypeChoices.FIXTURE_USER) class TestPopulateOrganizationType(MockEppLib): From e45ea48898a44e918ddd1ee2e23e0394597dc141 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 23 Apr 2024 17:57:41 -0400 Subject: [PATCH 18/62] cleanup --- src/Pipfile | 2 +- src/Pipfile.lock | 218 +++++++++++------------ src/registrar/config/settings.py | 7 + src/registrar/tests/test_views_domain.py | 2 - src/requirements.txt | 10 +- 5 files changed, 122 insertions(+), 117 deletions(-) diff --git a/src/Pipfile b/src/Pipfile index 1565af79b..5bab9cc95 100644 --- a/src/Pipfile +++ b/src/Pipfile @@ -9,7 +9,7 @@ cfenv = "*" django-cors-headers = "*" pycryptodomex = "*" django-allow-cidr = "*" -django-auditlog = "2.3.0" +django-auditlog = "*" django-csp = "*" environs = {extras=["django"]} Faker = "*" diff --git a/src/Pipfile.lock b/src/Pipfile.lock index 5940f455e..117290daa 100644 --- a/src/Pipfile.lock +++ b/src/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "ce10883aef7e1ce10421d99b3ac35ebf419857a3fe468f0e2d93785f4323eaa8" + "sha256": "16a0db98015509322cf1d27f06fced5b7635057c4eb98921a9419d63d51925ab" }, "pipfile-spec": 6, "requires": {}, @@ -32,20 +32,20 @@ }, "boto3": { "hashes": [ - "sha256:168894499578a9d69d6f7deb5811952bf4171c51b95749a9aef32cf67bc71f87", - "sha256:1bd4cef11b7c5f293cede50f3d33ca89fe3413c51f1864f40163c56a732dd6b3" + "sha256:2824e3dd18743ca50e5b10439d20e74647b1416e8a94509cb30beac92d27a18d", + "sha256:b2e5cb5b95efcc881e25a3bc872d7a24e75ff4e76f368138e4baf7b9d6ee3422" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.34.88" + "version": "==1.34.90" }, "botocore": { "hashes": [ - "sha256:36f2e9e8dfa856e55dbbe703aea601f134db3fddc3615f1020a755b27fd26a5e", - "sha256:e87a660599ed3e14b2a770f4efc3df2f2f6d04f3c7bfd64ddbae186667864a7b" + "sha256:113cd4c0cb63e13163ccbc2bb13d551be314ba7f8ba5bfab1c51a19ca01aa133", + "sha256:d48f152498e2c60b43ce25b579d26642346a327b6fb2c632d57219e0a4f63392" ], "markers": "python_version >= '3.8'", - "version": "==1.34.88" + "version": "==1.34.90" }, "cachetools": { "hashes": [ @@ -313,12 +313,12 @@ }, "django-auditlog": { "hashes": [ - "sha256:7bc2c87e4aff62dec9785d1b2359a2b27148f8c286f8a52b9114fc7876c5a9f7", - "sha256:b9d3acebb64f3f2785157efe3f2f802e0929aafc579d85bbfb9827db4adab532" + "sha256:92db1cf4a51ceca5c26b3ff46997d9e3305a02da1bd435e2efb5b8b6d300ce1f", + "sha256:9de49f80a4911135d136017123cd73461f869b4947eec14d5e76db4b88182f3f" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.3.0" + "markers": "python_version >= '3.8'", + "version": "==3.0.0" }, "django-cache-url": { "hashes": [ @@ -958,96 +958,96 @@ }, "pydantic": { "hashes": [ - "sha256:9dee74a271705f14f9a1567671d144a851c675b072736f0a7b2608fd9e495352", - "sha256:b5ecdd42262ca2462e2624793551e80911a1e989f462910bb81aef974b4bb383" + "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5", + "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc" ], "markers": "python_version >= '3.8'", - "version": "==2.7.0" + "version": "==2.7.1" }, "pydantic-core": { "hashes": [ - "sha256:030e4f9516f9947f38179249778709a460a3adb516bf39b5eb9066fcfe43d0e6", - "sha256:09f03dfc0ef8c22622eaa8608caa4a1e189cfb83ce847045eca34f690895eccb", - "sha256:12a05db5013ec0ca4a32cc6433f53faa2a014ec364031408540ba858c2172bb0", - "sha256:14fe73881cf8e4cbdaded8ca0aa671635b597e42447fec7060d0868b52d074e6", - "sha256:1a0c3e718f4e064efde68092d9d974e39572c14e56726ecfaeebbe6544521f47", - "sha256:1be91ad664fc9245404a789d60cba1e91c26b1454ba136d2a1bf0c2ac0c0505a", - "sha256:201713f2f462e5c015b343e86e68bd8a530a4f76609b33d8f0ec65d2b921712a", - "sha256:2027493cc44c23b598cfaf200936110433d9caa84e2c6cf487a83999638a96ac", - "sha256:250ae39445cb5475e483a36b1061af1bc233de3e9ad0f4f76a71b66231b07f88", - "sha256:2533ad2883f001efa72f3d0e733fb846710c3af6dcdd544fe5bf14fa5fe2d7db", - "sha256:25595ac311f20e5324d1941909b0d12933f1fd2171075fcff763e90f43e92a0d", - "sha256:2684a94fdfd1b146ff10689c6e4e815f6a01141781c493b97342cdc5b06f4d5d", - "sha256:27f1009dc292f3b7ca77feb3571c537276b9aad5dd4efb471ac88a8bd09024e9", - "sha256:2adaeea59849ec0939af5c5d476935f2bab4b7f0335b0110f0f069a41024278e", - "sha256:2ae80f72bb7a3e397ab37b53a2b49c62cc5496412e71bc4f1277620a7ce3f52b", - "sha256:2d5728e93d28a3c63ee513d9ffbac9c5989de8c76e049dbcb5bfe4b923a9739d", - "sha256:2e91711e36e229978d92642bfc3546333a9127ecebb3f2761372e096395fc649", - "sha256:2fe0c1ce5b129455e43f941f7a46f61f3d3861e571f2905d55cdbb8b5c6f5e2c", - "sha256:38a5024de321d672a132b1834a66eeb7931959c59964b777e8f32dbe9523f6b1", - "sha256:3e352f0191d99fe617371096845070dee295444979efb8f27ad941227de6ad09", - "sha256:48dd883db92e92519201f2b01cafa881e5f7125666141a49ffba8b9facc072b0", - "sha256:54764c083bbe0264f0f746cefcded6cb08fbbaaf1ad1d78fb8a4c30cff999a90", - "sha256:54c7375c62190a7845091f521add19b0f026bcf6ae674bdb89f296972272e86d", - "sha256:561cf62c8a3498406495cfc49eee086ed2bb186d08bcc65812b75fda42c38294", - "sha256:56823a92075780582d1ffd4489a2e61d56fd3ebb4b40b713d63f96dd92d28144", - "sha256:582cf2cead97c9e382a7f4d3b744cf0ef1a6e815e44d3aa81af3ad98762f5a9b", - "sha256:58aca931bef83217fca7a390e0486ae327c4af9c3e941adb75f8772f8eeb03a1", - "sha256:5f7973c381283783cd1043a8c8f61ea5ce7a3a58b0369f0ee0ee975eaf2f2a1b", - "sha256:6395a4435fa26519fd96fdccb77e9d00ddae9dd6c742309bd0b5610609ad7fb2", - "sha256:63d7523cd95d2fde0d28dc42968ac731b5bb1e516cc56b93a50ab293f4daeaad", - "sha256:641a018af4fe48be57a2b3d7a1f0f5dbca07c1d00951d3d7463f0ac9dac66622", - "sha256:667880321e916a8920ef49f5d50e7983792cf59f3b6079f3c9dac2b88a311d17", - "sha256:684d840d2c9ec5de9cb397fcb3f36d5ebb6fa0d94734f9886032dd796c1ead06", - "sha256:68717c38a68e37af87c4da20e08f3e27d7e4212e99e96c3d875fbf3f4812abfc", - "sha256:6b7bbb97d82659ac8b37450c60ff2e9f97e4eb0f8a8a3645a5568b9334b08b50", - "sha256:72722ce529a76a4637a60be18bd789d8fb871e84472490ed7ddff62d5fed620d", - "sha256:73c1bc8a86a5c9e8721a088df234265317692d0b5cd9e86e975ce3bc3db62a59", - "sha256:76909849d1a6bffa5a07742294f3fa1d357dc917cb1fe7b470afbc3a7579d539", - "sha256:76b86e24039c35280ceee6dce7e62945eb93a5175d43689ba98360ab31eebc4a", - "sha256:7a5d83efc109ceddb99abd2c1316298ced2adb4570410defe766851a804fcd5b", - "sha256:80e0e57cc704a52fb1b48f16d5b2c8818da087dbee6f98d9bf19546930dc64b5", - "sha256:85233abb44bc18d16e72dc05bf13848a36f363f83757541f1a97db2f8d58cfd9", - "sha256:907a4d7720abfcb1c81619863efd47c8a85d26a257a2dbebdb87c3b847df0278", - "sha256:9376d83d686ec62e8b19c0ac3bf8d28d8a5981d0df290196fb6ef24d8a26f0d6", - "sha256:94b9769ba435b598b547c762184bcfc4783d0d4c7771b04a3b45775c3589ca44", - "sha256:9a29726f91c6cb390b3c2338f0df5cd3e216ad7a938762d11c994bb37552edb0", - "sha256:9b6431559676a1079eac0f52d6d0721fb8e3c5ba43c37bc537c8c83724031feb", - "sha256:9ece8a49696669d483d206b4474c367852c44815fca23ac4e48b72b339807f80", - "sha256:a139fe9f298dc097349fb4f28c8b81cc7a202dbfba66af0e14be5cfca4ef7ce5", - "sha256:a32204489259786a923e02990249c65b0f17235073149d0033efcebe80095570", - "sha256:a3982b0a32d0a88b3907e4b0dc36809fda477f0757c59a505d4e9b455f384b8b", - "sha256:aad17e462f42ddbef5984d70c40bfc4146c322a2da79715932cd8976317054de", - "sha256:b560b72ed4816aee52783c66854d96157fd8175631f01ef58e894cc57c84f0f6", - "sha256:b6b0e4912030c6f28bcb72b9ebe4989d6dc2eebcd2a9cdc35fefc38052dd4fe8", - "sha256:baf1c7b78cddb5af00971ad5294a4583188bda1495b13760d9f03c9483bb6203", - "sha256:c0295d52b012cbe0d3059b1dba99159c3be55e632aae1999ab74ae2bd86a33d7", - "sha256:c562b49c96906b4029b5685075fe1ebd3b5cc2601dfa0b9e16c2c09d6cbce048", - "sha256:c69567ddbac186e8c0aadc1f324a60a564cfe25e43ef2ce81bcc4b8c3abffbae", - "sha256:ca71d501629d1fa50ea7fa3b08ba884fe10cefc559f5c6c8dfe9036c16e8ae89", - "sha256:ca976884ce34070799e4dfc6fbd68cb1d181db1eefe4a3a94798ddfb34b8867f", - "sha256:d0491006a6ad20507aec2be72e7831a42efc93193d2402018007ff827dc62926", - "sha256:d074b07a10c391fc5bbdcb37b2f16f20fcd9e51e10d01652ab298c0d07908ee2", - "sha256:d2ce426ee691319d4767748c8e0895cfc56593d725594e415f274059bcf3cb76", - "sha256:d4284c621f06a72ce2cb55f74ea3150113d926a6eb78ab38340c08f770eb9b4d", - "sha256:d5e6b7155b8197b329dc787356cfd2684c9d6a6b1a197f6bbf45f5555a98d411", - "sha256:d816f44a51ba5175394bc6c7879ca0bd2be560b2c9e9f3411ef3a4cbe644c2e9", - "sha256:dd3f79e17b56741b5177bcc36307750d50ea0698df6aa82f69c7db32d968c1c2", - "sha256:dd63cec4e26e790b70544ae5cc48d11b515b09e05fdd5eff12e3195f54b8a586", - "sha256:de9d3e8717560eb05e28739d1b35e4eac2e458553a52a301e51352a7ffc86a35", - "sha256:df4249b579e75094f7e9bb4bd28231acf55e308bf686b952f43100a5a0be394c", - "sha256:e178e5b66a06ec5bf51668ec0d4ac8cfb2bdcb553b2c207d58148340efd00143", - "sha256:e60defc3c15defb70bb38dd605ff7e0fae5f6c9c7cbfe0ad7868582cb7e844a6", - "sha256:ee2794111c188548a4547eccc73a6a8527fe2af6cf25e1a4ebda2fd01cdd2e60", - "sha256:ee7ccc7fb7e921d767f853b47814c3048c7de536663e82fbc37f5eb0d532224b", - "sha256:ee9cf33e7fe14243f5ca6977658eb7d1042caaa66847daacbd2117adb258b226", - "sha256:f0f17814c505f07806e22b28856c59ac80cee7dd0fbb152aed273e116378f519", - "sha256:f3202a429fe825b699c57892d4371c74cc3456d8d71b7f35d6028c96dfecad31", - "sha256:f7054fdc556f5421f01e39cbb767d5ec5c1139ea98c3e5b350e02e62201740c7", - "sha256:fd1a9edb9dd9d79fbeac1ea1f9a8dd527a6113b18d2e9bcc0d541d308dae639b" + "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b", + "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a", + "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90", + "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d", + "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e", + "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d", + "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027", + "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804", + "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347", + "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400", + "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3", + "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399", + "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349", + "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd", + "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c", + "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e", + "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413", + "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3", + "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e", + "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3", + "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91", + "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce", + "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c", + "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb", + "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664", + "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6", + "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd", + "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3", + "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af", + "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043", + "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350", + "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7", + "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0", + "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563", + "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761", + "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72", + "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3", + "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb", + "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788", + "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b", + "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c", + "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038", + "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250", + "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec", + "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c", + "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74", + "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81", + "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439", + "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75", + "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0", + "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8", + "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150", + "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438", + "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae", + "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857", + "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038", + "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374", + "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f", + "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241", + "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592", + "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4", + "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d", + "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b", + "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b", + "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182", + "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e", + "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641", + "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70", + "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9", + "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a", + "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543", + "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b", + "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f", + "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38", + "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845", + "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2", + "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0", + "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4", + "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242" ], "markers": "python_version >= '3.8'", - "version": "==2.18.1" + "version": "==2.18.2" }, "pydantic-settings": { "hashes": [ @@ -1281,12 +1281,12 @@ }, "boto3": { "hashes": [ - "sha256:168894499578a9d69d6f7deb5811952bf4171c51b95749a9aef32cf67bc71f87", - "sha256:1bd4cef11b7c5f293cede50f3d33ca89fe3413c51f1864f40163c56a732dd6b3" + "sha256:2824e3dd18743ca50e5b10439d20e74647b1416e8a94509cb30beac92d27a18d", + "sha256:b2e5cb5b95efcc881e25a3bc872d7a24e75ff4e76f368138e4baf7b9d6ee3422" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.34.88" + "version": "==1.34.90" }, "boto3-mocking": { "hashes": [ @@ -1299,28 +1299,28 @@ }, "boto3-stubs": { "hashes": [ - "sha256:23ca9e0cd0d3e7702d6631a1e94a4208a26b39fa6b12c734427e68a7fa649477", - "sha256:8f472d1bf09743c3d33304ecc8830d70ebe3ca19ac9604ae8da9af55421b0fce" + "sha256:7361f162523168ddcfb3e0cc70e5208e78f95b9f1f2553032036a2b67ab33355", + "sha256:c82f3db8558e28f766361ba1eea7c77dff735f72fef2a0b9dffaa9c0d9ae76a3" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.34.88" + "version": "==1.34.90" }, "botocore": { "hashes": [ - "sha256:36f2e9e8dfa856e55dbbe703aea601f134db3fddc3615f1020a755b27fd26a5e", - "sha256:e87a660599ed3e14b2a770f4efc3df2f2f6d04f3c7bfd64ddbae186667864a7b" + "sha256:113cd4c0cb63e13163ccbc2bb13d551be314ba7f8ba5bfab1c51a19ca01aa133", + "sha256:d48f152498e2c60b43ce25b579d26642346a327b6fb2c632d57219e0a4f63392" ], "markers": "python_version >= '3.8'", - "version": "==1.34.88" + "version": "==1.34.90" }, "botocore-stubs": { "hashes": [ - "sha256:656e966ea152a4f2828892aa7a9673bc91799998f5a8efd8e8fe390f61c2f4f1", - "sha256:f55b03ae2e1706bd56299fd2975bb048f96aa49012a866e931a040a74f85c3cc" + "sha256:b2d7416b524bce7325aa5fe09bb5e0b6bc9531d4136f4407fa39b6bc58507f34", + "sha256:d9b66542cbb8fbe28eef3c22caf941ae22d36cc1ef55b93fc0b52239457cab57" ], "markers": "python_version >= '3.8' and python_version < '4.0'", - "version": "==1.34.88" + "version": "==1.34.89" }, "click": { "hashes": [ @@ -1497,11 +1497,11 @@ }, "platformdirs": { "hashes": [ - "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068", - "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768" + "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf", + "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1" ], "markers": "python_version >= '3.8'", - "version": "==4.2.0" + "version": "==4.2.1" }, "pycodestyle": { "hashes": [ diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index 54b65e83e..cef2ec59d 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -780,3 +780,10 @@ if DEBUG: # due to Docker, bypass Debug Toolbar's check on INTERNAL_IPS "SHOW_TOOLBAR_CALLBACK": lambda _: True, } + +# From https://django-auditlog.readthedocs.io/en/latest/upgrade.html +# Run cf run-task getgov-<> --wait --command 'python manage.py auditlogmigratejson --traceback' --name auditlogmigratejson +# on our staging and stable, then remove these 2 variables or set to False +AUDITLOG_TWO_STEP_MIGRATION = True + +AUDITLOG_USE_TEXT_CHANGES_IF_JSON_IS_NOT_PRESENT = True diff --git a/src/registrar/tests/test_views_domain.py b/src/registrar/tests/test_views_domain.py index fc391f8b5..6448e91e1 100644 --- a/src/registrar/tests/test_views_domain.py +++ b/src/registrar/tests/test_views_domain.py @@ -344,8 +344,6 @@ class TestDomainManagers(TestDomainOverview): def tearDown(self): """Ensure that the user has its original permissions""" super().tearDown() - self.user.is_staff = False - self.user.save() def test_domain_managers(self): response = self.client.get(reverse("domain-users", kwargs={"pk": self.domain.id})) diff --git a/src/requirements.txt b/src/requirements.txt index 3c005f162..017d42542 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,8 +1,8 @@ -i https://pypi.python.org/simple annotated-types==0.6.0; python_version >= '3.8' asgiref==3.8.1; python_version >= '3.8' -boto3==1.34.88; python_version >= '3.8' -botocore==1.34.88; python_version >= '3.8' +boto3==1.34.90; python_version >= '3.8' +botocore==1.34.90; python_version >= '3.8' cachetools==5.3.3; python_version >= '3.7' certifi==2024.2.2; python_version >= '3.6' cfenv==0.5.3 @@ -15,7 +15,7 @@ dj-email-url==1.0.6 django==4.2.10; python_version >= '3.8' django-admin-multiple-choice-list-filter==0.1.1 django-allow-cidr==0.7.1 -django-auditlog==2.3.0; python_version >= '3.7' +django-auditlog==3.0.0; python_version >= '3.8' django-cache-url==3.4.5 django-cors-headers==4.3.1; python_version >= '3.8' django-csp==3.8 @@ -44,8 +44,8 @@ phonenumberslite==8.13.35 psycopg2-binary==2.9.9; python_version >= '3.7' pycparser==2.22; python_version >= '3.8' pycryptodomex==3.20.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' -pydantic==2.7.0; python_version >= '3.8' -pydantic-core==2.18.1; python_version >= '3.8' +pydantic==2.7.1; python_version >= '3.8' +pydantic-core==2.18.2; python_version >= '3.8' pydantic-settings==2.2.1; python_version >= '3.8' pyjwkest==1.4.2 python-dateutil==2.9.0.post0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' From ab1a63ee9c82b0762813644a2a49b4c21833918d Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 23 Apr 2024 18:07:43 -0400 Subject: [PATCH 19/62] lint --- src/registrar/config/settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index cef2ec59d..9817476bb 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -782,7 +782,8 @@ if DEBUG: } # From https://django-auditlog.readthedocs.io/en/latest/upgrade.html -# Run cf run-task getgov-<> --wait --command 'python manage.py auditlogmigratejson --traceback' --name auditlogmigratejson +# Run: +# cf run-task getgov-<> --wait --command 'python manage.py auditlogmigratejson --traceback' --name auditlogmigratejson # on our staging and stable, then remove these 2 variables or set to False AUDITLOG_TWO_STEP_MIGRATION = True From aad29096c1dbd6c279964094d67a6f88026e2018 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:19:15 -0600 Subject: [PATCH 20/62] Fix migrations --- ...ication_type.py => 0088_user_verification_type.py} | 4 ++-- .../django/admin/includes/detail_table_fieldset.html | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) rename src/registrar/migrations/{0087_user_verification_type.py => 0088_user_verification_type.py} (84%) diff --git a/src/registrar/migrations/0087_user_verification_type.py b/src/registrar/migrations/0088_user_verification_type.py similarity index 84% rename from src/registrar/migrations/0087_user_verification_type.py rename to src/registrar/migrations/0088_user_verification_type.py index 599d067d5..7fac95a3d 100644 --- a/src/registrar/migrations/0087_user_verification_type.py +++ b/src/registrar/migrations/0088_user_verification_type.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-04-22 16:40 +# Generated by Django 4.2.10 on 2024-04-23 20:47 from django.db import migrations, models @@ -6,7 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("registrar", "0086_domaininformation_updated_federal_agency_and_more"), + ("registrar", "0087_alter_domain_deleted_alter_domain_expiration_date_and_more"), ] operations = [ diff --git a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html index 6374406c1..0f4202b7e 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -4,10 +4,11 @@ {% comment %} This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endcomment %} + {% block field_readonly %} {% with all_contacts=original_object.other_contacts.all %} {% if field.field.name == "creator" %} -
{{ field.contents }} ({{ user.verification_type }})
+
{{ field.contents }} ({{ user.get_verification_type_display }})
{% elif field.field.name == "other_contacts" %} {% if all_contacts.count > 2 %}
@@ -68,9 +69,15 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endblock field_readonly %} {% block help_text %} -
+
{{ field.field.help_text|safe }}
+ + {% if not field.is_readonly and field.field.name == "creator" %} +
+
({{ user.get_verification_type_display }})
+
+ {% endif %} {% endblock help_text %} From 4fa777d671bfd46b233b67dd8c9dc34c74072bda Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 25 Apr 2024 09:58:45 -0600 Subject: [PATCH 21/62] Add verification type --- .../admin/includes/contact_detail_list.html | 7 ++++++- .../admin/includes/detail_table_fieldset.html | 19 ++----------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/registrar/templates/django/admin/includes/contact_detail_list.html b/src/registrar/templates/django/admin/includes/contact_detail_list.html index 5ac5452e3..3b49e62a4 100644 --- a/src/registrar/templates/django/admin/includes/contact_detail_list.html +++ b/src/registrar/templates/django/admin/includes/contact_detail_list.html @@ -47,7 +47,12 @@ {% else %} None
{% endif %} + {% else %} - No additional contact information found. + No additional contact information found.
+ {% endif %} + + {% if user_verification_type %} + {{ user_verification_type }} {% endif %}
diff --git a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html index 0f4202b7e..c85bfd27a 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -7,9 +7,7 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% block field_readonly %} {% with all_contacts=original_object.other_contacts.all %} - {% if field.field.name == "creator" %} -
{{ field.contents }} ({{ user.get_verification_type_display }})
- {% elif field.field.name == "other_contacts" %} + {% if field.field.name == "other_contacts" %} {% if all_contacts.count > 2 %}
{% for contact in all_contacts %} @@ -68,24 +66,11 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endwith %} {% endblock field_readonly %} -{% block help_text %} -
-
{{ field.field.help_text|safe }}
-
- - {% if not field.is_readonly and field.field.name == "creator" %} -
-
({{ user.get_verification_type_display }})
-
- {% endif %} -{% endblock help_text %} - - {% block after_help_text %} {% if field.field.name == "creator" %}
- {% include "django/admin/includes/contact_detail_list.html" with user=original_object.creator no_title_top_padding=field.is_readonly %} + {% include "django/admin/includes/contact_detail_list.html" with user=original_object.creator no_title_top_padding=field.is_readonly user_verification_type=original_object.creator.get_verification_type_display%}
{% include "django/admin/includes/user_detail_list.html" with user=original_object.creator no_title_top_padding=field.is_readonly %} {% elif field.field.name == "submitter" %} From 07ac2b8fa4f2f15fb62b5f550c44c28b275fb1fa Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 25 Apr 2024 10:06:23 -0600 Subject: [PATCH 22/62] Add padding --- src/registrar/assets/sass/_theme/_admin.scss | 4 ++++ .../django/admin/includes/detail_table_fieldset.html | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index f5717d067..e22ec905e 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -617,3 +617,7 @@ address.dja-address-contact-list { .usa-button__small-text { font-size: small; } + +.padding-left-0__important { + padding-left: 0px !important; +} diff --git a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html index c85bfd27a..123773313 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -66,9 +66,15 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endwith %} {% endblock field_readonly %} +{% block help_text %} +
+
{{ field.field.help_text|safe }}
+
+{% endblock help_text %} + {% block after_help_text %} {% if field.field.name == "creator" %} -
+
{% include "django/admin/includes/contact_detail_list.html" with user=original_object.creator no_title_top_padding=field.is_readonly user_verification_type=original_object.creator.get_verification_type_display%}
From 2a10e76c5d7b244e63c18b51c6f0e5e7ecf32a4c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 25 Apr 2024 10:12:50 -0600 Subject: [PATCH 23/62] Add additional padding --- .../django/admin/includes/detail_table_fieldset.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html index 123773313..d96fa469c 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -74,13 +74,13 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% block after_help_text %} {% if field.field.name == "creator" %} -
+
{% include "django/admin/includes/contact_detail_list.html" with user=original_object.creator no_title_top_padding=field.is_readonly user_verification_type=original_object.creator.get_verification_type_display%}
{% include "django/admin/includes/user_detail_list.html" with user=original_object.creator no_title_top_padding=field.is_readonly %} {% elif field.field.name == "submitter" %} -
+
{% include "django/admin/includes/contact_detail_list.html" with user=original_object.submitter no_title_top_padding=field.is_readonly %}
From 9e6cddb02e94e950ef4a01d7a0d9c6e0e17c8b22 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 25 Apr 2024 10:18:25 -0600 Subject: [PATCH 24/62] Margin top 2 --- .../django/admin/includes/detail_table_fieldset.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html index d96fa469c..9ec5e500d 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -74,13 +74,13 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% block after_help_text %} {% if field.field.name == "creator" %} -
+
{% include "django/admin/includes/contact_detail_list.html" with user=original_object.creator no_title_top_padding=field.is_readonly user_verification_type=original_object.creator.get_verification_type_display%}
{% include "django/admin/includes/user_detail_list.html" with user=original_object.creator no_title_top_padding=field.is_readonly %} {% elif field.field.name == "submitter" %} -
+
{% include "django/admin/includes/contact_detail_list.html" with user=original_object.submitter no_title_top_padding=field.is_readonly %}
From 9d1f87ce7873af9d53f599d2a4d521a592fcb5ff Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 25 Apr 2024 15:46:49 -0600 Subject: [PATCH 25/62] Switch to ABC --- .../management/commands/populate_verification_type.py | 4 ++-- .../management/commands/utility/terminal_helper.py | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/registrar/management/commands/populate_verification_type.py b/src/registrar/management/commands/populate_verification_type.py index da493b8cf..a20c3e809 100644 --- a/src/registrar/management/commands/populate_verification_type.py +++ b/src/registrar/management/commands/populate_verification_type.py @@ -1,13 +1,13 @@ import logging from typing import List from django.core.management import BaseCommand -from registrar.management.commands.utility.terminal_helper import ScriptTemplate, TerminalColors +from registrar.management.commands.utility.terminal_helper import PopulateScriptTemplate, TerminalColors from registrar.models import User logger = logging.getLogger(__name__) -class Command(ScriptTemplate): +class Command(BaseCommand, PopulateScriptTemplate): help = "Loops through each valid User object and updates its verification_type value" def handle(self, **kwargs): diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index 1d411e403..5076d1de8 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -1,5 +1,6 @@ import logging import sys +from abc import ABC, abstractmethod from django.core.paginator import Paginator from typing import List from django.core.management import BaseCommand @@ -59,9 +60,9 @@ class ScriptDataHelper: model_class.objects.bulk_update(page.object_list, fields_to_update) -class ScriptTemplate(BaseCommand): +class PopulateScriptTemplate(ABC): """ - Contains common script actions for our scripts which can be prefilled as templates. + Contains an ABC for generic populate scripts """ def mass_populate_field(self, sender, filter_conditions, fields_to_update): @@ -102,9 +103,10 @@ class ScriptTemplate(BaseCommand): # Log what happened TerminalHelper.log_script_run_summary(to_update, failed_to_update, skipped=[], debug=True) + @abstractmethod def populate_field(self, field_to_update): """Defines how we update each field. Must be defined before using mass_populate_field.""" - raise NotImplementedError("This method should be implemented by the child class.") + pass class TerminalHelper: From 86ef903a0794f05460ac1675fa0ffa052220dce2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 26 Apr 2024 08:00:34 -0600 Subject: [PATCH 26/62] Remove help text padding css --- src/registrar/assets/sass/_theme/_admin.scss | 5 +++-- .../django/admin/includes/detail_table_fieldset.html | 6 ------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index e22ec905e..5c577dbb7 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -618,6 +618,7 @@ address.dja-address-contact-list { font-size: small; } -.padding-left-0__important { - padding-left: 0px !important; +// Get rid of padding on all help texts +form .aligned p.help, form .aligned div.help { + padding-left: 0px } diff --git a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html index 9ec5e500d..ea8e7579f 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -66,12 +66,6 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endwith %} {% endblock field_readonly %} -{% block help_text %} -
-
{{ field.field.help_text|safe }}
-
-{% endblock help_text %} - {% block after_help_text %} {% if field.field.name == "creator" %}
From d9408076b13f63c5f85571da1487f0fb0ecf2af5 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 26 Apr 2024 08:07:50 -0600 Subject: [PATCH 27/62] Fix migrations, PR suggestions --- src/djangooidc/tests/test_views.py | 5 ++++- .../management/commands/utility/terminal_helper.py | 2 +- ..._verification_type.py => 0089_user_verification_type.py} | 4 ++-- src/registrar/models/user.py | 6 +++--- 4 files changed, 10 insertions(+), 7 deletions(-) rename src/registrar/migrations/{0088_user_verification_type.py => 0089_user_verification_type.py} (84%) diff --git a/src/djangooidc/tests/test_views.py b/src/djangooidc/tests/test_views.py index fc93db82e..faf9cbdd8 100644 --- a/src/djangooidc/tests/test_views.py +++ b/src/djangooidc/tests/test_views.py @@ -241,7 +241,10 @@ class ViewsTest(TestCase): @less_console_noise_decorator def test_login_callback_sets_verification_type_regular(self, mock_client): - """Test that openid sets the verification type to regular on the returned user""" + """ + Test that openid sets the verification type to regular on the returned user. + Regular, in this context, means that this user was "Verifed by Login.gov" + """ # SETUP session = self.client.session session.save() diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index 5076d1de8..2376439a6 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -93,7 +93,7 @@ class PopulateScriptTemplate(ABC): self.populate_field(updated_object) to_update.append(updated_object) except Exception as err: - to_update.append(updated_object) + failed_to_update.append(updated_object) logger.error(err) logger.error(f"{TerminalColors.FAIL}" f"Failed to update {updated_object}" f"{TerminalColors.ENDC}") diff --git a/src/registrar/migrations/0088_user_verification_type.py b/src/registrar/migrations/0089_user_verification_type.py similarity index 84% rename from src/registrar/migrations/0088_user_verification_type.py rename to src/registrar/migrations/0089_user_verification_type.py index 7fac95a3d..e021e89e1 100644 --- a/src/registrar/migrations/0088_user_verification_type.py +++ b/src/registrar/migrations/0089_user_verification_type.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-04-23 20:47 +# Generated by Django 4.2.10 on 2024-04-26 14:03 from django.db import migrations, models @@ -6,7 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("registrar", "0087_alter_domain_deleted_alter_domain_expiration_date_and_more"), + ("registrar", "0088_domaininformation_cisa_representative_email_and_more"), ] operations = [ diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index 0b16d45cb..0ae785562 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -149,10 +149,10 @@ class User(AbstractUser): # always exist at this point. We do it down the line. verification_type = cls.get_verification_type_from_email(email) + # Checks if the user needs verification. # The user needs identity verification if they don't meet # any special criteria, i.e. we are validating them "regularly" - needs_verification = verification_type == cls.VerificationTypeChoices.REGULAR - return needs_verification + return verification_type == cls.VerificationTypeChoices.REGULAR def set_user_verification_type(self): """ @@ -174,7 +174,7 @@ class User(AbstractUser): # If you joined BEFORE the oldest invitation was created, then you were verified normally. # (See logic in get_verification_type_from_email) - if invitation is not None and self.date_joined < invitation.created_at: + if not invitation and self.date_joined < invitation.created_at: verification_type = User.VerificationTypeChoices.REGULAR self.verification_type = verification_type From 83c7a5cdfdbbd22d1d3f224d3b7cf8ad5e1143be Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 26 Apr 2024 08:11:43 -0600 Subject: [PATCH 28/62] Linting --- src/djangooidc/tests/test_views.py | 22 +++++++++++++++------- src/registrar/admin.py | 7 ++++++- src/registrar/models/user.py | 1 - src/registrar/tests/test_admin.py | 10 +++++++++- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/djangooidc/tests/test_views.py b/src/djangooidc/tests/test_views.py index faf9cbdd8..36a7e1e47 100644 --- a/src/djangooidc/tests/test_views.py +++ b/src/djangooidc/tests/test_views.py @@ -17,7 +17,7 @@ class ViewsTest(TestCase): def setUp(self): self.client = Client() self.factory = RequestFactory() - + def tearDown(self): User.objects.all().delete() Contact.objects.all().delete() @@ -238,7 +238,7 @@ class ViewsTest(TestCase): # assert that redirect is to / when no 'next' is set self.assertEqual(response.status_code, 302) self.assertEqual(response.url, "/") - + @less_console_noise_decorator def test_login_callback_sets_verification_type_regular(self, mock_client): """ @@ -252,7 +252,9 @@ class ViewsTest(TestCase): # mock that callback returns user_info; this is the expected behavior mock_client.callback.side_effect = self.user_info # patch that the request does not require step up auth - with patch("djangooidc.views._requires_step_up_auth", return_value=False), patch("djangooidc.views._initialize_client") as mock_init_client: + with patch("djangooidc.views._requires_step_up_auth", return_value=False), patch( + "djangooidc.views._initialize_client" + ) as mock_init_client: with patch("djangooidc.views._client_is_none", return_value=True): # TEST # test the login callback url @@ -283,7 +285,9 @@ class ViewsTest(TestCase): # mock that callback returns user_info; this is the expected behavior mock_client.callback.side_effect = self.user_info # patch that the request does not require step up auth - with patch("djangooidc.views._requires_step_up_auth", return_value=False), patch("djangooidc.views._initialize_client") as mock_init_client: + with patch("djangooidc.views._requires_step_up_auth", return_value=False), patch( + "djangooidc.views._initialize_client" + ) as mock_init_client: with patch("djangooidc.views._client_is_none", return_value=True): # TEST # test the login callback url @@ -314,7 +318,9 @@ class ViewsTest(TestCase): td, _ = TransitionDomain.objects.get_or_create(username="test@example.com", domain_name="test123.gov") # patch that the request does not require step up auth - with patch("djangooidc.views._requires_step_up_auth", return_value=False), patch("djangooidc.views._initialize_client") as mock_init_client: + with patch("djangooidc.views._requires_step_up_auth", return_value=False), patch( + "djangooidc.views._initialize_client" + ) as mock_init_client: with patch("djangooidc.views._client_is_none", return_value=True): # TEST # test the login callback url @@ -334,7 +340,7 @@ class ViewsTest(TestCase): @less_console_noise_decorator def test_login_callback_sets_verification_type_verified_by_staff(self, mock_client): - """Test that openid sets the verification type to verified_by_staff + """Test that openid sets the verification type to verified_by_staff on a user which exists in our VerifiedByStaff table""" # SETUP session = self.client.session @@ -346,7 +352,9 @@ class ViewsTest(TestCase): vip, _ = VerifiedByStaff.objects.get_or_create(email="test@example.com") # patch that the request does not require step up auth - with patch("djangooidc.views._requires_step_up_auth", return_value=False), patch("djangooidc.views._initialize_client") as mock_init_client: + with patch("djangooidc.views._requires_step_up_auth", return_value=False), patch( + "djangooidc.views._initialize_client" + ) as mock_init_client: with patch("djangooidc.views._client_is_none", return_value=True): # TEST # test the login callback url diff --git a/src/registrar/admin.py b/src/registrar/admin.py index b7a5a0503..e1378ec53 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -563,7 +563,12 @@ class MyUserAdmin(BaseUserAdmin): analyst_fieldsets = ( ( None, - {"fields": ("status", "verification_type",)}, + { + "fields": ( + "status", + "verification_type", + ) + }, ), ("Personal Info", {"fields": ("first_name", "last_name", "email")}), ( diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index 0ae785562..5e4c88f63 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -1,4 +1,3 @@ -from enum import Enum import logging from django.contrib.auth.models import AbstractUser diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index eeda99efb..f853c7b11 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -3043,7 +3043,15 @@ class TestMyUserAdmin(TestCase): request.user = create_user() fieldsets = self.admin.get_fieldsets(request) expected_fieldsets = ( - (None, {"fields": ("status", "verification_type",)}), + ( + None, + { + "fields": ( + "status", + "verification_type", + ) + }, + ), ("Personal Info", {"fields": ("first_name", "last_name", "email")}), ("Permissions", {"fields": ("is_active", "groups")}), ("Important dates", {"fields": ("last_login", "date_joined")}), From a7ef0cfa03d2ee96804709240a38254a084c5dfa Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 26 Apr 2024 08:21:53 -0600 Subject: [PATCH 29/62] Update _admin.scss --- src/registrar/assets/sass/_theme/_admin.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index 5c577dbb7..82224e6ee 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -620,5 +620,5 @@ address.dja-address-contact-list { // Get rid of padding on all help texts form .aligned p.help, form .aligned div.help { - padding-left: 0px + padding-left: 0px !important; } From 0e92cd41dbfd1cbddf9900c74a835e7e003992e2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 26 Apr 2024 08:28:50 -0600 Subject: [PATCH 30/62] Linting pt 2 --- .../management/commands/populate_verification_type.py | 3 ++- src/registrar/management/commands/utility/terminal_helper.py | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/registrar/management/commands/populate_verification_type.py b/src/registrar/management/commands/populate_verification_type.py index a20c3e809..802116fbc 100644 --- a/src/registrar/management/commands/populate_verification_type.py +++ b/src/registrar/management/commands/populate_verification_type.py @@ -19,5 +19,6 @@ class Command(BaseCommand, PopulateScriptTemplate): """Defines how we update the verification_type field""" field_to_update.set_user_verification_type() logger.info( - f"{TerminalColors.OKCYAN}Updating {field_to_update} => {field_to_update.verification_type}{TerminalColors.OKCYAN}" + f"{TerminalColors.OKCYAN}Updating {field_to_update} => " + f"{field_to_update.verification_type}{TerminalColors.OKCYAN}" ) diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index 2376439a6..255d576db 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -3,7 +3,6 @@ import sys from abc import ABC, abstractmethod from django.core.paginator import Paginator from typing import List -from django.core.management import BaseCommand from registrar.utility.enums import LogCode logger = logging.getLogger(__name__) @@ -106,7 +105,7 @@ class PopulateScriptTemplate(ABC): @abstractmethod def populate_field(self, field_to_update): """Defines how we update each field. Must be defined before using mass_populate_field.""" - pass + pass # noqa class TerminalHelper: From 914cab8fc8cebadc0e86215ccdc218626400f000 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 26 Apr 2024 12:21:08 -0600 Subject: [PATCH 31/62] Linting fix --- src/djangooidc/tests/test_views.py | 3 ++- .../management/commands/populate_verification_type.py | 1 - src/registrar/management/commands/utility/terminal_helper.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/djangooidc/tests/test_views.py b/src/djangooidc/tests/test_views.py index 36a7e1e47..bdd61b346 100644 --- a/src/djangooidc/tests/test_views.py +++ b/src/djangooidc/tests/test_views.py @@ -307,7 +307,8 @@ class ViewsTest(TestCase): @less_console_noise_decorator def test_login_callback_sets_verification_type_grandfathered(self, mock_client): - """Test that openid sets the verification type to grandfathered on a user which exists in our TransitionDomain table""" + """Test that openid sets the verification type to grandfathered + on a user which exists in our TransitionDomain table""" # SETUP session = self.client.session session.save() diff --git a/src/registrar/management/commands/populate_verification_type.py b/src/registrar/management/commands/populate_verification_type.py index 802116fbc..b61521977 100644 --- a/src/registrar/management/commands/populate_verification_type.py +++ b/src/registrar/management/commands/populate_verification_type.py @@ -1,5 +1,4 @@ import logging -from typing import List from django.core.management import BaseCommand from registrar.management.commands.utility.terminal_helper import PopulateScriptTemplate, TerminalColors from registrar.models import User diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index 255d576db..db3e4a9d3 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -105,7 +105,7 @@ class PopulateScriptTemplate(ABC): @abstractmethod def populate_field(self, field_to_update): """Defines how we update each field. Must be defined before using mass_populate_field.""" - pass # noqa + raise NotImplementedError class TerminalHelper: From cb88a49079d2921cba819dae117639ba1ddaaac7 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 10:37:31 -0600 Subject: [PATCH 32/62] Update deploy-sandbox.yaml --- .github/workflows/deploy-sandbox.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index f7f4a0d65..a50abc421 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -38,6 +38,9 @@ jobs: runs-on: ubuntu-latest needs: [variables] steps: + - uses: actions/setup-node@v4 + with: + node-version: '23.0.0-nightly202404297c3dce0e4f' - uses: actions/checkout@v3 - name: Compile USWDS assets working-directory: ./src From eeb5206c157e7d152c48be040437e6fa81daca9f Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 10:43:08 -0600 Subject: [PATCH 33/62] Change uses order --- .github/workflows/deploy-sandbox.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index a50abc421..a6b315d6b 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -38,10 +38,10 @@ jobs: runs-on: ubuntu-latest needs: [variables] steps: + - uses: actions/checkout@v3 - uses: actions/setup-node@v4 with: node-version: '23.0.0-nightly202404297c3dce0e4f' - - uses: actions/checkout@v3 - name: Compile USWDS assets working-directory: ./src run: | From 00c8d1cd42a46f69e080eb8094c50be8decd53bb Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 12:29:05 -0600 Subject: [PATCH 34/62] Update package.json --- src/package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/package.json b/src/package.json index 274e0e282..780ef587f 100644 --- a/src/package.json +++ b/src/package.json @@ -3,6 +3,10 @@ "version": "1.0.0", "description": "========================", "main": "index.js", + "engines": { + "node": "21.7.3" + }, + "engineStrict": true, "scripts": { "pa11y-ci": "pa11y-ci", "test": "echo \"Error: no test specified\" && exit 1" From 13ab27aee27ad7baaf14214cfc9af8f8e1163016 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:02:06 -0600 Subject: [PATCH 35/62] Update version --- .github/workflows/deploy-sandbox.yaml | 3 --- src/node.Dockerfile | 6 ++++-- src/package-lock.json | 4 ++++ src/package.json | 3 ++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index a6b315d6b..f7f4a0d65 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -39,9 +39,6 @@ jobs: needs: [variables] steps: - uses: actions/checkout@v3 - - uses: actions/setup-node@v4 - with: - node-version: '23.0.0-nightly202404297c3dce0e4f' - name: Compile USWDS assets working-directory: ./src run: | diff --git a/src/node.Dockerfile b/src/node.Dockerfile index b478a8a26..cf0b6acc6 100644 --- a/src/node.Dockerfile +++ b/src/node.Dockerfile @@ -1,5 +1,5 @@ FROM docker.io/cimg/node:current-browsers - +FROM node:21.7.3 WORKDIR /app # Install app dependencies @@ -7,4 +7,6 @@ WORKDIR /app # where available (npm@5+) COPY --chown=circleci:circleci package*.json ./ -RUN npm install + +RUN npm install -g npm@10.5.0 +RUN npm install \ No newline at end of file diff --git a/src/package-lock.json b/src/package-lock.json index dc1464ee8..9df99a739 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -15,6 +15,10 @@ }, "devDependencies": { "@uswds/compile": "^1.0.0-beta.3" + }, + "engines": { + "node": "21.7.3", + "npm": "10.5.0" } }, "node_modules/@gulp-sourcemaps/identity-map": { diff --git a/src/package.json b/src/package.json index 780ef587f..1ceb8cb93 100644 --- a/src/package.json +++ b/src/package.json @@ -4,7 +4,8 @@ "description": "========================", "main": "index.js", "engines": { - "node": "21.7.3" + "node": "21.7.3", + "npm": "10.5.0" }, "engineStrict": true, "scripts": { From 09b8ec67ef38d59f517687fd617c2afdef219613 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:08:16 -0600 Subject: [PATCH 36/62] Update deploy-sandbox.yaml --- .github/workflows/deploy-sandbox.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index f7f4a0d65..3e0f1e881 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -39,6 +39,9 @@ jobs: needs: [variables] steps: - uses: actions/checkout@v3 + - uses: actions/setup-node@v4 + with: + node-version: '21.7.3' - name: Compile USWDS assets working-directory: ./src run: | From 5e03834954e51e43095418c43bf128b530fbebeb Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:12:57 -0600 Subject: [PATCH 37/62] Update deploy-sandbox.yaml --- .github/workflows/deploy-sandbox.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 3e0f1e881..78d924155 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -39,9 +39,13 @@ jobs: needs: [variables] steps: - uses: actions/checkout@v3 - - uses: actions/setup-node@v4 + - name: Set up Node js + uses: actions/setup-node@v4 with: node-version: '21.7.3' + run: | + npm install -g npm@10.5.0 + npm install -g node@21.7.3 - name: Compile USWDS assets working-directory: ./src run: | From ec69d7f6967f6eca39444d8d73aee35ed451f7e1 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:14:35 -0600 Subject: [PATCH 38/62] Update deploy-sandbox.yaml --- .github/workflows/deploy-sandbox.yaml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 78d924155..0eba23b83 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -39,16 +39,13 @@ jobs: needs: [variables] steps: - uses: actions/checkout@v3 - - name: Set up Node js - uses: actions/setup-node@v4 - with: - node-version: '21.7.3' - run: | - npm install -g npm@10.5.0 - npm install -g node@21.7.3 - name: Compile USWDS assets + uses: actions/setup-node@v4 + with: + node-version: '21.7.3' working-directory: ./src run: | + docker compose run node npm install -g npm@10.5.0 docker compose run node npm install && docker compose run node npx gulp copyAssets && docker compose run node npx gulp compile From e1e04d977859bb395629ce30e97bea4a7d4001d4 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:16:35 -0600 Subject: [PATCH 39/62] Specify node version --- .github/workflows/deploy-sandbox.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 0eba23b83..036cbe503 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -40,12 +40,10 @@ jobs: steps: - uses: actions/checkout@v3 - name: Compile USWDS assets - uses: actions/setup-node@v4 - with: - node-version: '21.7.3' working-directory: ./src run: | - docker compose run node npm install -g npm@10.5.0 + docker compose run node npm install -g node@21.7.3 && + docker compose run node npm install -g npm@10.5.0 && docker compose run node npm install && docker compose run node npx gulp copyAssets && docker compose run node npx gulp compile From 2f4ac994c7e02eae1201b71159ae97c791efb422 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:21:39 -0600 Subject: [PATCH 40/62] use nvm --- .github/workflows/deploy-sandbox.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 036cbe503..4253d9a41 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -42,8 +42,8 @@ jobs: - name: Compile USWDS assets working-directory: ./src run: | - docker compose run node npm install -g node@21.7.3 && - docker compose run node npm install -g npm@10.5.0 && + nvm install 21.7.3 && + nvm use 21.7.3 && docker compose run node npm install && docker compose run node npx gulp copyAssets && docker compose run node npx gulp compile From 3865d8440c7a327e0fa0f4ccf024627cacf9b7df Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:23:09 -0600 Subject: [PATCH 41/62] Update deploy-sandbox.yaml --- .github/workflows/deploy-sandbox.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 4253d9a41..7be51fbba 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -42,6 +42,10 @@ jobs: - name: Compile USWDS assets working-directory: ./src run: | + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" nvm install 21.7.3 && nvm use 21.7.3 && docker compose run node npm install && From 04080d85f32c9fe847f5755ece1192b74bac7eb9 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:28:27 -0600 Subject: [PATCH 42/62] Use correct node version --- .github/workflows/deploy-sandbox.yaml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 7be51fbba..77593b3d5 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -39,15 +39,13 @@ jobs: needs: [variables] steps: - uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '21.7.3' - name: Compile USWDS assets working-directory: ./src run: | - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash - export NVM_DIR="$HOME/.nvm" - [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" - nvm install 21.7.3 && - nvm use 21.7.3 && docker compose run node npm install && docker compose run node npx gulp copyAssets && docker compose run node npx gulp compile From 66558ae80c39e8abc34ad975cbbcb0387d1c3773 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:34:00 -0600 Subject: [PATCH 43/62] Update deploy-sandbox.yaml --- .github/workflows/deploy-sandbox.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 77593b3d5..67a8e7476 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -40,13 +40,21 @@ jobs: steps: - uses: actions/checkout@v3 - name: Setup Node.js + working-directory: ./src uses: actions/setup-node@v4 with: node-version: '21.7.3' + cache: 'npm' + + - name: Prepare Node environment and install dependencies + working-directory: ./src + run: | + docker compose run node npm install -g npm@10.5.0 + docker compose run node npm install + - name: Compile USWDS assets working-directory: ./src run: | - docker compose run node npm install && docker compose run node npx gulp copyAssets && docker compose run node npx gulp compile - name: Collect static assets From e663dee659cf38183cb3dc7e086e9bc36934e2fe Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:35:58 -0600 Subject: [PATCH 44/62] Update deploy-sandbox.yaml --- .github/workflows/deploy-sandbox.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 67a8e7476..e7972454f 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -45,12 +45,10 @@ jobs: with: node-version: '21.7.3' cache: 'npm' - - - name: Prepare Node environment and install dependencies + - name: Prepare Node environment and adjust permissions working-directory: ./src run: | - docker compose run node npm install -g npm@10.5.0 - docker compose run node npm install + docker compose run node sh -c "chown circleci:circleci package*.json && npm install -g npm@10.5.0 && npm install" - name: Compile USWDS assets working-directory: ./src From 1313527b9d45f663d904326155de92e8a789087c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:36:22 -0600 Subject: [PATCH 45/62] Remove cache --- .github/workflows/deploy-sandbox.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index e7972454f..b7798f858 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -44,7 +44,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: '21.7.3' - cache: 'npm' + - name: Prepare Node environment and adjust permissions working-directory: ./src run: | From 9ca48c06d9f082e4ccf452c9bc0661fa0382e71f Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:38:00 -0600 Subject: [PATCH 46/62] Update deploy-sandbox.yaml --- .github/workflows/deploy-sandbox.yaml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index b7798f858..6c9821112 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -44,15 +44,11 @@ jobs: uses: actions/setup-node@v4 with: node-version: '21.7.3' - - - name: Prepare Node environment and adjust permissions - working-directory: ./src - run: | - docker compose run node sh -c "chown circleci:circleci package*.json && npm install -g npm@10.5.0 && npm install" - - name: Compile USWDS assets working-directory: ./src run: | + docker compose run node npm install && + docker compose run node npm install npm@10.5.0 && docker compose run node npx gulp copyAssets && docker compose run node npx gulp compile - name: Collect static assets From 708c53b8cb49a61c91b0e97a6e69b5480f255841 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:38:44 -0600 Subject: [PATCH 47/62] Add correct spacing --- .github/workflows/deploy-sandbox.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 6c9821112..ec7e5623f 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -42,13 +42,17 @@ jobs: - name: Setup Node.js working-directory: ./src uses: actions/setup-node@v4 - with: - node-version: '21.7.3' + with: + node-version: '21.7.3' + + - name: Prepare Node environment and adjust permissions + working-directory: ./src + run: | + docker compose run node sh -c "chown circleci:circleci package*.json && npm install -g npm@10.5.0 && npm install" + - name: Compile USWDS assets working-directory: ./src run: | - docker compose run node npm install && - docker compose run node npm install npm@10.5.0 && docker compose run node npx gulp copyAssets && docker compose run node npx gulp compile - name: Collect static assets From bb24f723551c2485ee711ddfc4487e720aaba6af Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:43:59 -0600 Subject: [PATCH 48/62] Update deploy-sandbox.yaml --- .github/workflows/deploy-sandbox.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index ec7e5623f..b7798f858 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -42,8 +42,8 @@ jobs: - name: Setup Node.js working-directory: ./src uses: actions/setup-node@v4 - with: - node-version: '21.7.3' + with: + node-version: '21.7.3' - name: Prepare Node environment and adjust permissions working-directory: ./src From 0e66070e85ea9da09e870401c323def5e24ade1f Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:44:44 -0600 Subject: [PATCH 49/62] Update deploy-sandbox.yaml --- .github/workflows/deploy-sandbox.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index b7798f858..4dead7625 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -40,7 +40,6 @@ jobs: steps: - uses: actions/checkout@v3 - name: Setup Node.js - working-directory: ./src uses: actions/setup-node@v4 with: node-version: '21.7.3' From 3965c7d199c0d865c9a280739e61d0c84929ff4b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:46:36 -0600 Subject: [PATCH 50/62] Update deploy-sandbox.yaml --- .github/workflows/deploy-sandbox.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 4dead7625..e8edb096a 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -44,14 +44,11 @@ jobs: with: node-version: '21.7.3' - - name: Prepare Node environment and adjust permissions - working-directory: ./src - run: | - docker compose run node sh -c "chown circleci:circleci package*.json && npm install -g npm@10.5.0 && npm install" - - name: Compile USWDS assets working-directory: ./src run: | + npm install && + docker compose run node npm install npm@10.5.0 && docker compose run node npx gulp copyAssets && docker compose run node npx gulp compile - name: Collect static assets From ad72c49de7fa043935ec4e4331478e47d237a97a Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:55:13 -0600 Subject: [PATCH 51/62] Update deploy-sandbox.yaml --- .github/workflows/deploy-sandbox.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index e8edb096a..45975cfa8 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -39,10 +39,10 @@ jobs: needs: [variables] steps: - uses: actions/checkout@v3 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '21.7.3' + - uses: actions/setup-node@v4 + with: + node-version: '21.7.3' + cache: 'npm' - name: Compile USWDS assets working-directory: ./src From 1d58ead3e1407e79774e567aab60ef797d1a1aa3 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:55:21 -0600 Subject: [PATCH 52/62] Update deploy-sandbox.yaml --- .github/workflows/deploy-sandbox.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 45975cfa8..93564efb7 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -40,9 +40,9 @@ jobs: steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v4 - with: - node-version: '21.7.3' - cache: 'npm' + with: + node-version: '21.7.3' + cache: 'npm' - name: Compile USWDS assets working-directory: ./src From 65635dc8e3d11eb75fffaee825afd17cdd42c6f2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:55:47 -0600 Subject: [PATCH 53/62] Update deploy-sandbox.yaml --- .github/workflows/deploy-sandbox.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 93564efb7..62e27293e 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -47,7 +47,7 @@ jobs: - name: Compile USWDS assets working-directory: ./src run: | - npm install && + docker compose run node npm install && docker compose run node npm install npm@10.5.0 && docker compose run node npx gulp copyAssets && docker compose run node npx gulp compile From 596afdb6de3c902e1fc4dfe98aeea6f1b9b57cf7 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:57:20 -0600 Subject: [PATCH 54/62] Add cache dependency path --- .github/workflows/deploy-sandbox.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 62e27293e..916b8c6de 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -43,6 +43,7 @@ jobs: with: node-version: '21.7.3' cache: 'npm' + cache-dependency-path: ./src/package-lock.json - name: Compile USWDS assets working-directory: ./src From 6e06779c30efa1eeda72327126d168743e62242e Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:05:20 -0600 Subject: [PATCH 55/62] Update deploy-sandbox.yaml --- .github/workflows/deploy-sandbox.yaml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 916b8c6de..d32cbb3e2 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -39,17 +39,11 @@ jobs: needs: [variables] steps: - uses: actions/checkout@v3 - - uses: actions/setup-node@v4 - with: - node-version: '21.7.3' - cache: 'npm' - cache-dependency-path: ./src/package-lock.json - - name: Compile USWDS assets working-directory: ./src run: | + docker compose run node npm install --global --force node@21.7.3 && docker compose run node npm install && - docker compose run node npm install npm@10.5.0 && docker compose run node npx gulp copyAssets && docker compose run node npx gulp compile - name: Collect static assets From 40795f2e47dfe7d9575e7423cb2de27d330f5ed6 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:07:22 -0600 Subject: [PATCH 56/62] Force install node version --- .github/workflows/deploy-sandbox.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index d32cbb3e2..2af58f716 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -42,8 +42,8 @@ jobs: - name: Compile USWDS assets working-directory: ./src run: | - docker compose run node npm install --global --force node@21.7.3 && docker compose run node npm install && + docker compose run node npm install --global --force node@21.7.3 && docker compose run node npx gulp copyAssets && docker compose run node npx gulp compile - name: Collect static assets From 75f3a1cca1ecc13365cb1edb3ce01f42170af437 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:19:40 -0600 Subject: [PATCH 57/62] Try nvm --- .github/workflows/deploy-sandbox.yaml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 2af58f716..d53313e08 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -39,11 +39,20 @@ jobs: needs: [variables] steps: - uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '21.7.3' - name: Compile USWDS assets working-directory: ./src run: | + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" + docker compose run node nvm install 21.7.3 && + docker compose run node nvm use 21.7.3 && docker compose run node npm install && - docker compose run node npm install --global --force node@21.7.3 && docker compose run node npx gulp copyAssets && docker compose run node npx gulp compile - name: Collect static assets From 4c6e555a054e2ecce81ebf14e8ea94e970daace1 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:21:24 -0600 Subject: [PATCH 58/62] Run nvm inside of docker shell --- .github/workflows/deploy-sandbox.yaml | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index d53313e08..5e76e2eb7 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -46,15 +46,16 @@ jobs: - name: Compile USWDS assets working-directory: ./src run: | - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash - export NVM_DIR="$HOME/.nvm" - [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" - docker compose run node nvm install 21.7.3 && - docker compose run node nvm use 21.7.3 && - docker compose run node npm install && - docker compose run node npx gulp copyAssets && - docker compose run node npx gulp compile + docker compose run node bash -c "\ + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && \ + export NVM_DIR=\"\$HOME/.nvm\" && \ + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" && \ + [ -s \"\$NVM_DIR/bash_completion\" ] && \. \"\$NVM_DIR/bash_completion\" && \ + nvm install 21.7.3 && \ + nvm use 21.7.3 && \ + npm install && \ + npx gulp copyAssets && \ + npx gulp compile" - name: Collect static assets working-directory: ./src run: docker compose run app python manage.py collectstatic --no-input From 140735a7936a46a756693bf8552696854ec40c32 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:28:32 -0600 Subject: [PATCH 59/62] Remove setup node.js --- .github/workflows/deploy-sandbox.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index 5e76e2eb7..ffad69abe 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -39,10 +39,6 @@ jobs: needs: [variables] steps: - uses: actions/checkout@v3 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '21.7.3' - name: Compile USWDS assets working-directory: ./src run: | From 7848a6e6e5fa0ea1f8c0bf09f1b85049ae1d541b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:33:48 -0600 Subject: [PATCH 60/62] Add bash script for each yaml --- .github/workflows/deploy-development.yaml | 13 ++++++++++--- .github/workflows/deploy-stable.yaml | 13 ++++++++++--- .github/workflows/deploy-staging.yaml | 13 ++++++++++--- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/.github/workflows/deploy-development.yaml b/.github/workflows/deploy-development.yaml index 686635c20..12a1b5861 100644 --- a/.github/workflows/deploy-development.yaml +++ b/.github/workflows/deploy-development.yaml @@ -22,9 +22,16 @@ jobs: - name: Compile USWDS assets working-directory: ./src run: | - docker compose run node npm install && - docker compose run node npx gulp copyAssets && - docker compose run node npx gulp compile + docker compose run node bash -c "\ + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && \ + export NVM_DIR=\"\$HOME/.nvm\" && \ + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" && \ + [ -s \"\$NVM_DIR/bash_completion\" ] && \. \"\$NVM_DIR/bash_completion\" && \ + nvm install 21.7.3 && \ + nvm use 21.7.3 && \ + npm install && \ + npx gulp copyAssets && \ + npx gulp compile" - name: Collect static assets working-directory: ./src run: docker compose run app python manage.py collectstatic --no-input diff --git a/.github/workflows/deploy-stable.yaml b/.github/workflows/deploy-stable.yaml index 0ded4a3a6..9d0573e01 100644 --- a/.github/workflows/deploy-stable.yaml +++ b/.github/workflows/deploy-stable.yaml @@ -23,9 +23,16 @@ jobs: - name: Compile USWDS assets working-directory: ./src run: | - docker compose run node npm install && - docker compose run node npx gulp copyAssets && - docker compose run node npx gulp compile + docker compose run node bash -c "\ + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && \ + export NVM_DIR=\"\$HOME/.nvm\" && \ + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" && \ + [ -s \"\$NVM_DIR/bash_completion\" ] && \. \"\$NVM_DIR/bash_completion\" && \ + nvm install 21.7.3 && \ + nvm use 21.7.3 && \ + npm install && \ + npx gulp copyAssets && \ + npx gulp compile" - name: Collect static assets working-directory: ./src run: docker compose run app python manage.py collectstatic --no-input diff --git a/.github/workflows/deploy-staging.yaml b/.github/workflows/deploy-staging.yaml index 1df08f412..ad4a437c1 100644 --- a/.github/workflows/deploy-staging.yaml +++ b/.github/workflows/deploy-staging.yaml @@ -23,9 +23,16 @@ jobs: - name: Compile USWDS assets working-directory: ./src run: | - docker compose run node npm install && - docker compose run node npx gulp copyAssets && - docker compose run node npx gulp compile + docker compose run node bash -c "\ + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && \ + export NVM_DIR=\"\$HOME/.nvm\" && \ + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" && \ + [ -s \"\$NVM_DIR/bash_completion\" ] && \. \"\$NVM_DIR/bash_completion\" && \ + nvm install 21.7.3 && \ + nvm use 21.7.3 && \ + npm install && \ + npx gulp copyAssets && \ + npx gulp compile" - name: Collect static assets working-directory: ./src run: docker compose run app python manage.py collectstatic --no-input From 51c1baf0e04f1789c2885fc85cdcfb6b6fbe0524 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:35:11 -0600 Subject: [PATCH 61/62] Update node.Dockerfile --- src/node.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node.Dockerfile b/src/node.Dockerfile index cf0b6acc6..9178f8862 100644 --- a/src/node.Dockerfile +++ b/src/node.Dockerfile @@ -9,4 +9,4 @@ COPY --chown=circleci:circleci package*.json ./ RUN npm install -g npm@10.5.0 -RUN npm install \ No newline at end of file +RUN npm install From b75ae04812509f64a67e9d609af3fea567931c1a Mon Sep 17 00:00:00 2001 From: Rebecca Hsieh Date: Tue, 30 Apr 2024 15:02:17 -0700 Subject: [PATCH 62/62] Staging hotfix for gulp issue --- .github/workflows/deploy-development.yaml | 15 +++++++++++---- .github/workflows/deploy-sandbox.yaml | 15 +++++++++++---- .github/workflows/deploy-stable.yaml | 13 ++++++++++--- .github/workflows/deploy-staging.yaml | 15 +++++++++++---- src/node.Dockerfile | 6 ++++-- src/package-lock.json | 4 ++++ src/package.json | 7 ++++++- 7 files changed, 57 insertions(+), 18 deletions(-) diff --git a/.github/workflows/deploy-development.yaml b/.github/workflows/deploy-development.yaml index 686635c20..e62607d95 100644 --- a/.github/workflows/deploy-development.yaml +++ b/.github/workflows/deploy-development.yaml @@ -22,9 +22,16 @@ jobs: - name: Compile USWDS assets working-directory: ./src run: | - docker compose run node npm install && - docker compose run node npx gulp copyAssets && - docker compose run node npx gulp compile + docker compose run node bash -c "\ + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && \ + export NVM_DIR=\"\$HOME/.nvm\" && \ + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" && \ + [ -s \"\$NVM_DIR/bash_completion\" ] && \. \"\$NVM_DIR/bash_completion\" && \ + nvm install 21.7.3 && \ + nvm use 21.7.3 && \ + npm install && \ + npx gulp copyAssets && \ + npx gulp compile" - name: Collect static assets working-directory: ./src run: docker compose run app python manage.py collectstatic --no-input @@ -45,4 +52,4 @@ jobs: cf_password: ${{ secrets.CF_DEVELOPMENT_PASSWORD }} cf_org: cisa-dotgov cf_space: development - cf_command: "run-task getgov-development --command 'python manage.py migrate' --name migrate" + cf_command: "run-task getgov-development --command 'python manage.py migrate' --name migrate" \ No newline at end of file diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index f7f4a0d65..d9d7cbe14 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -42,9 +42,16 @@ jobs: - name: Compile USWDS assets working-directory: ./src run: | - docker compose run node npm install && - docker compose run node npx gulp copyAssets && - docker compose run node npx gulp compile + docker compose run node bash -c "\ + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && \ + export NVM_DIR=\"\$HOME/.nvm\" && \ + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" && \ + [ -s \"\$NVM_DIR/bash_completion\" ] && \. \"\$NVM_DIR/bash_completion\" && \ + nvm install 21.7.3 && \ + nvm use 21.7.3 && \ + npm install && \ + npx gulp copyAssets && \ + npx gulp compile" - name: Collect static assets working-directory: ./src run: docker compose run app python manage.py collectstatic --no-input @@ -75,4 +82,4 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, body: '🥳 Successfully deployed to developer sandbox **[${{ env.ENVIRONMENT }}](https://getgov-${{ env.ENVIRONMENT }}.app.cloud.gov/)**.' - }) + }) \ No newline at end of file diff --git a/.github/workflows/deploy-stable.yaml b/.github/workflows/deploy-stable.yaml index 0ded4a3a6..9d0573e01 100644 --- a/.github/workflows/deploy-stable.yaml +++ b/.github/workflows/deploy-stable.yaml @@ -23,9 +23,16 @@ jobs: - name: Compile USWDS assets working-directory: ./src run: | - docker compose run node npm install && - docker compose run node npx gulp copyAssets && - docker compose run node npx gulp compile + docker compose run node bash -c "\ + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && \ + export NVM_DIR=\"\$HOME/.nvm\" && \ + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" && \ + [ -s \"\$NVM_DIR/bash_completion\" ] && \. \"\$NVM_DIR/bash_completion\" && \ + nvm install 21.7.3 && \ + nvm use 21.7.3 && \ + npm install && \ + npx gulp copyAssets && \ + npx gulp compile" - name: Collect static assets working-directory: ./src run: docker compose run app python manage.py collectstatic --no-input diff --git a/.github/workflows/deploy-staging.yaml b/.github/workflows/deploy-staging.yaml index 1df08f412..9584985f0 100644 --- a/.github/workflows/deploy-staging.yaml +++ b/.github/workflows/deploy-staging.yaml @@ -23,9 +23,16 @@ jobs: - name: Compile USWDS assets working-directory: ./src run: | - docker compose run node npm install && - docker compose run node npx gulp copyAssets && - docker compose run node npx gulp compile + docker compose run node bash -c "\ + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && \ + export NVM_DIR=\"\$HOME/.nvm\" && \ + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" && \ + [ -s \"\$NVM_DIR/bash_completion\" ] && \. \"\$NVM_DIR/bash_completion\" && \ + nvm install 21.7.3 && \ + nvm use 21.7.3 && \ + npm install && \ + npx gulp copyAssets && \ + npx gulp compile" - name: Collect static assets working-directory: ./src run: docker compose run app python manage.py collectstatic --no-input @@ -44,4 +51,4 @@ jobs: cf_password: ${{ secrets.CF_STAGING_PASSWORD }} cf_org: cisa-dotgov cf_space: staging - cf_command: "run-task getgov-staging --command 'python manage.py migrate' --name migrate" + cf_command: "run-task getgov-staging --command 'python manage.py migrate' --name migrate" \ No newline at end of file diff --git a/src/node.Dockerfile b/src/node.Dockerfile index b478a8a26..cf0b6acc6 100644 --- a/src/node.Dockerfile +++ b/src/node.Dockerfile @@ -1,5 +1,5 @@ FROM docker.io/cimg/node:current-browsers - +FROM node:21.7.3 WORKDIR /app # Install app dependencies @@ -7,4 +7,6 @@ WORKDIR /app # where available (npm@5+) COPY --chown=circleci:circleci package*.json ./ -RUN npm install + +RUN npm install -g npm@10.5.0 +RUN npm install \ No newline at end of file diff --git a/src/package-lock.json b/src/package-lock.json index dc1464ee8..9df99a739 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -15,6 +15,10 @@ }, "devDependencies": { "@uswds/compile": "^1.0.0-beta.3" + }, + "engines": { + "node": "21.7.3", + "npm": "10.5.0" } }, "node_modules/@gulp-sourcemaps/identity-map": { diff --git a/src/package.json b/src/package.json index 274e0e282..3afce297f 100644 --- a/src/package.json +++ b/src/package.json @@ -3,6 +3,11 @@ "version": "1.0.0", "description": "========================", "main": "index.js", + "engines": { + "node": "21.7.3", + "npm": "10.5.0" + }, + "engineStrict": true, "scripts": { "pa11y-ci": "pa11y-ci", "test": "echo \"Error: no test specified\" && exit 1" @@ -17,4 +22,4 @@ "devDependencies": { "@uswds/compile": "^1.0.0-beta.3" } -} +} \ No newline at end of file