mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-20 11:29:25 +02:00
Merge MAIN to nl/926-transition-domain-object-creation
Signed-off-by: CocoByte <nicolle.leclair@gmail.com>
This commit is contained in:
commit
b68d66db9e
8 changed files with 161 additions and 33 deletions
|
@ -138,24 +138,12 @@ class MyUserAdmin(BaseUserAdmin):
|
||||||
"email",
|
"email",
|
||||||
"first_name",
|
"first_name",
|
||||||
"last_name",
|
"last_name",
|
||||||
|
# Group is a custom property defined within this file,
|
||||||
|
# rather than in a model like the other properties
|
||||||
"group",
|
"group",
|
||||||
"status",
|
"status",
|
||||||
)
|
)
|
||||||
|
|
||||||
list_filter = (
|
|
||||||
"is_active",
|
|
||||||
"groups",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Let's define First group
|
|
||||||
# (which should in theory be the ONLY group)
|
|
||||||
def group(self, obj):
|
|
||||||
if obj.groups.filter(name="full_access_group").exists():
|
|
||||||
return "Full access"
|
|
||||||
elif obj.groups.filter(name="cisa_analysts_group").exists():
|
|
||||||
return "Analyst"
|
|
||||||
return ""
|
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
|
@ -222,6 +210,20 @@ class MyUserAdmin(BaseUserAdmin):
|
||||||
"date_joined",
|
"date_joined",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
list_filter = (
|
||||||
|
"is_active",
|
||||||
|
"groups",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Let's define First group
|
||||||
|
# (which should in theory be the ONLY group)
|
||||||
|
def group(self, obj):
|
||||||
|
if obj.groups.filter(name="full_access_group").exists():
|
||||||
|
return "Full access"
|
||||||
|
elif obj.groups.filter(name="cisa_analysts_group").exists():
|
||||||
|
return "Analyst"
|
||||||
|
return ""
|
||||||
|
|
||||||
def get_list_display(self, request):
|
def get_list_display(self, request):
|
||||||
# The full_access_permission perm will load onto the full_access_group
|
# The full_access_permission perm will load onto the full_access_group
|
||||||
# which is equivalent to superuser. The other group we use to manage
|
# which is equivalent to superuser. The other group we use to manage
|
||||||
|
|
|
@ -64,7 +64,7 @@ class DomainSecurityEmailForm(forms.Form):
|
||||||
|
|
||||||
"""Form for adding or editing a security email to a domain."""
|
"""Form for adding or editing a security email to a domain."""
|
||||||
|
|
||||||
security_email = forms.EmailField(label="Security email")
|
security_email = forms.EmailField(label="Security email", required=False)
|
||||||
|
|
||||||
|
|
||||||
class DomainOrgNameAddressForm(forms.ModelForm):
|
class DomainOrgNameAddressForm(forms.ModelForm):
|
||||||
|
|
|
@ -15,7 +15,7 @@ from epplibwrapper import (
|
||||||
RegistryError,
|
RegistryError,
|
||||||
ErrorCode,
|
ErrorCode,
|
||||||
)
|
)
|
||||||
from registrar.models.utility.contact_error import ContactError
|
from registrar.models.utility.contact_error import ContactError, ContactErrorCodes
|
||||||
|
|
||||||
from .utility.domain_field import DomainField
|
from .utility.domain_field import DomainField
|
||||||
from .utility.domain_helper import DomainHelper
|
from .utility.domain_helper import DomainHelper
|
||||||
|
@ -625,7 +625,10 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
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
|
if secContact is not None:
|
||||||
|
return secContact.email
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def clientHoldStatus(self):
|
def clientHoldStatus(self):
|
||||||
return epp.Status(state=self.Status.CLIENT_HOLD, description="", lang="en")
|
return epp.Status(state=self.Status.CLIENT_HOLD, description="", lang="en")
|
||||||
|
@ -698,10 +701,10 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if contact_type is None:
|
if contact_type is None:
|
||||||
raise ContactError("contact_type is None")
|
raise ContactError(code=ContactErrorCodes.CONTACT_TYPE_NONE)
|
||||||
|
|
||||||
if contact_id is None:
|
if contact_id is None:
|
||||||
raise ContactError("contact_id is None")
|
raise ContactError(code=ContactErrorCodes.CONTACT_ID_NONE)
|
||||||
|
|
||||||
# Since contact_id is registry_id,
|
# Since contact_id is registry_id,
|
||||||
# check that its the right length
|
# check that its the right length
|
||||||
|
@ -710,14 +713,11 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
contact_id_length > PublicContact.get_max_id_length()
|
contact_id_length > PublicContact.get_max_id_length()
|
||||||
or contact_id_length < 1
|
or contact_id_length < 1
|
||||||
):
|
):
|
||||||
raise ContactError(
|
raise ContactError(code=ContactErrorCodes.CONTACT_ID_INVALID_LENGTH)
|
||||||
"contact_id is of invalid length. "
|
|
||||||
"Cannot exceed 16 characters, "
|
|
||||||
f"got {contact_id} with a length of {contact_id_length}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if not isinstance(contact, eppInfo.InfoContactResultData):
|
if not isinstance(contact, eppInfo.InfoContactResultData):
|
||||||
raise ContactError("Contact must be of type InfoContactResultData")
|
raise ContactError(code=ContactErrorCodes.CONTACT_INVALID_TYPE)
|
||||||
|
|
||||||
auth_info = contact.auth_info
|
auth_info = contact.auth_info
|
||||||
postal_info = contact.postal_info
|
postal_info = contact.postal_info
|
||||||
addr = postal_info.addr
|
addr = postal_info.addr
|
||||||
|
@ -880,8 +880,8 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
|
|
||||||
return self._handle_registrant_contact(desired_contact)
|
return self._handle_registrant_contact(desired_contact)
|
||||||
|
|
||||||
_registry_id: str
|
_registry_id: str = ""
|
||||||
if contact_type in contacts:
|
if contacts is not None and contact_type in contacts:
|
||||||
_registry_id = contacts.get(contact_type)
|
_registry_id = contacts.get(contact_type)
|
||||||
|
|
||||||
desired = PublicContact.objects.filter(
|
desired = PublicContact.objects.filter(
|
||||||
|
|
|
@ -1,2 +1,51 @@
|
||||||
|
from enum import IntEnum
|
||||||
|
|
||||||
|
|
||||||
|
class ContactErrorCodes(IntEnum):
|
||||||
|
"""Used in the ContactError class for
|
||||||
|
error mapping.
|
||||||
|
|
||||||
|
Overview of contact error codes:
|
||||||
|
- 2000 CONTACT_TYPE_NONE
|
||||||
|
- 2001 CONTACT_ID_NONE
|
||||||
|
- 2002 CONTACT_ID_INVALID_LENGTH
|
||||||
|
- 2003 CONTACT_INVALID_TYPE
|
||||||
|
"""
|
||||||
|
|
||||||
|
CONTACT_TYPE_NONE = 2000
|
||||||
|
CONTACT_ID_NONE = 2001
|
||||||
|
CONTACT_ID_INVALID_LENGTH = 2002
|
||||||
|
CONTACT_INVALID_TYPE = 2003
|
||||||
|
CONTACT_NOT_FOUND = 2004
|
||||||
|
|
||||||
|
|
||||||
class ContactError(Exception):
|
class ContactError(Exception):
|
||||||
...
|
"""
|
||||||
|
Overview of contact error codes:
|
||||||
|
- 2000 CONTACT_TYPE_NONE
|
||||||
|
- 2001 CONTACT_ID_NONE
|
||||||
|
- 2002 CONTACT_ID_INVALID_LENGTH
|
||||||
|
- 2003 CONTACT_INVALID_TYPE
|
||||||
|
- 2004 CONTACT_NOT_FOUND
|
||||||
|
"""
|
||||||
|
|
||||||
|
# For linter
|
||||||
|
_contact_id_error = "contact_id has an invalid length. Cannot exceed 16 characters."
|
||||||
|
_contact_invalid_error = "Contact must be of type InfoContactResultData"
|
||||||
|
_contact_not_found_error = "No contact was found in cache or the registry"
|
||||||
|
_error_mapping = {
|
||||||
|
ContactErrorCodes.CONTACT_TYPE_NONE: "contact_type is None",
|
||||||
|
ContactErrorCodes.CONTACT_ID_NONE: "contact_id is None",
|
||||||
|
ContactErrorCodes.CONTACT_ID_INVALID_LENGTH: _contact_id_error,
|
||||||
|
ContactErrorCodes.CONTACT_INVALID_TYPE: _contact_invalid_error,
|
||||||
|
ContactErrorCodes.CONTACT_NOT_FOUND: _contact_not_found_error,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, *args, code=None, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.code = code
|
||||||
|
if self.code in self._error_mapping:
|
||||||
|
self.message = self._error_mapping.get(self.code)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.message}"
|
||||||
|
|
|
@ -46,8 +46,11 @@
|
||||||
{% include "includes/summary_item.html" with title='Your contact information' value=request.user.contact contact='true' edit_link=url %}
|
{% include "includes/summary_item.html" with title='Your contact information' value=request.user.contact contact='true' edit_link=url %}
|
||||||
|
|
||||||
{% url 'domain-security-email' pk=domain.id as url %}
|
{% url 'domain-security-email' pk=domain.id as url %}
|
||||||
{% include "includes/summary_item.html" with title='Security email' value=domain.security_email edit_link=url %}
|
{% if security_email is not None and security_email != default_security_email%}
|
||||||
|
{% include "includes/summary_item.html" with title='Security email' value=security_email edit_link=url %}
|
||||||
|
{% else %}
|
||||||
|
{% include "includes/summary_item.html" with title='Security email' value='None provided' edit_link=url %}
|
||||||
|
{% endif %}
|
||||||
{% url 'domain-users' pk=domain.id as url %}
|
{% url 'domain-users' pk=domain.id as url %}
|
||||||
{% include "includes/summary_item.html" with title='User management' users='true' list=True value=domain.permissions.all edit_link=url %}
|
{% include "includes/summary_item.html" with title='User management' users='true' list=True value=domain.permissions.all edit_link=url %}
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
|
|
||||||
<p>A security contact should be capable of evaluating or triaging security reports for your entire domain. Use a team email address, not an individual’s email. We recommend using an alias, like security@domain.gov.</p>
|
<p>A security contact should be capable of evaluating or triaging security reports for your entire domain. Use a team email address, not an individual’s email. We recommend using an alias, like security@domain.gov.</p>
|
||||||
|
|
||||||
{% include "includes/required_fields.html" %}
|
|
||||||
|
|
||||||
<form class="usa-form usa-form--large" method="post" novalidate>
|
<form class="usa-form usa-form--large" method="post" novalidate>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ from registrar.models.domain_information import DomainInformation
|
||||||
from registrar.models.draft_domain import DraftDomain
|
from registrar.models.draft_domain import DraftDomain
|
||||||
from registrar.models.public_contact import PublicContact
|
from registrar.models.public_contact import PublicContact
|
||||||
from registrar.models.user import User
|
from registrar.models.user import User
|
||||||
|
from registrar.models.utility.contact_error import ContactError, ContactErrorCodes
|
||||||
from .common import MockEppLib
|
from .common import MockEppLib
|
||||||
from django_fsm import TransitionNotAllowed # type: ignore
|
from django_fsm import TransitionNotAllowed # type: ignore
|
||||||
from epplibwrapper import (
|
from epplibwrapper import (
|
||||||
|
@ -193,6 +194,56 @@ class TestDomainCache(MockEppLib):
|
||||||
self.assertEqual(cached_contact, in_db.registry_id)
|
self.assertEqual(cached_contact, in_db.registry_id)
|
||||||
self.assertEqual(domain.security_contact.email, "123test@mail.gov")
|
self.assertEqual(domain.security_contact.email, "123test@mail.gov")
|
||||||
|
|
||||||
|
def test_errors_map_epp_contact_to_public_contact(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant gets invalid data from EPPLib
|
||||||
|
When the `map_epp_contact_to_public_contact` function
|
||||||
|
gets invalid data from EPPLib
|
||||||
|
Then the function throws the expected ContactErrors
|
||||||
|
"""
|
||||||
|
domain, _ = Domain.objects.get_or_create(name="registry.gov")
|
||||||
|
fakedEpp = self.fakedEppObject()
|
||||||
|
invalid_length = fakedEpp.dummyInfoContactResultData(
|
||||||
|
"Cymaticsisasubsetofmodalvibrationalphenomena", "lengthInvalid@mail.gov"
|
||||||
|
)
|
||||||
|
valid_object = fakedEpp.dummyInfoContactResultData("valid", "valid@mail.gov")
|
||||||
|
|
||||||
|
desired_error = ContactErrorCodes.CONTACT_ID_INVALID_LENGTH
|
||||||
|
with self.assertRaises(ContactError) as context:
|
||||||
|
domain.map_epp_contact_to_public_contact(
|
||||||
|
invalid_length,
|
||||||
|
invalid_length.id,
|
||||||
|
PublicContact.ContactTypeChoices.SECURITY,
|
||||||
|
)
|
||||||
|
self.assertEqual(context.exception.code, desired_error)
|
||||||
|
|
||||||
|
desired_error = ContactErrorCodes.CONTACT_ID_NONE
|
||||||
|
with self.assertRaises(ContactError) as context:
|
||||||
|
domain.map_epp_contact_to_public_contact(
|
||||||
|
valid_object,
|
||||||
|
None,
|
||||||
|
PublicContact.ContactTypeChoices.SECURITY,
|
||||||
|
)
|
||||||
|
self.assertEqual(context.exception.code, desired_error)
|
||||||
|
|
||||||
|
desired_error = ContactErrorCodes.CONTACT_INVALID_TYPE
|
||||||
|
with self.assertRaises(ContactError) as context:
|
||||||
|
domain.map_epp_contact_to_public_contact(
|
||||||
|
"bad_object",
|
||||||
|
valid_object.id,
|
||||||
|
PublicContact.ContactTypeChoices.SECURITY,
|
||||||
|
)
|
||||||
|
self.assertEqual(context.exception.code, desired_error)
|
||||||
|
|
||||||
|
desired_error = ContactErrorCodes.CONTACT_TYPE_NONE
|
||||||
|
with self.assertRaises(ContactError) as context:
|
||||||
|
domain.map_epp_contact_to_public_contact(
|
||||||
|
valid_object,
|
||||||
|
valid_object.id,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
self.assertEqual(context.exception.code, desired_error)
|
||||||
|
|
||||||
|
|
||||||
class TestDomainCreation(MockEppLib):
|
class TestDomainCreation(MockEppLib):
|
||||||
"""Rule: An approved domain application must result in a domain"""
|
"""Rule: An approved domain application must result in a domain"""
|
||||||
|
|
|
@ -21,6 +21,7 @@ from registrar.models import (
|
||||||
User,
|
User,
|
||||||
UserDomainRole,
|
UserDomainRole,
|
||||||
)
|
)
|
||||||
|
from registrar.models.public_contact import PublicContact
|
||||||
|
|
||||||
from ..forms import (
|
from ..forms import (
|
||||||
ContactForm,
|
ContactForm,
|
||||||
|
@ -42,6 +43,19 @@ class DomainView(DomainPermissionView):
|
||||||
|
|
||||||
template_name = "domain_detail.html"
|
template_name = "domain_detail.html"
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
default_email = Domain().get_default_security_contact().email
|
||||||
|
context["default_security_email"] = default_email
|
||||||
|
|
||||||
|
security_email = self.get_object().get_security_email()
|
||||||
|
if security_email is None or security_email == default_email:
|
||||||
|
context["security_email"] = None
|
||||||
|
return context
|
||||||
|
context["security_email"] = security_email
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
class DomainOrgNameAddressView(DomainPermissionView, FormMixin):
|
class DomainOrgNameAddressView(DomainPermissionView, FormMixin):
|
||||||
"""Organization name and mailing address view"""
|
"""Organization name and mailing address view"""
|
||||||
|
@ -284,10 +298,21 @@ class DomainSecurityEmailView(DomainPermissionView, FormMixin):
|
||||||
"""The form is valid, call setter in model."""
|
"""The form is valid, call setter in model."""
|
||||||
|
|
||||||
# Set the security email from the form
|
# Set the security email from the form
|
||||||
new_email = form.cleaned_data.get("security_email", "")
|
new_email: str = form.cleaned_data.get("security_email", "")
|
||||||
|
|
||||||
|
# If we pass nothing for the sec email, set to the default
|
||||||
|
if new_email is None or new_email.strip() == "":
|
||||||
|
new_email = PublicContact.get_default_security().email
|
||||||
|
|
||||||
domain = self.get_object()
|
domain = self.get_object()
|
||||||
contact = domain.security_contact
|
contact = domain.security_contact
|
||||||
|
|
||||||
|
# If no default is created for security_contact,
|
||||||
|
# then we cannot connect to the registry.
|
||||||
|
if contact is None:
|
||||||
|
messages.error(self.request, "Update failed. Cannot contact the registry.")
|
||||||
|
return redirect(self.get_success_url())
|
||||||
|
|
||||||
contact.email = new_email
|
contact.email = new_email
|
||||||
contact.save()
|
contact.save()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue