mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-06-03 02:57:25 +02:00
Merge branch 'main' of https://github.com/cisagov/manage.get.gov into rh/2909-new-agency-field
This commit is contained in:
commit
e74d98ca71
7 changed files with 248 additions and 16 deletions
|
@ -663,6 +663,7 @@ class ContactAdmin(ListHeaderAdmin):
|
|||
list_display = [
|
||||
"contact",
|
||||
"email",
|
||||
"user_exists",
|
||||
]
|
||||
# this ordering effects the ordering of results
|
||||
# in autocomplete_fields for user
|
||||
|
@ -679,6 +680,13 @@ class ContactAdmin(ListHeaderAdmin):
|
|||
|
||||
change_form_template = "django/admin/email_clipboard_change_form.html"
|
||||
|
||||
def user_exists(self, obj):
|
||||
"""Check if the Contact has a related User"""
|
||||
return "Yes" if obj.user is not None else "No"
|
||||
|
||||
user_exists.short_description = "Is user" # type: ignore
|
||||
user_exists.admin_order_field = "user" # type: ignore
|
||||
|
||||
# We name the custom prop 'contact' because linter
|
||||
# is not allowing a short_description attr on it
|
||||
# This gets around the linter limitation, for now.
|
||||
|
@ -1435,12 +1443,36 @@ class DomainRequestAdmin(ListHeaderAdmin):
|
|||
"""
|
||||
Override changelist_view to set the selected value of status filter.
|
||||
"""
|
||||
# there are two conditions which should set the default selected filter:
|
||||
# 1 - there are no query parameters in the request and the request is the
|
||||
# initial request for this view
|
||||
# 2 - there are no query parameters in the request and the referring url is
|
||||
# the change view for a domain request
|
||||
should_apply_default_filter = False
|
||||
# use http_referer in order to distinguish between request as a link from another page
|
||||
# and request as a removal of all filters
|
||||
http_referer = request.META.get("HTTP_REFERER", "")
|
||||
# if there are no query parameters in the request
|
||||
# and the request is the initial request for this view
|
||||
if not bool(request.GET) and request.path not in http_referer:
|
||||
if not bool(request.GET):
|
||||
# if the request is the initial request for this view
|
||||
if request.path not in http_referer:
|
||||
should_apply_default_filter = True
|
||||
# elif the request is a referral from changelist view or from
|
||||
# domain request change view
|
||||
elif request.path in http_referer:
|
||||
# find the index to determine the referring url after the path
|
||||
index = http_referer.find(request.path)
|
||||
# Check if there is a character following the path in http_referer
|
||||
next_char_index = index + len(request.path)
|
||||
if index + next_char_index < len(http_referer):
|
||||
next_char = http_referer[next_char_index]
|
||||
|
||||
# Check if the next character is a digit, if so, this indicates
|
||||
# a change view for domain request
|
||||
if next_char.isdigit():
|
||||
should_apply_default_filter = True
|
||||
|
||||
if should_apply_default_filter:
|
||||
# modify the GET of the request to set the selected filter
|
||||
modified_get = copy.deepcopy(request.GET)
|
||||
modified_get["status__in"] = "submitted,in review,action needed"
|
||||
|
|
|
@ -530,7 +530,7 @@ function hideDeletedForms() {
|
|||
let isDotgovDomain = document.querySelector(".dotgov-domain-form");
|
||||
// The Nameservers formset features 2 required and 11 optionals
|
||||
if (isNameserversForm) {
|
||||
cloneIndex = 2;
|
||||
// cloneIndex = 2;
|
||||
formLabel = "Name server";
|
||||
// DNSSEC: DS Data
|
||||
} else if (isDsDataForm) {
|
||||
|
@ -766,3 +766,21 @@ function toggleTwoDomElements(ele1, ele2, index) {
|
|||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* An IIFE that disables the delete buttons on nameserver forms on page load if < 3 forms
|
||||
*
|
||||
*/
|
||||
(function nameserversFormListener() {
|
||||
let isNameserversForm = document.querySelector(".nameservers-form");
|
||||
if (isNameserversForm) {
|
||||
let forms = document.querySelectorAll(".repeatable-form");
|
||||
if (forms.length < 3) {
|
||||
// Hide the delete buttons on the 2 nameservers
|
||||
forms.forEach((form) => {
|
||||
Array.from(form.querySelectorAll('.delete-record')).forEach((deleteButton) => {
|
||||
deleteButton.setAttribute("disabled", "true");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -83,25 +83,34 @@ class DomainNameserverForm(forms.Form):
|
|||
# after clean_fields. it is used to determine form level errors.
|
||||
# is_valid is typically called from view during a post
|
||||
cleaned_data = super().clean()
|
||||
|
||||
self.clean_empty_strings(cleaned_data)
|
||||
|
||||
server = cleaned_data.get("server", "")
|
||||
# remove ANY spaces in the server field
|
||||
server = server.replace(" ", "")
|
||||
# lowercase the server
|
||||
server = server.lower()
|
||||
server = server.replace(" ", "").lower()
|
||||
cleaned_data["server"] = server
|
||||
ip = cleaned_data.get("ip", None)
|
||||
# remove ANY spaces in the ip field
|
||||
|
||||
ip = cleaned_data.get("ip", "")
|
||||
ip = ip.replace(" ", "")
|
||||
cleaned_data["ip"] = ip
|
||||
|
||||
domain = cleaned_data.get("domain", "")
|
||||
|
||||
ip_list = self.extract_ip_list(ip)
|
||||
|
||||
# validate if the form has a server or an ip
|
||||
# Capture the server_value
|
||||
server_value = self.cleaned_data.get("server")
|
||||
|
||||
# Validate if the form has a server or an ip
|
||||
if (ip and ip_list) or server:
|
||||
self.validate_nameserver_ip_combo(domain, server, ip_list)
|
||||
|
||||
# Re-set the server value:
|
||||
# add_error which is called on validate_nameserver_ip_combo will clean-up (delete) any invalid data.
|
||||
# We need that data because we need to know the total server entries (even if invalid) in the formset
|
||||
# clean method where we determine whether a blank first and/or second entry should throw a required error.
|
||||
self.cleaned_data["server"] = server_value
|
||||
|
||||
return cleaned_data
|
||||
|
||||
def clean_empty_strings(self, cleaned_data):
|
||||
|
@ -149,6 +158,19 @@ class BaseNameserverFormset(forms.BaseFormSet):
|
|||
"""
|
||||
Check for duplicate entries in the formset.
|
||||
"""
|
||||
|
||||
# Check if there are at least two valid servers
|
||||
valid_servers_count = sum(
|
||||
1 for form in self.forms if form.cleaned_data.get("server") and form.cleaned_data.get("server").strip()
|
||||
)
|
||||
if valid_servers_count >= 2:
|
||||
# If there are, remove the "At least two name servers are required" error from each form
|
||||
# This will allow for successful submissions when the first or second entries are blanked
|
||||
# but there are enough entries total
|
||||
for form in self.forms:
|
||||
if form.errors.get("server") == ["At least two name servers are required."]:
|
||||
form.errors.pop("server")
|
||||
|
||||
if any(self.errors):
|
||||
# Don't bother validating the formset unless each form is valid on its own
|
||||
return
|
||||
|
@ -156,10 +178,13 @@ class BaseNameserverFormset(forms.BaseFormSet):
|
|||
data = []
|
||||
duplicates = []
|
||||
|
||||
for form in self.forms:
|
||||
for index, form in enumerate(self.forms):
|
||||
if form.cleaned_data:
|
||||
value = form.cleaned_data["server"]
|
||||
if value in data:
|
||||
# We need to make sure not to trigger the duplicate error in case the first and second nameservers
|
||||
# are empty. If there are enough records in the formset, that error is an unecessary blocker.
|
||||
# If there aren't, the required error will block the submit.
|
||||
if value in data and not (form.cleaned_data.get("server", "").strip() == "" and index == 1):
|
||||
form.add_error(
|
||||
"server",
|
||||
NameserverError(code=nsErrorCodes.DUPLICATE_HOST, nameserver=value),
|
||||
|
|
|
@ -1152,6 +1152,18 @@ class MockEppLib(TestCase):
|
|||
],
|
||||
)
|
||||
|
||||
infoDomainFourHosts = fakedEppObject(
|
||||
"fournameserversDomain.gov",
|
||||
cr_date=make_aware(datetime(2023, 5, 25, 19, 45, 35)),
|
||||
contacts=[],
|
||||
hosts=[
|
||||
"ns1.my-nameserver-1.com",
|
||||
"ns1.my-nameserver-2.com",
|
||||
"ns1.cats-are-superior3.com",
|
||||
"ns1.explosive-chicken-nuggets.com",
|
||||
],
|
||||
)
|
||||
|
||||
infoDomainNoHost = fakedEppObject(
|
||||
"my-nameserver.gov",
|
||||
cr_date=make_aware(datetime(2023, 5, 25, 19, 45, 35)),
|
||||
|
@ -1452,7 +1464,9 @@ class MockEppLib(TestCase):
|
|||
)
|
||||
|
||||
def mockInfoDomainCommands(self, _request, cleaned):
|
||||
request_name = getattr(_request, "name", None)
|
||||
request_name = getattr(_request, "name", None).lower()
|
||||
|
||||
print(request_name)
|
||||
|
||||
# Define a dictionary to map request names to data and extension values
|
||||
request_mappings = {
|
||||
|
@ -1474,7 +1488,8 @@ class MockEppLib(TestCase):
|
|||
"nameserverwithip.gov": (self.infoDomainHasIP, None),
|
||||
"namerserversubdomain.gov": (self.infoDomainCheckHostIPCombo, None),
|
||||
"freeman.gov": (self.InfoDomainWithContacts, None),
|
||||
"threenameserversDomain.gov": (self.infoDomainThreeHosts, None),
|
||||
"threenameserversdomain.gov": (self.infoDomainThreeHosts, None),
|
||||
"fournameserversdomain.gov": (self.infoDomainFourHosts, None),
|
||||
"defaultsecurity.gov": (self.InfoDomainWithDefaultSecurityContact, None),
|
||||
"adomain2.gov": (self.InfoDomainWithVerisignSecurityContact, None),
|
||||
"defaulttechnical.gov": (self.InfoDomainWithDefaultTechnicalContact, None),
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.conf import settings
|
|||
from django.urls import reverse
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from .common import MockSESClient, create_user # type: ignore
|
||||
from .common import MockEppLib, MockSESClient, create_user # type: ignore
|
||||
from django_webtest import WebTest # type: ignore
|
||||
import boto3_mocking # type: ignore
|
||||
|
||||
|
@ -71,11 +71,14 @@ class TestWithDomainPermissions(TestWithUser):
|
|||
# that inherit this setUp
|
||||
self.domain_dnssec_none, _ = Domain.objects.get_or_create(name="dnssec-none.gov")
|
||||
|
||||
self.domain_with_four_nameservers, _ = Domain.objects.get_or_create(name="fournameserversDomain.gov")
|
||||
|
||||
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
|
||||
|
||||
DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain_dsdata)
|
||||
DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain_multdsdata)
|
||||
DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain_dnssec_none)
|
||||
DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain_with_four_nameservers)
|
||||
DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain_with_ip)
|
||||
DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain_just_nameserver)
|
||||
DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain_on_hold)
|
||||
|
@ -98,6 +101,11 @@ class TestWithDomainPermissions(TestWithUser):
|
|||
domain=self.domain_dnssec_none,
|
||||
role=UserDomainRole.Roles.MANAGER,
|
||||
)
|
||||
UserDomainRole.objects.get_or_create(
|
||||
user=self.user,
|
||||
domain=self.domain_with_four_nameservers,
|
||||
role=UserDomainRole.Roles.MANAGER,
|
||||
)
|
||||
UserDomainRole.objects.get_or_create(
|
||||
user=self.user,
|
||||
domain=self.domain_with_ip,
|
||||
|
@ -727,7 +735,7 @@ class TestDomainManagers(TestDomainOverview):
|
|||
self.assertContains(home_page, self.domain.name)
|
||||
|
||||
|
||||
class TestDomainNameservers(TestDomainOverview):
|
||||
class TestDomainNameservers(TestDomainOverview, MockEppLib):
|
||||
def test_domain_nameservers(self):
|
||||
"""Can load domain's nameservers page."""
|
||||
page = self.client.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
|
||||
|
@ -974,6 +982,117 @@ class TestDomainNameservers(TestDomainOverview):
|
|||
page = result.follow()
|
||||
self.assertContains(page, "The name servers for this domain have been updated")
|
||||
|
||||
def test_domain_nameservers_can_blank_out_first_or_second_one_if_enough_entries(self):
|
||||
"""Nameserver form submits successfully with 2 valid inputs, even if the first or
|
||||
second entries are blanked out.
|
||||
|
||||
Uses self.app WebTest because we need to interact with forms.
|
||||
"""
|
||||
|
||||
nameserver1 = ""
|
||||
nameserver2 = "ns2.igorville.gov"
|
||||
nameserver3 = "ns3.igorville.gov"
|
||||
valid_ip = ""
|
||||
valid_ip_2 = "128.0.0.2"
|
||||
valid_ip_3 = "128.0.0.3"
|
||||
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
nameservers_page.form["form-0-server"] = nameserver1
|
||||
nameservers_page.form["form-0-ip"] = valid_ip
|
||||
nameservers_page.form["form-1-server"] = nameserver2
|
||||
nameservers_page.form["form-1-ip"] = valid_ip_2
|
||||
nameservers_page.form["form-2-server"] = nameserver3
|
||||
nameservers_page.form["form-2-ip"] = valid_ip_3
|
||||
with less_console_noise(): # swallow log warning message
|
||||
result = nameservers_page.form.submit()
|
||||
|
||||
# form submission was a successful post, response should be a 302
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(
|
||||
result["Location"],
|
||||
reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}),
|
||||
)
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
nameservers_page = result.follow()
|
||||
self.assertContains(nameservers_page, "The name servers for this domain have been updated")
|
||||
|
||||
nameserver1 = "ns1.igorville.gov"
|
||||
nameserver2 = ""
|
||||
nameserver3 = "ns3.igorville.gov"
|
||||
valid_ip = "128.0.0.1"
|
||||
valid_ip_2 = ""
|
||||
valid_ip_3 = "128.0.0.3"
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
nameservers_page.form["form-0-server"] = nameserver1
|
||||
nameservers_page.form["form-0-ip"] = valid_ip
|
||||
nameservers_page.form["form-1-server"] = nameserver2
|
||||
nameservers_page.form["form-1-ip"] = valid_ip_2
|
||||
nameservers_page.form["form-2-server"] = nameserver3
|
||||
nameservers_page.form["form-2-ip"] = valid_ip_3
|
||||
with less_console_noise(): # swallow log warning message
|
||||
result = nameservers_page.form.submit()
|
||||
|
||||
# form submission was a successful post, response should be a 302
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(
|
||||
result["Location"],
|
||||
reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}),
|
||||
)
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
nameservers_page = result.follow()
|
||||
self.assertContains(nameservers_page, "The name servers for this domain have been updated")
|
||||
|
||||
def test_domain_nameservers_can_blank_out_first_and_second_one_if_enough_entries(self):
|
||||
"""Nameserver form submits successfully with 2 valid inputs, even if the first and
|
||||
second entries are blanked out.
|
||||
|
||||
Uses self.app WebTest because we need to interact with forms.
|
||||
"""
|
||||
|
||||
# We need to start with a domain with 4 nameservers otherwise the formset in the test environment
|
||||
# will only have 3 forms
|
||||
nameserver1 = ""
|
||||
nameserver2 = ""
|
||||
nameserver3 = "ns3.igorville.gov"
|
||||
nameserver4 = "ns4.igorville.gov"
|
||||
valid_ip = ""
|
||||
valid_ip_2 = ""
|
||||
valid_ip_3 = ""
|
||||
valid_ip_4 = ""
|
||||
nameservers_page = self.app.get(
|
||||
reverse("domain-dns-nameservers", kwargs={"pk": self.domain_with_four_nameservers.id})
|
||||
)
|
||||
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
||||
# Minimal check to ensure the form is loaded correctly
|
||||
self.assertEqual(nameservers_page.form["form-0-server"].value, "ns1.my-nameserver-1.com")
|
||||
self.assertEqual(nameservers_page.form["form-3-server"].value, "ns1.explosive-chicken-nuggets.com")
|
||||
|
||||
nameservers_page.form["form-0-server"] = nameserver1
|
||||
nameservers_page.form["form-0-ip"] = valid_ip
|
||||
nameservers_page.form["form-1-server"] = nameserver2
|
||||
nameservers_page.form["form-1-ip"] = valid_ip_2
|
||||
nameservers_page.form["form-2-server"] = nameserver3
|
||||
nameservers_page.form["form-2-ip"] = valid_ip_3
|
||||
nameservers_page.form["form-3-server"] = nameserver4
|
||||
nameservers_page.form["form-3-ip"] = valid_ip_4
|
||||
with less_console_noise(): # swallow log warning message
|
||||
result = nameservers_page.form.submit()
|
||||
|
||||
# form submission was a successful post, response should be a 302
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(
|
||||
result["Location"],
|
||||
reverse("domain-dns-nameservers", kwargs={"pk": self.domain_with_four_nameservers.id}),
|
||||
)
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
nameservers_page = result.follow()
|
||||
self.assertContains(nameservers_page, "The name servers for this domain have been updated")
|
||||
|
||||
def test_domain_nameservers_form_invalid(self):
|
||||
"""Nameserver form does not submit with invalid data.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue