fixed error with save inside of transition

This commit is contained in:
Alysia Broddrick 2023-09-10 14:22:43 -07:00
parent 53e9d090d9
commit a9f608e353
No known key found for this signature in database
GPG key ID: 03917052CD0F06B7
5 changed files with 209 additions and 79 deletions

View file

@ -4,6 +4,7 @@ from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.http.response import HttpResponseRedirect from django.http.response import HttpResponseRedirect
from django.urls import reverse from django.urls import reverse
from registrar.models.public_contact import PublicContact
from registrar.models.utility.admin_sort_fields import AdminSortFields from registrar.models.utility.admin_sort_fields import AdminSortFields
from . import models from . import models
@ -150,6 +151,12 @@ class DomainAdmin(ListHeaderAdmin):
GET_SECURITY_EMAIL = "_get_security_email" GET_SECURITY_EMAIL = "_get_security_email"
SET_SECURITY_CONTACT = "_set_security_contact" SET_SECURITY_CONTACT = "_set_security_contact"
MAKE_DOMAIN = "_make_domain_in_registry" MAKE_DOMAIN = "_make_domain_in_registry"
MAKE_NAMESERVERS = "_make_nameservers"
GET_NAMESERVERS="_get_nameservers"
GET_STATUS = "_get_status"
SET_CLIENT_HOLD="_set_client_hold"
REMOVE_CLIENT_HOLD="_rem_client_hold"
DELETE_DOMAIN="_delete_domain"
logger.info("in response") logger.info("in response")
if ACTION_BUTTON in request.POST: if ACTION_BUTTON in request.POST:
logger.info("in action button") logger.info("in action button")
@ -171,29 +178,39 @@ class DomainAdmin(ListHeaderAdmin):
if GET_SECURITY_EMAIL in request.POST: if GET_SECURITY_EMAIL in request.POST:
try: try:
security_email = obj.get_security_email() contacts=obj._get_property("contacts")
email=None
for contact in contacts:
if ["type","email"] in contact.keys() and contact["type"]=="security":
email=contact["email"]
if email is None:
raise ValueError("Security contact type is not available on this domain")
except Exception as err: except Exception as err:
self.message_user(request, err, messages.ERROR) self.message_user(request, err, messages.ERROR)
else: else:
self.message_user( self.message_user(
request, request,
("The security email is %" ". Thanks!") % security_email, ("The security email is %s" ". Thanks!") % email,
) )
return HttpResponseRedirect(".") return HttpResponseRedirect(".")
if SET_SECURITY_CONTACT in request.POST: if SET_SECURITY_CONTACT in request.POST:
try: try:
security_contact = obj.get_default_security_contact() fake_email="manuallyEnteredEmail@test.gov"
security_contact.email = "ab@test.gov" if PublicContact.objects.filter(domain=obj, contact_type="security").exists():
sec_contact=PublicContact.objects.filter(domain=obj, contact_type="security").get()
else:
sec_contact=obj.get_default_security_contact()
sec_contact.email=fake_email
sec_contact.save()
obj.security_contact = security_contact
except Exception as err: except Exception as err:
self.message_user(request, err, messages.ERROR) self.message_user(request, err, messages.ERROR)
else: else:
self.message_user( self.message_user(
request, request,
("The security email is %" ". Thanks!") % security_email, ("The security email is %" ". Thanks!") % fake_email,
) )
print("above make domain") print("above make domain")
@ -207,31 +224,99 @@ class DomainAdmin(ListHeaderAdmin):
else: else:
self.message_user( self.message_user(
request, request,
("Domain created with %" ". Thanks!") % obj.name, ("Domain created with %s" ". Thanks!") % obj.name,
)
return HttpResponseRedirect(".")
#make nameservers here
if MAKE_NAMESERVERS in request.POST:
print("in make domain")
try:
hosts=[("ns1.example.com",None),("ns2.example.com",None) ]
obj.nameservers=hosts
except Exception as err:
self.message_user(request, err, messages.ERROR)
else:
self.message_user(
request,
("Hosts set to be %s" ". Thanks!") % hosts,
)
return HttpResponseRedirect(".")
if GET_NAMESERVERS in request.POST:
print("in make domain")
try:
nameservers=obj.nameservers
except Exception as err:
self.message_user(request, err, messages.ERROR)
else:
self.message_user(
request,
("Nameservers are %s" ". Thanks!") % nameservers,
)
return HttpResponseRedirect(".")
if GET_STATUS in request.POST:
print("in make domain")
try:
statuses=obj.statuses
except Exception as err:
self.message_user(request, err, messages.ERROR)
else:
self.message_user(
request,
("Domain statuses are %s" ". Thanks!") % statuses,
)
return HttpResponseRedirect(".")
if SET_CLIENT_HOLD in request.POST:
print("in make domain")
try:
obj.clientHold()
obj.save()
except Exception as err:
self.message_user(request, err, messages.ERROR)
else:
self.message_user(
request,
("Domain %s is now in clientHold") % obj.name,
)
return HttpResponseRedirect(".")
if REMOVE_CLIENT_HOLD in request.POST:
print("in make domain")
try:
obj.revertClientHold()
obj.save()
except Exception as err:
self.message_user(request, err, messages.ERROR)
else:
self.message_user(
request,
("Domain %s will now have client hold removed") % obj.name,
)
return HttpResponseRedirect(".")
if DELETE_DOMAIN in request.POST:
print("in make domain")
try:
obj.deleted()
obj.save()
except Exception as err:
self.message_user(request, err, messages.ERROR)
else:
self.message_user(
request,
("Domain %s Should now be deleted " ". Thanks!") % obj.name,
) )
return HttpResponseRedirect(".") return HttpResponseRedirect(".")
return super().response_change(request, obj) return super().response_change(request, obj)
# def response_change(self, request, obj):
# ACTION_BUTTON = "_get_security_email"
# if ACTION_BUTTON in request.POST:
# try:
# obj.security
# except Exception as err:
# self.message_user(request, err, messages.ERROR)
# else:
# self.message_user(
# request,
# (
# "%s is in client hold. This domain is no longer accessible on"
# " the public internet."
# )
# % obj.name,
# )
# return HttpResponseRedirect(".")
# return super().response_change(request, obj)
class ContactAdmin(ListHeaderAdmin): class ContactAdmin(ListHeaderAdmin):

View file

@ -62,7 +62,7 @@ class Domain(TimeStampedModel, DomainHelper):
SERVER_DELETE_PROHIBITED = "serverDeleteProhibited" SERVER_DELETE_PROHIBITED = "serverDeleteProhibited"
# DNS delegation information MUST NOT be published for the object. # DNS delegation information MUST NOT be published for the object.
CLIENT_HOLD = "clientHold" ON_HOLD = "clientHold"
SERVER_HOLD = "serverHold" SERVER_HOLD = "serverHold"
# Requests to renew the object MUST be rejected. # Requests to renew the object MUST be rejected.
@ -96,7 +96,7 @@ class Domain(TimeStampedModel, DomainHelper):
# for human review or third-party action. A transform command that # for human review or third-party action. A transform command that
# is processed, but whose requested action is pending, is noted with # is processed, but whose requested action is pending, is noted with
# response code 1001. # response code 1001.
PENDING_CREATE = "pendingCreate" DNS_NEEDED = "pendingCreate"
PENDING_DELETE = "pendingDelete" PENDING_DELETE = "pendingDelete"
PENDING_RENEW = "pendingRenew" PENDING_RENEW = "pendingRenew"
PENDING_TRANSFER = "pendingTransfer" PENDING_TRANSFER = "pendingTransfer"
@ -109,16 +109,14 @@ class Domain(TimeStampedModel, DomainHelper):
UNKNOWN = "unknown" UNKNOWN = "unknown"
# The domain object exists in the registry but nameservers don't exist for it yet # The domain object exists in the registry but nameservers don't exist for it yet
PENDING_CREATE = "pending create" DNS_NEEDED = "dns needed"
# Domain has had nameservers set, may or may not be active # Domain has had nameservers set, may or may not be active
CREATED = "created" READY = "ready"
# Registrar manually changed state to client hold # Registrar manually changed state to client hold
CLIENT_HOLD = "client hold" ON_HOLD = "client hold"
# Registry
SERVER_HOLD = "server hold"
# previously existed but has been deleted from the registry # previously existed but has been deleted from the registry
DELETED = "deleted" DELETED = "deleted"
@ -227,21 +225,21 @@ class Domain(TimeStampedModel, DomainHelper):
Subordinate hosts (something.your-domain.gov) MUST have IP addresses, Subordinate hosts (something.your-domain.gov) MUST have IP addresses,
while non-subordinate hosts MUST NOT. while non-subordinate hosts MUST NOT.
""" """
hosts = self._get_property("hosts") try:
hosts = self._get_property("hosts")
except KeyError as err:
logger.info("Domain is missing nameservers")
return None
hostList = [] hostList = []
for host in hosts: for host in hosts:
logger.info(host) logger.info(host)
# TODO - this should actually have a second tuple value with the ip address # TODO - this should actually have a second tuple value with the ip address
# ignored because uncertain if we will even have a way to display mult. # ignored because uncertain if we will even have a way to display mult.
# and adresses can be a list of mult address # and adresses can be a list of mult address
hostList.append((host.name,)) hostList.append((host["name"],))
print(hostList) return hostList
return [
("ns1.example.com",),
("ns2.example.com",),
("ns3.example.com",),
]
def _check_host(self, hostnames: list[str]): def _check_host(self, hostnames: list[str]):
"""check if host is available, True if available """check if host is available, True if available
@ -265,7 +263,7 @@ class Domain(TimeStampedModel, DomainHelper):
returns int response code""" returns int response code"""
logger.info("_create_host()->addresses is NONE") logger.info("_create_host()->addresses is NONE")
if not addrs is None: if not addrs is None and addrs!=[]:
logger.info("addresses is not None %s" % addrs) logger.info("addresses is not None %s" % addrs)
addresses = [epp.Ip(addr=addr) for addr in addrs] addresses = [epp.Ip(addr=addr) for addr in addrs]
request = commands.CreateHost(name=host, addrs=addresses) request = commands.CreateHost(name=host, addrs=addresses)
@ -323,10 +321,10 @@ class Domain(TimeStampedModel, DomainHelper):
% (e.code, e) % (e.code, e)
) )
if self.state == self.State.PENDING_CREATE and hostSuccessCount >= 2: if self.state == self.State.DNS_NEEDED and hostSuccessCount >= 2:
self.created() self.created()
self.save() self.save()
##TODO - handle removed nameservers here will need to change the state go back to pending_create ##TODO - handle removed nameservers here will need to change the state go back to DNS_NEEDED
@Cache @Cache
def statuses(self) -> list[str]: def statuses(self) -> list[str]:
@ -634,7 +632,7 @@ class Domain(TimeStampedModel, DomainHelper):
# add new contact # add new contact
security_contact = self.get_default_security_contact() security_contact = self.get_default_security_contact()
security_contact.save() security_contact.save()
logger.info("done with contacts")
@security_contact.setter # type: ignore @security_contact.setter # type: ignore
def security_contact(self, contact: PublicContact): def security_contact(self, contact: PublicContact):
"""makes the contact in the registry, """makes the contact in the registry,
@ -665,7 +663,7 @@ class Domain(TimeStampedModel, DomainHelper):
Is the domain live on the inter webs? Is the domain live on the inter webs?
could be replaced with request to see if ok status is set could be replaced with request to see if ok status is set
""" """
return self.state == self.State.CREATED return self.state == self.State.READY
def transfer(self): def transfer(self):
"""Going somewhere. Not implemented.""" """Going somewhere. Not implemented."""
@ -675,18 +673,31 @@ class Domain(TimeStampedModel, DomainHelper):
"""Time to renew. Not implemented.""" """Time to renew. Not implemented."""
raise NotImplementedError() raise NotImplementedError()
def place_client_hold(self):
"""This domain should not be active."""
raise NotImplementedError("This is not implemented yet.")
def get_security_email(self): def get_security_email(self):
logger.info("get_security_email-> getting the contact ") logger.info("get_security_email-> getting the contact ")
secContact = self.security_contact secContact = self.security_contact
return secContact.email return secContact.email
def clientHoldStatus(self):
return epp.Status(state=self.Status.ON_HOLD, description="", lang="en")
def _place_client_hold(self):
"""This domain should not be active.
may raises RegistryError, should be caught or handled correctly by caller """
request=commands.UpdateDomain(name=self.name,add=[self.clientHoldStatus()])
registry.send(request)
def remove_client_hold(self): def _remove_client_hold(self):
"""This domain is okay to be active.""" """This domain is okay to be active.
raise NotImplementedError() may raises RegistryError, should be caught or handled correctly by caller"""
request=commands.UpdateDomain(name=self.name,rem=[self.clientHoldStatus() ])
registry.send(request)
def _delete_domain(self):
"""This domain should be deleted from the registry
may raises RegistryError, should be caught or handled correctly by caller"""
request=commands.DeleteDomain(name=self.name)
registry.send(request)
def __str__(self) -> str: def __str__(self) -> str:
return self.name return self.name
@ -747,8 +758,9 @@ class Domain(TimeStampedModel, DomainHelper):
def _get_or_create_domain(self): def _get_or_create_domain(self):
"""Try to fetch info about this domain. Create it if it does not exist.""" """Try to fetch info about this domain. Create it if it does not exist."""
already_tried_to_create = False already_tried_to_create = False
exitEarly=False
count = 0 count = 0
while not already_tried_to_create and count < 3: while not exitEarly and count < 3:
try: try:
logger.info( logger.info(
"_get_or_create_domain()-> getting info on the domain, should hit an error" "_get_or_create_domain()-> getting info on the domain, should hit an error"
@ -756,7 +768,7 @@ class Domain(TimeStampedModel, DomainHelper):
req = commands.InfoDomain(name=self.name) req = commands.InfoDomain(name=self.name)
domainInfo = registry.send(req, cleaned=True).res_data[0] domainInfo = registry.send(req, cleaned=True).res_data[0]
already_tried_to_create = True exitEarly=True
return domainInfo return domainInfo
except RegistryError as e: except RegistryError as e:
count += 1 count += 1
@ -775,20 +787,24 @@ class Domain(TimeStampedModel, DomainHelper):
logger.error(e) logger.error(e)
logger.error(e.code) logger.error(e.code)
raise e raise e
def addRegistrant(self):
@transition(field="state", source=State.UNKNOWN, target=State.PENDING_CREATE)
def pendingCreate(self):
logger.info("In make domain in registry ")
registrant = PublicContact.get_default_registrant() registrant = PublicContact.get_default_registrant()
registrant.domain = self registrant.domain = self
registrant.save() ##calls the registrant_contact.setter registrant.save() ##calls the registrant_contact.setter
logger.info("registrant is %s" % registrant) logger.info("registrant is %s" % registrant)
return registrant.registry_id
@transition(field="state", source=State.UNKNOWN, target=State.DNS_NEEDED)
def pendingCreate(self):
logger.info("In make domain in registry ")
registrantID=self.addRegistrant()
# TODO-notes no chg item for registrant in the epplib should # TODO-notes no chg item for registrant in the epplib should
req = commands.CreateDomain( req = commands.CreateDomain(
name=self.name, name=self.name,
registrant=registrant.registry_id, registrant=registrantID,
auth_info=epp.DomainAuthInfo(pw="2fooBAR123fooBaz"), # not a password auth_info=epp.DomainAuthInfo(pw="2fooBAR123fooBaz"), # not a password
) )
logger.info("_get_or_create_domain()-> about to send domain request") logger.info("_get_or_create_domain()-> about to send domain request")
@ -833,29 +849,42 @@ class Domain(TimeStampedModel, DomainHelper):
# technical_contact.save() # technical_contact.save()
# administrative_contact.save() # administrative_contact.save()
@transition(field="state", source=State.PENDING_CREATE, target=State.CLIENT_HOLD) @transition(field="state", source=State.DNS_NEEDED, target=State.ON_HOLD)
def clientHold(self): def clientHold(self):
##TODO - check to see if client hold is allowed should happen outside of this function ##TODO - check to see if client hold is allowed should happen outside of this function
# (check prohibited statuses) # (check prohibited statuses)
logger.info("clientHold()-> inside clientHold") logger.info("clientHold()-> inside clientHold")
pass self._place_client_hold()
# TODO -send clientHold here # TODO -on the client hold ticket any additional error handling here
@transition(field="state", source=State.ON_HOLD, target=State.DNS_NEEDED)
def revertClientHold(self):
##TODO - check to see if client hold is allowed should happen outside of this function
# (check prohibited statuses)
logger.info("clientHold()-> inside clientHold")
self._remove_client_hold()
# TODO -on the client hold ticket any additional error handling here
@transition(field="state", source=State.CLIENT_HOLD, target=State.DELETED) @transition(field="state", source=State.ON_HOLD, target=State.DELETED)
def deleted(self): def deleted(self):
logger.info("pendingCreate()-> inside pending create") logger.info("pendingCreate()-> inside pending create")
pass self._delete_domain()
# TODO - send delete here # TODO - delete ticket any additional error handling here
@transition( @transition(
field="state", field="state",
source=[State.PENDING_CREATE, State.SERVER_HOLD, State.CLIENT_HOLD], source=[State.DNS_NEEDED],
target=State.CREATED, target=State.READY,
) )
def created(self): def created(self):
logger.info("created()-> inside setting create") logger.info("created()-> inside setting create")
# TODO - do anything else here? # TODO - in nameservers ticket check if has everything for creation
#admin, tech and security
#2 or more (but less than 13) nameservers
#if any of the above is violated raise the user raise a human readable error
#not there is another ticket for error handling keep a todo here if
#user friendly error portion is postponed
def _disclose_fields(self, contact: PublicContact): def _disclose_fields(self, contact: PublicContact):
"""creates a disclose object that can be added to a contact Create using """creates a disclose object that can be added to a contact Create using
@ -966,8 +995,8 @@ class Domain(TimeStampedModel, DomainHelper):
"Registry threw error for contact id %s contact type is %s, error code is\n %s full error is %s", "Registry threw error for contact id %s contact type is %s, error code is\n %s full error is %s",
contact.registry_id, contact.registry_id,
contact.contact_type, contact.contact_type,
err.code, e.code,
err, e,
) )
raise e raise e
@ -977,7 +1006,7 @@ class Domain(TimeStampedModel, DomainHelper):
def _delete_host(self, host): def _delete_host(self, host):
raise NotImplementedError() raise NotImplementedError()
def _fetch_cache(self, fetch_hosts=False, fetch_contacts=False): def _fetch_cache(self, fetch_hosts=False, fetch_contacts=False):
"""Contact registry for info about a domain.""" """Contact registry for info about a domain."""
try: try:
@ -1003,6 +1032,9 @@ class Domain(TimeStampedModel, DomainHelper):
cleaned = {k: v for k, v in cache.items() if v is not ...} cleaned = {k: v for k, v in cache.items() if v is not ...}
logger.info("_fetch_cache()-> cleaned is " + str(cleaned)) logger.info("_fetch_cache()-> cleaned is " + str(cleaned))
#statuses can just be a list no need to keep the epp object
if "statuses" in cleaned.keys():
cleaned["statuses"]=[status.state for status in cleaned["statuses"]]
# get contact info, if there are any # get contact info, if there are any
if ( if (
# fetch_contacts and # fetch_contacts and
@ -1011,11 +1043,14 @@ class Domain(TimeStampedModel, DomainHelper):
and len(cleaned["_contacts"]) and len(cleaned["_contacts"])
): ):
cleaned["contacts"] = [] cleaned["contacts"] = []
for id in cleaned["_contacts"]: for domainContact in cleaned["_contacts"]:
# we do not use _get_or_create_* because we expect the object we # we do not use _get_or_create_* because we expect the object we
# just asked the registry for still exists -- # just asked the registry for still exists --
# if not, that's a problem # if not, that's a problem
req = commands.InfoContact(id=id)
#TODO- discuss-should we check if contact is in public contacts
#and add it if not- this is really to keep in mine the transisiton
req = commands.InfoContact(id=domainContact.contact)
data = registry.send(req, cleaned=True).res_data[0] data = registry.send(req, cleaned=True).res_data[0]
# extract properties from response # extract properties from response
@ -1024,6 +1059,7 @@ class Domain(TimeStampedModel, DomainHelper):
logger.info(data) logger.info(data)
contact = { contact = {
"id": id, "id": id,
"type":domainContact.type,
"auth_info": getattr(data, "auth_info", ...), "auth_info": getattr(data, "auth_info", ...),
"cr_date": getattr(data, "cr_date", ...), "cr_date": getattr(data, "cr_date", ...),
"disclose": getattr(data, "disclose", ...), "disclose": getattr(data, "disclose", ...),
@ -1035,7 +1071,7 @@ class Domain(TimeStampedModel, DomainHelper):
"up_date": getattr(data, "up_date", ...), "up_date": getattr(data, "up_date", ...),
"voice": getattr(data, "voice", ...), "voice": getattr(data, "voice", ...),
} }
cleaned["contacts"].append( cleaned["contacts"].append(
{k: v for k, v in contact.items() if v is not ...} {k: v for k, v in contact.items() if v is not ...}
) )

View file

@ -4,9 +4,14 @@
<div class="submit-row"> <div class="submit-row">
<input type="submit" value="Place hold" name="_place_client_hold"> <input type="submit" value="Place hold" name="_place_client_hold">
<input type="submit" value="Get the email" name="_get_security_email"> <input type="submit" value="Get the email" name="_get_security_email">
<input type="submit" value="Set Security Contact" name="_set_security_contact"> <input type="submit" value="Set New Security Contact" name="_set_security_contact">
<input type="submit" value="Create the domain obj" name="_make_domain_in_registry">
<input type="submit" value="Create the domain obj" name="_make_domain_in_registry"> <input type="submit" value="Create the domain obj" name="_make_domain_in_registry">
<input type="submit" value="add nameservers" name="_make_nameservers">
<input type="submit" value="get nameservers" name="_get_nameservers">
<input type="submit" value="get status" name="_get_status">
<input type="submit" value="add nameservers" name="_set_client_hold">
<input type="submit" value="get nameservers" name="_rem_client_hold">
<input type="submit" value="get status" name="_delete_domain">
</div> </div>
{{ block.super }} {{ block.super }}
{% endblock %} {% endblock %}

View file

@ -63,7 +63,7 @@ class MockEppLib(TestCase):
and self.mockedSendFunction.call_count == 3 and self.mockedSendFunction.call_count == 3
): ):
print("raising error") print("raising error")
print()
raise RegistryError(code=ErrorCode.OBJECT_EXISTS) raise RegistryError(code=ErrorCode.OBJECT_EXISTS)
return MagicMock(res_data=[self.mockDataInfoHosts]) return MagicMock(res_data=[self.mockDataInfoHosts])

View file

@ -135,6 +135,10 @@ class DomainNameserversView(DomainPermissionView, FormMixin):
def get_initial(self): def get_initial(self):
"""The initial value for the form (which is a formset here).""" """The initial value for the form (which is a formset here)."""
domain = self.get_object() domain = self.get_object()
nameservers=domain.nameservers
if nameservers is None:
return []
return [{"server": name} for name, *ip in domain.nameservers] return [{"server": name} for name, *ip in domain.nameservers]
def get_success_url(self): def get_success_url(self):