mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-16 09:37:03 +02:00
Merge branch 'main' into za/transfer-transition-incremental
This commit is contained in:
commit
8798ed4c07
45 changed files with 625 additions and 249 deletions
86
docs/developer/generating-emails-guide.md
Normal file
86
docs/developer/generating-emails-guide.md
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
**Below is a step by step guide for generating emails (of each type) in our application. These instructions are UI facing, with the assumption that the user has analyst (or above) permissions.**
|
||||||
|
|
||||||
|
## Domain Invitation
|
||||||
|
- Starting Location: Home page
|
||||||
|
- Workflow: (Domains Table) Manage domain
|
||||||
|
- Workflow Step: Click "Manage" -> Click "Domain managers" -> Click "Add a domain manager" -> (enter email) -> Click "Add user"
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/domain_invitation.txt)
|
||||||
|
|
||||||
|
### Domain Invitation Subject
|
||||||
|
- Notes: Subject line of the "Domain Invitation" email
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/domain_invitation_subject.txt)
|
||||||
|
|
||||||
|
## Domain Request Withdrawn
|
||||||
|
- Starting Location: Home page
|
||||||
|
- Workflow: (Domain requests Table) Manage domain
|
||||||
|
- Workflow Step: Click "Manage" -> Click "Withdraw request" -> (confirmation prompt) -> Click "Withdraw request" (inside prompt)
|
||||||
|
- Notes: You can also do this through Django Admin by switching a domain of status "submitted" to "withdrawn", but you need to be the submitter (email listed on Your Contact Information).
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/domain_request_withdrawn.txt)
|
||||||
|
|
||||||
|
### Domain Request Withdrawn Subject
|
||||||
|
- Notes: Subject line of the "Domain Request Withdrawn" email
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/domain_request_withdrawn_subject.txt)
|
||||||
|
|
||||||
|
## Status Change Action Needed
|
||||||
|
- Starting Location: Django Admin
|
||||||
|
- Workflow: Analyst Admin
|
||||||
|
- Workflow Step: Click "Domain applications" -> Click an application with a status of "in review" or "rejected" -> Click status dropdown -> (select "action needed") -> click "Save"
|
||||||
|
- Notes: Note that this will send an email to the submitter (email listed on Your Contact Information). To test this with your own email, you need to create an application, set the status to either "in review" or "rejected" (and click save), then set the status to "action needed". This will send you an email.
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/status_change_action_needed.txt)
|
||||||
|
|
||||||
|
### Status Change Action Needed Subject
|
||||||
|
- Notes: Subject line of the "Status Change Action Needed" email
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/status_change_action_needed_subject.txt)
|
||||||
|
|
||||||
|
## Status Change in Review
|
||||||
|
- Starting Location: Django Admin
|
||||||
|
- Workflow: Analyst Admin
|
||||||
|
- Workflow Step: Click "Domain applications" -> Click an application with a status of "submitted" -> Click status dropdown -> (select "In review") -> click "Save"
|
||||||
|
- Notes: Note that this will send an email to the submitter (email listed on Your Contact Information). To test this with your own email, you need to create an application, then set the status to "In review". This will send you an email.
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/status_change_approved.txt)
|
||||||
|
|
||||||
|
### Status Change in Review Subject
|
||||||
|
- Notes: This is the subject line of the "Status Change In Review" email
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/status_change_in_review_subject.txt)
|
||||||
|
|
||||||
|
## Status Change Approved
|
||||||
|
- Starting Location: Django Admin
|
||||||
|
- Workflow: Analyst Admin
|
||||||
|
- Workflow Step: Click "Domain applications" -> Click an application in a status of "submitted", "In review", "rejected", or "ineligible" -> Click status dropdown -> (select "approved") -> click "Save"
|
||||||
|
- Notes: Note that this will send an email to the submitter (email listed on Your Contact Information). To test this with your own email, you need to create an application, then set the status to "approved". This will send you an email.
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/status_change_approved.txt)
|
||||||
|
|
||||||
|
### Status Change Approved Subject
|
||||||
|
- Notes: This is the subject line of the "Status Change Approved" email
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/status_change_approved_subject.txt)
|
||||||
|
|
||||||
|
## Status Change Rejected
|
||||||
|
- Starting Location: Django Admin
|
||||||
|
- Workflow: Analyst Admin
|
||||||
|
- Workflow Step: Click "Domain applications" -> Click an application in a status of "In review", or "approved" -> Click status dropdown -> (select "rejected") -> click "Save"
|
||||||
|
- Notes: Note that this will send an email to the submitter (email listed on Your Contact Information). To test this with your own email, you need to create an application, then set the status to "in review" (and click save). Then, go back to the same application and set the status to "rejected". This will send you an email.
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/status_change_rejected.txt)
|
||||||
|
|
||||||
|
### Status Change Rejected Subject
|
||||||
|
- Notes: Subject line of the "Status Change Rejected" email
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/status_change_rejected_subject.txt)
|
||||||
|
|
||||||
|
## Submission Confirmation
|
||||||
|
- Starting Location: Home Page
|
||||||
|
- Workflow: Start domain request
|
||||||
|
- Workflow Step: Click "Start a new domain request" -> (fill out the form) -> On the last step ("Review and submit your domain request "), click "Submit your domain request"
|
||||||
|
- Notes: Note that this will send an email to the submitter (email listed on Your Contact Information)
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/submission_confirmation.txt)
|
||||||
|
|
||||||
|
### Submission Confirmation Subject
|
||||||
|
- Notes: This is the subject line of the "Submission Confirmation Subject" email
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/submission_confirmation_subject.txt)
|
||||||
|
|
||||||
|
## Transition Domain Invitation
|
||||||
|
- Notes: This email is generated during the migration process, meaning that there is no using-facing method to receive this email. This email will be sent out when the following conditions are true: a) The domain exists in the data that Verisign sent us, b) the transition domain script ran successfully, and c) invitations are sent out
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/transition_domain_invitation.txt)
|
||||||
|
|
||||||
|
### Transition Domain Invitation Subject
|
||||||
|
- Notes: This is the subject line of the "Transition Domain Invitation" email
|
||||||
|
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/transition_domain_invitation_subject.txt)
|
||||||
|
|
|
@ -15,7 +15,7 @@ from epplibwrapper import (
|
||||||
commands,
|
commands,
|
||||||
)
|
)
|
||||||
|
|
||||||
API_BASE_PATH = "/api/v1/available/"
|
API_BASE_PATH = "/api/v1/available/?domain="
|
||||||
|
|
||||||
|
|
||||||
class AvailableViewTest(MockEppLib):
|
class AvailableViewTest(MockEppLib):
|
||||||
|
@ -69,8 +69,8 @@ class AvailableViewTest(MockEppLib):
|
||||||
self.assertTrue(check_domain_available("igorville.gov"))
|
self.assertTrue(check_domain_available("igorville.gov"))
|
||||||
# input is lowercased so GSA.GOV should also not be available
|
# input is lowercased so GSA.GOV should also not be available
|
||||||
self.assertFalse(check_domain_available("GSA.gov"))
|
self.assertFalse(check_domain_available("GSA.gov"))
|
||||||
# input is lowercased so IGORVILLE.GOV should also not be available
|
# input is lowercased so IGORVILLE.GOV should also be available
|
||||||
self.assertFalse(check_domain_available("IGORVILLE.gov"))
|
self.assertTrue(check_domain_available("IGORVILLE.gov"))
|
||||||
|
|
||||||
def test_domain_available_dotgov(self):
|
def test_domain_available_dotgov(self):
|
||||||
"""Domain searches work without trailing .gov"""
|
"""Domain searches work without trailing .gov"""
|
||||||
|
|
|
@ -32,7 +32,9 @@ DOMAIN_API_MESSAGES = {
|
||||||
"Read more about choosing your .gov domain.</a>".format(public_site_url("domains/choosing"))
|
"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).",
|
"invalid": "Enter a domain using only letters, numbers, or hyphens (though we don't recommend using hyphens).",
|
||||||
"success": "That domain is available!",
|
"success": "That domain is available! We’ll try to give you the domain you want, \
|
||||||
|
but it's not guaranteed. After you complete this form, we’ll \
|
||||||
|
evaluate whether your request meets our requirements.",
|
||||||
"error": GenericError.get_error_message(GenericErrorCodes.CANNOT_CONTACT_REGISTRY),
|
"error": GenericError.get_error_message(GenericErrorCodes.CANNOT_CONTACT_REGISTRY),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +86,7 @@ def available(request, domain=""):
|
||||||
Response is a JSON dictionary with the key "available" and value true or
|
Response is a JSON dictionary with the key "available" and value true or
|
||||||
false.
|
false.
|
||||||
"""
|
"""
|
||||||
|
domain = request.GET.get("domain", "")
|
||||||
DraftDomain = apps.get_model("registrar.DraftDomain")
|
DraftDomain = apps.get_model("registrar.DraftDomain")
|
||||||
# validate that the given domain could be a domain name and fail early if
|
# validate that the given domain could be a domain name and fail early if
|
||||||
# not.
|
# not.
|
||||||
|
|
|
@ -100,7 +100,9 @@ class Client(oic.Client):
|
||||||
"state": session["state"],
|
"state": session["state"],
|
||||||
"nonce": session["nonce"],
|
"nonce": session["nonce"],
|
||||||
"redirect_uri": self.registration_response["redirect_uris"][0],
|
"redirect_uri": self.registration_response["redirect_uris"][0],
|
||||||
"acr_values": self.behaviour.get("acr_value"),
|
# acr_value may be passed in session if overriding, as in the case
|
||||||
|
# of step up auth, otherwise get from settings.py
|
||||||
|
"acr_values": session.get("acr_value") or self.behaviour.get("acr_value"),
|
||||||
}
|
}
|
||||||
|
|
||||||
if extra_args is not None:
|
if extra_args is not None:
|
||||||
|
@ -162,7 +164,6 @@ class Client(oic.Client):
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
logger.error("Unable to parse response for %s" % state)
|
logger.error("Unable to parse response for %s" % state)
|
||||||
raise o_e.AuthenticationFailed(locator=state)
|
raise o_e.AuthenticationFailed(locator=state)
|
||||||
|
|
||||||
# ErrorResponse is not raised, it is passed back...
|
# ErrorResponse is not raised, it is passed back...
|
||||||
if isinstance(authn_response, ErrorResponse):
|
if isinstance(authn_response, ErrorResponse):
|
||||||
error = authn_response.get("error", "")
|
error = authn_response.get("error", "")
|
||||||
|
@ -207,7 +208,6 @@ class Client(oic.Client):
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
logger.error("Unable to request user info for %s" % state)
|
logger.error("Unable to request user info for %s" % state)
|
||||||
raise o_e.AuthenticationFailed(locator=state)
|
raise o_e.AuthenticationFailed(locator=state)
|
||||||
|
|
||||||
# ErrorResponse is not raised, it is passed back...
|
# ErrorResponse is not raised, it is passed back...
|
||||||
if isinstance(info_response, ErrorResponse):
|
if isinstance(info_response, ErrorResponse):
|
||||||
logger.error("Unable to get user info (%s) for %s" % (info_response.get("error", ""), state))
|
logger.error("Unable to get user info (%s) for %s" % (info_response.get("error", ""), state))
|
||||||
|
@ -272,6 +272,11 @@ class Client(oic.Client):
|
||||||
|
|
||||||
super(Client, self).store_response(resp, info)
|
super(Client, self).store_response(resp, info)
|
||||||
|
|
||||||
|
def get_step_up_acr_value(self):
|
||||||
|
"""returns the step_up_acr_value from settings
|
||||||
|
this helper function is called from djangooidc views"""
|
||||||
|
return self.behaviour.get("step_up_acr_value")
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "Client {} {} {}".format(
|
return "Client {} {} {}".format(
|
||||||
self.client_id,
|
self.client_id,
|
||||||
|
|
30
src/djangooidc/tests/test_oidc.py
Normal file
30
src/djangooidc/tests/test_oidc.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from djangooidc.oidc import Client
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class OidcTest(TestCase):
|
||||||
|
def test_oidc_create_authn_request_with_acr_value(self):
|
||||||
|
"""Test that create_authn_request returns a redirect with an acr_value
|
||||||
|
when an acr_value is passed through session.
|
||||||
|
|
||||||
|
This test is only valid locally. On local, client can be initialized.
|
||||||
|
Client initialization does not work in pipeline, so test is useless in
|
||||||
|
pipeline. However, it will not fail in pipeline."""
|
||||||
|
try:
|
||||||
|
# Initialize provider using pyOICD
|
||||||
|
OP = getattr(settings, "OIDC_ACTIVE_PROVIDER")
|
||||||
|
CLIENT = Client(OP)
|
||||||
|
session = {"acr_value": "some_acr_value_maybe_ial2"}
|
||||||
|
response = CLIENT.create_authn_request(session)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertIn("some_acr_value_maybe_ial2", response.url)
|
||||||
|
except Exception as err:
|
||||||
|
logger.warning(err)
|
||||||
|
logger.warning("Unable to configure OpenID Connect provider in pipeline. Cannot execute this test.")
|
|
@ -1,8 +1,9 @@
|
||||||
from unittest.mock import patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.test import Client, TestCase
|
from django.test import Client, TestCase, RequestFactory
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from ..views import login_callback
|
||||||
|
|
||||||
from .common import less_console_noise
|
from .common import less_console_noise
|
||||||
|
|
||||||
|
@ -11,6 +12,7 @@ from .common import less_console_noise
|
||||||
class ViewsTest(TestCase):
|
class ViewsTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = Client()
|
self.client = Client()
|
||||||
|
self.factory = RequestFactory()
|
||||||
|
|
||||||
def say_hi(*args):
|
def say_hi(*args):
|
||||||
return HttpResponse("Hi")
|
return HttpResponse("Hi")
|
||||||
|
@ -59,19 +61,83 @@ class ViewsTest(TestCase):
|
||||||
# mock
|
# mock
|
||||||
mock_client.callback.side_effect = self.user_info
|
mock_client.callback.side_effect = self.user_info
|
||||||
# test
|
# test
|
||||||
with less_console_noise():
|
with patch("djangooidc.views.requires_step_up_auth", return_value=False), less_console_noise():
|
||||||
response = self.client.get(reverse("openid_login_callback"))
|
response = self.client.get(reverse("openid_login_callback"))
|
||||||
# assert
|
# assert
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response.url, reverse("logout"))
|
self.assertEqual(response.url, reverse("logout"))
|
||||||
|
|
||||||
|
def test_login_callback_no_step_up_auth(self, mock_client):
|
||||||
|
"""Walk through login_callback when requires_step_up_auth returns False
|
||||||
|
and assert that we have a redirect to /"""
|
||||||
|
# setup
|
||||||
|
session = self.client.session
|
||||||
|
session.save()
|
||||||
|
# mock
|
||||||
|
mock_client.callback.side_effect = self.user_info
|
||||||
|
# test
|
||||||
|
with patch("djangooidc.views.requires_step_up_auth", return_value=False), less_console_noise():
|
||||||
|
response = self.client.get(reverse("openid_login_callback"))
|
||||||
|
# assert
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertEqual(response.url, "/")
|
||||||
|
|
||||||
|
def test_requires_step_up_auth(self, mock_client):
|
||||||
|
"""Invoke login_callback passing it a request when requires_step_up_auth returns True
|
||||||
|
and assert that session is updated and create_authn_request (mock) is called."""
|
||||||
|
# Configure the mock to return an expected value for get_step_up_acr_value
|
||||||
|
mock_client.return_value.get_step_up_acr_value.return_value = "step_up_acr_value"
|
||||||
|
|
||||||
|
# Create a mock request
|
||||||
|
request = self.factory.get("/some-url")
|
||||||
|
request.session = {"acr_value": ""}
|
||||||
|
|
||||||
|
# Ensure that the CLIENT instance used in login_callback is the mock
|
||||||
|
# patch requires_step_up_auth to return True
|
||||||
|
with patch("djangooidc.views.requires_step_up_auth", return_value=True), patch(
|
||||||
|
"djangooidc.views.CLIENT.create_authn_request", return_value=MagicMock()
|
||||||
|
) as mock_create_authn_request:
|
||||||
|
login_callback(request)
|
||||||
|
|
||||||
|
# create_authn_request only gets called when requires_step_up_auth is True
|
||||||
|
# and it changes this acr_value in request.session
|
||||||
|
|
||||||
|
# Assert that acr_value is no longer empty string
|
||||||
|
self.assertNotEqual(request.session["acr_value"], "")
|
||||||
|
# And create_authn_request was called again
|
||||||
|
mock_create_authn_request.assert_called_once()
|
||||||
|
|
||||||
|
def test_does_not_requires_step_up_auth(self, mock_client):
|
||||||
|
"""Invoke login_callback passing it a request when requires_step_up_auth returns False
|
||||||
|
and assert that session is not updated and create_authn_request (mock) is not called.
|
||||||
|
|
||||||
|
Possibly redundant with test_login_callback_requires_step_up_auth"""
|
||||||
|
# Create a mock request
|
||||||
|
request = self.factory.get("/some-url")
|
||||||
|
request.session = {"acr_value": ""}
|
||||||
|
|
||||||
|
# Ensure that the CLIENT instance used in login_callback is the mock
|
||||||
|
# patch requires_step_up_auth to return False
|
||||||
|
with patch("djangooidc.views.requires_step_up_auth", return_value=False), patch(
|
||||||
|
"djangooidc.views.CLIENT.create_authn_request", return_value=MagicMock()
|
||||||
|
) as mock_create_authn_request:
|
||||||
|
login_callback(request)
|
||||||
|
|
||||||
|
# create_authn_request only gets called when requires_step_up_auth is True
|
||||||
|
# and it changes this acr_value in request.session
|
||||||
|
|
||||||
|
# Assert that acr_value is NOT updated by testing that it is still an empty string
|
||||||
|
self.assertEqual(request.session["acr_value"], "")
|
||||||
|
# Assert create_authn_request was not called
|
||||||
|
mock_create_authn_request.assert_not_called()
|
||||||
|
|
||||||
@patch("djangooidc.views.authenticate")
|
@patch("djangooidc.views.authenticate")
|
||||||
def test_login_callback_raises(self, mock_auth, mock_client):
|
def test_login_callback_raises(self, mock_auth, mock_client):
|
||||||
# mock
|
# mock
|
||||||
mock_client.callback.side_effect = self.user_info
|
mock_client.callback.side_effect = self.user_info
|
||||||
mock_auth.return_value = None
|
mock_auth.return_value = None
|
||||||
# test
|
# test
|
||||||
with less_console_noise():
|
with patch("djangooidc.views.requires_step_up_auth", return_value=False), less_console_noise():
|
||||||
response = self.client.get(reverse("openid_login_callback"))
|
response = self.client.get(reverse("openid_login_callback"))
|
||||||
# assert
|
# assert
|
||||||
self.assertEqual(response.status_code, 401)
|
self.assertEqual(response.status_code, 401)
|
||||||
|
|
|
@ -11,7 +11,7 @@ from urllib.parse import parse_qs, urlencode
|
||||||
|
|
||||||
from djangooidc.oidc import Client
|
from djangooidc.oidc import Client
|
||||||
from djangooidc import exceptions as o_e
|
from djangooidc import exceptions as o_e
|
||||||
|
from registrar.models import User
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -68,6 +68,12 @@ def login_callback(request):
|
||||||
try:
|
try:
|
||||||
query = parse_qs(request.GET.urlencode())
|
query = parse_qs(request.GET.urlencode())
|
||||||
userinfo = CLIENT.callback(query, request.session)
|
userinfo = CLIENT.callback(query, request.session)
|
||||||
|
# test for need for identity verification and if it is satisfied
|
||||||
|
# if not satisfied, redirect user to login with stepped up acr_value
|
||||||
|
if requires_step_up_auth(userinfo):
|
||||||
|
# add acr_value to request.session
|
||||||
|
request.session["acr_value"] = CLIENT.get_step_up_acr_value()
|
||||||
|
return CLIENT.create_authn_request(request.session)
|
||||||
user = authenticate(request=request, **userinfo)
|
user = authenticate(request=request, **userinfo)
|
||||||
if user:
|
if user:
|
||||||
login(request, user)
|
login(request, user)
|
||||||
|
@ -79,10 +85,27 @@ def login_callback(request):
|
||||||
return error_page(request, err)
|
return error_page(request, err)
|
||||||
|
|
||||||
|
|
||||||
|
def requires_step_up_auth(userinfo):
|
||||||
|
"""if User.needs_identity_verification and step_up_acr_value not in
|
||||||
|
ial returned from callback, return True"""
|
||||||
|
step_up_acr_value = CLIENT.get_step_up_acr_value()
|
||||||
|
acr_value = userinfo.get("ial", "")
|
||||||
|
uuid = userinfo.get("sub", "")
|
||||||
|
email = userinfo.get("email", "")
|
||||||
|
if acr_value != step_up_acr_value:
|
||||||
|
# The acr of this attempt is not at the highest level
|
||||||
|
# so check if the user needs the higher level
|
||||||
|
return User.needs_identity_verification(email, uuid)
|
||||||
|
else:
|
||||||
|
# This attempt already came back at the highest level
|
||||||
|
# so does not require step up
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def logout(request, next_page=None):
|
def logout(request, next_page=None):
|
||||||
"""Redirect the user to the authentication provider (OP) logout page."""
|
"""Redirect the user to the authentication provider (OP) logout page."""
|
||||||
try:
|
try:
|
||||||
username = request.user.username
|
user = request.user
|
||||||
request_args = {
|
request_args = {
|
||||||
"client_id": CLIENT.client_id,
|
"client_id": CLIENT.client_id,
|
||||||
"state": request.session["state"],
|
"state": request.session["state"],
|
||||||
|
@ -94,7 +117,6 @@ def logout(request, next_page=None):
|
||||||
request_args.update(
|
request_args.update(
|
||||||
{"post_logout_redirect_uri": CLIENT.registration_response["post_logout_redirect_uris"][0]}
|
{"post_logout_redirect_uri": CLIENT.registration_response["post_logout_redirect_uris"][0]}
|
||||||
)
|
)
|
||||||
|
|
||||||
url = CLIENT.provider_info["end_session_endpoint"]
|
url = CLIENT.provider_info["end_session_endpoint"]
|
||||||
url += "?" + urlencode(request_args)
|
url += "?" + urlencode(request_args)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
@ -104,7 +126,7 @@ def logout(request, next_page=None):
|
||||||
# Always remove Django session stuff - even if not logged out from OP.
|
# Always remove Django session stuff - even if not logged out from OP.
|
||||||
# Don't wait for the callback as it may never come.
|
# Don't wait for the callback as it may never come.
|
||||||
auth_logout(request)
|
auth_logout(request)
|
||||||
logger.info("Successfully logged out user %s" % username)
|
logger.info("Successfully logged out user %s" % user)
|
||||||
next_page = getattr(settings, "LOGOUT_REDIRECT_URL", None)
|
next_page = getattr(settings, "LOGOUT_REDIRECT_URL", None)
|
||||||
if next_page:
|
if next_page:
|
||||||
request.session["next"] = next_page
|
request.session["next"] = next_page
|
||||||
|
|
|
@ -17,7 +17,7 @@ except ImportError:
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from .cert import Cert, Key
|
from .cert import Cert, Key
|
||||||
from .errors import LoginError, RegistryError
|
from .errors import ErrorCode, LoginError, RegistryError
|
||||||
from .socket import Socket
|
from .socket import Socket
|
||||||
from .utility.pool import EPPConnectionPool
|
from .utility.pool import EPPConnectionPool
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ class EPPLibWrapper:
|
||||||
except TransportError as err:
|
except TransportError as err:
|
||||||
message = f"{cmd_type} failed to execute due to a connection error."
|
message = f"{cmd_type} failed to execute due to a connection error."
|
||||||
logger.error(f"{message} Error: {err}", exc_info=True)
|
logger.error(f"{message} Error: {err}", exc_info=True)
|
||||||
raise RegistryError(message) from err
|
raise RegistryError(message, code=ErrorCode.TRANSPORT_ERROR) from err
|
||||||
except LoginError as err:
|
except LoginError as err:
|
||||||
# For linter due to it not liking this line length
|
# For linter due to it not liking this line length
|
||||||
text = "failed to execute due to a registry login error."
|
text = "failed to execute due to a registry login error."
|
||||||
|
@ -163,7 +163,8 @@ class EPPLibWrapper:
|
||||||
try:
|
try:
|
||||||
return self._send(command)
|
return self._send(command)
|
||||||
except RegistryError as err:
|
except RegistryError as err:
|
||||||
if err.should_retry() and counter < 3:
|
if counter < 3 and (err.should_retry() or err.is_transport_error()):
|
||||||
|
logger.info(f"Retrying transport error. Attempt #{counter+1} of 3.")
|
||||||
counter += 1
|
counter += 1
|
||||||
sleep((counter * 50) / 1000) # sleep 50 ms to 150 ms
|
sleep((counter * 50) / 1000) # sleep 50 ms to 150 ms
|
||||||
else: # don't try again
|
else: # don't try again
|
||||||
|
|
|
@ -4,13 +4,15 @@ from enum import IntEnum
|
||||||
class ErrorCode(IntEnum):
|
class ErrorCode(IntEnum):
|
||||||
"""
|
"""
|
||||||
Overview of registry response codes from RFC 5730. See RFC 5730 for full text.
|
Overview of registry response codes from RFC 5730. See RFC 5730 for full text.
|
||||||
|
- 0 System connection error
|
||||||
- 1000 - 1500 Success
|
- 1000 - 1500 Success
|
||||||
- 2000 - 2308 Registrar did something silly
|
- 2000 - 2308 Registrar did something silly
|
||||||
- 2400 - 2500 Registry did something silly
|
- 2400 - 2500 Registry did something silly
|
||||||
- 2501 - 2502 Something malicious or abusive may have occurred
|
- 2501 - 2502 Something malicious or abusive may have occurred
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
TRANSPORT_ERROR = 0
|
||||||
|
|
||||||
COMMAND_COMPLETED_SUCCESSFULLY = 1000
|
COMMAND_COMPLETED_SUCCESSFULLY = 1000
|
||||||
COMMAND_COMPLETED_SUCCESSFULLY_ACTION_PENDING = 1001
|
COMMAND_COMPLETED_SUCCESSFULLY_ACTION_PENDING = 1001
|
||||||
COMMAND_COMPLETED_SUCCESSFULLY_NO_MESSAGES = 1300
|
COMMAND_COMPLETED_SUCCESSFULLY_NO_MESSAGES = 1300
|
||||||
|
@ -67,6 +69,9 @@ class RegistryError(Exception):
|
||||||
def should_retry(self):
|
def should_retry(self):
|
||||||
return self.code == ErrorCode.COMMAND_FAILED
|
return self.code == ErrorCode.COMMAND_FAILED
|
||||||
|
|
||||||
|
def is_transport_error(self):
|
||||||
|
return self.code == ErrorCode.TRANSPORT_ERROR
|
||||||
|
|
||||||
# connection errors have error code of None and [Errno 99] in the err message
|
# connection errors have error code of None and [Errno 99] in the err message
|
||||||
def is_connection_error(self):
|
def is_connection_error(self):
|
||||||
return self.code is None
|
return self.code is None
|
||||||
|
|
|
@ -527,14 +527,14 @@ class DomainApplicationAdminForm(forms.ModelForm):
|
||||||
current_state = application.status
|
current_state = application.status
|
||||||
|
|
||||||
# first option in status transitions is current state
|
# first option in status transitions is current state
|
||||||
available_transitions = [(current_state, current_state)]
|
available_transitions = [(current_state, application.get_status_display())]
|
||||||
|
|
||||||
transitions = get_available_FIELD_transitions(
|
transitions = get_available_FIELD_transitions(
|
||||||
application, models.DomainApplication._meta.get_field("status")
|
application, models.DomainApplication._meta.get_field("status")
|
||||||
)
|
)
|
||||||
|
|
||||||
for transition in transitions:
|
for transition in transitions:
|
||||||
available_transitions.append((transition.target, transition.target))
|
available_transitions.append((transition.target, transition.target.label))
|
||||||
|
|
||||||
# only set the available transitions if the user is not restricted
|
# only set the available transitions if the user is not restricted
|
||||||
# from editing the domain application; otherwise, the form will be
|
# from editing the domain application; otherwise, the form will be
|
||||||
|
@ -650,10 +650,10 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
||||||
|
|
||||||
if (
|
if (
|
||||||
obj
|
obj
|
||||||
and original_obj.status == models.DomainApplication.APPROVED
|
and original_obj.status == models.DomainApplication.ApplicationStatus.APPROVED
|
||||||
and (
|
and (
|
||||||
obj.status == models.DomainApplication.REJECTED
|
obj.status == models.DomainApplication.ApplicationStatus.REJECTED
|
||||||
or obj.status == models.DomainApplication.INELIGIBLE
|
or obj.status == models.DomainApplication.ApplicationStatus.INELIGIBLE
|
||||||
)
|
)
|
||||||
and not obj.domain_is_not_active()
|
and not obj.domain_is_not_active()
|
||||||
):
|
):
|
||||||
|
@ -675,14 +675,14 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
||||||
else:
|
else:
|
||||||
if obj.status != original_obj.status:
|
if obj.status != original_obj.status:
|
||||||
status_method_mapping = {
|
status_method_mapping = {
|
||||||
models.DomainApplication.STARTED: None,
|
models.DomainApplication.ApplicationStatus.STARTED: None,
|
||||||
models.DomainApplication.SUBMITTED: obj.submit,
|
models.DomainApplication.ApplicationStatus.SUBMITTED: obj.submit,
|
||||||
models.DomainApplication.IN_REVIEW: obj.in_review,
|
models.DomainApplication.ApplicationStatus.IN_REVIEW: obj.in_review,
|
||||||
models.DomainApplication.ACTION_NEEDED: obj.action_needed,
|
models.DomainApplication.ApplicationStatus.ACTION_NEEDED: obj.action_needed,
|
||||||
models.DomainApplication.APPROVED: obj.approve,
|
models.DomainApplication.ApplicationStatus.APPROVED: obj.approve,
|
||||||
models.DomainApplication.WITHDRAWN: obj.withdraw,
|
models.DomainApplication.ApplicationStatus.WITHDRAWN: obj.withdraw,
|
||||||
models.DomainApplication.REJECTED: obj.reject,
|
models.DomainApplication.ApplicationStatus.REJECTED: obj.reject,
|
||||||
models.DomainApplication.INELIGIBLE: (obj.reject_with_prejudice),
|
models.DomainApplication.ApplicationStatus.INELIGIBLE: (obj.reject_with_prejudice),
|
||||||
}
|
}
|
||||||
selected_method = status_method_mapping.get(obj.status)
|
selected_method = status_method_mapping.get(obj.status)
|
||||||
if selected_method is None:
|
if selected_method is None:
|
||||||
|
|
|
@ -142,7 +142,7 @@ function _checkDomainAvailability(el) {
|
||||||
inlineToast(el.parentElement, el.id, ERROR, response.message);
|
inlineToast(el.parentElement, el.id, ERROR, response.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fetchJSON(`available/${el.value}`, callback);
|
fetchJSON(`available/?domain=${el.value}`, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Call the API to see if the domain is good. */
|
/** Call the API to see if the domain is good. */
|
||||||
|
|
|
@ -541,6 +541,7 @@ OIDC_PROVIDERS = {
|
||||||
"scope": ["email", "profile:name", "phone"],
|
"scope": ["email", "profile:name", "phone"],
|
||||||
"user_info_request": ["email", "first_name", "last_name", "phone"],
|
"user_info_request": ["email", "first_name", "last_name", "phone"],
|
||||||
"acr_value": "http://idmanagement.gov/ns/assurance/ial/1",
|
"acr_value": "http://idmanagement.gov/ns/assurance/ial/1",
|
||||||
|
"step_up_acr_value": "http://idmanagement.gov/ns/assurance/ial/2",
|
||||||
},
|
},
|
||||||
"client_registration": {
|
"client_registration": {
|
||||||
"client_id": "cisa_dotgov_registrar",
|
"client_id": "cisa_dotgov_registrar",
|
||||||
|
@ -558,6 +559,7 @@ OIDC_PROVIDERS = {
|
||||||
"scope": ["email", "profile:name", "phone"],
|
"scope": ["email", "profile:name", "phone"],
|
||||||
"user_info_request": ["email", "first_name", "last_name", "phone"],
|
"user_info_request": ["email", "first_name", "last_name", "phone"],
|
||||||
"acr_value": "http://idmanagement.gov/ns/assurance/ial/1",
|
"acr_value": "http://idmanagement.gov/ns/assurance/ial/1",
|
||||||
|
"step_up_acr_value": "http://idmanagement.gov/ns/assurance/ial/2",
|
||||||
},
|
},
|
||||||
"client_registration": {
|
"client_registration": {
|
||||||
"client_id": ("urn:gov:cisa:openidconnect.profiles:sp:sso:cisa:dotgov_registrar"),
|
"client_id": ("urn:gov:cisa:openidconnect.profiles:sp:sso:cisa:dotgov_registrar"),
|
||||||
|
|
|
@ -73,7 +73,7 @@ urlpatterns = [
|
||||||
path("health/", views.health),
|
path("health/", views.health),
|
||||||
path("openid/", include("djangooidc.urls")),
|
path("openid/", include("djangooidc.urls")),
|
||||||
path("register/", include((application_urls, APPLICATION_NAMESPACE))),
|
path("register/", include((application_urls, APPLICATION_NAMESPACE))),
|
||||||
path("api/v1/available/<domain>", available, name="available"),
|
path("api/v1/available/", available, name="available"),
|
||||||
path("api/v1/get-report/current-federal", get_current_federal, name="get-current-federal"),
|
path("api/v1/get-report/current-federal", get_current_federal, name="get-current-federal"),
|
||||||
path("api/v1/get-report/current-full", get_current_full, name="get-current-full"),
|
path("api/v1/get-report/current-full", get_current_full, name="get-current-full"),
|
||||||
path(
|
path(
|
||||||
|
|
|
@ -49,28 +49,28 @@ class DomainApplicationFixture:
|
||||||
# },
|
# },
|
||||||
DA = [
|
DA = [
|
||||||
{
|
{
|
||||||
"status": "started",
|
"status": DomainApplication.ApplicationStatus.STARTED,
|
||||||
"organization_name": "Example - Finished but not Submitted",
|
"organization_name": "Example - Finished but not submitted",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"status": "submitted",
|
"status": DomainApplication.ApplicationStatus.SUBMITTED,
|
||||||
"organization_name": "Example - Submitted but pending Investigation",
|
"organization_name": "Example - Submitted but pending investigation",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"status": "in review",
|
"status": DomainApplication.ApplicationStatus.IN_REVIEW,
|
||||||
"organization_name": "Example - In Investigation",
|
"organization_name": "Example - In investigation",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"status": "in review",
|
"status": DomainApplication.ApplicationStatus.IN_REVIEW,
|
||||||
"organization_name": "Example - Approved",
|
"organization_name": "Example - Approved",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"status": "withdrawn",
|
"status": DomainApplication.ApplicationStatus.WITHDRAWN,
|
||||||
"organization_name": "Example - Withdrawn",
|
"organization_name": "Example - Withdrawn",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"status": "action needed",
|
"status": DomainApplication.ApplicationStatus.ACTION_NEEDED,
|
||||||
"organization_name": "Example - Action Needed",
|
"organization_name": "Example - Action needed",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"status": "rejected",
|
"status": "rejected",
|
||||||
|
@ -214,7 +214,9 @@ class DomainFixture(DomainApplicationFixture):
|
||||||
|
|
||||||
for user in users:
|
for user in users:
|
||||||
# approve one of each users in review status domains
|
# approve one of each users in review status domains
|
||||||
application = DomainApplication.objects.filter(creator=user, status=DomainApplication.IN_REVIEW).last()
|
application = DomainApplication.objects.filter(
|
||||||
|
creator=user, status=DomainApplication.ApplicationStatus.IN_REVIEW
|
||||||
|
).last()
|
||||||
logger.debug(f"Approving {application} for {user}")
|
logger.debug(f"Approving {application} for {user}")
|
||||||
application.approve()
|
application.approve()
|
||||||
application.save()
|
application.save()
|
||||||
|
|
|
@ -262,7 +262,7 @@ class OrganizationContactForm(RegistrarForm):
|
||||||
validators=[
|
validators=[
|
||||||
RegexValidator(
|
RegexValidator(
|
||||||
"^[0-9]{5}(?:-[0-9]{4})?$|^$",
|
"^[0-9]{5}(?:-[0-9]{4})?$|^$",
|
||||||
message="Enter a zip code in the form of 12345 or 12345-6789.",
|
message="Enter a zip code in the required format, like 12345 or 12345-6789.",
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# common.py
|
# common.py
|
||||||
#
|
#
|
||||||
# ALGORITHM_CHOICES are options for alg attribute in DS Data
|
# ALGORITHM_CHOICES are options for alg attribute in DS data
|
||||||
# reference:
|
# reference:
|
||||||
# https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
|
# https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
|
||||||
ALGORITHM_CHOICES = [
|
ALGORITHM_CHOICES = [
|
||||||
|
@ -18,7 +18,7 @@ ALGORITHM_CHOICES = [
|
||||||
(15, "(15) Ed25519"),
|
(15, "(15) Ed25519"),
|
||||||
(16, "(16) Ed448"),
|
(16, "(16) Ed448"),
|
||||||
]
|
]
|
||||||
# DIGEST_TYPE_CHOICES are options for digestType attribute in DS Data
|
# DIGEST_TYPE_CHOICES are options for digestType attribute in DS data
|
||||||
# reference: https://datatracker.ietf.org/doc/html/rfc4034#appendix-A.2
|
# reference: https://datatracker.ietf.org/doc/html/rfc4034#appendix-A.2
|
||||||
DIGEST_TYPE_CHOICES = [
|
DIGEST_TYPE_CHOICES = [
|
||||||
(1, "(1) SHA-1"),
|
(1, "(1) SHA-1"),
|
||||||
|
|
|
@ -239,7 +239,7 @@ class DomainOrgNameAddressForm(forms.ModelForm):
|
||||||
validators=[
|
validators=[
|
||||||
RegexValidator(
|
RegexValidator(
|
||||||
"^[0-9]{5}(?:-[0-9]{4})?$|^$",
|
"^[0-9]{5}(?:-[0-9]{4})?$|^$",
|
||||||
message="Enter a zip code in the form of 12345 or 12345-6789.",
|
message="Enter a zip code in the required format, like 12345 or 12345-6789.",
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -302,7 +302,7 @@ class DomainDnssecForm(forms.Form):
|
||||||
|
|
||||||
|
|
||||||
class DomainDsdataForm(forms.Form):
|
class DomainDsdataForm(forms.Form):
|
||||||
"""Form for adding or editing DNSSEC DS Data to a domain."""
|
"""Form for adding or editing DNSSEC DS data to a domain."""
|
||||||
|
|
||||||
def validate_hexadecimal(value):
|
def validate_hexadecimal(value):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -62,7 +62,7 @@ class Command(BaseCommand):
|
||||||
DomainInvitation(
|
DomainInvitation(
|
||||||
email=email_address.lower(),
|
email=email_address.lower(),
|
||||||
domain=domain,
|
domain=domain,
|
||||||
status=DomainInvitation.INVITED,
|
status=DomainInvitation.DomainInvitationStatus.INVITED,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
logger.info("Creating %d invitations", len(to_create))
|
logger.info("Creating %d invitations", len(to_create))
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
# Generated by Django 4.2.7 on 2023-12-06 16:16
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django_fsm
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("registrar", "0054_alter_domainapplication_federal_agency_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="domain",
|
||||||
|
name="state",
|
||||||
|
field=django_fsm.FSMField(
|
||||||
|
choices=[
|
||||||
|
("unknown", "Unknown"),
|
||||||
|
("dns needed", "Dns needed"),
|
||||||
|
("ready", "Ready"),
|
||||||
|
("on hold", "On hold"),
|
||||||
|
("deleted", "Deleted"),
|
||||||
|
],
|
||||||
|
default="unknown",
|
||||||
|
help_text="Very basic info about the lifecycle of this domain object",
|
||||||
|
max_length=21,
|
||||||
|
protected=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="domainapplication",
|
||||||
|
name="status",
|
||||||
|
field=django_fsm.FSMField(
|
||||||
|
choices=[
|
||||||
|
("started", "Started"),
|
||||||
|
("submitted", "Submitted"),
|
||||||
|
("in review", "In review"),
|
||||||
|
("action needed", "Action needed"),
|
||||||
|
("approved", "Approved"),
|
||||||
|
("withdrawn", "Withdrawn"),
|
||||||
|
("rejected", "Rejected"),
|
||||||
|
("ineligible", "Ineligible"),
|
||||||
|
],
|
||||||
|
default="started",
|
||||||
|
max_length=50,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="domaininvitation",
|
||||||
|
name="status",
|
||||||
|
field=django_fsm.FSMField(
|
||||||
|
choices=[("invited", "Invited"), ("retrieved", "Retrieved")],
|
||||||
|
default="invited",
|
||||||
|
max_length=50,
|
||||||
|
protected=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="transitiondomain",
|
||||||
|
name="status",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True,
|
||||||
|
choices=[("ready", "Ready"), ("on hold", "On hold"), ("unknown", "Unknown")],
|
||||||
|
default="ready",
|
||||||
|
help_text="domain status during the transfer",
|
||||||
|
max_length=255,
|
||||||
|
verbose_name="Status",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -122,20 +122,20 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
"""These capture (some of) the states a domain object can be in."""
|
"""These capture (some of) the states a domain object can be in."""
|
||||||
|
|
||||||
# the state is indeterminate
|
# the state is indeterminate
|
||||||
UNKNOWN = "unknown"
|
UNKNOWN = "unknown", "Unknown"
|
||||||
|
|
||||||
# The domain object exists in the registry
|
# The domain object exists in the registry
|
||||||
# but nameservers don't exist for it yet
|
# but nameservers don't exist for it yet
|
||||||
DNS_NEEDED = "dns needed"
|
DNS_NEEDED = "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
|
||||||
READY = "ready"
|
READY = "ready", "Ready"
|
||||||
|
|
||||||
# Registrar manually changed state to client hold
|
# Registrar manually changed state to client hold
|
||||||
ON_HOLD = "on hold"
|
ON_HOLD = "on hold", "On hold"
|
||||||
|
|
||||||
# previously existed but has been deleted from the registry
|
# previously existed but has been deleted from the registry
|
||||||
DELETED = "deleted"
|
DELETED = "deleted", "Deleted"
|
||||||
|
|
||||||
class Cache(property):
|
class Cache(property):
|
||||||
"""
|
"""
|
||||||
|
@ -174,7 +174,8 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
"""Check if a domain is available."""
|
"""Check if a domain is available."""
|
||||||
if not cls.string_could_be_domain(domain):
|
if not cls.string_could_be_domain(domain):
|
||||||
raise ValueError("Not a valid domain: %s" % str(domain))
|
raise ValueError("Not a valid domain: %s" % str(domain))
|
||||||
req = commands.CheckDomain([domain])
|
domain_name = domain.lower()
|
||||||
|
req = commands.CheckDomain([domain_name])
|
||||||
return registry.send(req, cleaned=True).res_data[0].avail
|
return registry.send(req, cleaned=True).res_data[0].avail
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -19,25 +19,16 @@ class DomainApplication(TimeStampedModel):
|
||||||
|
|
||||||
"""A registrant's application for a new domain."""
|
"""A registrant's application for a new domain."""
|
||||||
|
|
||||||
# #### Constants for choice fields ####
|
# Constants for choice fields
|
||||||
STARTED = "started"
|
class ApplicationStatus(models.TextChoices):
|
||||||
SUBMITTED = "submitted"
|
STARTED = "started", "Started"
|
||||||
IN_REVIEW = "in review"
|
SUBMITTED = "submitted", "Submitted"
|
||||||
ACTION_NEEDED = "action needed"
|
IN_REVIEW = "in review", "In review"
|
||||||
APPROVED = "approved"
|
ACTION_NEEDED = "action needed", "Action needed"
|
||||||
WITHDRAWN = "withdrawn"
|
APPROVED = "approved", "Approved"
|
||||||
REJECTED = "rejected"
|
WITHDRAWN = "withdrawn", "Withdrawn"
|
||||||
INELIGIBLE = "ineligible"
|
REJECTED = "rejected", "Rejected"
|
||||||
STATUS_CHOICES = [
|
INELIGIBLE = "ineligible", "Ineligible"
|
||||||
(STARTED, STARTED),
|
|
||||||
(SUBMITTED, SUBMITTED),
|
|
||||||
(IN_REVIEW, IN_REVIEW),
|
|
||||||
(ACTION_NEEDED, ACTION_NEEDED),
|
|
||||||
(APPROVED, APPROVED),
|
|
||||||
(WITHDRAWN, WITHDRAWN),
|
|
||||||
(REJECTED, REJECTED),
|
|
||||||
(INELIGIBLE, INELIGIBLE),
|
|
||||||
]
|
|
||||||
|
|
||||||
class StateTerritoryChoices(models.TextChoices):
|
class StateTerritoryChoices(models.TextChoices):
|
||||||
ALABAMA = "AL", "Alabama (AL)"
|
ALABAMA = "AL", "Alabama (AL)"
|
||||||
|
@ -363,8 +354,8 @@ class DomainApplication(TimeStampedModel):
|
||||||
|
|
||||||
# #### Internal fields about the application #####
|
# #### Internal fields about the application #####
|
||||||
status = FSMField(
|
status = FSMField(
|
||||||
choices=STATUS_CHOICES, # possible states as an array of constants
|
choices=ApplicationStatus.choices, # possible states as an array of constants
|
||||||
default=STARTED, # sensible default
|
default=ApplicationStatus.STARTED, # sensible default
|
||||||
protected=False, # can change state directly, particularly in Django admin
|
protected=False, # can change state directly, particularly in Django admin
|
||||||
)
|
)
|
||||||
# This is the application user who created this application. The contact
|
# This is the application user who created this application. The contact
|
||||||
|
@ -592,7 +583,11 @@ class DomainApplication(TimeStampedModel):
|
||||||
except EmailSendingError:
|
except EmailSendingError:
|
||||||
logger.warning("Failed to send confirmation email", exc_info=True)
|
logger.warning("Failed to send confirmation email", exc_info=True)
|
||||||
|
|
||||||
@transition(field="status", source=[STARTED, ACTION_NEEDED, WITHDRAWN], target=SUBMITTED)
|
@transition(
|
||||||
|
field="status",
|
||||||
|
source=[ApplicationStatus.STARTED, ApplicationStatus.ACTION_NEEDED, ApplicationStatus.WITHDRAWN],
|
||||||
|
target=ApplicationStatus.SUBMITTED,
|
||||||
|
)
|
||||||
def submit(self):
|
def submit(self):
|
||||||
"""Submit an application that is started.
|
"""Submit an application that is started.
|
||||||
|
|
||||||
|
@ -618,7 +613,7 @@ class DomainApplication(TimeStampedModel):
|
||||||
"emails/submission_confirmation_subject.txt",
|
"emails/submission_confirmation_subject.txt",
|
||||||
)
|
)
|
||||||
|
|
||||||
@transition(field="status", source=SUBMITTED, target=IN_REVIEW)
|
@transition(field="status", source=ApplicationStatus.SUBMITTED, target=ApplicationStatus.IN_REVIEW)
|
||||||
def in_review(self):
|
def in_review(self):
|
||||||
"""Investigate an application that has been submitted.
|
"""Investigate an application that has been submitted.
|
||||||
|
|
||||||
|
@ -630,7 +625,11 @@ class DomainApplication(TimeStampedModel):
|
||||||
"emails/status_change_in_review_subject.txt",
|
"emails/status_change_in_review_subject.txt",
|
||||||
)
|
)
|
||||||
|
|
||||||
@transition(field="status", source=[IN_REVIEW, REJECTED], target=ACTION_NEEDED)
|
@transition(
|
||||||
|
field="status",
|
||||||
|
source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.REJECTED],
|
||||||
|
target=ApplicationStatus.ACTION_NEEDED,
|
||||||
|
)
|
||||||
def action_needed(self):
|
def action_needed(self):
|
||||||
"""Send back an application that is under investigation or rejected.
|
"""Send back an application that is under investigation or rejected.
|
||||||
|
|
||||||
|
@ -644,8 +643,13 @@ class DomainApplication(TimeStampedModel):
|
||||||
|
|
||||||
@transition(
|
@transition(
|
||||||
field="status",
|
field="status",
|
||||||
source=[SUBMITTED, IN_REVIEW, REJECTED, INELIGIBLE],
|
source=[
|
||||||
target=APPROVED,
|
ApplicationStatus.SUBMITTED,
|
||||||
|
ApplicationStatus.IN_REVIEW,
|
||||||
|
ApplicationStatus.REJECTED,
|
||||||
|
ApplicationStatus.INELIGIBLE,
|
||||||
|
],
|
||||||
|
target=ApplicationStatus.APPROVED,
|
||||||
)
|
)
|
||||||
def approve(self):
|
def approve(self):
|
||||||
"""Approve an application that has been submitted.
|
"""Approve an application that has been submitted.
|
||||||
|
@ -678,7 +682,11 @@ class DomainApplication(TimeStampedModel):
|
||||||
"emails/status_change_approved_subject.txt",
|
"emails/status_change_approved_subject.txt",
|
||||||
)
|
)
|
||||||
|
|
||||||
@transition(field="status", source=[SUBMITTED, IN_REVIEW], target=WITHDRAWN)
|
@transition(
|
||||||
|
field="status",
|
||||||
|
source=[ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW],
|
||||||
|
target=ApplicationStatus.WITHDRAWN,
|
||||||
|
)
|
||||||
def withdraw(self):
|
def withdraw(self):
|
||||||
"""Withdraw an application that has been submitted."""
|
"""Withdraw an application that has been submitted."""
|
||||||
self._send_status_update_email(
|
self._send_status_update_email(
|
||||||
|
@ -689,8 +697,8 @@ class DomainApplication(TimeStampedModel):
|
||||||
|
|
||||||
@transition(
|
@transition(
|
||||||
field="status",
|
field="status",
|
||||||
source=[IN_REVIEW, APPROVED],
|
source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.APPROVED],
|
||||||
target=REJECTED,
|
target=ApplicationStatus.REJECTED,
|
||||||
conditions=[domain_is_not_active],
|
conditions=[domain_is_not_active],
|
||||||
)
|
)
|
||||||
def reject(self):
|
def reject(self):
|
||||||
|
@ -698,7 +706,7 @@ class DomainApplication(TimeStampedModel):
|
||||||
|
|
||||||
As side effects this will delete the domain and domain_information
|
As side effects this will delete the domain and domain_information
|
||||||
(will cascade), and send an email notification."""
|
(will cascade), and send an email notification."""
|
||||||
if self.status == self.APPROVED:
|
if self.status == self.ApplicationStatus.APPROVED:
|
||||||
domain_state = self.approved_domain.state
|
domain_state = self.approved_domain.state
|
||||||
# Only reject if it exists on EPP
|
# Only reject if it exists on EPP
|
||||||
if domain_state != Domain.State.UNKNOWN:
|
if domain_state != Domain.State.UNKNOWN:
|
||||||
|
@ -714,8 +722,8 @@ class DomainApplication(TimeStampedModel):
|
||||||
|
|
||||||
@transition(
|
@transition(
|
||||||
field="status",
|
field="status",
|
||||||
source=[IN_REVIEW, APPROVED],
|
source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.APPROVED],
|
||||||
target=INELIGIBLE,
|
target=ApplicationStatus.INELIGIBLE,
|
||||||
conditions=[domain_is_not_active],
|
conditions=[domain_is_not_active],
|
||||||
)
|
)
|
||||||
def reject_with_prejudice(self):
|
def reject_with_prejudice(self):
|
||||||
|
@ -727,7 +735,7 @@ class DomainApplication(TimeStampedModel):
|
||||||
permissions classes test against. This will also delete the domain
|
permissions classes test against. This will also delete the domain
|
||||||
and domain_information (will cascade) when they exist."""
|
and domain_information (will cascade) when they exist."""
|
||||||
|
|
||||||
if self.status == self.APPROVED:
|
if self.status == self.ApplicationStatus.APPROVED:
|
||||||
domain_state = self.approved_domain.state
|
domain_state = self.approved_domain.state
|
||||||
# Only reject if it exists on EPP
|
# Only reject if it exists on EPP
|
||||||
if domain_state != Domain.State.UNKNOWN:
|
if domain_state != Domain.State.UNKNOWN:
|
||||||
|
|
|
@ -15,8 +15,10 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DomainInvitation(TimeStampedModel):
|
class DomainInvitation(TimeStampedModel):
|
||||||
INVITED = "invited"
|
# Constants for status field
|
||||||
RETRIEVED = "retrieved"
|
class DomainInvitationStatus(models.TextChoices):
|
||||||
|
INVITED = "invited", "Invited"
|
||||||
|
RETRIEVED = "retrieved", "Retrieved"
|
||||||
|
|
||||||
email = models.EmailField(
|
email = models.EmailField(
|
||||||
null=False,
|
null=False,
|
||||||
|
@ -31,18 +33,15 @@ class DomainInvitation(TimeStampedModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
status = FSMField(
|
status = FSMField(
|
||||||
choices=[
|
choices=DomainInvitationStatus.choices,
|
||||||
(INVITED, INVITED),
|
default=DomainInvitationStatus.INVITED,
|
||||||
(RETRIEVED, RETRIEVED),
|
|
||||||
],
|
|
||||||
default=INVITED,
|
|
||||||
protected=True, # can't alter state except through transition methods!
|
protected=True, # can't alter state except through transition methods!
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Invitation for {self.email} on {self.domain} is {self.status}"
|
return f"Invitation for {self.email} on {self.domain} is {self.status}"
|
||||||
|
|
||||||
@transition(field="status", source=INVITED, target=RETRIEVED)
|
@transition(field="status", source=DomainInvitationStatus.INVITED, target=DomainInvitationStatus.RETRIEVED)
|
||||||
def retrieve(self):
|
def retrieve(self):
|
||||||
"""When an invitation is retrieved, create the corresponding permission.
|
"""When an invitation is retrieved, create the corresponding permission.
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from .utility.time_stamped_model import TimeStampedModel
|
||||||
|
|
||||||
class StatusChoices(models.TextChoices):
|
class StatusChoices(models.TextChoices):
|
||||||
READY = "ready", "Ready"
|
READY = "ready", "Ready"
|
||||||
ON_HOLD = "on hold", "On Hold"
|
ON_HOLD = "on hold", "On hold"
|
||||||
UNKNOWN = "unknown", "Unknown"
|
UNKNOWN = "unknown", "Unknown"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@ import logging
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
from registrar.models.user_domain_role import UserDomainRole
|
||||||
|
|
||||||
from .domain_invitation import DomainInvitation
|
from .domain_invitation import DomainInvitation
|
||||||
from .transition_domain import TransitionDomain
|
from .transition_domain import TransitionDomain
|
||||||
from .domain import Domain
|
from .domain import Domain
|
||||||
|
@ -64,10 +66,43 @@ class User(AbstractUser):
|
||||||
def is_restricted(self):
|
def is_restricted(self):
|
||||||
return self.status == self.RESTRICTED
|
return self.status == self.RESTRICTED
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def needs_identity_verification(cls, email, uuid):
|
||||||
|
"""A method used by our oidc classes to test whether a user needs email/uuid verification
|
||||||
|
or the full identity PII verification"""
|
||||||
|
|
||||||
|
# An existing user who is a domain manager of a domain (that is,
|
||||||
|
# they have an entry in UserDomainRole for their User)
|
||||||
|
try:
|
||||||
|
existing_user = cls.objects.get(username=uuid)
|
||||||
|
if existing_user and UserDomainRole.objects.filter(user=existing_user).exists():
|
||||||
|
return False
|
||||||
|
except cls.DoesNotExist:
|
||||||
|
# Do nothing when the user is not found, as we're checking for existence.
|
||||||
|
pass
|
||||||
|
except Exception as err:
|
||||||
|
raise err
|
||||||
|
|
||||||
|
# A new incoming user who is a domain manager for one of the domains
|
||||||
|
# that we inputted from Verisign (that is, their email address appears
|
||||||
|
# in the username field of a TransitionDomain)
|
||||||
|
if TransitionDomain.objects.filter(username=email).exists():
|
||||||
|
return False
|
||||||
|
|
||||||
|
# A new incoming user who is being invited to be a domain manager (that is,
|
||||||
|
# their email address is in DomainInvitation for an invitation that is not yet "retrieved").
|
||||||
|
invited = DomainInvitation.DomainInvitationStatus.INVITED
|
||||||
|
if DomainInvitation.objects.filter(email=email, status=invited).exists():
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def check_domain_invitations_on_login(self):
|
def check_domain_invitations_on_login(self):
|
||||||
"""When a user first arrives on the site, we need to retrieve any domain
|
"""When a user first arrives on the site, we need to retrieve any domain
|
||||||
invitations that match their email address."""
|
invitations that match their email address."""
|
||||||
for invitation in DomainInvitation.objects.filter(email=self.email, status=DomainInvitation.INVITED):
|
for invitation in DomainInvitation.objects.filter(
|
||||||
|
email=self.email, status=DomainInvitation.DomainInvitationStatus.INVITED
|
||||||
|
):
|
||||||
try:
|
try:
|
||||||
invitation.retrieve()
|
invitation.retrieve()
|
||||||
invitation.save()
|
invitation.save()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block title %}Thanks for your domain request!{% endblock %}
|
{% block title %}Thanks for your domain request! | {% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main id="main-content" class="grid-container register-form-step">
|
<main id="main-content" class="grid-container register-form-step">
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
Status:
|
Status:
|
||||||
</span>
|
</span>
|
||||||
{% if domainapplication.status == 'approved' %} Approved
|
{% if domainapplication.status == 'approved' %} Approved
|
||||||
{% elif domainapplication.status == 'in review' %} In Review
|
{% elif domainapplication.status == 'in review' %} In review
|
||||||
{% elif domainapplication.status == 'rejected' %} Rejected
|
{% elif domainapplication.status == 'rejected' %} Rejected
|
||||||
{% elif domainapplication.status == 'submitted' %} Submitted
|
{% elif domainapplication.status == 'submitted' %} Submitted
|
||||||
{% elif domainapplication.status == 'ineligible' %} Ineligible
|
{% elif domainapplication.status == 'ineligible' %} Ineligible
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block title %}Withdraw request for {{ domainapplication.requested_domain.name }}{% endblock %}
|
{% block title %}Withdraw request for {{ domainapplication.requested_domain.name }} | {% endblock %}
|
||||||
{% load static url_helpers %}
|
{% load static url_helpers %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
|
@ -149,7 +149,7 @@
|
||||||
{% block logo %}
|
{% block logo %}
|
||||||
<div class="usa-logo display-inline-block" id="extended-logo">
|
<div class="usa-logo display-inline-block" id="extended-logo">
|
||||||
<strong class="usa-logo__text" >
|
<strong class="usa-logo__text" >
|
||||||
<a href="{% url 'home' %}">.gov registrar </a>
|
<a href="{% url 'home' %}">.gov Registrar </a>
|
||||||
</strong>
|
</strong>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% extends "domain_base.html" %}
|
{% extends "domain_base.html" %}
|
||||||
{% load static field_helpers %}
|
{% load static field_helpers %}
|
||||||
|
|
||||||
{% block title %}Add another user{% endblock %}
|
{% block title %}Add another user | {% endblock %}
|
||||||
|
|
||||||
{% block domain_content %}
|
{% block domain_content %}
|
||||||
<h1>Add a domain manager</h1>
|
<h1>Add a domain manager</h1>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
Status:
|
Status:
|
||||||
</span>
|
</span>
|
||||||
{% if domain.state == domain.State.UNKNOWN or domain.state == domain.State.DNS_NEEDED%}
|
{% if domain.state == domain.State.UNKNOWN or domain.state == domain.State.DNS_NEEDED%}
|
||||||
DNS Needed
|
DNS needed
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ domain.state|title }}
|
{{ domain.state|title }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{% extends "domain_base.html" %}
|
{% extends "domain_base.html" %}
|
||||||
{% load static field_helpers url_helpers %}
|
{% load static field_helpers url_helpers %}
|
||||||
|
|
||||||
{% block title %}DS Data | {{ domain.name }} | {% endblock %}
|
{% block title %}DS data | {{ domain.name }} | {% endblock %}
|
||||||
|
|
||||||
{% block domain_content %}
|
{% block domain_content %}
|
||||||
{% if domain.dnssecdata is None %}
|
{% if domain.dnssecdata is None %}
|
||||||
<div class="usa-alert usa-alert--info usa-alert--slim margin-bottom-3">
|
<div class="usa-alert usa-alert--info usa-alert--slim margin-bottom-3">
|
||||||
<div class="usa-alert__body">
|
<div class="usa-alert__body">
|
||||||
You have no DS Data added. Enable DNSSEC by adding DS Data.
|
You have no DS data added. Enable DNSSEC by adding DS data.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -16,11 +16,11 @@
|
||||||
{% include "includes/form_errors.html" with form=form %}
|
{% include "includes/form_errors.html" with form=form %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<h1>DS Data</h1>
|
<h1>DS data</h1>
|
||||||
|
|
||||||
<p>In order to enable DNSSEC, you must first configure it with your DNS hosting service.</p>
|
<p>In order to enable DNSSEC, you must first configure it with your DNS hosting service.</p>
|
||||||
|
|
||||||
<p>Enter the values given by your DNS provider for DS Data.</p>
|
<p>Enter the values given by your DNS provider for DS data.</p>
|
||||||
|
|
||||||
{% include "includes/required_fields.html" %}
|
{% include "includes/required_fields.html" %}
|
||||||
|
|
||||||
|
@ -31,9 +31,9 @@
|
||||||
{% for form in formset %}
|
{% for form in formset %}
|
||||||
<fieldset class="repeatable-form">
|
<fieldset class="repeatable-form">
|
||||||
|
|
||||||
<legend class="sr-only">DS Data record {{forloop.counter}}</legend>
|
<legend class="sr-only">DS data record {{forloop.counter}}</legend>
|
||||||
|
|
||||||
<h2 class="margin-top-0">DS Data record {{forloop.counter}}</h2>
|
<h2 class="margin-top-0">DS data record {{forloop.counter}}</h2>
|
||||||
|
|
||||||
<div class="grid-row grid-gap-2 flex-end">
|
<div class="grid-row grid-gap-2 flex-end">
|
||||||
<div class="tablet:grid-col-4">
|
<div class="tablet:grid-col-4">
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
<a href="{{ url }}"
|
<a href="{{ url }}"
|
||||||
{% if request.path == url %}class="usa-current"{% endif %}
|
{% if request.path == url %}class="usa-current"{% endif %}
|
||||||
>
|
>
|
||||||
DS Data
|
DS data
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block title %} Hello {% endblock %}
|
{% block title %} Home | {% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main id="main-content" class="grid-container">
|
<main id="main-content" class="grid-container">
|
||||||
|
@ -53,7 +53,7 @@
|
||||||
<td data-sort-value="{{ domain.created_time|date:"U" }}" data-label="Date created">{{ domain.created_time|date }}</td>
|
<td data-sort-value="{{ domain.created_time|date:"U" }}" data-label="Date created">{{ domain.created_time|date }}</td>
|
||||||
<td data-label="Status">
|
<td data-label="Status">
|
||||||
{% if domain.state == "unknown" or domain.state == "dns needed"%}
|
{% if domain.state == "unknown" or domain.state == "dns needed"%}
|
||||||
DNS Needed
|
DNS needed
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ domain.state|title }}
|
{{ domain.state|title }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -111,7 +111,7 @@
|
||||||
{{ application.requested_domain.name|default:"New domain request" }}
|
{{ application.requested_domain.name|default:"New domain request" }}
|
||||||
</th>
|
</th>
|
||||||
<td data-sort-value="{{ application.created_at|date:"U" }}" data-label="Date created">{{ application.created_at|date }}</td>
|
<td data-sort-value="{{ application.created_at|date:"U" }}" data-label="Date created">{{ application.created_at|date }}</td>
|
||||||
<td data-label="Status">{{ application.status|title }}</td>
|
<td data-label="Status">{{ application.get_status_display }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if application.status == "started" or application.status == "action needed" or application.status == "withdrawn" %}
|
{% if application.status == "started" or application.status == "action needed" or application.status == "withdrawn" %}
|
||||||
<a href="{% url 'edit-application' application.pk %}">
|
<a href="{% url 'edit-application' application.pk %}">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block title %}
|
{% block title %}
|
||||||
Edit your User Profile
|
Edit your User Profile |
|
||||||
{% endblock title %}
|
{% endblock title %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
|
@ -294,7 +294,7 @@ class AuditedAdminMockData:
|
||||||
self,
|
self,
|
||||||
domain_type,
|
domain_type,
|
||||||
item_name,
|
item_name,
|
||||||
status=DomainApplication.STARTED,
|
status=DomainApplication.ApplicationStatus.STARTED,
|
||||||
org_type="federal",
|
org_type="federal",
|
||||||
federal_type="executive",
|
federal_type="executive",
|
||||||
purpose="Purpose of the site",
|
purpose="Purpose of the site",
|
||||||
|
@ -311,7 +311,7 @@ class AuditedAdminMockData:
|
||||||
title, email, and username.
|
title, email, and username.
|
||||||
|
|
||||||
status (str - optional): Defines the status for DomainApplication,
|
status (str - optional): Defines the status for DomainApplication,
|
||||||
e.g. DomainApplication.STARTED
|
e.g. DomainApplication.ApplicationStatus.STARTED
|
||||||
|
|
||||||
org_type (str - optional): Sets a domains org_type
|
org_type (str - optional): Sets a domains org_type
|
||||||
|
|
||||||
|
@ -344,23 +344,23 @@ class AuditedAdminMockData:
|
||||||
full_arg_dict = dict(
|
full_arg_dict = dict(
|
||||||
email="test_mail@mail.com",
|
email="test_mail@mail.com",
|
||||||
domain=self.dummy_domain(item_name, True),
|
domain=self.dummy_domain(item_name, True),
|
||||||
status=DomainInvitation.INVITED,
|
status=DomainInvitation.DomainInvitationStatus.INVITED,
|
||||||
)
|
)
|
||||||
return full_arg_dict
|
return full_arg_dict
|
||||||
|
|
||||||
def create_full_dummy_domain_application(self, item_name, status=DomainApplication.STARTED):
|
def create_full_dummy_domain_application(self, item_name, status=DomainApplication.ApplicationStatus.STARTED):
|
||||||
"""Creates a dummy domain application object"""
|
"""Creates a dummy domain application object"""
|
||||||
domain_application_kwargs = self.dummy_kwarg_boilerplate(self.APPLICATION, item_name, status)
|
domain_application_kwargs = self.dummy_kwarg_boilerplate(self.APPLICATION, item_name, status)
|
||||||
application = DomainApplication.objects.get_or_create(**domain_application_kwargs)[0]
|
application = DomainApplication.objects.get_or_create(**domain_application_kwargs)[0]
|
||||||
return application
|
return application
|
||||||
|
|
||||||
def create_full_dummy_domain_information(self, item_name, status=DomainApplication.STARTED):
|
def create_full_dummy_domain_information(self, item_name, status=DomainApplication.ApplicationStatus.STARTED):
|
||||||
"""Creates a dummy domain information object"""
|
"""Creates a dummy domain information object"""
|
||||||
domain_application_kwargs = self.dummy_kwarg_boilerplate(self.INFORMATION, item_name, status)
|
domain_application_kwargs = self.dummy_kwarg_boilerplate(self.INFORMATION, item_name, status)
|
||||||
application = DomainInformation.objects.get_or_create(**domain_application_kwargs)[0]
|
application = DomainInformation.objects.get_or_create(**domain_application_kwargs)[0]
|
||||||
return application
|
return application
|
||||||
|
|
||||||
def create_full_dummy_domain_invitation(self, item_name, status=DomainApplication.STARTED):
|
def create_full_dummy_domain_invitation(self, item_name, status=DomainApplication.ApplicationStatus.STARTED):
|
||||||
"""Creates a dummy domain invitation object"""
|
"""Creates a dummy domain invitation object"""
|
||||||
domain_application_kwargs = self.dummy_kwarg_boilerplate(self.INVITATION, item_name, status)
|
domain_application_kwargs = self.dummy_kwarg_boilerplate(self.INVITATION, item_name, status)
|
||||||
application = DomainInvitation.objects.get_or_create(**domain_application_kwargs)[0]
|
application = DomainInvitation.objects.get_or_create(**domain_application_kwargs)[0]
|
||||||
|
@ -374,7 +374,7 @@ class AuditedAdminMockData:
|
||||||
has_other_contacts=True,
|
has_other_contacts=True,
|
||||||
has_current_website=True,
|
has_current_website=True,
|
||||||
has_alternative_gov_domain=True,
|
has_alternative_gov_domain=True,
|
||||||
status=DomainApplication.STARTED,
|
status=DomainApplication.ApplicationStatus.STARTED,
|
||||||
):
|
):
|
||||||
"""A helper to create a dummy domain application object"""
|
"""A helper to create a dummy domain application object"""
|
||||||
application = None
|
application = None
|
||||||
|
@ -455,7 +455,7 @@ def completed_application(
|
||||||
has_alternative_gov_domain=True,
|
has_alternative_gov_domain=True,
|
||||||
has_about_your_organization=True,
|
has_about_your_organization=True,
|
||||||
has_anything_else=True,
|
has_anything_else=True,
|
||||||
status=DomainApplication.STARTED,
|
status=DomainApplication.ApplicationStatus.STARTED,
|
||||||
user=False,
|
user=False,
|
||||||
name="city.gov",
|
name="city.gov",
|
||||||
):
|
):
|
||||||
|
@ -829,10 +829,8 @@ class MockEppLib(TestCase):
|
||||||
def mockCheckDomainCommand(self, _request, cleaned):
|
def mockCheckDomainCommand(self, _request, cleaned):
|
||||||
if "gsa.gov" in getattr(_request, "names", None):
|
if "gsa.gov" in getattr(_request, "names", None):
|
||||||
return self._mockDomainName("gsa.gov", False)
|
return self._mockDomainName("gsa.gov", False)
|
||||||
elif "GSA.gov" in getattr(_request, "names", None):
|
|
||||||
return self._mockDomainName("GSA.gov", False)
|
|
||||||
elif "igorville.gov" in getattr(_request, "names", None):
|
elif "igorville.gov" in getattr(_request, "names", None):
|
||||||
return self._mockDomainName("igorvilleremixed.gov", True)
|
return self._mockDomainName("igorville.gov", True)
|
||||||
elif "top-level-agency.gov" in getattr(_request, "names", None):
|
elif "top-level-agency.gov" in getattr(_request, "names", None):
|
||||||
return self._mockDomainName("top-level-agency.gov", True)
|
return self._mockDomainName("top-level-agency.gov", True)
|
||||||
elif "city.gov" in getattr(_request, "names", None):
|
elif "city.gov" in getattr(_request, "names", None):
|
||||||
|
|
|
@ -61,7 +61,7 @@ class TestDomainAdmin(MockEppLib):
|
||||||
Make sure the short name is displaying in admin on the list page
|
Make sure the short name is displaying in admin on the list page
|
||||||
"""
|
"""
|
||||||
self.client.force_login(self.superuser)
|
self.client.force_login(self.superuser)
|
||||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
application.approve()
|
application.approve()
|
||||||
|
|
||||||
response = self.client.get("/admin/registrar/domain/")
|
response = self.client.get("/admin/registrar/domain/")
|
||||||
|
@ -282,7 +282,7 @@ class TestDomainApplicationAdminForm(TestCase):
|
||||||
form = DomainApplicationAdminForm(instance=self.application)
|
form = DomainApplicationAdminForm(instance=self.application)
|
||||||
|
|
||||||
# Verify that the form choices match the available transitions for started
|
# Verify that the form choices match the available transitions for started
|
||||||
expected_choices = [("started", "started"), ("submitted", "submitted")]
|
expected_choices = [("started", "Started"), ("submitted", "Submitted")]
|
||||||
self.assertEqual(form.fields["status"].widget.choices, expected_choices)
|
self.assertEqual(form.fields["status"].widget.choices, expected_choices)
|
||||||
|
|
||||||
def test_form_choices_when_no_instance(self):
|
def test_form_choices_when_no_instance(self):
|
||||||
|
@ -355,7 +355,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||||
|
|
||||||
# Modify the application's property
|
# Modify the application's property
|
||||||
application.status = DomainApplication.SUBMITTED
|
application.status = DomainApplication.ApplicationStatus.SUBMITTED
|
||||||
|
|
||||||
# Use the model admin's save_model method
|
# Use the model admin's save_model method
|
||||||
self.admin.save_model(request, application, form=None, change=True)
|
self.admin.save_model(request, application, form=None, change=True)
|
||||||
|
@ -390,13 +390,13 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||||
# Create a sample application
|
# Create a sample application
|
||||||
application = completed_application(status=DomainApplication.SUBMITTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED)
|
||||||
|
|
||||||
# Create a mock request
|
# Create a mock request
|
||||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||||
|
|
||||||
# Modify the application's property
|
# Modify the application's property
|
||||||
application.status = DomainApplication.IN_REVIEW
|
application.status = DomainApplication.ApplicationStatus.IN_REVIEW
|
||||||
|
|
||||||
# Use the model admin's save_model method
|
# Use the model admin's save_model method
|
||||||
self.admin.save_model(request, application, form=None, change=True)
|
self.admin.save_model(request, application, form=None, change=True)
|
||||||
|
@ -431,13 +431,13 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||||
# Create a sample application
|
# Create a sample application
|
||||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
|
||||||
# Create a mock request
|
# Create a mock request
|
||||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||||
|
|
||||||
# Modify the application's property
|
# Modify the application's property
|
||||||
application.status = DomainApplication.APPROVED
|
application.status = DomainApplication.ApplicationStatus.APPROVED
|
||||||
|
|
||||||
# Use the model admin's save_model method
|
# Use the model admin's save_model method
|
||||||
self.admin.save_model(request, application, form=None, change=True)
|
self.admin.save_model(request, application, form=None, change=True)
|
||||||
|
@ -467,13 +467,13 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
User.objects.filter(email=EMAIL).delete()
|
User.objects.filter(email=EMAIL).delete()
|
||||||
|
|
||||||
# Create a sample application
|
# Create a sample application
|
||||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
|
||||||
# Create a mock request
|
# Create a mock request
|
||||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||||
|
|
||||||
# Modify the application's property
|
# Modify the application's property
|
||||||
application.status = DomainApplication.APPROVED
|
application.status = DomainApplication.ApplicationStatus.APPROVED
|
||||||
|
|
||||||
# Use the model admin's save_model method
|
# Use the model admin's save_model method
|
||||||
self.admin.save_model(request, application, form=None, change=True)
|
self.admin.save_model(request, application, form=None, change=True)
|
||||||
|
@ -492,13 +492,13 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||||
# Create a sample application
|
# Create a sample application
|
||||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
|
||||||
# Create a mock request
|
# Create a mock request
|
||||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||||
|
|
||||||
# Modify the application's property
|
# Modify the application's property
|
||||||
application.status = DomainApplication.ACTION_NEEDED
|
application.status = DomainApplication.ApplicationStatus.ACTION_NEEDED
|
||||||
|
|
||||||
# Use the model admin's save_model method
|
# Use the model admin's save_model method
|
||||||
self.admin.save_model(request, application, form=None, change=True)
|
self.admin.save_model(request, application, form=None, change=True)
|
||||||
|
@ -533,13 +533,13 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||||
# Create a sample application
|
# Create a sample application
|
||||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
|
||||||
# Create a mock request
|
# Create a mock request
|
||||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||||
|
|
||||||
# Modify the application's property
|
# Modify the application's property
|
||||||
application.status = DomainApplication.REJECTED
|
application.status = DomainApplication.ApplicationStatus.REJECTED
|
||||||
|
|
||||||
# Use the model admin's save_model method
|
# Use the model admin's save_model method
|
||||||
self.admin.save_model(request, application, form=None, change=True)
|
self.admin.save_model(request, application, form=None, change=True)
|
||||||
|
@ -569,13 +569,13 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
User.objects.filter(email=EMAIL).delete()
|
User.objects.filter(email=EMAIL).delete()
|
||||||
|
|
||||||
# Create a sample application
|
# Create a sample application
|
||||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
|
||||||
# Create a mock request
|
# Create a mock request
|
||||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||||
|
|
||||||
# Modify the application's property
|
# Modify the application's property
|
||||||
application.status = DomainApplication.INELIGIBLE
|
application.status = DomainApplication.ApplicationStatus.INELIGIBLE
|
||||||
|
|
||||||
# Use the model admin's save_model method
|
# Use the model admin's save_model method
|
||||||
self.admin.save_model(request, application, form=None, change=True)
|
self.admin.save_model(request, application, form=None, change=True)
|
||||||
|
@ -584,7 +584,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
self.assertEqual(application.creator.status, "restricted")
|
self.assertEqual(application.creator.status, "restricted")
|
||||||
|
|
||||||
def test_readonly_when_restricted_creator(self):
|
def test_readonly_when_restricted_creator(self):
|
||||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
application.creator.status = User.RESTRICTED
|
application.creator.status = User.RESTRICTED
|
||||||
application.creator.save()
|
application.creator.save()
|
||||||
|
|
||||||
|
@ -662,7 +662,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
def test_saving_when_restricted_creator(self):
|
def test_saving_when_restricted_creator(self):
|
||||||
# Create an instance of the model
|
# Create an instance of the model
|
||||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
application.creator.status = User.RESTRICTED
|
application.creator.status = User.RESTRICTED
|
||||||
application.creator.save()
|
application.creator.save()
|
||||||
|
|
||||||
|
@ -681,11 +681,11 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Assert that the status has not changed
|
# Assert that the status has not changed
|
||||||
self.assertEqual(application.status, DomainApplication.IN_REVIEW)
|
self.assertEqual(application.status, DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
|
||||||
def test_change_view_with_restricted_creator(self):
|
def test_change_view_with_restricted_creator(self):
|
||||||
# Create an instance of the model
|
# Create an instance of the model
|
||||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
application.creator.status = User.RESTRICTED
|
application.creator.status = User.RESTRICTED
|
||||||
application.creator.save()
|
application.creator.save()
|
||||||
|
|
||||||
|
@ -704,7 +704,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
def test_error_when_saving_approved_to_rejected_and_domain_is_active(self):
|
def test_error_when_saving_approved_to_rejected_and_domain_is_active(self):
|
||||||
# Create an instance of the model
|
# Create an instance of the model
|
||||||
application = completed_application(status=DomainApplication.APPROVED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||||
domain = Domain.objects.create(name=application.requested_domain.name)
|
domain = Domain.objects.create(name=application.requested_domain.name)
|
||||||
application.approved_domain = domain
|
application.approved_domain = domain
|
||||||
application.save()
|
application.save()
|
||||||
|
@ -724,7 +724,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
stack.enter_context(patch.object(messages, "error"))
|
stack.enter_context(patch.object(messages, "error"))
|
||||||
|
|
||||||
# Simulate saving the model
|
# Simulate saving the model
|
||||||
application.status = DomainApplication.REJECTED
|
application.status = DomainApplication.ApplicationStatus.REJECTED
|
||||||
self.admin.save_model(request, application, None, True)
|
self.admin.save_model(request, application, None, True)
|
||||||
|
|
||||||
# Assert that the error message was called with the correct argument
|
# Assert that the error message was called with the correct argument
|
||||||
|
@ -735,7 +735,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
def test_side_effects_when_saving_approved_to_rejected(self):
|
def test_side_effects_when_saving_approved_to_rejected(self):
|
||||||
# Create an instance of the model
|
# Create an instance of the model
|
||||||
application = completed_application(status=DomainApplication.APPROVED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||||
domain = Domain.objects.create(name=application.requested_domain.name)
|
domain = Domain.objects.create(name=application.requested_domain.name)
|
||||||
domain_information = DomainInformation.objects.create(creator=self.superuser, domain=domain)
|
domain_information = DomainInformation.objects.create(creator=self.superuser, domain=domain)
|
||||||
application.approved_domain = domain
|
application.approved_domain = domain
|
||||||
|
@ -756,7 +756,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
stack.enter_context(patch.object(messages, "error"))
|
stack.enter_context(patch.object(messages, "error"))
|
||||||
|
|
||||||
# Simulate saving the model
|
# Simulate saving the model
|
||||||
application.status = DomainApplication.REJECTED
|
application.status = DomainApplication.ApplicationStatus.REJECTED
|
||||||
self.admin.save_model(request, application, None, True)
|
self.admin.save_model(request, application, None, True)
|
||||||
|
|
||||||
# Assert that the error message was never called
|
# Assert that the error message was never called
|
||||||
|
@ -774,7 +774,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
def test_error_when_saving_approved_to_ineligible_and_domain_is_active(self):
|
def test_error_when_saving_approved_to_ineligible_and_domain_is_active(self):
|
||||||
# Create an instance of the model
|
# Create an instance of the model
|
||||||
application = completed_application(status=DomainApplication.APPROVED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||||
domain = Domain.objects.create(name=application.requested_domain.name)
|
domain = Domain.objects.create(name=application.requested_domain.name)
|
||||||
application.approved_domain = domain
|
application.approved_domain = domain
|
||||||
application.save()
|
application.save()
|
||||||
|
@ -794,7 +794,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
stack.enter_context(patch.object(messages, "error"))
|
stack.enter_context(patch.object(messages, "error"))
|
||||||
|
|
||||||
# Simulate saving the model
|
# Simulate saving the model
|
||||||
application.status = DomainApplication.INELIGIBLE
|
application.status = DomainApplication.ApplicationStatus.INELIGIBLE
|
||||||
self.admin.save_model(request, application, None, True)
|
self.admin.save_model(request, application, None, True)
|
||||||
|
|
||||||
# Assert that the error message was called with the correct argument
|
# Assert that the error message was called with the correct argument
|
||||||
|
@ -805,7 +805,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
def test_side_effects_when_saving_approved_to_ineligible(self):
|
def test_side_effects_when_saving_approved_to_ineligible(self):
|
||||||
# Create an instance of the model
|
# Create an instance of the model
|
||||||
application = completed_application(status=DomainApplication.APPROVED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||||
domain = Domain.objects.create(name=application.requested_domain.name)
|
domain = Domain.objects.create(name=application.requested_domain.name)
|
||||||
domain_information = DomainInformation.objects.create(creator=self.superuser, domain=domain)
|
domain_information = DomainInformation.objects.create(creator=self.superuser, domain=domain)
|
||||||
application.approved_domain = domain
|
application.approved_domain = domain
|
||||||
|
@ -826,7 +826,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
stack.enter_context(patch.object(messages, "error"))
|
stack.enter_context(patch.object(messages, "error"))
|
||||||
|
|
||||||
# Simulate saving the model
|
# Simulate saving the model
|
||||||
application.status = DomainApplication.INELIGIBLE
|
application.status = DomainApplication.ApplicationStatus.INELIGIBLE
|
||||||
self.admin.save_model(request, application, None, True)
|
self.admin.save_model(request, application, None, True)
|
||||||
|
|
||||||
# Assert that the error message was never called
|
# Assert that the error message was never called
|
||||||
|
@ -877,12 +877,14 @@ class DomainInvitationAdminTest(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Assert that the filters are added
|
# Assert that the filters are added
|
||||||
self.assertContains(response, "invited", count=4)
|
self.assertContains(response, "invited", count=2)
|
||||||
self.assertContains(response, "retrieved", count=4)
|
self.assertContains(response, "Invited", count=2)
|
||||||
|
self.assertContains(response, "retrieved", count=2)
|
||||||
|
self.assertContains(response, "Retrieved", count=2)
|
||||||
|
|
||||||
# Check for the HTML context specificially
|
# Check for the HTML context specificially
|
||||||
invited_html = '<a href="?status__exact=invited">invited</a>'
|
invited_html = '<a href="?status__exact=invited">Invited</a>'
|
||||||
retrieved_html = '<a href="?status__exact=retrieved">retrieved</a>'
|
retrieved_html = '<a href="?status__exact=retrieved">Retrieved</a>'
|
||||||
|
|
||||||
self.assertContains(response, invited_html, count=1)
|
self.assertContains(response, invited_html, count=1)
|
||||||
self.assertContains(response, retrieved_html, count=1)
|
self.assertContains(response, retrieved_html, count=1)
|
||||||
|
|
|
@ -30,7 +30,7 @@ class TestFormValidation(MockEppLib):
|
||||||
form = OrganizationContactForm(data={"zipcode": "nah"})
|
form = OrganizationContactForm(data={"zipcode": "nah"})
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
form.errors["zipcode"],
|
form.errors["zipcode"],
|
||||||
["Enter a zip code in the form of 12345 or 12345-6789."],
|
["Enter a zip code in the required format, like 12345 or 12345-6789."],
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_org_contact_zip_valid(self):
|
def test_org_contact_zip_valid(self):
|
||||||
|
@ -74,6 +74,24 @@ class TestFormValidation(MockEppLib):
|
||||||
["Enter the .gov domain you want without any periods."],
|
["Enter the .gov domain you want without any periods."],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_requested_domain_two_dots_invalid(self):
|
||||||
|
"""don't accept domains that are subdomains"""
|
||||||
|
form = DotGovDomainForm(data={"requested_domain": "sub.top-level-agency.gov"})
|
||||||
|
self.assertEqual(
|
||||||
|
form.errors["requested_domain"],
|
||||||
|
["Enter the .gov domain you want without any periods."],
|
||||||
|
)
|
||||||
|
form = DotGovDomainForm(data={"requested_domain": ".top-level-agency.gov"})
|
||||||
|
self.assertEqual(
|
||||||
|
form.errors["requested_domain"],
|
||||||
|
["Enter the .gov domain you want without any periods."],
|
||||||
|
)
|
||||||
|
form = DotGovDomainForm(data={"requested_domain": "..gov"})
|
||||||
|
self.assertEqual(
|
||||||
|
form.errors["requested_domain"],
|
||||||
|
["Enter the .gov domain you want without any periods."],
|
||||||
|
)
|
||||||
|
|
||||||
def test_requested_domain_invalid_characters(self):
|
def test_requested_domain_invalid_characters(self):
|
||||||
"""must be a valid .gov domain name."""
|
"""must be a valid .gov domain name."""
|
||||||
form = DotGovDomainForm(data={"requested_domain": "underscores_forever"})
|
form = DotGovDomainForm(data={"requested_domain": "underscores_forever"})
|
||||||
|
|
|
@ -35,7 +35,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Can create with just a creator."""
|
"""Can create with just a creator."""
|
||||||
user, _ = User.objects.get_or_create()
|
user, _ = User.objects.get_or_create()
|
||||||
application = DomainApplication.objects.create(creator=user)
|
application = DomainApplication.objects.create(creator=user)
|
||||||
self.assertEqual(application.status, DomainApplication.STARTED)
|
self.assertEqual(application.status, DomainApplication.ApplicationStatus.STARTED)
|
||||||
|
|
||||||
def test_full_create(self):
|
def test_full_create(self):
|
||||||
"""Can create with all fields."""
|
"""Can create with all fields."""
|
||||||
|
@ -108,7 +108,7 @@ class TestDomainApplication(TestCase):
|
||||||
# no submitter email so this emits a log warning
|
# no submitter email so this emits a log warning
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
application.submit()
|
application.submit()
|
||||||
self.assertEqual(application.status, application.SUBMITTED)
|
self.assertEqual(application.status, application.ApplicationStatus.SUBMITTED)
|
||||||
|
|
||||||
def test_submit_sends_email(self):
|
def test_submit_sends_email(self):
|
||||||
"""Create an application and submit it and see if email was sent."""
|
"""Create an application and submit it and see if email was sent."""
|
||||||
|
@ -139,7 +139,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status submitted and call submit
|
"""Create an application with status submitted and call submit
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.SUBMITTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.submit()
|
application.submit()
|
||||||
|
@ -148,7 +148,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status in review and call submit
|
"""Create an application with status in review and call submit
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.submit()
|
application.submit()
|
||||||
|
@ -157,7 +157,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status approved and call submit
|
"""Create an application with status approved and call submit
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.APPROVED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.submit()
|
application.submit()
|
||||||
|
@ -166,7 +166,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status rejected and call submit
|
"""Create an application with status rejected and call submit
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.REJECTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.submit()
|
application.submit()
|
||||||
|
@ -175,7 +175,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status ineligible and call submit
|
"""Create an application with status ineligible and call submit
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.INELIGIBLE)
|
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.submit()
|
application.submit()
|
||||||
|
@ -184,7 +184,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status started and call in_review
|
"""Create an application with status started and call in_review
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.STARTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.in_review()
|
application.in_review()
|
||||||
|
@ -193,7 +193,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status in review and call in_review
|
"""Create an application with status in review and call in_review
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.in_review()
|
application.in_review()
|
||||||
|
@ -202,7 +202,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status approved and call in_review
|
"""Create an application with status approved and call in_review
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.APPROVED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.in_review()
|
application.in_review()
|
||||||
|
@ -211,7 +211,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status action needed and call in_review
|
"""Create an application with status action needed and call in_review
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ACTION_NEEDED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.in_review()
|
application.in_review()
|
||||||
|
@ -220,7 +220,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status rejected and call in_review
|
"""Create an application with status rejected and call in_review
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.REJECTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.in_review()
|
application.in_review()
|
||||||
|
@ -229,7 +229,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status withdrawn and call in_review
|
"""Create an application with status withdrawn and call in_review
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.WITHDRAWN)
|
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.in_review()
|
application.in_review()
|
||||||
|
@ -238,7 +238,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status ineligible and call in_review
|
"""Create an application with status ineligible and call in_review
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.INELIGIBLE)
|
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.in_review()
|
application.in_review()
|
||||||
|
@ -247,7 +247,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status started and call action_needed
|
"""Create an application with status started and call action_needed
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.STARTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.action_needed()
|
application.action_needed()
|
||||||
|
@ -256,7 +256,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status submitted and call action_needed
|
"""Create an application with status submitted and call action_needed
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.SUBMITTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.action_needed()
|
application.action_needed()
|
||||||
|
@ -265,7 +265,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status action needed and call action_needed
|
"""Create an application with status action needed and call action_needed
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ACTION_NEEDED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.action_needed()
|
application.action_needed()
|
||||||
|
@ -274,7 +274,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status approved and call action_needed
|
"""Create an application with status approved and call action_needed
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.APPROVED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.action_needed()
|
application.action_needed()
|
||||||
|
@ -283,7 +283,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status withdrawn and call action_needed
|
"""Create an application with status withdrawn and call action_needed
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.WITHDRAWN)
|
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.action_needed()
|
application.action_needed()
|
||||||
|
@ -292,7 +292,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status ineligible and call action_needed
|
"""Create an application with status ineligible and call action_needed
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.INELIGIBLE)
|
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.action_needed()
|
application.action_needed()
|
||||||
|
@ -301,7 +301,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status started and call approve
|
"""Create an application with status started and call approve
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.STARTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.approve()
|
application.approve()
|
||||||
|
@ -310,7 +310,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status approved and call approve
|
"""Create an application with status approved and call approve
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.APPROVED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.approve()
|
application.approve()
|
||||||
|
@ -319,7 +319,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status action needed and call approve
|
"""Create an application with status action needed and call approve
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ACTION_NEEDED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.approve()
|
application.approve()
|
||||||
|
@ -328,7 +328,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status withdrawn and call approve
|
"""Create an application with status withdrawn and call approve
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.WITHDRAWN)
|
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.approve()
|
application.approve()
|
||||||
|
@ -337,7 +337,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status started and call withdraw
|
"""Create an application with status started and call withdraw
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.STARTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.withdraw()
|
application.withdraw()
|
||||||
|
@ -346,7 +346,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status approved and call withdraw
|
"""Create an application with status approved and call withdraw
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.APPROVED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.withdraw()
|
application.withdraw()
|
||||||
|
@ -355,7 +355,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status action needed and call withdraw
|
"""Create an application with status action needed and call withdraw
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ACTION_NEEDED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.withdraw()
|
application.withdraw()
|
||||||
|
@ -364,7 +364,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status rejected and call withdraw
|
"""Create an application with status rejected and call withdraw
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.REJECTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.withdraw()
|
application.withdraw()
|
||||||
|
@ -373,7 +373,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status withdrawn and call withdraw
|
"""Create an application with status withdrawn and call withdraw
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.WITHDRAWN)
|
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.withdraw()
|
application.withdraw()
|
||||||
|
@ -382,7 +382,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status ineligible and call withdraw
|
"""Create an application with status ineligible and call withdraw
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.INELIGIBLE)
|
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.withdraw()
|
application.withdraw()
|
||||||
|
@ -391,7 +391,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status started and call reject
|
"""Create an application with status started and call reject
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.STARTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.reject()
|
application.reject()
|
||||||
|
@ -400,7 +400,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status submitted and call reject
|
"""Create an application with status submitted and call reject
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.SUBMITTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.reject()
|
application.reject()
|
||||||
|
@ -409,7 +409,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status action needed and call reject
|
"""Create an application with status action needed and call reject
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ACTION_NEEDED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.reject()
|
application.reject()
|
||||||
|
@ -418,7 +418,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status withdrawn and call reject
|
"""Create an application with status withdrawn and call reject
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.WITHDRAWN)
|
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.reject()
|
application.reject()
|
||||||
|
@ -427,7 +427,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status rejected and call reject
|
"""Create an application with status rejected and call reject
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.REJECTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.reject()
|
application.reject()
|
||||||
|
@ -436,7 +436,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status ineligible and call reject
|
"""Create an application with status ineligible and call reject
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.INELIGIBLE)
|
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.reject()
|
application.reject()
|
||||||
|
@ -445,7 +445,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status approved, create a matching domain that
|
"""Create an application with status approved, create a matching domain that
|
||||||
is active, and call reject against transition rules"""
|
is active, and call reject against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.APPROVED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||||
domain = Domain.objects.create(name=application.requested_domain.name)
|
domain = Domain.objects.create(name=application.requested_domain.name)
|
||||||
application.approved_domain = domain
|
application.approved_domain = domain
|
||||||
application.save()
|
application.save()
|
||||||
|
@ -464,7 +464,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status started and call reject
|
"""Create an application with status started and call reject
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.STARTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.reject_with_prejudice()
|
application.reject_with_prejudice()
|
||||||
|
@ -473,7 +473,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status submitted and call reject
|
"""Create an application with status submitted and call reject
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.SUBMITTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.reject_with_prejudice()
|
application.reject_with_prejudice()
|
||||||
|
@ -482,7 +482,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status action needed and call reject
|
"""Create an application with status action needed and call reject
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ACTION_NEEDED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.reject_with_prejudice()
|
application.reject_with_prejudice()
|
||||||
|
@ -491,7 +491,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status withdrawn and call reject
|
"""Create an application with status withdrawn and call reject
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.WITHDRAWN)
|
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.reject_with_prejudice()
|
application.reject_with_prejudice()
|
||||||
|
@ -500,7 +500,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status rejected and call reject
|
"""Create an application with status rejected and call reject
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.REJECTED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.reject_with_prejudice()
|
application.reject_with_prejudice()
|
||||||
|
@ -509,7 +509,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status ineligible and call reject
|
"""Create an application with status ineligible and call reject
|
||||||
against transition rules"""
|
against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.INELIGIBLE)
|
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.reject_with_prejudice()
|
application.reject_with_prejudice()
|
||||||
|
@ -518,7 +518,7 @@ class TestDomainApplication(TestCase):
|
||||||
"""Create an application with status approved, create a matching domain that
|
"""Create an application with status approved, create a matching domain that
|
||||||
is active, and call reject_with_prejudice against transition rules"""
|
is active, and call reject_with_prejudice against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.APPROVED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||||
domain = Domain.objects.create(name=application.requested_domain.name)
|
domain = Domain.objects.create(name=application.requested_domain.name)
|
||||||
application.approved_domain = domain
|
application.approved_domain = domain
|
||||||
application.save()
|
application.save()
|
||||||
|
@ -543,7 +543,7 @@ class TestPermissions(TestCase):
|
||||||
user, _ = User.objects.get_or_create()
|
user, _ = User.objects.get_or_create()
|
||||||
application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain)
|
application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain)
|
||||||
# skip using the submit method
|
# skip using the submit method
|
||||||
application.status = DomainApplication.SUBMITTED
|
application.status = DomainApplication.ApplicationStatus.SUBMITTED
|
||||||
application.approve()
|
application.approve()
|
||||||
|
|
||||||
# should be a role for this user
|
# should be a role for this user
|
||||||
|
@ -560,7 +560,7 @@ class TestDomainInfo(TestCase):
|
||||||
user, _ = User.objects.get_or_create()
|
user, _ = User.objects.get_or_create()
|
||||||
application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain)
|
application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain)
|
||||||
# skip using the submit method
|
# skip using the submit method
|
||||||
application.status = DomainApplication.SUBMITTED
|
application.status = DomainApplication.ApplicationStatus.SUBMITTED
|
||||||
application.approve()
|
application.approve()
|
||||||
|
|
||||||
# should be an information present for this domain
|
# should be an information present for this domain
|
||||||
|
@ -597,7 +597,7 @@ class TestInvitations(TestCase):
|
||||||
# this is not an error but does produce a console warning
|
# this is not an error but does produce a console warning
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
self.invitation.retrieve()
|
self.invitation.retrieve()
|
||||||
self.assertEqual(self.invitation.status, DomainInvitation.RETRIEVED)
|
self.assertEqual(self.invitation.status, DomainInvitation.DomainInvitationStatus.RETRIEVED)
|
||||||
|
|
||||||
def test_retrieve_on_each_login(self):
|
def test_retrieve_on_each_login(self):
|
||||||
"""A user's authenticate on_each_login callback retrieves their invitations."""
|
"""A user's authenticate on_each_login callback retrieves their invitations."""
|
||||||
|
@ -606,19 +606,15 @@ class TestInvitations(TestCase):
|
||||||
|
|
||||||
|
|
||||||
class TestUser(TestCase):
|
class TestUser(TestCase):
|
||||||
"""For now, just test actions that
|
"""Test actions that occur on user login,
|
||||||
occur on user login."""
|
test class method that controls how users get validated."""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.email = "mayor@igorville.gov"
|
self.email = "mayor@igorville.gov"
|
||||||
self.domain_name = "igorvilleInTransition.gov"
|
self.domain_name = "igorvilleInTransition.gov"
|
||||||
|
self.domain, _ = Domain.objects.get_or_create(name="igorville.gov")
|
||||||
self.user, _ = User.objects.get_or_create(email=self.email)
|
self.user, _ = User.objects.get_or_create(email=self.email)
|
||||||
|
|
||||||
# clean out the roles each time
|
|
||||||
UserDomainRole.objects.all().delete()
|
|
||||||
|
|
||||||
TransitionDomain.objects.get_or_create(username="mayor@igorville.gov", domain_name=self.domain_name)
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
Domain.objects.all().delete()
|
Domain.objects.all().delete()
|
||||||
|
@ -626,6 +622,7 @@ class TestUser(TestCase):
|
||||||
DomainInformation.objects.all().delete()
|
DomainInformation.objects.all().delete()
|
||||||
TransitionDomain.objects.all().delete()
|
TransitionDomain.objects.all().delete()
|
||||||
User.objects.all().delete()
|
User.objects.all().delete()
|
||||||
|
UserDomainRole.objects.all().delete()
|
||||||
|
|
||||||
def test_check_transition_domains_without_domains_on_login(self):
|
def test_check_transition_domains_without_domains_on_login(self):
|
||||||
"""A user's on_each_login callback does not check transition domains.
|
"""A user's on_each_login callback does not check transition domains.
|
||||||
|
@ -634,3 +631,26 @@ class TestUser(TestCase):
|
||||||
are created."""
|
are created."""
|
||||||
self.user.on_each_login()
|
self.user.on_each_login()
|
||||||
self.assertFalse(Domain.objects.filter(name=self.domain_name).exists())
|
self.assertFalse(Domain.objects.filter(name=self.domain_name).exists())
|
||||||
|
|
||||||
|
def test_identity_verification_with_domain_manager(self):
|
||||||
|
"""A domain manager should return False when tested with class
|
||||||
|
method needs_identity_verification"""
|
||||||
|
UserDomainRole.objects.get_or_create(user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER)
|
||||||
|
self.assertFalse(User.needs_identity_verification(self.user.email, self.user.username))
|
||||||
|
|
||||||
|
def test_identity_verification_with_transition_user(self):
|
||||||
|
"""A user from the Verisign transition should return False
|
||||||
|
when tested with class method needs_identity_verification"""
|
||||||
|
TransitionDomain.objects.get_or_create(username=self.user.email, domain_name=self.domain_name)
|
||||||
|
self.assertFalse(User.needs_identity_verification(self.user.email, self.user.username))
|
||||||
|
|
||||||
|
def test_identity_verification_with_invited_user(self):
|
||||||
|
"""An invited user should return False when tested with class
|
||||||
|
method needs_identity_verification"""
|
||||||
|
DomainInvitation.objects.get_or_create(email=self.user.email, domain=self.domain)
|
||||||
|
self.assertFalse(User.needs_identity_verification(self.user.email, self.user.username))
|
||||||
|
|
||||||
|
def test_identity_verification_with_new_user(self):
|
||||||
|
"""A new user who's neither transitioned nor invited should
|
||||||
|
return True when tested with class method needs_identity_verification"""
|
||||||
|
self.assertTrue(User.needs_identity_verification(self.user.email, self.user.username))
|
||||||
|
|
|
@ -261,7 +261,7 @@ class TestDomainCreation(MockEppLib):
|
||||||
user, _ = User.objects.get_or_create()
|
user, _ = User.objects.get_or_create()
|
||||||
application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain)
|
application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain)
|
||||||
# skip using the submit method
|
# skip using the submit method
|
||||||
application.status = DomainApplication.SUBMITTED
|
application.status = DomainApplication.ApplicationStatus.SUBMITTED
|
||||||
# transition to approve state
|
# transition to approve state
|
||||||
application.approve()
|
application.approve()
|
||||||
# should have information present for this domain
|
# should have information present for this domain
|
||||||
|
@ -1506,7 +1506,7 @@ class TestRegistrantNameservers(MockEppLib):
|
||||||
]
|
]
|
||||||
|
|
||||||
def test_setting_not_allowed(self):
|
def test_setting_not_allowed(self):
|
||||||
"""Scenario: A domain state is not Ready or DNS Needed
|
"""Scenario: A domain state is not Ready or DNS needed
|
||||||
then setting nameservers is not allowed"""
|
then setting nameservers is not allowed"""
|
||||||
domain, _ = Domain.objects.get_or_create(name="onholdDomain.gov", state=Domain.State.ON_HOLD)
|
domain, _ = Domain.objects.get_or_create(name="onholdDomain.gov", state=Domain.State.ON_HOLD)
|
||||||
with self.assertRaises(ActionNotAllowed):
|
with self.assertRaises(ActionNotAllowed):
|
||||||
|
|
|
@ -258,7 +258,6 @@ class ExportDataTest(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
# Dummy push - will remove
|
|
||||||
Domain.objects.all().delete()
|
Domain.objects.all().delete()
|
||||||
DomainInformation.objects.all().delete()
|
DomainInformation.objects.all().delete()
|
||||||
User.objects.all().delete()
|
User.objects.all().delete()
|
||||||
|
|
|
@ -110,7 +110,7 @@ class TestURLAuth(TestCase):
|
||||||
"/openid/callback",
|
"/openid/callback",
|
||||||
"/openid/callback/login/",
|
"/openid/callback/login/",
|
||||||
"/openid/callback/logout/",
|
"/openid/callback/logout/",
|
||||||
"/api/v1/available/whitehouse.gov",
|
"/api/v1/available/",
|
||||||
"/api/v1/get-report/current-federal",
|
"/api/v1/get-report/current-federal",
|
||||||
"/api/v1/get-report/current-full",
|
"/api/v1/get-report/current-full",
|
||||||
]
|
]
|
||||||
|
|
|
@ -100,7 +100,7 @@ class LoggedInTests(TestWithUser):
|
||||||
response = self.client.get("/")
|
response = self.client.get("/")
|
||||||
# count = 2 because it is also in screenreader content
|
# count = 2 because it is also in screenreader content
|
||||||
self.assertContains(response, "igorville.gov", count=2)
|
self.assertContains(response, "igorville.gov", count=2)
|
||||||
self.assertContains(response, "DNS Needed")
|
self.assertContains(response, "DNS needed")
|
||||||
# clean up
|
# clean up
|
||||||
role.delete()
|
role.delete()
|
||||||
|
|
||||||
|
@ -1079,7 +1079,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
||||||
Make sure the long name is displaying in the application summary
|
Make sure the long name is displaying in the application summary
|
||||||
page (manage your application)
|
page (manage your application)
|
||||||
"""
|
"""
|
||||||
completed_application(status=DomainApplication.SUBMITTED, user=self.user)
|
completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED, user=self.user)
|
||||||
home_page = self.app.get("/")
|
home_page = self.app.get("/")
|
||||||
self.assertContains(home_page, "city.gov")
|
self.assertContains(home_page, "city.gov")
|
||||||
# click the "Edit" link
|
# click the "Edit" link
|
||||||
|
@ -1941,19 +1941,19 @@ class TestDomainDNSSEC(TestDomainOverview):
|
||||||
self.assertContains(updated_page, "Enable DNSSEC")
|
self.assertContains(updated_page, "Enable DNSSEC")
|
||||||
|
|
||||||
def test_ds_form_loads_with_no_domain_data(self):
|
def test_ds_form_loads_with_no_domain_data(self):
|
||||||
"""DNSSEC Add DS Data page loads when there is no
|
"""DNSSEC Add DS data page loads when there is no
|
||||||
domain DNSSEC data and shows a button to Add new record"""
|
domain DNSSEC data and shows a button to Add new record"""
|
||||||
|
|
||||||
page = self.client.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dnssec_none.id}))
|
page = self.client.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dnssec_none.id}))
|
||||||
self.assertContains(page, "You have no DS Data added")
|
self.assertContains(page, "You have no DS data added")
|
||||||
self.assertContains(page, "Add new record")
|
self.assertContains(page, "Add new record")
|
||||||
|
|
||||||
def test_ds_form_loads_with_ds_data(self):
|
def test_ds_form_loads_with_ds_data(self):
|
||||||
"""DNSSEC Add DS Data page loads when there is
|
"""DNSSEC Add DS data page loads when there is
|
||||||
domain DNSSEC DS data and shows the data"""
|
domain DNSSEC DS data and shows the data"""
|
||||||
|
|
||||||
page = self.client.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}))
|
page = self.client.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}))
|
||||||
self.assertContains(page, "DS Data record 1")
|
self.assertContains(page, "DS data record 1")
|
||||||
|
|
||||||
def test_ds_data_form_modal(self):
|
def test_ds_data_form_modal(self):
|
||||||
"""When user clicks on save, a modal pops up."""
|
"""When user clicks on save, a modal pops up."""
|
||||||
|
@ -1974,7 +1974,7 @@ class TestDomainDNSSEC(TestDomainOverview):
|
||||||
self.assertContains(response, "Trigger Disable DNSSEC Modal")
|
self.assertContains(response, "Trigger Disable DNSSEC Modal")
|
||||||
|
|
||||||
def test_ds_data_form_submits(self):
|
def test_ds_data_form_submits(self):
|
||||||
"""DS Data form submits successfully
|
"""DS data form submits successfully
|
||||||
|
|
||||||
Uses self.app WebTest because we need to interact with forms.
|
Uses self.app WebTest because we need to interact with forms.
|
||||||
"""
|
"""
|
||||||
|
@ -1991,10 +1991,10 @@ class TestDomainDNSSEC(TestDomainOverview):
|
||||||
)
|
)
|
||||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
page = result.follow()
|
page = result.follow()
|
||||||
self.assertContains(page, "The DS Data records for this domain have been updated.")
|
self.assertContains(page, "The DS data records for this domain have been updated.")
|
||||||
|
|
||||||
def test_ds_data_form_invalid(self):
|
def test_ds_data_form_invalid(self):
|
||||||
"""DS Data form errors with invalid data (missing required fields)
|
"""DS data form errors with invalid data (missing required fields)
|
||||||
|
|
||||||
Uses self.app WebTest because we need to interact with forms.
|
Uses self.app WebTest because we need to interact with forms.
|
||||||
"""
|
"""
|
||||||
|
@ -2017,7 +2017,7 @@ class TestDomainDNSSEC(TestDomainOverview):
|
||||||
self.assertContains(result, "Digest is required", count=2, status_code=200)
|
self.assertContains(result, "Digest is required", count=2, status_code=200)
|
||||||
|
|
||||||
def test_ds_data_form_invalid_keytag(self):
|
def test_ds_data_form_invalid_keytag(self):
|
||||||
"""DS Data form errors with invalid data (key tag too large)
|
"""DS data form errors with invalid data (key tag too large)
|
||||||
|
|
||||||
Uses self.app WebTest because we need to interact with forms.
|
Uses self.app WebTest because we need to interact with forms.
|
||||||
"""
|
"""
|
||||||
|
@ -2040,7 +2040,7 @@ class TestDomainDNSSEC(TestDomainOverview):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_ds_data_form_invalid_digest_chars(self):
|
def test_ds_data_form_invalid_digest_chars(self):
|
||||||
"""DS Data form errors with invalid data (digest contains non hexadecimal chars)
|
"""DS data form errors with invalid data (digest contains non hexadecimal chars)
|
||||||
|
|
||||||
Uses self.app WebTest because we need to interact with forms.
|
Uses self.app WebTest because we need to interact with forms.
|
||||||
"""
|
"""
|
||||||
|
@ -2063,7 +2063,7 @@ class TestDomainDNSSEC(TestDomainOverview):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_ds_data_form_invalid_digest_sha1(self):
|
def test_ds_data_form_invalid_digest_sha1(self):
|
||||||
"""DS Data form errors with invalid data (digest is invalid sha-1)
|
"""DS data form errors with invalid data (digest is invalid sha-1)
|
||||||
|
|
||||||
Uses self.app WebTest because we need to interact with forms.
|
Uses self.app WebTest because we need to interact with forms.
|
||||||
"""
|
"""
|
||||||
|
@ -2086,7 +2086,7 @@ class TestDomainDNSSEC(TestDomainOverview):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_ds_data_form_invalid_digest_sha256(self):
|
def test_ds_data_form_invalid_digest_sha256(self):
|
||||||
"""DS Data form errors with invalid data (digest is invalid sha-256)
|
"""DS data form errors with invalid data (digest is invalid sha-256)
|
||||||
|
|
||||||
Uses self.app WebTest because we need to interact with forms.
|
Uses self.app WebTest because we need to interact with forms.
|
||||||
"""
|
"""
|
||||||
|
@ -2117,7 +2117,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
||||||
|
|
||||||
def test_application_status(self):
|
def test_application_status(self):
|
||||||
"""Checking application status page"""
|
"""Checking application status page"""
|
||||||
application = completed_application(status=DomainApplication.SUBMITTED, user=self.user)
|
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED, user=self.user)
|
||||||
application.save()
|
application.save()
|
||||||
|
|
||||||
home_page = self.app.get("/")
|
home_page = self.app.get("/")
|
||||||
|
@ -2137,7 +2137,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
||||||
self.user.status = "ineligible"
|
self.user.status = "ineligible"
|
||||||
self.user.save()
|
self.user.save()
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.SUBMITTED, user=self.user)
|
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED, user=self.user)
|
||||||
application.save()
|
application.save()
|
||||||
|
|
||||||
home_page = self.app.get("/")
|
home_page = self.app.get("/")
|
||||||
|
@ -2152,7 +2152,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
||||||
|
|
||||||
def test_application_withdraw(self):
|
def test_application_withdraw(self):
|
||||||
"""Checking application status page"""
|
"""Checking application status page"""
|
||||||
application = completed_application(status=DomainApplication.SUBMITTED, user=self.user)
|
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED, user=self.user)
|
||||||
application.save()
|
application.save()
|
||||||
|
|
||||||
home_page = self.app.get("/")
|
home_page = self.app.get("/")
|
||||||
|
@ -2182,7 +2182,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
||||||
|
|
||||||
def test_application_status_no_permissions(self):
|
def test_application_status_no_permissions(self):
|
||||||
"""Can't access applications without being the creator."""
|
"""Can't access applications without being the creator."""
|
||||||
application = completed_application(status=DomainApplication.SUBMITTED, user=self.user)
|
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED, user=self.user)
|
||||||
other_user = User()
|
other_user = User()
|
||||||
other_user.save()
|
other_user.save()
|
||||||
application.creator = other_user
|
application.creator = other_user
|
||||||
|
@ -2202,7 +2202,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
||||||
def test_approved_application_not_in_active_requests(self):
|
def test_approved_application_not_in_active_requests(self):
|
||||||
"""An approved application is not shown in the Active
|
"""An approved application is not shown in the Active
|
||||||
Requests table on home.html."""
|
Requests table on home.html."""
|
||||||
application = completed_application(status=DomainApplication.APPROVED, user=self.user)
|
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED, user=self.user)
|
||||||
application.save()
|
application.save()
|
||||||
|
|
||||||
home_page = self.app.get("/")
|
home_page = self.app.get("/")
|
||||||
|
|
|
@ -293,9 +293,9 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
||||||
return self.pending_applications()
|
return self.pending_applications()
|
||||||
|
|
||||||
def approved_applications_exist(self):
|
def approved_applications_exist(self):
|
||||||
"""Checks if user is creator of applications with APPROVED status"""
|
"""Checks if user is creator of applications with ApplicationStatus.APPROVED status"""
|
||||||
approved_application_count = DomainApplication.objects.filter(
|
approved_application_count = DomainApplication.objects.filter(
|
||||||
creator=self.request.user, status=DomainApplication.APPROVED
|
creator=self.request.user, status=DomainApplication.ApplicationStatus.APPROVED
|
||||||
).count()
|
).count()
|
||||||
return approved_application_count > 0
|
return approved_application_count > 0
|
||||||
|
|
||||||
|
@ -308,11 +308,15 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
||||||
|
|
||||||
def pending_applications(self):
|
def pending_applications(self):
|
||||||
"""Returns a List of user's applications with one of the following states:
|
"""Returns a List of user's applications with one of the following states:
|
||||||
SUBMITTED, IN_REVIEW, ACTION_NEEDED"""
|
ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW, ApplicationStatus.ACTION_NEEDED"""
|
||||||
# if the current application has ACTION_NEEDED status, this check should not be performed
|
# if the current application has ApplicationStatus.ACTION_NEEDED status, this check should not be performed
|
||||||
if self.application.status == DomainApplication.ACTION_NEEDED:
|
if self.application.status == DomainApplication.ApplicationStatus.ACTION_NEEDED:
|
||||||
return []
|
return []
|
||||||
check_statuses = [DomainApplication.SUBMITTED, DomainApplication.IN_REVIEW, DomainApplication.ACTION_NEEDED]
|
check_statuses = [
|
||||||
|
DomainApplication.ApplicationStatus.SUBMITTED,
|
||||||
|
DomainApplication.ApplicationStatus.IN_REVIEW,
|
||||||
|
DomainApplication.ApplicationStatus.ACTION_NEEDED,
|
||||||
|
]
|
||||||
return DomainApplication.objects.filter(creator=self.request.user, status__in=check_statuses)
|
return DomainApplication.objects.filter(creator=self.request.user, status__in=check_statuses)
|
||||||
|
|
||||||
def get_context_data(self):
|
def get_context_data(self):
|
||||||
|
|
|
@ -434,7 +434,7 @@ class DomainDsDataView(DomainFormBaseView):
|
||||||
return initial_data
|
return initial_data
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
"""Redirect to the DS Data page for the domain."""
|
"""Redirect to the DS data page for the domain."""
|
||||||
return reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.object.pk})
|
return reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.object.pk})
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
@ -473,7 +473,7 @@ class DomainDsDataView(DomainFormBaseView):
|
||||||
modal_button = (
|
modal_button = (
|
||||||
'<button type="submit" '
|
'<button type="submit" '
|
||||||
'class="usa-button usa-button--secondary" '
|
'class="usa-button usa-button--secondary" '
|
||||||
'name="disable-override-click">Remove all DS Data</button>'
|
'name="disable-override-click">Remove all DS data</button>'
|
||||||
)
|
)
|
||||||
|
|
||||||
# context to back out of a broken form on all fields delete
|
# context to back out of a broken form on all fields delete
|
||||||
|
@ -523,7 +523,7 @@ class DomainDsDataView(DomainFormBaseView):
|
||||||
logger.error(f"Registry error: {err}")
|
logger.error(f"Registry error: {err}")
|
||||||
return self.form_invalid(formset)
|
return self.form_invalid(formset)
|
||||||
else:
|
else:
|
||||||
messages.success(self.request, "The DS Data records for this domain have been updated.")
|
messages.success(self.request, "The DS data records for this domain have been updated.")
|
||||||
# superclass has the redirect
|
# superclass has the redirect
|
||||||
return super().form_valid(formset)
|
return super().form_valid(formset)
|
||||||
|
|
||||||
|
|
|
@ -101,10 +101,10 @@ class DomainPermission(PermissionsLoginMixin):
|
||||||
|
|
||||||
# Analysts may manage domains, when they are in these statuses:
|
# Analysts may manage domains, when they are in these statuses:
|
||||||
valid_domain_statuses = [
|
valid_domain_statuses = [
|
||||||
DomainApplication.APPROVED,
|
DomainApplication.ApplicationStatus.APPROVED,
|
||||||
DomainApplication.IN_REVIEW,
|
DomainApplication.ApplicationStatus.IN_REVIEW,
|
||||||
DomainApplication.REJECTED,
|
DomainApplication.ApplicationStatus.REJECTED,
|
||||||
DomainApplication.ACTION_NEEDED,
|
DomainApplication.ApplicationStatus.ACTION_NEEDED,
|
||||||
# Edge case - some domains do not have
|
# Edge case - some domains do not have
|
||||||
# a status or DomainInformation... aka a status of 'None'.
|
# a status or DomainInformation... aka a status of 'None'.
|
||||||
# It is necessary to access those to correct errors.
|
# It is necessary to access those to correct errors.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue