From 2c49ea97ef98f7378c1c9d18a2a02f46ce46ae9f Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 8 Nov 2023 10:40:09 -0700 Subject: [PATCH] Fix merge issues and test --- .../runbooks/rotate_application_secrets.md | 31 ++++++++--- src/registrar/assets/sass/_theme/_admin.scss | 2 +- src/registrar/assets/sass/_theme/_alerts.scss | 6 +- src/registrar/config/settings.py | 12 +++- src/registrar/templates/base.html | 7 ++- .../django/admin/domain_change_list.html | 2 +- .../tests/data/test_migrationFilepaths.json | 12 ++++ .../tests/data/test_organization_adhoc.txt | 2 +- .../test_transition_domain_migrations.py | 55 ++++++++----------- src/registrar/utility/csv_export.py | 26 ++++++++- 10 files changed, 102 insertions(+), 53 deletions(-) create mode 100644 src/registrar/tests/data/test_migrationFilepaths.json diff --git a/docs/operations/runbooks/rotate_application_secrets.md b/docs/operations/runbooks/rotate_application_secrets.md index e91e8427e..78c402efe 100644 --- a/docs/operations/runbooks/rotate_application_secrets.md +++ b/docs/operations/runbooks/rotate_application_secrets.md @@ -76,16 +76,24 @@ These are the client certificate and its private key used to identify the regist The private key is protected by a passphrase for safer transport and storage. -These were generated with: +These were generated with the following steps: + +### Step 1: Generate an unencrypted private key with a named curve ```bash -openssl genpkey -out client.key \ - -algorithm EC -pkeyopt ec_paramgen_curve:P-256 \ - -aes-256-cbc -openssl req -new -x509 -days 365 \ - -key client.key -out client.crt \ - -subj "/C=US/ST=DC/L=Washington/O=GSA/OU=18F/CN=GOV Prototype Registrar" +openssl ecparam -name prime256v1 -genkey -out client_unencrypted.key +``` +### Step 2: Create an encrypted private key with a passphrase + +```bash +openssl pkcs8 -topk8 -v2 aes-256-cbc -in client_unencrypted.key -out client.key +``` + +### Generate the certificate + +```bash +openssl req -new -x509 -days 365 -key client.key -out client.crt -subj "/C=US/ST=DC/L=Washington/O=GSA/OU=18F/CN=GOV Prototype Registrar" ``` (If you can't use openssl on your computer directly, you can access it using Docker as `docker run --platform=linux/amd64 -it --rm -v $(pwd):/apps -w /apps alpine/openssl`.) @@ -97,7 +105,14 @@ base64 client.key base64 client.crt ``` -You'll need to give the new certificate to the registry vendor _before_ rotating it in production. +Note depending on your system you may need to instead run: + +```bash +base64 -i client.key +base64 -i client.crt +``` + +You'll need to give the new certificate to the registry vendor _before_ rotating it in production. Once it has been accepted by the vender, make sure to update the kdbx file on Google Drive. ## REGISTRY_HOSTNAME diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index 68ff51597..080a343ee 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -187,7 +187,7 @@ h1, h2, h3 { .object-tools li a, .object-tools p a { font-family: "Source Sans Pro Web", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif; - text-transform: capitalize!important; + text-transform: none!important; font-size: 14px!important; } diff --git a/src/registrar/assets/sass/_theme/_alerts.scss b/src/registrar/assets/sass/_theme/_alerts.scss index dd51734ed..9ee28a357 100644 --- a/src/registrar/assets/sass/_theme/_alerts.scss +++ b/src/registrar/assets/sass/_theme/_alerts.scss @@ -5,7 +5,11 @@ font-size: units(3); } } - + +.usa-alert__text.measure-none { + max-width: measure(none); +} + // The icon was off center for some reason // Fixes that issue @media (min-width: 64em){ diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index e10b8584c..896691efb 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -46,7 +46,6 @@ path = Path(__file__) env_db_url = env.dj_db_url("DATABASE_URL") env_debug = env.bool("DJANGO_DEBUG", default=False) -env_is_production = env.bool("IS_PRODUCTION", default=False) env_log_level = env.str("DJANGO_LOG_LEVEL", "DEBUG") env_base_url = env.str("DJANGO_BASE_URL") env_getgov_public_site_url = env.str("GETGOV_PUBLIC_SITE_URL", "") @@ -74,8 +73,6 @@ BASE_DIR = path.resolve().parent.parent.parent # SECURITY WARNING: don't run with debug turned on in production! DEBUG = env_debug -# Controls production specific feature toggles -IS_PRODUCTION = env_is_production # Applications are modular pieces of code. # They are provided by Django, by third-parties, or by yourself. @@ -122,6 +119,8 @@ INSTALLED_APPS = [ "api", # Only for generating documentation, uncomment to run manage.py generate_puml # "puml_generator", + # supports necessary headers for Django cross origin + "corsheaders", ] # Middleware are routines for processing web requests. @@ -130,6 +129,8 @@ INSTALLED_APPS = [ MIDDLEWARE = [ # django-allow-cidr: enable use of CIDR IP ranges in ALLOWED_HOSTS "allow_cidr.middleware.AllowCIDRMiddleware", + # django-cors-headers: listen to cors responses + "corsheaders.middleware.CorsMiddleware", # serve static assets in production "whitenoise.middleware.WhiteNoiseMiddleware", # provide security enhancements to the request/response cycle @@ -295,6 +296,11 @@ CSP_DEFAULT_SRC = allowed_sources CSP_FRAME_ANCESTORS = allowed_sources CSP_FORM_ACTION = allowed_sources +# Cross-Origin Resource Sharing (CORS) configuration +# Sets clients that allow access control to manage.get.gov +# TODO: remove :8080 to see if we can have all localhost access +CORS_ALLOWED_ORIGINS = ["http://localhost:8080", "https://beta.get.gov"] + # Content-Length header is set by django.middleware.common.CommonMiddleware diff --git a/src/registrar/templates/base.html b/src/registrar/templates/base.html index 72c30f323..68a5c69ea 100644 --- a/src/registrar/templates/base.html +++ b/src/registrar/templates/base.html @@ -62,10 +62,11 @@ {% if IS_DEMO_SITE %}
-
+
-

- BETA SITE: We’re building a new way to get a .gov. Take a look around, but don’t rely on this site yet. This site is for testing purposes only. Don’t enter real data into any form on this site. To learn about requesting a .gov domain, visit get.gov +

New domain requests are paused

+

+ This is the new registrar for managing .gov domains. Note that we’re not accepting requests for new .gov domains until January 2024. Follow .gov updates at get.gov/updates/.

diff --git a/src/registrar/templates/django/admin/domain_change_list.html b/src/registrar/templates/django/admin/domain_change_list.html index 68fdbe7aa..22df74685 100644 --- a/src/registrar/templates/django/admin/domain_change_list.html +++ b/src/registrar/templates/django/admin/domain_change_list.html @@ -15,7 +15,7 @@ {% if has_add_permission %}
  • - Add Domain + Add domain
  • {% endif %} diff --git a/src/registrar/tests/data/test_migrationFilepaths.json b/src/registrar/tests/data/test_migrationFilepaths.json new file mode 100644 index 000000000..605f02f92 --- /dev/null +++ b/src/registrar/tests/data/test_migrationFilepaths.json @@ -0,0 +1,12 @@ +{ + "directory": "registrar/tests/data", + "agency_adhoc_filename": "test_agency_adhoc.txt", + "authority_adhoc_filename": "test_authority_adhoc.txt", + "organization_adhoc_filename": "test_organization_adhoc.txt", + "domain_adhoc_filename": "test_domain_types_adhoc.txt", + "domain_additional_filename": "test_domain_additional.txt", + "domain_contacts_filename": "test_domain_contacts.txt", + "domain_escrow_filename": "test_escrow_domains_daily.txt", + "domain_statuses_filename": "test_domain_statuses.txt", + "contacts_filename": "test_contacts.txt" +} \ No newline at end of file diff --git a/src/registrar/tests/data/test_organization_adhoc.txt b/src/registrar/tests/data/test_organization_adhoc.txt index a39361a10..43ad637b2 100644 --- a/src/registrar/tests/data/test_organization_adhoc.txt +++ b/src/registrar/tests/data/test_organization_adhoc.txt @@ -1,6 +1,6 @@ orgid|orgname|orgstreet|orgcity|orgstate|orgzip|orgcountrycode 1|Flashdog|298 Monument Hill|Lakeland|Florida|33805|US 2|Gigaclub|782 Mosinee Lane|Alexandria|Louisiana|71307|US -3|Midel|376 Joe Pass|Waco|Texas|76705|US +3|corrupt data|376 Joe Pass|Waco | corruption|Texas|76705|US 4|Fanoodle|93001 Arizona Drive|Columbus|Ohio|43268|US 5|Sushi|9999 Sushi Way|Columbus|Ohio|43268|US \ No newline at end of file diff --git a/src/registrar/tests/test_transition_domain_migrations.py b/src/registrar/tests/test_transition_domain_migrations.py index f14f42545..7781e66e9 100644 --- a/src/registrar/tests/test_transition_domain_migrations.py +++ b/src/registrar/tests/test_transition_domain_migrations.py @@ -1,4 +1,4 @@ -from io import StringIO +import datetime from django.test import TestCase from registrar.models import ( @@ -20,7 +20,7 @@ class TestMigrations(TestCase): # self.transfer_script = "transfer_transition_domains_to_domains", # self.master_script = "load_transition_domain", - self.test_data_file_location = "/app/registrar/tests/data" + self.test_data_file_location = "registrar/tests/data" self.test_domain_contact_filename = "test_domain_contacts.txt" self.test_contact_filename = "test_contacts.txt" self.test_domain_status_filename = "test_domain_statuses.txt" @@ -32,6 +32,7 @@ class TestMigrations(TestCase): self.test_domain_types_adhoc = "test_domain_types_adhoc.txt" self.test_escrow_domains_daily = "test_escrow_domains_daily" self.test_organization_adhoc = "test_organization_adhoc.txt" + self.migration_json_filename = "test_migrationFilepaths.json" def tearDown(self): # Delete domain information @@ -48,16 +49,8 @@ class TestMigrations(TestCase): with patch('registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit', return_value=True): call_command( "load_transition_domain", - f"{self.test_data_file_location}/{self.test_domain_contact_filename}", - f"{self.test_data_file_location}/{self.test_contact_filename}", - f"{self.test_data_file_location}/{self.test_domain_status_filename}", - directory=self.test_data_file_location, - agency_adhoc_filename=self.test_agency_adhoc_filename, - domain_additional_filename=self.test_domain_additional, - domain_escrow_filename=self.test_escrow_domains_daily, - domain_adhoc_filename=self.test_domain_types_adhoc, - organization_adhoc_filename=self.test_organization_adhoc, - authority_adhoc_filename=self.test_authority_adhoc_filename, + f"{self.test_data_file_location}/{self.migration_json_filename}", + directory=self.test_data_file_location ) def run_transfer_domains(self): @@ -68,12 +61,8 @@ class TestMigrations(TestCase): call_command( "master_domain_migrations", runMigrations=True, - migrationDirectory=f"{self.test_data_file_location}", - migrationFilenames=( - f"{self.test_domain_contact_filename}," - f"{self.test_contact_filename}," - f"{self.test_domain_status_filename}" - ), + migrationDirectory=self.test_data_file_location, + migration_json_filename=self.migration_json_filename, ) def compare_tables( @@ -313,24 +302,26 @@ class TestMigrations(TestCase): expected_missing_domain_invitations, ) - expected_anomaly_domains = Domain.objects.filter(name="anomaly.gov") - self.assertEqual(expected_anomaly_domains.count(), 1) - expected_anomaly = expected_anomaly_domains.get() + anomaly_domains = Domain.objects.filter(name="anomaly.gov") + self.assertEqual(anomaly_domains.count(), 1) + anomaly = anomaly_domains.get() - self.assertEqual(expected_anomaly.expiration_date, "test") - self.assertEqual(expected_anomaly.creation_date, "test") - self.assertEqual(expected_anomaly.name, "anomaly.gov") - self.assertEqual(expected_anomaly.state, "ready") + self.assertEqual(anomaly.expiration_date, datetime.date(2023, 3, 9)) + self.assertEqual( + anomaly.created_at, datetime.datetime(2023, 11, 8, 17, 23, 46, 764663, tzinfo=datetime.timezone.utc) + ) + self.assertEqual(anomaly.name, "anomaly.gov") + self.assertEqual(anomaly.state, "ready") - expected_testdomain_domains = Domain.objects.filter(name="anomaly.gov") - self.assertEqual(expected_testdomain_domains.count(), 1) + testdomain_domains = Domain.objects.filter(name="testdomain.gov") + self.assertEqual(testdomain_domains.count(), 1) - expected_testdomain = expected_testdomain_domains.get() + testdomain = testdomain_domains.get() - self.assertEqual(expected_testdomain.expiration_date, "test") - self.assertEqual(expected_testdomain.creation_date, "test") - self.assertEqual(expected_testdomain.name, "anomaly.gov") - self.assertEqual(expected_testdomain.state, "ready") + self.assertEqual(testdomain.expiration_date, datetime.date(2023, 3, 9)) + self.assertEqual(testdomain.created_at, "test") + self.assertEqual(testdomain.name, "anomaly.gov") + self.assertEqual(testdomain.state, "ready") expected_domains = [ Domain( diff --git a/src/registrar/utility/csv_export.py b/src/registrar/utility/csv_export.py index ffada0a0b..d9127d72c 100644 --- a/src/registrar/utility/csv_export.py +++ b/src/registrar/utility/csv_export.py @@ -2,6 +2,8 @@ import csv from registrar.models.domain import Domain from registrar.models.domain_information import DomainInformation from registrar.models.public_contact import PublicContact +from django.db.models import Value +from django.db.models.functions import Coalesce def export_domains_to_writer(writer, columns, sort_fields, filter_condition): @@ -61,7 +63,13 @@ def export_data_type_to_csv(csv_file): "Status", "Expiration Date", ] - sort_fields = ["domain__name"] + # Coalesce is used to replace federal_type of None with ZZZZZ + sort_fields = [ + "organization_type", + Coalesce("federal_type", Value("ZZZZZ")), + "federal_agency", + "domain__name", + ] filter_condition = { "domain__state__in": [ Domain.State.READY, @@ -84,7 +92,13 @@ def export_data_full_to_csv(csv_file): "State", "Security Contact Email", ] - sort_fields = ["domain__name", "federal_agency", "organization_type"] + # Coalesce is used to replace federal_type of None with ZZZZZ + sort_fields = [ + "organization_type", + Coalesce("federal_type", Value("ZZZZZ")), + "federal_agency", + "domain__name", + ] filter_condition = { "domain__state__in": [ Domain.State.READY, @@ -107,7 +121,13 @@ def export_data_federal_to_csv(csv_file): "State", "Security Contact Email", ] - sort_fields = ["domain__name", "federal_agency", "organization_type"] + # Coalesce is used to replace federal_type of None with ZZZZZ + sort_fields = [ + "organization_type", + Coalesce("federal_type", Value("ZZZZZ")), + "federal_agency", + "domain__name", + ] filter_condition = { "organization_type__icontains": "federal", "domain__state__in": [