switches states and updates, deletes and creates hosts

This commit is contained in:
Alysia Broddrick 2023-09-27 16:57:03 -07:00
parent 06dd9a4f19
commit 026860c88a
No known key found for this signature in database
GPG key ID: 03917052CD0F06B7
3 changed files with 144 additions and 27 deletions

View file

@ -292,6 +292,15 @@ class Domain(TimeStampedModel, DomainHelper):
newDict[tup[0]] = tup[1]
return newDict
def isDotGov(self, nameserver):
return nameserver.find(".gov")!=-1
def checkHostIPCombo(self, nameserver:str, ip:list):
if ( self.isDotGov(nameserver) and (ip is None or
ip==[]) ):
raise ValueError("Nameserver %s needs to have an ip address", nameserver)
return None
def getNameserverChanges(self, hosts:list[tuple[str]]):
"""
calls self.nameserver, it should pull from cache but may result
@ -316,16 +325,25 @@ class Domain(TimeStampedModel, DomainHelper):
# but are not in the list of new host values
if prevHost not in newHostDict:
deleted_values.append((prevHost,addrs))
#if the host exists in both, check if the addresses changed
# if the host exists in both, check if the addresses changed
else:
#TODO - host is being updated when previous was None and new is an empty list
#add check here
if newHostDict[prevHost] != addrs:
# add check here
if (newHostDict[prevHost] != addrs
and newHostDict[prevHost] is not None):
# could raise error here if new value is empty and is a dotgov
self.checkHostIPCombo(nameserver=prevHost, ip=newHostDict[prevHost])
updated_values.append((prevHost,newHostDict[prevHost]))
new_values=set(newHostDict)-set(previousHostDict) #returns actually a set
final_new_values = dict.fromkeys(new_values, None)
# loop in final new values to check for .gov and missing addresses
for nameserver, ip in final_new_values.items():
# check the new values for missing IPs
# raise error if missing
self.checkHostIPCombo(nameserver=nameserver,ip=ip)
return (deleted_values,updated_values,final_new_values, previousHostDict)
@nameservers.setter # type: ignore
@ -333,7 +351,7 @@ class Domain(TimeStampedModel, DomainHelper):
"""host should be a tuple of type str, str,... where the elements are
Fully qualified host name, addresses associated with the host
example: [(ns1.okay.gov, 127.0.0.1, others ips)]"""
# TODO-848: Finish this implementation of delete + update nameserver
# TODO-848: ip version checking may need to be added in a different ticket
# We currently don't have IP address functionality
@ -906,16 +924,29 @@ class Domain(TimeStampedModel, DomainHelper):
self._delete_domain()
# TODO - delete ticket any additional error handling here
def is_dns_needed(self):
self._invalidate_cache()
nameserverList = self.nameservers
return len(nameserverList) < 2
# def is_dns_needed(self):
# """Commented out and kept in the codebase
# as this call should be made, but adds
# a lot of processing time
# when EPP calling is made more efficient
# this should be added back in
# The goal is to double check that
# the nameservers we set are in fact
# on the registry
# """
# self._invalidate_cache()
# nameserverList = self.nameservers
# return len(nameserverList) < 2
# def dns_not_needed(self):
# return not self.is_dns_needed()
@transition(
field="state",
source=[State.DNS_NEEDED],
target=State.READY,
conditions=[lambda x : not is_dns_needed]
# conditions=[dns_not_needed]
)
def ready(self):
"""Transition to the ready state
@ -929,7 +960,7 @@ class Domain(TimeStampedModel, DomainHelper):
field="state",
source=[State.READY],
target=State.DNS_NEEDED,
conditions=[is_dns_needed]
# conditions=[is_dns_needed]
)
def dns_needed(self):
"""Transition to the DNS_NEEDED state
@ -1054,17 +1085,21 @@ class Domain(TimeStampedModel, DomainHelper):
def _convert_ips(self, ip_list: list[str]):
edited_ip_list = []
if ip_list is None:
return []
for ip_addr in ip_list:
if self.is_ipv6():
edited_ip_list.append(epp.Ip(addr=ip_addr, ip="v6"))
else: # default ip addr is v4
edited_ip_list.append(epp.Ip(addr=ip_addr))
return edited_ip_list
def _update_host(self, nameserver: str, ip_list: list[str], old_ip_list: list[str]):
try:
if len(ip_list) == 0:
if ip_list is None or len(ip_list) == 0 and isinstance(old_ip_list,list) and len(old_ip_list)!=0 :
return ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY
request = commands.UpdateHost(name=nameserver, add=self._convert_ips(ip_list), rem=self._convert_ips(old_ip_list))

View file

@ -581,15 +581,36 @@ class MockEppLib(TestCase):
contacts=[],
hosts=["fake.host.com"],
)
infoDomainThreeHosts =fakedEppObject(
"my-nameserver.gov",
cr_date=datetime.datetime(2023, 5, 25, 19, 45, 35),
contacts=[],
hosts=["ns1.my-nameserver-1.com","ns1.my-nameserver-2.com","ns1.cats-are-superior3.com"],
)
infoDomainNoHost =fakedEppObject(
"my-nameserver.gov",
cr_date=datetime.datetime(2023, 5, 25, 19, 45, 35),
contacts=[],
hosts=[],
)
infoDomainTwoHosts =fakedEppObject(
"my-nameserver.gov",
cr_date=datetime.datetime(2023, 5, 25, 19, 45, 35),
contacts=[],
hosts=["ns1.my-nameserver-1.com","ns1.my-nameserver-2.com"],
)
mockDataInfoContact = fakedEppObject(
"anotherPw", cr_date=datetime.datetime(2023, 7, 25, 19, 45, 35)
)
mockDataInfoHosts = fakedEppObject(
"lastPw", cr_date=datetime.datetime(2023, 8, 25, 19, 45, 35), addrs=["1.2.3", "2.3.4"]
)
mockDataHostChange =fakedEppObject(
"lastPw", cr_date=datetime.datetime(2023, 8, 25, 19, 45, 35)
)
extendedValues=False
def mockSend(self, _request, cleaned):
"""Mocks the registry.send function used inside of domain.py
@ -599,6 +620,13 @@ class MockEppLib(TestCase):
if isinstance(_request, commands.InfoDomain):
if getattr(_request, "name", None) == "security.gov":
return MagicMock(res_data=[self.infoDomainNoContact])
elif getattr(_request, "name", None) == "my-nameserver.gov":
if self.extendedValues:
return MagicMock(res_data=[self.infoDomainThreeHosts])
elif self.mockedSendFunction.call_count==5: ## remove this breaks anything?
return MagicMock(res_data=[self.infoDomainTwoHosts])
else:
return MagicMock(res_data=[self.infoDomainNoHost])
return MagicMock(res_data=[self.mockDataInfoDomain])
elif isinstance(_request, commands.InfoContact):
return MagicMock(res_data=[self.mockDataInfoContact])

View file

@ -599,7 +599,7 @@ class TestRegistrantNameservers(MockEppLib):
"""
# set 2 nameservers
print("DOCKER DIDNT SUCK THIS TIME")
self.domain.nameservers = [(self.nameserver1,), (self.nameserver2,)]
# when you create a host, you also have to update at same time
@ -609,8 +609,10 @@ class TestRegistrantNameservers(MockEppLib):
created_host2 = commands.CreateHost(self.nameserver2)
update_domain_with_created2 = commands.UpdateDomain(name=self.domain.name, add=[common.HostObjSet([created_host2.name])])
infoDomain = commands.InfoDomain(name='my-nameserver.gov', auth_info=None)
# checking if commands were sent (commands have to be sent in order)
expectedCalls = [
call(infoDomain, cleaned=True),
call(created_host1, cleaned=True),
call(update_domain_with_created1, cleaned=True),
call(created_host2, cleaned=True),
@ -620,8 +622,8 @@ class TestRegistrantNameservers(MockEppLib):
print("self.mockedSendFunction.call_args_list is ")
print(self.mockedSendFunction.call_args_list)
self.mockedSendFunction.assert_has_calls(expectedCalls)
self.mockedSendFunction.assert_has_calls(expectedCalls, any_order=True)
self.assertEqual(5, self.mockedSendFunction.call_count)
# check that status is READY
self.assertTrue(self.domain.is_active())
@ -657,7 +659,6 @@ class TestRegistrantNameservers(MockEppLib):
self.assertRaises(ValueError, _get_14_nameservers)
self.assertEqual(self.mockedSendFunction.call_count, 0)
@skip("not implemented yet")
def test_user_removes_some_nameservers(self):
"""
Scenario: Registrant removes some nameservers, while keeping at least 2
@ -667,29 +668,59 @@ class TestRegistrantNameservers(MockEppLib):
to the registry
And `domain.is_active` returns True
"""
#Given the domain has 3 nameservers
self.domain.nameservers = [(self.nameserver1,), (self.nameserver2,),(self.nameserver3,)]
#now remove one
# Mock is set to return 3 nameservers on infodomain
self.extendedValues=True
self.domain.nameservers = [(self.nameserver1,), (self.nameserver2,)]
expectedCalls=[
# calls info domain, and info on all hosts
# to get past values
# then removes the single host and updates domain
call(commands.InfoDomain(name='my-nameserver.gov', auth_info=None), cleaned=True),
call(commands.InfoHost(name='ns1.my-nameserver-1.com'), cleaned=True),
call(commands.InfoHost(name='ns1.my-nameserver-2.com'), cleaned=True),
call(commands.InfoHost(name='ns1.cats-are-superior3.com'), cleaned=True),
call(commands.UpdateDomain(name='my-nameserver.gov', add=[], rem=[common.HostObjSet(hosts=['ns1.cats-are-superior3.com'])], nsset=None, keyset=None, registrant=None, auth_info=None), cleaned=True),
call(commands.DeleteHost(name='ns1.cats-are-superior3.com'), cleaned=True)]
#assert updatedomain called
#assert call deletehost?
raise
print("self.mockedSendFunction.call_args_list is ")
print(self.mockedSendFunction.call_args_list)
self.mockedSendFunction.assert_has_calls(expectedCalls, any_order=True)
self.assertTrue(self.domain.is_active())
@skip("not implemented yet")
def test_user_removes_too_many_nameservers(self):
"""
Scenario: Registrant removes some nameservers, bringing the total to less than 2
Given the domain has 3 nameservers
Given the domain has 2 nameservers
When `domain.nameservers` is set to an array containing nameserver #1
Then `commands.UpdateDomain` and `commands.DeleteHost` is sent
to the registry
And `domain.is_active` returns False
"""
raise
self.extendedValues=True
print("domain state")
print(self.domain.state)
self.domain.ready()
print("Domain state is now")
print(self.domain.state)
self.domain.nameservers = [(self.nameserver1,)]
print("self.mockedSendFunction.call_args_list is ")
print(self.mockedSendFunction.call_args_list)
expectedCalls=[call(commands.InfoDomain(name='my-nameserver.gov', auth_info=None), cleaned=True),
call(commands.InfoHost(name='ns1.my-nameserver-1.com'), cleaned=True),
call(commands.InfoHost(name='ns1.my-nameserver-2.com'), cleaned=True),
call(commands.InfoHost(name='ns1.cats-are-superior3.com'), cleaned=True),
call(commands.UpdateDomain(name='my-nameserver.gov', add=[], rem=[common.HostObjSet(hosts=['ns1.my-nameserver-2.com'])], nsset=None, keyset=None, registrant=None, auth_info=None), cleaned=True),
call(commands.DeleteHost(name='ns1.my-nameserver-2.com'), cleaned=True),
call(commands.UpdateDomain(name='my-nameserver.gov', add=[], rem=[common.HostObjSet(hosts=['ns1.cats-are-superior3.com'])], nsset=None, keyset=None, registrant=None, auth_info=None), cleaned=True),
call(commands.DeleteHost(name='ns1.cats-are-superior3.com'), cleaned=True)]
self.mockedSendFunction.assert_has_calls(expectedCalls, any_order=True)
self.assertFalse(self.domain.is_active())
@skip("not implemented yet")
def test_user_replaces_nameservers(self):
"""
Scenario: Registrant simultaneously adds and removes some nameservers
@ -700,9 +731,27 @@ class TestRegistrantNameservers(MockEppLib):
And `commands.UpdateDomain` is sent to add #4 and #5 plus remove #2 and #3
And `commands.DeleteHost` is sent to delete #2 and #3
"""
raise
self.extendedValues=True
self.domain.ready()
self.domain.nameservers=[(self.nameserver1,), ("ns1.cats-are-superior1.com",), ("ns1.cats-are-superior2.com",)]
print("self.mockedSendFunction.call_args_list is ")
print(self.mockedSendFunction.call_args_list)
expectedCalls=[call(commands.InfoDomain(name='my-nameserver.gov', auth_info=None), cleaned=True),
call(commands.InfoHost(name='ns1.my-nameserver-1.com'), cleaned=True),
call(commands.InfoHost(name='ns1.my-nameserver-2.com'), cleaned=True),
call(commands.InfoHost(name='ns1.cats-are-superior3.com'), cleaned=True),
call(commands.UpdateDomain(name='my-nameserver.gov', add=[], rem=[common.HostObjSet(hosts=['ns1.my-nameserver-2.com'])], nsset=None, keyset=None, registrant=None, auth_info=None), cleaned=True),
call(commands.DeleteHost(name='ns1.my-nameserver-2.com'), cleaned=True),
call(commands.CreateHost(name='ns1.cats-are-superior1.com', addrs=[]), cleaned=True),
call(commands.UpdateDomain(name='my-nameserver.gov', add=[common.HostObjSet(hosts=['ns1.cats-are-superior1.com'])], rem=[], nsset=None, keyset=None, registrant=None, auth_info=None), cleaned=True),
call(commands.CreateHost(name='ns1.cats-are-superior2.com', addrs=[]), cleaned=True),
call(commands.UpdateDomain(name='my-nameserver.gov', add=[common.HostObjSet(hosts=['ns1.cats-are-superior2.com'])], rem=[], nsset=None, keyset=None, registrant=None, auth_info=None), cleaned=True)
]
self.mockedSendFunction.assert_has_calls(expectedCalls, any_order=True)
self.assertTrue(self.domain.is_active())
@skip("not implemented yet")
def test_user_cannot_add_subordinate_without_ip(self):
"""
Scenario: Registrant adds a nameserver which is a subdomain of their .gov
@ -711,6 +760,8 @@ class TestRegistrantNameservers(MockEppLib):
with a subdomain of the domain and no IP addresses
Then Domain raises a user-friendly error
"""
#add a nameserver with a .gov and no ip
##assertRaises error
raise
@skip("not implemented yet")
@ -758,6 +809,9 @@ class TestRegistrantNameservers(MockEppLib):
"""
raise
def tearDown(self):
self.extendedValues=False
return super().tearDown()
class TestRegistrantDNSSEC(TestCase):
"""Rule: Registrants may modify their secure DNS data"""