Merge pull request #1347 from cisagov/dk/1217-domain-expiration-epp

Issue #1217 - Domain expiration date - EPP
This commit is contained in:
dave-kennedy-ecs 2023-11-21 15:51:58 -05:00 committed by GitHub
commit 310da70913
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 102 additions and 5 deletions

View file

@ -736,7 +736,7 @@ class DomainAdmin(ListHeaderAdmin):
search_help_text = "Search by domain name."
change_form_template = "django/admin/domain_change_form.html"
change_list_template = "django/admin/domain_change_list.html"
readonly_fields = ["state"]
readonly_fields = ["state", "expiration_date"]
def export_data_type(self, request):
# match the CSV example with all the fields

View file

@ -211,12 +211,56 @@ class Domain(TimeStampedModel, DomainHelper):
@Cache
def registry_expiration_date(self) -> date:
"""Get or set the `ex_date` element from the registry."""
return self._get_property("ex_date")
"""Get or set the `ex_date` element from the registry.
Additionally, update the expiration date in the registrar"""
try:
self.expiration_date = self._get_property("ex_date")
self.save()
return self.expiration_date
except Exception as e:
# exception raised during the save to registrar
logger.error(f"error updating expiration date in registrar: {e}")
raise (e)
@registry_expiration_date.setter # type: ignore
def registry_expiration_date(self, ex_date: date):
pass
"""
Direct setting of the expiration date in the registry is not implemented.
To update the expiration date, use renew_domain method."""
raise NotImplementedError()
def renew_domain(self, length: int = 1, unit: epp.Unit = epp.Unit.YEAR):
"""
Renew the domain to a length and unit of time relative to the current
expiration date.
Default length and unit of time are 1 year.
"""
# if no expiration date from registry, set to today
try:
cur_exp_date = self.registry_expiration_date
except KeyError:
logger.warning("current expiration date not set; setting to today")
cur_exp_date = date.today()
# create RenewDomain request
request = commands.RenewDomain(name=self.name, cur_exp_date=cur_exp_date, period=epp.Period(length, unit))
try:
# update expiration date in registry, and set the updated
# expiration date in the registrar, and in the cache
self._cache["ex_date"] = registry.send(request, cleaned=True).res_data[0].ex_date
self.expiration_date = self._cache["ex_date"]
self.save()
except RegistryError as err:
# if registry error occurs, log the error, and raise it as well
logger.error(f"registry error renewing domain: {err}")
raise (err)
except Exception as e:
# exception raised during the save to registrar
logger.error(f"error updating expiration date in registrar: {e}")
raise (e)
@Cache
def password(self) -> str:

View file

@ -556,6 +556,7 @@ class MockEppLib(TestCase):
avail=...,
addrs=...,
registrant=...,
ex_date=...,
):
self.auth_info = auth_info
self.cr_date = cr_date
@ -565,6 +566,7 @@ class MockEppLib(TestCase):
self.avail = avail # use for CheckDomain
self.addrs = addrs
self.registrant = registrant
self.ex_date = ex_date
def dummyInfoContactResultData(
self,
@ -811,6 +813,11 @@ class MockEppLib(TestCase):
],
)
mockRenewedDomainExpDate = fakedEppObject(
"fake.gov",
ex_date=datetime.date(2023, 5, 25),
)
def _mockDomainName(self, _name, _avail=False):
return MagicMock(
res_data=[
@ -870,6 +877,8 @@ class MockEppLib(TestCase):
return self.mockCheckDomainCommand(_request, cleaned)
case commands.DeleteDomain:
return self.mockDeleteDomainCommands(_request, cleaned)
case commands.RenewDomain:
return self.mockRenewDomainCommand(_request, cleaned)
case _:
return MagicMock(res_data=[self.mockDataInfoHosts])
@ -890,6 +899,15 @@ class MockEppLib(TestCase):
raise RegistryError(code=ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION)
return None
def mockRenewDomainCommand(self, _request, cleaned):
if getattr(_request, "name", None) == "fake-error.gov":
raise RegistryError(code=ErrorCode.PARAMETER_VALUE_RANGE_ERROR)
else:
return MagicMock(
res_data=[self.mockRenewedDomainExpDate],
code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY,
)
def mockInfoDomainCommands(self, _request, cleaned):
request_name = getattr(_request, "name", None)

View file

@ -56,7 +56,7 @@ class TestDomainCache(MockEppLib):
self.assertFalse("avail" in domain._cache.keys())
# using a setter should clear the cache
domain.registry_expiration_date = datetime.date.today()
domain.dnssecdata = []
self.assertEquals(domain._cache, {})
# send should have been called only once
@ -1953,6 +1953,41 @@ class TestRegistrantDNSSEC(MockEppLib):
self.assertTrue(err.is_client_error() or err.is_session_error() or err.is_server_error())
class TestExpirationDate(MockEppLib):
"""User may renew expiration date by a number of units of time"""
def setUp(self):
"""
Domain exists in registry
"""
super().setUp()
# for the tests, need a domain in the ready state
self.domain, _ = Domain.objects.get_or_create(name="fake.gov", state=Domain.State.READY)
# for the test, need a domain that will raise an exception
self.domain_w_error, _ = Domain.objects.get_or_create(name="fake-error.gov", state=Domain.State.READY)
def tearDown(self):
Domain.objects.all().delete()
super().tearDown()
def test_expiration_date_setter_not_implemented(self):
"""assert that the setter for expiration date is not implemented and will raise error"""
with self.assertRaises(NotImplementedError):
self.domain.registry_expiration_date = datetime.date.today()
def test_renew_domain(self):
"""assert that the renew_domain sets new expiration date in cache and saves to registrar"""
self.domain.renew_domain()
test_date = datetime.date(2023, 5, 25)
self.assertEquals(self.domain._cache["ex_date"], test_date)
self.assertEquals(self.domain.expiration_date, test_date)
def test_renew_domain_error(self):
"""assert that the renew_domain raises an exception when registry raises error"""
with self.assertRaises(RegistryError):
self.domain_w_error.renew_domain()
class TestAnalystClientHold(MockEppLib):
"""Rule: Analysts may suspend or restore a domain by using client hold"""