mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-06-05 03:57:25 +02:00
Merge branch 'main' of github.com:cisagov/manage.get.gov into es/1378-availability-bugfix
This commit is contained in:
commit
69dd932320
12 changed files with 119 additions and 12 deletions
1
.github/workflows/deploy-sandbox.yaml
vendored
1
.github/workflows/deploy-sandbox.yaml
vendored
|
@ -21,6 +21,7 @@ jobs:
|
|||
|| startsWith(github.head_ref, 'dk/')
|
||||
|| startsWith(github.head_ref, 'es/')
|
||||
|| startsWith(github.head_ref, 'ky/')
|
||||
|| startsWith(github.head_ref, 'backup/')
|
||||
outputs:
|
||||
environment: ${{ steps.var.outputs.environment}}
|
||||
runs-on: "ubuntu-latest"
|
||||
|
|
1
.github/workflows/migrate.yaml
vendored
1
.github/workflows/migrate.yaml
vendored
|
@ -16,6 +16,7 @@ on:
|
|||
- stable
|
||||
- staging
|
||||
- development
|
||||
- backup
|
||||
- ky
|
||||
- es
|
||||
- nl
|
||||
|
|
1
.github/workflows/reset-db.yaml
vendored
1
.github/workflows/reset-db.yaml
vendored
|
@ -16,6 +16,7 @@ on:
|
|||
options:
|
||||
- staging
|
||||
- development
|
||||
- backup
|
||||
- ky
|
||||
- es
|
||||
- nl
|
||||
|
|
32
ops/manifests/manifest-backup.yaml
Normal file
32
ops/manifests/manifest-backup.yaml
Normal file
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
applications:
|
||||
- name: getgov-backup
|
||||
buildpacks:
|
||||
- python_buildpack
|
||||
path: ../../src
|
||||
instances: 1
|
||||
memory: 512M
|
||||
stack: cflinuxfs4
|
||||
timeout: 180
|
||||
command: ./run.sh
|
||||
health-check-type: http
|
||||
health-check-http-endpoint: /health
|
||||
health-check-invocation-timeout: 40
|
||||
env:
|
||||
# Send stdout and stderr straight to the terminal without buffering
|
||||
PYTHONUNBUFFERED: yup
|
||||
# Tell Django where to find its configuration
|
||||
DJANGO_SETTINGS_MODULE: registrar.config.settings
|
||||
# Tell Django where it is being hosted
|
||||
DJANGO_BASE_URL: https://getgov-backup.app.cloud.gov
|
||||
# Tell Django how much stuff to log
|
||||
DJANGO_LOG_LEVEL: INFO
|
||||
# default public site location
|
||||
GETGOV_PUBLIC_SITE_URL: https://beta.get.gov
|
||||
# Flag to disable/enable features in prod environments
|
||||
IS_PRODUCTION: False
|
||||
routes:
|
||||
- route: getgov-backup.app.cloud.gov
|
||||
services:
|
||||
- getgov-credentials
|
||||
- getgov-backup-database
|
|
@ -2,6 +2,9 @@
|
|||
from django.apps import apps
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.http import JsonResponse
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from registrar.templatetags.url_helpers import public_site_url
|
||||
|
||||
import requests
|
||||
|
||||
|
@ -18,8 +21,13 @@ DOMAIN_API_MESSAGES = {
|
|||
" For example, if you want www.city.gov, you would enter “city”"
|
||||
" (without the quotes).",
|
||||
"extra_dots": "Enter the .gov domain you want without any periods.",
|
||||
"unavailable": "That domain isn’t available. Try entering another one."
|
||||
" Contact us if you need help coming up with a domain.",
|
||||
# message below is considered safe; no user input can be inserted into the message
|
||||
# body; public_site_url() function reads from local app settings and therefore safe
|
||||
"unavailable": mark_safe( # nosec
|
||||
"That domain isn’t available. "
|
||||
"<a class='usa-link' href='{}' target='_blank'>"
|
||||
"Read more about choosing your .gov domain.</a>".format(public_site_url("domains/choosing"))
|
||||
),
|
||||
"invalid": "Enter a domain using only letters, numbers, or hyphens (though we don't recommend using hyphens).",
|
||||
"success": "That domain is available!",
|
||||
"error": "Error finding domain availability. Please wait a few minutes and try again. If you continue \
|
||||
|
|
|
@ -322,7 +322,7 @@ class WebsiteAdmin(ListHeaderAdmin):
|
|||
|
||||
|
||||
class UserDomainRoleAdmin(ListHeaderAdmin):
|
||||
"""Custom domain role admin class."""
|
||||
"""Custom user domain role admin class."""
|
||||
|
||||
# Columns
|
||||
list_display = [
|
||||
|
@ -340,6 +340,8 @@ class UserDomainRoleAdmin(ListHeaderAdmin):
|
|||
]
|
||||
search_help_text = "Search by user, domain, or role."
|
||||
|
||||
autocomplete_fields = ["user", "domain"]
|
||||
|
||||
|
||||
class DomainInvitationAdmin(ListHeaderAdmin):
|
||||
"""Custom domain invitation admin class."""
|
||||
|
|
|
@ -115,14 +115,14 @@ function inlineToast(el, id, style, msg) {
|
|||
toast.className = `usa-alert usa-alert--${style} usa-alert--slim`;
|
||||
toastBody.classList.add("usa-alert__body");
|
||||
p.classList.add("usa-alert__text");
|
||||
p.innerText = msg;
|
||||
p.innerHTML = msg;
|
||||
toastBody.appendChild(p);
|
||||
toast.appendChild(toastBody);
|
||||
el.parentNode.insertBefore(toast, el.nextSibling);
|
||||
} else {
|
||||
// update and show the existing message div
|
||||
toast.className = `usa-alert usa-alert--${style} usa-alert--slim`;
|
||||
toast.querySelector("div p").innerText = msg;
|
||||
toast.querySelector("div p").innerHTML = msg;
|
||||
makeVisible(toast);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -245,3 +245,9 @@ h1, h2, h3 {
|
|||
padding-left: 90px;
|
||||
}
|
||||
}
|
||||
|
||||
// Combo box
|
||||
#select2-id_domain-results,
|
||||
#select2-id_user-results {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -627,6 +627,7 @@ ALLOWED_HOSTS = [
|
|||
"getgov-stable.app.cloud.gov",
|
||||
"getgov-staging.app.cloud.gov",
|
||||
"getgov-development.app.cloud.gov",
|
||||
"getgov-backup.app.cloud.gov",
|
||||
"getgov-ky.app.cloud.gov",
|
||||
"getgov-es.app.cloud.gov",
|
||||
"getgov-nl.app.cloud.gov",
|
||||
|
|
|
@ -118,8 +118,34 @@ class DomainNameserverForm(forms.Form):
|
|||
self.add_error("ip", str(e))
|
||||
|
||||
|
||||
class BaseNameserverFormset(forms.BaseFormSet):
|
||||
def clean(self):
|
||||
"""
|
||||
Check for duplicate entries in the formset.
|
||||
"""
|
||||
if any(self.errors):
|
||||
# Don't bother validating the formset unless each form is valid on its own
|
||||
return
|
||||
|
||||
data = []
|
||||
duplicates = []
|
||||
|
||||
for form in self.forms:
|
||||
if form.cleaned_data:
|
||||
value = form.cleaned_data["server"]
|
||||
if value in data:
|
||||
form.add_error(
|
||||
"server",
|
||||
NameserverError(code=nsErrorCodes.DUPLICATE_HOST, nameserver=value),
|
||||
)
|
||||
duplicates.append(value)
|
||||
else:
|
||||
data.append(value)
|
||||
|
||||
|
||||
NameserverFormset = formset_factory(
|
||||
DomainNameserverForm,
|
||||
formset=BaseNameserverFormset,
|
||||
extra=1,
|
||||
max_num=13,
|
||||
validate_max=True,
|
||||
|
|
|
@ -1219,6 +1219,8 @@ class TestDomainOverview(TestWithDomainPermissions, WebTest):
|
|||
self.app.set_user(self.user.username)
|
||||
self.client.force_login(self.user)
|
||||
|
||||
|
||||
class TestDomainDetail(TestDomainOverview):
|
||||
def test_domain_detail_link_works(self):
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "igorville.gov")
|
||||
|
@ -1227,7 +1229,7 @@ class TestDomainOverview(TestWithDomainPermissions, WebTest):
|
|||
self.assertContains(detail_page, "igorville.gov")
|
||||
self.assertContains(detail_page, "Status")
|
||||
|
||||
def test_domain_overview_blocked_for_ineligible_user(self):
|
||||
def test_domain_detail_blocked_for_ineligible_user(self):
|
||||
"""We could easily duplicate this test for all domain management
|
||||
views, but a single url test should be solid enough since all domain
|
||||
management pages share the same permissions class"""
|
||||
|
@ -1239,7 +1241,7 @@ class TestDomainOverview(TestWithDomainPermissions, WebTest):
|
|||
response = self.client.get(reverse("domain", kwargs={"pk": self.domain.id}))
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
def test_domain_overview_allowed_for_on_hold(self):
|
||||
def test_domain_detail_allowed_for_on_hold(self):
|
||||
"""Test that the domain overview page displays for on hold domain"""
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "on-hold.gov")
|
||||
|
@ -1248,7 +1250,7 @@ class TestDomainOverview(TestWithDomainPermissions, WebTest):
|
|||
detail_page = self.client.get(reverse("domain", kwargs={"pk": self.domain_on_hold.id}))
|
||||
self.assertNotContains(detail_page, "Edit")
|
||||
|
||||
def test_domain_see_just_nameserver(self):
|
||||
def test_domain_detail_see_just_nameserver(self):
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "justnameserver.com")
|
||||
|
||||
|
@ -1259,7 +1261,7 @@ class TestDomainOverview(TestWithDomainPermissions, WebTest):
|
|||
self.assertContains(detail_page, "ns1.justnameserver.com")
|
||||
self.assertContains(detail_page, "ns2.justnameserver.com")
|
||||
|
||||
def test_domain_see_nameserver_and_ip(self):
|
||||
def test_domain_detail_see_nameserver_and_ip(self):
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "nameserverwithip.gov")
|
||||
|
||||
|
@ -1275,7 +1277,7 @@ class TestDomainOverview(TestWithDomainPermissions, WebTest):
|
|||
self.assertContains(detail_page, "(1.2.3.4,")
|
||||
self.assertContains(detail_page, "2.3.4.5)")
|
||||
|
||||
def test_domain_with_no_information_or_application(self):
|
||||
def test_domain_detail_with_no_information_or_application(self):
|
||||
"""Test that domain management page returns 200 and displays error
|
||||
when no domain information or domain application exist"""
|
||||
# have to use staff user for this test
|
||||
|
@ -1506,6 +1508,30 @@ class TestDomainNameservers(TestDomainOverview):
|
|||
status_code=200,
|
||||
)
|
||||
|
||||
def test_domain_nameservers_form_submit_duplicate_host(self):
|
||||
"""Nameserver form catches error when host is duplicated.
|
||||
|
||||
Uses self.app WebTest because we need to interact with forms.
|
||||
"""
|
||||
# initial nameservers page has one server with two ips
|
||||
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)
|
||||
# attempt to submit the form with duplicate host names of fake.host.com
|
||||
nameservers_page.form["form-0-ip"] = ""
|
||||
nameservers_page.form["form-1-server"] = "fake.host.com"
|
||||
with less_console_noise(): # swallow log warning message
|
||||
result = nameservers_page.form.submit()
|
||||
# form submission was a post with an error, response should be a 200
|
||||
# error text appears twice, once at the top of the page, once around
|
||||
# the required field. remove duplicate entry
|
||||
self.assertContains(
|
||||
result,
|
||||
str(NameserverError(code=NameserverErrorCodes.DUPLICATE_HOST)),
|
||||
count=2,
|
||||
status_code=200,
|
||||
)
|
||||
|
||||
def test_domain_nameservers_form_submit_whitespace(self):
|
||||
"""Nameserver form removes whitespace from ip.
|
||||
|
||||
|
|
|
@ -68,7 +68,8 @@ class NameserverErrorCodes(IntEnum):
|
|||
- 4 TOO_MANY_HOSTS more than the max allowed host values
|
||||
- 5 MISSING_HOST host is missing for a nameserver
|
||||
- 6 INVALID_HOST host is invalid for a nameserver
|
||||
- 7 BAD_DATA bad data input for nameserver
|
||||
- 7 DUPLICATE_HOST host is a duplicate
|
||||
- 8 BAD_DATA bad data input for nameserver
|
||||
"""
|
||||
|
||||
MISSING_IP = 1
|
||||
|
@ -77,7 +78,8 @@ class NameserverErrorCodes(IntEnum):
|
|||
TOO_MANY_HOSTS = 4
|
||||
MISSING_HOST = 5
|
||||
INVALID_HOST = 6
|
||||
BAD_DATA = 7
|
||||
DUPLICATE_HOST = 7
|
||||
BAD_DATA = 8
|
||||
|
||||
|
||||
class NameserverError(Exception):
|
||||
|
@ -93,6 +95,7 @@ class NameserverError(Exception):
|
|||
NameserverErrorCodes.TOO_MANY_HOSTS: ("Too many hosts provided, you may not have more than 13 nameservers."),
|
||||
NameserverErrorCodes.MISSING_HOST: ("Name server must be provided to enter IP address."),
|
||||
NameserverErrorCodes.INVALID_HOST: ("Enter a name server in the required format, like ns1.example.com"),
|
||||
NameserverErrorCodes.DUPLICATE_HOST: ("Remove duplicate entry"),
|
||||
NameserverErrorCodes.BAD_DATA: (
|
||||
"There’s something wrong with the name server information you provided. "
|
||||
"If you need help email us at help@get.gov."
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue