Skip to content

Commit

Permalink
Merge pull request #2305 from cisagov/rh/2273-ds-record-change
Browse files Browse the repository at this point in the history
ISSUE #2273: Add logging for DS data changes
  • Loading branch information
therealslimhsiehdy authored Jun 17, 2024
2 parents ab40353 + 026a25c commit 7c74eca
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 16 deletions.
18 changes: 18 additions & 0 deletions src/registrar/migrations/0102_domain_dsdata_last_change.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.10 on 2024-06-14 19:26

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("registrar", "0101_domaininformation_cisa_representative_first_name_and_more"),
]

operations = [
migrations.AddField(
model_name="domain",
name="dsdata_last_change",
field=models.TextField(blank=True, help_text="Record of the last change event for ds data", null=True),
),
]
30 changes: 28 additions & 2 deletions src/registrar/models/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@

from .public_contact import PublicContact

from .user_domain_role import UserDomainRole

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -672,11 +674,29 @@ def dnssecdata(self, _dnssecdata: Optional[extensions.DNSSECExtension]):
remRequest = commands.UpdateDomain(name=self.name)
remExtension = commands.UpdateDomainDNSSECExtension(**remParams)
remRequest.add_extension(remExtension)
dsdata_change_log = ""

# Get the user's email
user_domain_role = UserDomainRole.objects.filter(domain=self).first()
user_email = user_domain_role.user.email if user_domain_role else "unknown user"

try:
if "dsData" in _addDnssecdata and _addDnssecdata["dsData"] is not None:
added_record = "dsData" in _addDnssecdata and _addDnssecdata["dsData"] is not None
deleted_record = "dsData" in _remDnssecdata and _remDnssecdata["dsData"] is not None

if added_record:
registry.send(addRequest, cleaned=True)
if "dsData" in _remDnssecdata and _remDnssecdata["dsData"] is not None:
dsdata_change_log = f"{user_email} added a DS data record"
if deleted_record:
registry.send(remRequest, cleaned=True)
if dsdata_change_log != "": # if they add and remove a record at same time
dsdata_change_log = f"{user_email} added and deleted a DS data record"
else:
dsdata_change_log = f"{user_email} deleted a DS data record"
if dsdata_change_log != "":
self.dsdata_last_change = dsdata_change_log
self.save() # audit log will now record this as a change

except RegistryError as e:
logger.error("Error updating DNSSEC, code was %s error was %s" % (e.code, e))
raise e
Expand Down Expand Up @@ -1057,6 +1077,12 @@ def __str__(self) -> str:
verbose_name="first ready on",
)

dsdata_last_change = TextField(
null=True,
blank=True,
help_text="Record of the last change event for ds data",
)

def isActive(self):
return self.state == Domain.State.CREATED

Expand Down
48 changes: 34 additions & 14 deletions src/registrar/tests/test_models_domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -1901,12 +1901,8 @@ def test_user_adds_dnssec_data(self):
3 - setter adds the UpdateDNSSECExtension extension to the command
4 - setter causes the getter to call info domain on next get from cache
5 - getter properly parses dnssecdata from InfoDomain response and sets to cache
"""

# need to use a separate patcher and side_effect for this test, as
# response from InfoDomain must be different for different iterations
# of the same command
def side_effect(_request, cleaned):
if isinstance(_request, commands.InfoDomain):
if mocked_send.call_count == 1:
Expand All @@ -1924,17 +1920,30 @@ def side_effect(_request, cleaned):
mocked_send = patcher.start()
mocked_send.side_effect = side_effect
domain, _ = Domain.objects.get_or_create(name="dnssec-dsdata.gov")

# Check initial dsdata_last_change value (should be None)
initial_change = domain.dsdata_last_change

# Adding dnssec data
domain.dnssecdata = self.dnssecExtensionWithDsData
# get the DNS SEC extension added to the UpdateDomain command and

# Check dsdata_last_change is updated after adding data
domain = Domain.objects.get(name="dnssec-dsdata.gov")
self.assertIsNotNone(domain.dsdata_last_change)

self.assertNotEqual(domain.dsdata_last_change, initial_change)

# Get the DNS SEC extension added to the UpdateDomain command and
# verify that it is properly sent
# args[0] is the _request sent to registry
args, _ = mocked_send.call_args
# assert that the extension on the update matches
# Assert that the extension on the update matches
self.assertEquals(
args[0].extensions[0],
self.createUpdateExtension(self.dnssecExtensionWithDsData),
)
# test that the dnssecdata getter is functioning properly

# Test that the dnssecdata getter is functioning properly
dnssecdata_get = domain.dnssecdata
mocked_send.assert_has_calls(
[
Expand Down Expand Up @@ -2129,13 +2138,9 @@ def test_user_removes_dnssec_data(self):
2 - first setter calls UpdateDomain command
3 - second setter calls InfoDomain command again
3 - setter then calls UpdateDomain command
4 - setter adds the UpdateDNSSECExtension extension to the command with rem
4 - setter adds the UpdateDNSSExtension extension to the command with rem
"""

# need to use a separate patcher and side_effect for this test, as
# response from InfoDomain must be different for different iterations
# of the same command
def side_effect(_request, cleaned):
if isinstance(_request, commands.InfoDomain):
if mocked_send.call_count == 1:
Expand All @@ -2153,10 +2158,25 @@ def side_effect(_request, cleaned):
mocked_send = patcher.start()
mocked_send.side_effect = side_effect
domain, _ = Domain.objects.get_or_create(name="dnssec-dsdata.gov")
# dnssecdata_get_initial = domain.dnssecdata # call to force initial mock
# domain._invalidate_cache()

# Initial setting of dnssec data
domain.dnssecdata = self.dnssecExtensionWithDsData

# Check dsdata_last_change is updated
domain = Domain.objects.get(name="dnssec-dsdata.gov")
self.assertIsNotNone(domain.dsdata_last_change)

initial_change = domain.dsdata_last_change

# Remove dnssec data
domain.dnssecdata = self.dnssecExtensionRemovingDsData

# Check that dsdata_last_change is updated again
domain = Domain.objects.get(name="dnssec-dsdata.gov")
self.assertIsNotNone(domain.dsdata_last_change)

self.assertNotEqual(domain.dsdata_last_change, initial_change)

# get the DNS SEC extension added to the UpdateDomain command and
# verify that it is properly sent
# args[0] is the _request sent to registry
Expand Down

0 comments on commit 7c74eca

Please sign in to comment.